/Users/deen/code/yugabyte-db/src/yb/tablet/tablet-schema-test.cc
Line | Count | Source |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | // |
18 | | // The following only applies to changes made to this file as part of YugaByte development. |
19 | | // |
20 | | // Portions Copyright (c) YugaByte, Inc. |
21 | | // |
22 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
23 | | // in compliance with the License. You may obtain a copy of the License at |
24 | | // |
25 | | // http://www.apache.org/licenses/LICENSE-2.0 |
26 | | // |
27 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
28 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
29 | | // or implied. See the License for the specific language governing permissions and limitations |
30 | | // under the License. |
31 | | // |
32 | | |
33 | | #include <algorithm> |
34 | | #include <limits> |
35 | | #include <string> |
36 | | #include <unordered_set> |
37 | | #include <vector> |
38 | | |
39 | | #include <glog/logging.h> |
40 | | #include <gtest/gtest.h> |
41 | | |
42 | | #include "yb/common/partial_row.h" |
43 | | #include "yb/common/ql_protocol_util.h" |
44 | | #include "yb/common/schema.h" |
45 | | |
46 | | #include "yb/docdb/ql_rowwise_iterator_interface.h" |
47 | | |
48 | | #include "yb/gutil/strings/numbers.h" |
49 | | #include "yb/gutil/strings/substitute.h" |
50 | | |
51 | | #include "yb/tablet/local_tablet_writer.h" |
52 | | #include "yb/tablet/tablet-test-util.h" |
53 | | #include "yb/tablet/tablet.h" |
54 | | #include "yb/tablet/tablet_metadata.h" |
55 | | |
56 | | #include "yb/util/env.h" |
57 | | #include "yb/util/status_log.h" |
58 | | #include "yb/util/test_macros.h" |
59 | | #include "yb/util/test_util.h" |
60 | | |
61 | | using strings::Substitute; |
62 | | |
63 | | namespace yb { |
64 | | namespace tablet { |
65 | | |
66 | | class TestTabletSchema : public YBTabletTest { |
67 | | public: |
68 | | TestTabletSchema() |
69 | 3 | : YBTabletTest(CreateBaseSchema(), YQL_TABLE_TYPE) { |
70 | 3 | } |
71 | | |
72 | 1 | void InsertRows(int32_t first_key, int32_t nrows) { |
73 | 11 | for (int32_t i = first_key; i < nrows; ++i) { |
74 | 10 | InsertRow(i); |
75 | 10 | if (i == (nrows / 2)) { |
76 | 1 | ASSERT_OK(tablet()->Flush(tablet::FlushMode::kSync)); |
77 | 1 | } |
78 | 10 | } |
79 | 1 | } |
80 | | |
81 | 13 | void InsertRow(int32_t key) { |
82 | 13 | LocalTabletWriter writer(tablet().get()); |
83 | 13 | QLWriteRequestPB req; |
84 | 13 | QLAddInt32HashValue(&req, key); |
85 | 13 | QLAddInt32ColumnValue(&req, kFirstColumnId + 1, key); |
86 | 13 | ASSERT_OK(writer.Write(&req)); |
87 | 13 | } |
88 | | |
89 | 1 | void DeleteRow(int32_t key) { |
90 | 1 | LocalTabletWriter writer(tablet().get()); |
91 | 1 | QLWriteRequestPB req; |
92 | 1 | req.set_type(QLWriteRequestPB::QL_STMT_DELETE); |
93 | 1 | QLAddInt32HashValue(&req, key); |
94 | 1 | ASSERT_OK(writer.Write(&req)); |
95 | 1 | } |
96 | | |
97 | 2 | void MutateRow(int32_t key, int32_t col_idx, int32_t new_val) { |
98 | 2 | LocalTabletWriter writer(tablet().get()); |
99 | 2 | QLWriteRequestPB req; |
100 | 2 | QLAddInt32HashValue(&req, key); |
101 | 2 | QLAddInt32ColumnValue(&req, kFirstColumnId + col_idx, new_val); |
102 | 2 | ASSERT_OK(writer.Write(&req)); |
103 | 2 | } |
104 | | |
105 | | void VerifyTabletRows(const Schema& projection, |
106 | 4 | const std::vector<std::pair<string, string> >& keys) { |
107 | 4 | typedef std::pair<string, string> StringPair; |
108 | | |
109 | 4 | vector<string> rows; |
110 | 4 | ASSERT_OK(DumpTablet(*tablet(), projection, &rows)); |
111 | 4 | std::sort(rows.begin(), rows.end()); |
112 | 5 | for (const string& row : rows) { |
113 | 5 | bool found = false; |
114 | 6 | for (const StringPair& k : keys) { |
115 | 6 | if (row.find(k.first) != string::npos) { |
116 | 5 | ASSERT_STR_CONTAINS(row, k.second); |
117 | 5 | found = true; |
118 | 5 | break; |
119 | 5 | } |
120 | 6 | } |
121 | 10 | ASSERT_TRUE(found) << "Row: " << row << ", keys: " << yb::ToString(keys); |
122 | 5 | } |
123 | 4 | } |
124 | | |
125 | | private: |
126 | 3 | Schema CreateBaseSchema() { |
127 | 3 | return Schema({ ColumnSchema("key", INT32, false, true), |
128 | 3 | ColumnSchema("c1", INT32) }, 1); |
129 | 3 | } |
130 | | }; |
131 | | |
132 | | // Read from a tablet using a projection schema with columns not present in |
133 | | // the original schema. Verify that the server reject the request. |
134 | 1 | TEST_F(TestTabletSchema, TestRead) { |
135 | 1 | const size_t kNumRows = 10; |
136 | 1 | Schema projection({ ColumnSchema("key", INT32, false, true), |
137 | 1 | ColumnSchema("c2", INT64), |
138 | 1 | ColumnSchema("c3", STRING) }, |
139 | 1 | 1); |
140 | | |
141 | 1 | InsertRows(0, kNumRows); |
142 | | |
143 | 1 | auto iter = tablet()->NewRowIterator(projection); |
144 | 1 | ASSERT_TRUE(!iter.ok() && iter.status().IsInvalidArgument()); |
145 | 1 | ASSERT_STR_CONTAINS(iter.status().message().ToBuffer(), |
146 | 1 | "Some columns are not present in the current schema: c2, c3"); |
147 | 1 | } |
148 | | |
149 | | // Write to the table using a projection schema with a renamed field. |
150 | 1 | TEST_F(TestTabletSchema, TestRenameProjection) { |
151 | 1 | std::vector<std::pair<string, string> > keys; |
152 | | |
153 | | // Insert with the base schema |
154 | 1 | InsertRow(1); |
155 | | |
156 | | // Switch schema to s2 |
157 | 1 | SchemaBuilder builder(*tablet()->metadata()->schema()); |
158 | 1 | ASSERT_OK(builder.RenameColumn("c1", "c1_renamed")); |
159 | 1 | AlterSchema(builder.Build()); |
160 | 1 | Schema s2 = builder.BuildWithoutIds(); |
161 | | |
162 | | // Insert with the s2 schema after AlterSchema(s2) |
163 | 1 | InsertRow(2); |
164 | | |
165 | | // Read and verify using the s2 schema |
166 | 1 | keys.clear(); |
167 | 5 | for (int i = 1; i <= 4; ++i) { |
168 | 4 | keys.push_back(std::pair<string, string>(Substitute("{ int32_value: $0", i), |
169 | 4 | Substitute("int32_value: $0 }", i))); |
170 | 4 | } |
171 | 1 | VerifyTabletRows(s2, keys); |
172 | | |
173 | | // Delete the first two rows |
174 | 1 | DeleteRow(/* key= */ 1); |
175 | | |
176 | | // Alter the remaining row |
177 | 1 | MutateRow(/* key= */ 2, /* col_idx= */ 1, /* new_val= */ 6); |
178 | | |
179 | | // Read and verify using the s2 schema |
180 | 1 | keys.clear(); |
181 | 1 | keys.push_back(std::pair<string, string>("{ int32_value: 2", "int32_value: 6 }")); |
182 | 1 | VerifyTabletRows(s2, keys); |
183 | 1 | } |
184 | | |
185 | | // Verify that removing a column and re-adding it will not result in making old data visible |
186 | 1 | TEST_F(TestTabletSchema, TestDeleteAndReAddColumn) { |
187 | 1 | std::vector<std::pair<string, string> > keys; |
188 | | |
189 | | // Insert and Mutate with the base schema |
190 | 1 | InsertRow(1); |
191 | 1 | MutateRow(/* key= */ 1, /* col_idx= */ 1, /* new_val= */ 2); |
192 | | |
193 | 1 | keys.clear(); |
194 | 1 | keys.push_back(std::pair<string, string>("{ int32_value: 1", "int32_value: 2 }")); |
195 | 1 | VerifyTabletRows(client_schema_, keys); |
196 | | |
197 | | // Switch schema to s2 |
198 | 1 | SchemaBuilder builder(*tablet()->metadata()->schema()); |
199 | 1 | ASSERT_OK(builder.RemoveColumn("c1")); |
200 | | // NOTE this new 'c1' will have a different id from the previous one |
201 | | // so the data added to the previous 'c1' will not be visible. |
202 | 1 | ASSERT_OK(builder.AddNullableColumn("c1", INT32)); |
203 | 1 | AlterSchema(builder.Build()); |
204 | 1 | Schema s2 = builder.BuildWithoutIds(); |
205 | | |
206 | | // Verify that the new 'c1' have the default value |
207 | 1 | keys.clear(); |
208 | 1 | keys.push_back(std::pair<string, string>("{ int32_value: 1", "null }")); |
209 | 1 | VerifyTabletRows(s2, keys); |
210 | 1 | } |
211 | | |
212 | | } // namespace tablet |
213 | | } // namespace yb |