/Users/deen/code/yugabyte-db/src/yb/common/schema.cc
Line | Count | Source (jump to first uncovered line) |
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 "yb/common/schema.h" |
34 | | |
35 | | #include <algorithm> |
36 | | #include <set> |
37 | | |
38 | | #include "yb/common/common.pb.h" |
39 | | #include "yb/common/key_encoder.h" |
40 | | #include "yb/common/ql_type.h" |
41 | | #include "yb/common/row.h" |
42 | | |
43 | | #include "yb/gutil/casts.h" |
44 | | #include "yb/gutil/map-util.h" |
45 | | #include "yb/gutil/stringprintf.h" |
46 | | #include "yb/gutil/strings/join.h" |
47 | | |
48 | | #include "yb/util/malloc.h" |
49 | | #include "yb/util/result.h" |
50 | | #include "yb/util/status_format.h" |
51 | | #include "yb/util/status_log.h" |
52 | | |
53 | | namespace yb { |
54 | | |
55 | | using std::shared_ptr; |
56 | | using std::set; |
57 | | using std::unordered_map; |
58 | | using std::unordered_set; |
59 | | |
60 | | // ------------------------------------------------------------------------------------------------ |
61 | | // ColumnSchema |
62 | | // ------------------------------------------------------------------------------------------------ |
63 | | |
64 | | ColumnSchema::ColumnSchema(std::string name, |
65 | | DataType type, |
66 | | bool is_nullable, |
67 | | bool is_hash_key, |
68 | | bool is_static, |
69 | | bool is_counter, |
70 | | int32_t order, |
71 | | SortingType sorting_type, |
72 | | int32_t pg_type_oid) |
73 | | : ColumnSchema(name, QLType::Create(type), is_nullable, is_hash_key, is_static, is_counter, |
74 | 1.52k | order, sorting_type, pg_type_oid) { |
75 | 1.52k | } Unexecuted instantiation: _ZN2yb12ColumnSchemaC2ENSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS_8DataTypeEbbbbiNS_11SortingTypeEi _ZN2yb12ColumnSchemaC1ENSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS_8DataTypeEbbbbiNS_11SortingTypeEi Line | Count | Source | 74 | 1.52k | order, sorting_type, pg_type_oid) { | 75 | 1.52k | } |
|
76 | | |
77 | 59.4M | const TypeInfo* ColumnSchema::type_info() const { |
78 | 59.4M | return type_->type_info(); |
79 | 59.4M | } |
80 | | |
81 | 752k | bool ColumnSchema::CompTypeInfo(const ColumnSchema &a, const ColumnSchema &b) { |
82 | 752k | return a.type_info()->type() == b.type_info()->type(); |
83 | 752k | } |
84 | | |
85 | 2 | int ColumnSchema::Compare(const void *lhs, const void *rhs) const { |
86 | 2 | return type_info()->Compare(lhs, rhs); |
87 | 2 | } |
88 | | |
89 | | // Stringify the given cell. This just stringifies the cell contents, |
90 | | // and doesn't include the column name or type. |
91 | 0 | std::string ColumnSchema::Stringify(const void *cell) const { |
92 | 0 | std::string ret; |
93 | 0 | type_info()->AppendDebugStringForValue(cell, &ret); |
94 | 0 | return ret; |
95 | 0 | } |
96 | | |
97 | 43 | void ColumnSchema::DoDebugCellAppend(const void* cell, std::string* ret) const { |
98 | 43 | ret->append(type_info()->name()); |
99 | 43 | ret->append(" "); |
100 | 43 | ret->append(name_); |
101 | 43 | ret->append("="); |
102 | 43 | if (is_nullable_ && cell == nullptr) { |
103 | 1 | ret->append("NULL"); |
104 | 42 | } else { |
105 | 42 | type_info()->AppendDebugStringForValue(cell, ret); |
106 | 42 | } |
107 | 43 | } |
108 | | |
109 | | // TODO: include attributes_.ToString() -- need to fix unit tests |
110 | | // first |
111 | 530k | string ColumnSchema::ToString() const { |
112 | 530k | return strings::Substitute("$0[$1]", |
113 | 530k | name_, |
114 | 530k | TypeToString()); |
115 | 530k | } |
116 | | |
117 | 530k | string ColumnSchema::TypeToString() const { |
118 | 530k | return strings::Substitute("$0 $1 $2", |
119 | 530k | type_info()->name(), |
120 | 278k | is_nullable_ ? "NULLABLE" : "NOT NULL", |
121 | 375k | is_hash_key_ ? "PARTITION KEY" : "NOT A PARTITION KEY"); |
122 | 530k | } |
123 | | |
124 | 3 | size_t ColumnSchema::memory_footprint_excluding_this() const { |
125 | | // Rough approximation. |
126 | 3 | return name_.capacity(); |
127 | 3 | } |
128 | | |
129 | 0 | size_t ColumnSchema::memory_footprint_including_this() const { |
130 | 0 | return malloc_usable_size(this) + memory_footprint_excluding_this(); |
131 | 0 | } |
132 | | |
133 | | // ------------------------------------------------------------------------------------------------ |
134 | | // TableProperties |
135 | | // ------------------------------------------------------------------------------------------------ |
136 | | |
137 | 140M | void TableProperties::ToTablePropertiesPB(TablePropertiesPB *pb) const { |
138 | 140M | if (HasDefaultTimeToLive()) { |
139 | 11.4k | pb->set_default_time_to_live(default_time_to_live_); |
140 | 11.4k | } |
141 | 140M | pb->set_contain_counters(contain_counters_); |
142 | 140M | pb->set_is_transactional(is_transactional_); |
143 | 140M | pb->set_consistency_level(consistency_level_); |
144 | 140M | if (HasCopartitionTableId()) { |
145 | 0 | pb->set_copartition_table_id(copartition_table_id_); |
146 | 0 | } |
147 | 140M | pb->set_use_mangled_column_name(use_mangled_column_name_); |
148 | 140M | if (HasNumTablets()) { |
149 | 96.3k | pb->set_num_tablets(num_tablets_); |
150 | 96.3k | } |
151 | 140M | pb->set_is_ysql_catalog_table(is_ysql_catalog_table_); |
152 | 140M | pb->set_retain_delete_markers(retain_delete_markers_); |
153 | 140M | } |
154 | | |
155 | 1.34M | TableProperties TableProperties::FromTablePropertiesPB(const TablePropertiesPB& pb) { |
156 | 1.34M | TableProperties table_properties; |
157 | 1.34M | if (pb.has_default_time_to_live()) { |
158 | 4.11k | table_properties.SetDefaultTimeToLive(pb.default_time_to_live()); |
159 | 4.11k | } |
160 | 1.34M | if (pb.has_contain_counters()) { |
161 | 1.34M | table_properties.SetContainCounters(pb.contain_counters()); |
162 | 1.34M | } |
163 | 1.34M | if (pb.has_is_transactional()) { |
164 | 1.34M | table_properties.SetTransactional(pb.is_transactional()); |
165 | 1.34M | } |
166 | 1.34M | if (pb.has_consistency_level()) { |
167 | 1.34M | table_properties.SetConsistencyLevel(pb.consistency_level()); |
168 | 1.34M | } |
169 | 1.34M | if (pb.has_copartition_table_id()) { |
170 | 0 | table_properties.SetCopartitionTableId(pb.copartition_table_id()); |
171 | 0 | } |
172 | 1.34M | if (pb.has_use_mangled_column_name()) { |
173 | 1.34M | table_properties.SetUseMangledColumnName(pb.use_mangled_column_name()); |
174 | 1.34M | } |
175 | 1.34M | if (pb.has_num_tablets()) { |
176 | 32.9k | table_properties.SetNumTablets(pb.num_tablets()); |
177 | 32.9k | } |
178 | 1.34M | if (pb.has_is_ysql_catalog_table()) { |
179 | 1.34M | table_properties.set_is_ysql_catalog_table(pb.is_ysql_catalog_table()); |
180 | 1.34M | } |
181 | 1.34M | if (pb.has_retain_delete_markers()) { |
182 | 1.34M | table_properties.SetRetainDeleteMarkers(pb.retain_delete_markers()); |
183 | 1.34M | } |
184 | 1.34M | return table_properties; |
185 | 1.34M | } |
186 | | |
187 | 7 | void TableProperties::AlterFromTablePropertiesPB(const TablePropertiesPB& pb) { |
188 | 7 | if (pb.has_default_time_to_live()) { |
189 | 7 | SetDefaultTimeToLive(pb.default_time_to_live()); |
190 | 7 | } |
191 | 7 | if (pb.has_is_transactional()) { |
192 | 7 | SetTransactional(pb.is_transactional()); |
193 | 7 | } |
194 | 7 | if (pb.has_consistency_level()) { |
195 | 7 | SetConsistencyLevel(pb.consistency_level()); |
196 | 7 | } |
197 | 7 | if (pb.has_copartition_table_id()) { |
198 | 0 | SetCopartitionTableId(pb.copartition_table_id()); |
199 | 0 | } |
200 | 7 | if (pb.has_use_mangled_column_name()) { |
201 | 7 | SetUseMangledColumnName(pb.use_mangled_column_name()); |
202 | 7 | } |
203 | 7 | if (pb.has_num_tablets()) { |
204 | 0 | SetNumTablets(pb.num_tablets()); |
205 | 0 | } |
206 | 7 | if (pb.has_is_ysql_catalog_table()) { |
207 | 7 | set_is_ysql_catalog_table(pb.is_ysql_catalog_table()); |
208 | 7 | } |
209 | 7 | if (pb.has_retain_delete_markers()) { |
210 | 7 | SetRetainDeleteMarkers(pb.retain_delete_markers()); |
211 | 7 | } |
212 | 7 | } |
213 | | |
214 | 42.9k | void TableProperties::Reset() { |
215 | 42.9k | default_time_to_live_ = kNoDefaultTtl; |
216 | 42.9k | contain_counters_ = false; |
217 | 42.9k | is_transactional_ = false; |
218 | 42.9k | consistency_level_ = YBConsistencyLevel::STRONG; |
219 | 42.9k | copartition_table_id_ = kNoCopartitionTableId; |
220 | 42.9k | use_mangled_column_name_ = false; |
221 | 42.9k | num_tablets_ = 0; |
222 | 42.9k | is_ysql_catalog_table_ = false; |
223 | 42.9k | retain_delete_markers_ = false; |
224 | 42.9k | } |
225 | | |
226 | 0 | string TableProperties::ToString() const { |
227 | 0 | std::string result("{ "); |
228 | 0 | if (HasDefaultTimeToLive()) { |
229 | 0 | result += Format("default_time_to_live: $0 ", default_time_to_live_); |
230 | 0 | } |
231 | 0 | result += Format("contain_counters: $0 is_transactional: $1 ", |
232 | 0 | contain_counters_, is_transactional_); |
233 | 0 | if (HasCopartitionTableId()) { |
234 | 0 | result += Format("copartition_table_id: $0 ", copartition_table_id_); |
235 | 0 | } |
236 | 0 | return result + Format( |
237 | 0 | "consistency_level: $0 is_ysql_catalog_table: $1 }", |
238 | 0 | consistency_level_, |
239 | 0 | is_ysql_catalog_table_); |
240 | 0 | } |
241 | | |
242 | | // ------------------------------------------------------------------------------------------------ |
243 | | // Schema |
244 | | // ------------------------------------------------------------------------------------------------ |
245 | | |
246 | | Schema::Schema(const Schema& other) |
247 | | : // TODO: C++11 provides a single-arg constructor |
248 | | name_to_index_(10, |
249 | | NameToIndexMap::hasher(), |
250 | | NameToIndexMap::key_equal(), |
251 | 1.72M | NameToIndexMapAllocator(&name_to_index_bytes_)) { |
252 | 1.72M | CopyFrom(other); |
253 | 1.72M | } |
254 | | |
255 | | Schema::Schema(const vector<ColumnSchema>& cols, |
256 | | size_t key_columns, |
257 | | const TableProperties& table_properties, |
258 | | const Uuid& cotable_id, |
259 | | const PgTableOid pgtable_id, |
260 | | const PgSchemaName pgschema_name) |
261 | | : // TODO: C++11 provides a single-arg constructor |
262 | | name_to_index_(10, |
263 | | NameToIndexMap::hasher(), |
264 | | NameToIndexMap::key_equal(), |
265 | 310k | NameToIndexMapAllocator(&name_to_index_bytes_)) { |
266 | 310k | CHECK_OK(Reset(cols, key_columns, table_properties, cotable_id, pgtable_id, pgschema_name)); |
267 | 310k | } |
268 | | |
269 | | Schema::Schema(const vector<ColumnSchema>& cols, |
270 | | const vector<ColumnId>& ids, |
271 | | size_t key_columns, |
272 | | const TableProperties& table_properties, |
273 | | const Uuid& cotable_id, |
274 | | const PgTableOid pgtable_id, |
275 | | const PgSchemaName pgschema_name) |
276 | | : // TODO: C++11 provides a single-arg constructor |
277 | | name_to_index_(10, |
278 | | NameToIndexMap::hasher(), |
279 | | NameToIndexMap::key_equal(), |
280 | 211k | NameToIndexMapAllocator(&name_to_index_bytes_)) { |
281 | 211k | CHECK_OK(Reset(cols, ids, key_columns, table_properties, cotable_id, pgtable_id, pgschema_name)); |
282 | 211k | } |
283 | | |
284 | 57.0k | Schema& Schema::operator=(const Schema& other) { |
285 | 57.0k | if (&other != this) { |
286 | 57.0k | CopyFrom(other); |
287 | 57.0k | } |
288 | 57.0k | return *this; |
289 | 57.0k | } |
290 | | |
291 | 1.77M | void Schema::CopyFrom(const Schema& other) { |
292 | 1.77M | num_key_columns_ = other.num_key_columns_; |
293 | 1.77M | num_hash_key_columns_ = other.num_hash_key_columns_; |
294 | 1.77M | cols_ = other.cols_; |
295 | 1.77M | col_ids_ = other.col_ids_; |
296 | 1.77M | col_offsets_ = other.col_offsets_; |
297 | 1.77M | id_to_index_ = other.id_to_index_; |
298 | | |
299 | | // We can't simply copy name_to_index_ since the GStringPiece keys |
300 | | // reference the other Schema's ColumnSchema objects. |
301 | 1.77M | name_to_index_.clear(); |
302 | 1.77M | int i = 0; |
303 | 11.4M | for (const ColumnSchema &col : cols_) { |
304 | | // The map uses the 'name' string from within the ColumnSchema object. |
305 | 11.4M | name_to_index_[col.name()] = i++; |
306 | 11.4M | } |
307 | | |
308 | 1.77M | has_nullables_ = other.has_nullables_; |
309 | 1.77M | has_statics_ = other.has_statics_; |
310 | 1.77M | table_properties_ = other.table_properties_; |
311 | 1.77M | cotable_id_ = other.cotable_id_; |
312 | 1.77M | pgtable_id_ = other.pgtable_id_; |
313 | 1.77M | pgschema_name_ = other.pgschema_name_; |
314 | | |
315 | | // Schema cannot have both, cotable ID and pgtable ID. |
316 | 1.77M | DCHECK(cotable_id_.IsNil() || pgtable_id_ == 0); |
317 | 1.77M | } |
318 | | |
319 | 2 | void Schema::swap(Schema& other) { |
320 | 2 | std::swap(num_key_columns_, other.num_key_columns_); |
321 | 2 | std::swap(num_hash_key_columns_, other.num_hash_key_columns_); |
322 | 2 | cols_.swap(other.cols_); |
323 | 2 | col_ids_.swap(other.col_ids_); |
324 | 2 | col_offsets_.swap(other.col_offsets_); |
325 | 2 | name_to_index_.swap(other.name_to_index_); |
326 | 2 | id_to_index_.swap(other.id_to_index_); |
327 | 2 | std::swap(has_nullables_, other.has_nullables_); |
328 | 2 | std::swap(has_statics_, other.has_statics_); |
329 | 2 | std::swap(table_properties_, other.table_properties_); |
330 | 2 | std::swap(cotable_id_, other.cotable_id_); |
331 | 2 | std::swap(pgtable_id_, other.pgtable_id_); |
332 | 2 | std::swap(pgschema_name_, other.pgschema_name_); |
333 | | |
334 | | // Schema cannot have both, cotable ID or pgtable ID. |
335 | 2 | DCHECK(cotable_id_.IsNil() || pgtable_id_ == 0); |
336 | 2 | } |
337 | | |
338 | 17.4M | void Schema::ResetColumnIds(const vector<ColumnId>& ids) { |
339 | | // Initialize IDs mapping. |
340 | 17.4M | col_ids_ = ids; |
341 | 17.4M | id_to_index_.clear(); |
342 | 17.4M | max_col_id_ = 0; |
343 | 54.5M | for (size_t i = 0; i < ids.size(); ++i) { |
344 | 37.1M | if (ids[i] > max_col_id_) { |
345 | 35.6M | max_col_id_ = ids[i]; |
346 | 35.6M | } |
347 | 37.1M | id_to_index_.set(ids[i], narrow_cast<int>(i)); |
348 | 37.1M | } |
349 | 17.4M | } |
350 | | |
351 | | Status Schema::Reset(const vector<ColumnSchema>& cols, size_t key_columns, |
352 | | const TableProperties& table_properties, |
353 | | const Uuid& cotable_id, |
354 | | const PgTableOid pgtable_id, |
355 | 312k | const PgSchemaName pgschema_name) { |
356 | 312k | return Reset(cols, {}, key_columns, table_properties, cotable_id, pgtable_id, pgschema_name); |
357 | 312k | } |
358 | | |
359 | | Status Schema::Reset(const vector<ColumnSchema>& cols, |
360 | | const vector<ColumnId>& ids, |
361 | | size_t key_columns, |
362 | | const TableProperties& table_properties, |
363 | | const Uuid& cotable_id, |
364 | | const PgTableOid pgtable_id, |
365 | 17.3M | const PgSchemaName pgschema_name) { |
366 | 17.3M | cols_ = cols; |
367 | 17.3M | num_key_columns_ = key_columns; |
368 | 17.3M | num_hash_key_columns_ = 0; |
369 | 17.3M | table_properties_ = table_properties; |
370 | 17.3M | cotable_id_ = cotable_id; |
371 | 17.3M | pgtable_id_ = pgtable_id; |
372 | 17.3M | pgschema_name_ = pgschema_name; |
373 | | |
374 | | // Determine whether any column is nullable or static, and count number of hash columns. |
375 | 17.3M | has_nullables_ = false; |
376 | 17.3M | has_statics_ = false; |
377 | 38.5M | for (const ColumnSchema& col : cols_) { |
378 | 38.5M | if (col.is_hash_key() && num_hash_key_columns_ < key_columns) { |
379 | 1.03M | num_hash_key_columns_++; |
380 | 1.03M | } |
381 | 38.5M | if (col.is_nullable()) { |
382 | 18.8M | has_nullables_ = true; |
383 | 18.8M | } |
384 | 38.5M | if (col.is_static()) { |
385 | 2.72k | has_statics_ = true; |
386 | 2.72k | } |
387 | 38.5M | } |
388 | | |
389 | 17.3M | if (PREDICT_FALSE(key_columns > cols_.size())) { |
390 | 0 | return STATUS(InvalidArgument, |
391 | 0 | "Bad schema", "More key columns than columns"); |
392 | 0 | } |
393 | | |
394 | 17.3M | if (PREDICT_FALSE(!ids.empty() && ids.size() != cols_.size())) { |
395 | 0 | return STATUS(InvalidArgument, "Bad schema", |
396 | 0 | "The number of ids does not match with the number of columns"); |
397 | 0 | } |
398 | | |
399 | 17.3M | if (PREDICT_FALSE(!cotable_id.IsNil() && pgtable_id > 0)) { |
400 | 0 | return STATUS(InvalidArgument, |
401 | 0 | "Bad schema", "Cannot have both cotable ID and pgtable ID"); |
402 | 0 | } |
403 | | |
404 | | // Verify that the key columns are not nullable nor static |
405 | 20.7M | for (size_t i = 0; i < key_columns; ++i) { |
406 | 3.43M | if (PREDICT_FALSE(cols_[i].is_nullable())) { |
407 | 0 | return STATUS(InvalidArgument, |
408 | 0 | "Bad schema", strings::Substitute("Nullable key columns are not " |
409 | 0 | "supported: $0", cols_[i].name())); |
410 | 0 | } |
411 | 3.43M | if (PREDICT_FALSE(cols_[i].is_static())) { |
412 | 0 | return STATUS(InvalidArgument, |
413 | 0 | "Bad schema", strings::Substitute("Static key columns are not " |
414 | 0 | "allowed: $0", cols_[i].name())); |
415 | 0 | } |
416 | 3.43M | if (PREDICT_FALSE(cols_[i].is_counter())) { |
417 | 0 | return STATUS(InvalidArgument, |
418 | 0 | "Bad schema", strings::Substitute("Counter key columns are not allowed: $0", |
419 | 0 | cols_[i].name())); |
420 | 0 | } |
421 | 3.43M | } |
422 | | |
423 | | // Calculate the offset of each column in the row format. |
424 | 17.3M | col_offsets_.reserve(cols_.size() + 1); // Include space for total byte size at the end. |
425 | 17.3M | size_t off = 0; |
426 | 17.3M | size_t idx = 0; |
427 | 17.3M | name_to_index_.clear(); |
428 | 38.5M | for (const ColumnSchema &col : cols_) { |
429 | | // The map uses the 'name' string from within the ColumnSchema object. |
430 | 38.5M | if (!InsertIfNotPresent(&name_to_index_, col.name(), idx++)) { |
431 | 0 | return STATUS(InvalidArgument, "Duplicate column name", col.name()); |
432 | 0 | } |
433 | | |
434 | 38.5M | col_offsets_.push_back(off); |
435 | 38.5M | off += col.type_info()->size(); |
436 | 38.5M | } |
437 | | |
438 | | // Add an extra element on the end for the total |
439 | | // byte size |
440 | 17.3M | col_offsets_.push_back(off); |
441 | | |
442 | | // Initialize IDs mapping |
443 | 17.3M | ResetColumnIds(ids); |
444 | | |
445 | | // Ensure clustering columns have a default sorting type of 'ASC' if not specified. |
446 | 19.6M | for (auto i = num_hash_key_columns_; i < num_key_columns(); ++i) { |
447 | 2.39M | ColumnSchema& col = cols_[i]; |
448 | 2.39M | if (col.sorting_type() == SortingType::kNotSpecified) { |
449 | 46.0k | col.set_sorting_type(SortingType::kAscending); |
450 | 46.0k | } |
451 | 2.39M | } |
452 | 17.3M | return Status::OK(); |
453 | 17.3M | } |
454 | | |
455 | | Status Schema::CreateProjectionByNames(const std::vector<GStringPiece>& col_names, |
456 | 1.43k | Schema* out, size_t num_key_columns) const { |
457 | 1.43k | vector<ColumnId> ids; |
458 | 1.43k | vector<ColumnSchema> cols; |
459 | 1.44k | for (const GStringPiece& name : col_names) { |
460 | 1.44k | auto idx = find_column(name); |
461 | 1.44k | if (idx == kColumnNotFound) { |
462 | 1 | return STATUS(NotFound, "Column not found", name); |
463 | 1 | } |
464 | 1.44k | if (has_column_ids()) { |
465 | 1.43k | ids.push_back(column_id(idx)); |
466 | 1.43k | } |
467 | 1.44k | cols.push_back(column(idx)); |
468 | 1.44k | } |
469 | 1.43k | return out->Reset(cols, ids, num_key_columns, TableProperties(), cotable_id_, |
470 | 1.43k | pgtable_id_, pgschema_name_); |
471 | 1.43k | } |
472 | | |
473 | | Status Schema::CreateProjectionByIdsIgnoreMissing(const std::vector<ColumnId>& col_ids, |
474 | 15.3M | Schema* out) const { |
475 | 15.3M | vector<ColumnSchema> cols; |
476 | 15.3M | vector<ColumnId> filtered_col_ids; |
477 | 26.3M | for (ColumnId id : col_ids) { |
478 | 26.3M | int idx = find_column_by_id(id); |
479 | 26.3M | if (idx == -1) { |
480 | 1 | continue; |
481 | 1 | } |
482 | 26.3M | cols.push_back(column(idx)); |
483 | 26.3M | filtered_col_ids.push_back(id); |
484 | 26.3M | } |
485 | 15.3M | return out->Reset(cols, filtered_col_ids, 0, TableProperties(), cotable_id_, |
486 | 15.3M | pgtable_id_, pgschema_name_); |
487 | 15.3M | } |
488 | | |
489 | | namespace { |
490 | | |
491 | 4.09k | vector<ColumnId> DefaultColumnIds(ColumnIdRep num_columns) { |
492 | 4.09k | vector<ColumnId> ids; |
493 | 15.9k | for (ColumnIdRep i = 0; i < num_columns; ++i) { |
494 | 11.8k | ids.push_back(ColumnId(kFirstColumnId + i)); |
495 | 11.8k | } |
496 | 4.09k | return ids; |
497 | 4.09k | } |
498 | | |
499 | | } // namespace |
500 | | |
501 | 4.09k | void Schema::InitColumnIdsByDefault() { |
502 | 4.09k | CHECK(!has_column_ids()); |
503 | 4.09k | ResetColumnIds(DefaultColumnIds(narrow_cast<ColumnIdRep>(cols_.size()))); |
504 | 4.09k | } |
505 | | |
506 | 130k | Schema Schema::CopyWithoutColumnIds() const { |
507 | 130k | CHECK(has_column_ids()); |
508 | 130k | return Schema(cols_, num_key_columns_, table_properties_, cotable_id_, |
509 | 130k | pgtable_id_, pgschema_name_); |
510 | 130k | } |
511 | | |
512 | 132k | Status Schema::VerifyProjectionCompatibility(const Schema& projection) const { |
513 | 106 | DCHECK(has_column_ids()) << "The server schema must have IDs"; |
514 | | |
515 | 132k | if (projection.has_column_ids()) { |
516 | 0 | return STATUS(InvalidArgument, "User requests should not have Column IDs"); |
517 | 0 | } |
518 | | |
519 | 132k | vector<string> missing_columns; |
520 | 419k | for (const ColumnSchema& pcol : projection.columns()) { |
521 | 419k | auto index = find_column(pcol.name()); |
522 | 419k | if (index == kColumnNotFound) { |
523 | 2 | missing_columns.push_back(pcol.name()); |
524 | 419k | } else if (!pcol.EqualsType(cols_[index])) { |
525 | | // TODO: We don't support query with type adaptors yet |
526 | 0 | return STATUS(InvalidArgument, "The column '" + pcol.name() + "' must have type " + |
527 | 0 | cols_[index].TypeToString() + " found " + pcol.TypeToString()); |
528 | 0 | } |
529 | 419k | } |
530 | | |
531 | 132k | if (!missing_columns.empty()) { |
532 | 1 | return STATUS(InvalidArgument, "Some columns are not present in the current schema", |
533 | 1 | JoinStrings(missing_columns, ", ")); |
534 | 1 | } |
535 | 132k | return Status::OK(); |
536 | 132k | } |
537 | | |
538 | | Status Schema::GetMappedReadProjection(const Schema& projection, |
539 | 132k | Schema *mapped_projection) const { |
540 | | // - The user projection may have different columns from the ones on the tablet |
541 | | // - User columns non present in the tablet are considered errors |
542 | | // - The user projection is not supposed to have the defaults or the nullable |
543 | | // information on each field. The current tablet schema is supposed to. |
544 | 132k | RETURN_NOT_OK(VerifyProjectionCompatibility(projection)); |
545 | | |
546 | | // Get the Projection Mapping |
547 | 132k | vector<ColumnSchema> mapped_cols; |
548 | 132k | vector<ColumnId> mapped_ids; |
549 | | |
550 | 132k | mapped_cols.reserve(projection.num_columns()); |
551 | 132k | mapped_ids.reserve(projection.num_columns()); |
552 | | |
553 | 418k | for (const ColumnSchema& col : projection.columns()) { |
554 | 418k | auto index = find_column(col.name()); |
555 | 0 | DCHECK_GE(index, 0) << col.name(); |
556 | 418k | mapped_cols.push_back(cols_[index]); |
557 | 418k | mapped_ids.push_back(col_ids_[index]); |
558 | 418k | } |
559 | | |
560 | 132k | CHECK_OK(mapped_projection->Reset(mapped_cols, mapped_ids, projection.num_key_columns())); |
561 | 132k | return Status::OK(); |
562 | 132k | } |
563 | | |
564 | 103k | string Schema::ToString() const { |
565 | 103k | vector<string> col_strs; |
566 | 103k | if (has_column_ids()) { |
567 | 632k | for (size_t i = 0; i < cols_.size(); ++i) { |
568 | 529k | col_strs.push_back(Format("$0:$1", col_ids_[i], cols_[i].ToString())); |
569 | 529k | } |
570 | 448 | } else { |
571 | 12 | for (const ColumnSchema &col : cols_) { |
572 | 12 | col_strs.push_back(col.ToString()); |
573 | 12 | } |
574 | 448 | } |
575 | | |
576 | 103k | TablePropertiesPB tablet_properties_pb; |
577 | 103k | table_properties_.ToTablePropertiesPB(&tablet_properties_pb); |
578 | | |
579 | 103k | return StrCat("Schema [\n\t", |
580 | 103k | JoinStrings(col_strs, ",\n\t"), |
581 | 103k | "\n]\nproperties: ", |
582 | 103k | tablet_properties_pb.ShortDebugString(), |
583 | 102k | cotable_id_.IsNil() ? "" : ("\ncotable_id: " + cotable_id_.ToString()), |
584 | 102k | pgtable_id_ == 0 ? "" : ("\npgtable_id: " + std::to_string(pgtable_id_))); |
585 | 103k | } |
586 | | |
587 | | Status Schema::DecodeRowKey(Slice encoded_key, |
588 | | uint8_t* buffer, |
589 | 6 | Arena* arena) const { |
590 | 6 | ContiguousRow row(this, buffer); |
591 | | |
592 | 14 | for (size_t col_idx = 0; col_idx < num_key_columns(); ++col_idx) { |
593 | 11 | const ColumnSchema& col = column(col_idx); |
594 | 11 | const KeyEncoder<faststring>& key_encoder = GetKeyEncoder<faststring>(col.type_info()); |
595 | 11 | bool is_last = col_idx == (num_key_columns() - 1); |
596 | 11 | RETURN_NOT_OK_PREPEND(key_encoder.Decode(&encoded_key, |
597 | 11 | is_last, |
598 | 11 | arena, |
599 | 11 | row.mutable_cell_ptr(col_idx)), |
600 | 11 | strings::Substitute("Error decoding composite key component '$0'", |
601 | 8 | col.name())); |
602 | 8 | } |
603 | 3 | return Status::OK(); |
604 | 6 | } |
605 | | |
606 | 8 | string Schema::DebugEncodedRowKey(Slice encoded_key, StartOrEnd start_or_end) const { |
607 | 8 | if (encoded_key.empty()) { |
608 | 2 | switch (start_or_end) { |
609 | 1 | case START_KEY: return "<start of table>"; |
610 | 1 | case END_KEY: return "<end of table>"; |
611 | 6 | } |
612 | 6 | } |
613 | | |
614 | 6 | Arena arena(1024, 128 * 1024); |
615 | 6 | uint8_t* buf = reinterpret_cast<uint8_t*>(arena.AllocateBytes(key_byte_size())); |
616 | 6 | Status s = DecodeRowKey(encoded_key, buf, &arena); |
617 | 6 | if (!s.ok()) { |
618 | 3 | return "<invalid key: " + s.ToString(/* no file/line */ false) + ">"; |
619 | 3 | } |
620 | 3 | ConstContiguousRow row(this, buf); |
621 | 3 | return DebugRowKey(row); |
622 | 3 | } |
623 | | |
624 | 3 | size_t Schema::memory_footprint_excluding_this() const { |
625 | 3 | size_t size = 0; |
626 | 3 | for (const ColumnSchema& col : cols_) { |
627 | 3 | size += col.memory_footprint_excluding_this(); |
628 | 3 | } |
629 | | |
630 | 3 | if (cols_.capacity() > 0) { |
631 | 1 | size += malloc_usable_size(cols_.data()); |
632 | 1 | } |
633 | 3 | if (col_ids_.capacity() > 0) { |
634 | 0 | size += malloc_usable_size(col_ids_.data()); |
635 | 0 | } |
636 | 3 | if (col_offsets_.capacity() > 0) { |
637 | 1 | size += malloc_usable_size(col_offsets_.data()); |
638 | 1 | } |
639 | 3 | size += name_to_index_bytes_; |
640 | 3 | size += id_to_index_.memory_footprint_excluding_this(); |
641 | | |
642 | 3 | return size; |
643 | 3 | } |
644 | | |
645 | 0 | size_t Schema::memory_footprint_including_this() const { |
646 | 0 | return malloc_usable_size(this) + memory_footprint_excluding_this(); |
647 | 0 | } |
648 | | |
649 | 78.7k | Result<ssize_t> Schema::ColumnIndexByName(GStringPiece col_name) const { |
650 | 78.7k | auto index = find_column(col_name); |
651 | 78.7k | if (index == kColumnNotFound) { |
652 | 0 | return STATUS_FORMAT(Corruption, "$0 not found in schema $1", col_name, name_to_index_); |
653 | 0 | } |
654 | 78.7k | return index; |
655 | 78.7k | } |
656 | | |
657 | 431k | Result<ColumnId> Schema::ColumnIdByName(const std::string& column_name) const { |
658 | 431k | auto column_index = find_column(column_name); |
659 | 431k | if (column_index == kColumnNotFound) { |
660 | 0 | return STATUS_FORMAT(NotFound, "Couldn't find column $0 in the schema", column_name); |
661 | 0 | } |
662 | 431k | return ColumnId(column_id(column_index)); |
663 | 431k | } |
664 | | |
665 | 919 | ColumnId Schema::first_column_id() { |
666 | 919 | return kFirstColumnId; |
667 | 919 | } |
668 | | |
669 | 21.4M | Result<const ColumnSchema&> Schema::column_by_id(ColumnId id) const { |
670 | 21.4M | int idx = find_column_by_id(id); |
671 | 21.4M | if (idx < 0) { |
672 | 0 | return STATUS_FORMAT(InvalidArgument, "Column id $0 not found", id.ToString()); |
673 | 0 | } |
674 | 21.4M | return cols_[idx]; |
675 | 21.4M | } |
676 | | |
677 | | // ============================================================================ |
678 | | // Schema Builder |
679 | | // ============================================================================ |
680 | 42.9k | void SchemaBuilder::Reset() { |
681 | 42.9k | cols_.clear(); |
682 | 42.9k | col_ids_.clear(); |
683 | 42.9k | col_names_.clear(); |
684 | 42.9k | num_key_columns_ = 0; |
685 | 42.9k | next_id_ = kFirstColumnId; |
686 | 42.9k | table_properties_.Reset(); |
687 | 42.9k | pgtable_id_ = 0; |
688 | 42.9k | pgschema_name_ = ""; |
689 | 42.9k | cotable_id_ = Uuid::Nil(); |
690 | 42.9k | } |
691 | | |
692 | 1.19k | void SchemaBuilder::Reset(const Schema& schema) { |
693 | 1.19k | cols_ = schema.cols_; |
694 | 1.19k | col_ids_ = schema.col_ids_; |
695 | 1.19k | num_key_columns_ = schema.num_key_columns_; |
696 | 4.62k | for (const auto& column : cols_) { |
697 | 4.62k | col_names_.insert(column.name()); |
698 | 4.62k | } |
699 | | |
700 | 1.19k | if (col_ids_.empty()) { |
701 | 3.61k | for (ColumnIdRep i = 0; i < narrow_cast<ColumnIdRep>(cols_.size()); ++i) { |
702 | 2.69k | col_ids_.push_back(ColumnId(kFirstColumnId + i)); |
703 | 2.69k | } |
704 | 919 | } |
705 | 1.19k | if (col_ids_.empty()) { |
706 | 0 | next_id_ = kFirstColumnId; |
707 | 1.19k | } else { |
708 | 1.19k | next_id_ = *std::max_element(col_ids_.begin(), col_ids_.end()) + 1; |
709 | 1.19k | } |
710 | 1.19k | table_properties_ = schema.table_properties_; |
711 | 1.19k | pgtable_id_ = schema.pgtable_id_; |
712 | 1.19k | pgschema_name_ = schema.pgschema_name_; |
713 | 1.19k | cotable_id_ = schema.cotable_id_; |
714 | 1.19k | } |
715 | | |
716 | 18.0k | Status SchemaBuilder::AddKeyColumn(const string& name, const shared_ptr<QLType>& type) { |
717 | 18.0k | return AddColumn(ColumnSchema(name, type), /* is_nullable */ true); |
718 | 18.0k | } |
719 | | |
720 | 35.6k | Status SchemaBuilder::AddKeyColumn(const string& name, DataType type) { |
721 | 35.6k | return AddColumn(ColumnSchema(name, QLType::Create(type)), /* is_nullable */ true); |
722 | 35.6k | } |
723 | | |
724 | 18.0k | Status SchemaBuilder::AddHashKeyColumn(const string& name, const shared_ptr<QLType>& type) { |
725 | 18.0k | return AddColumn(ColumnSchema(name, type, false, true), true); |
726 | 18.0k | } |
727 | | |
728 | 14.0k | Status SchemaBuilder::AddHashKeyColumn(const string& name, DataType type) { |
729 | 14.0k | return AddColumn(ColumnSchema(name, QLType::Create(type), false, true), true); |
730 | 14.0k | } |
731 | | |
732 | 24.8k | Status SchemaBuilder::AddColumn(const std::string& name, DataType type) { |
733 | 24.8k | return AddColumn(name, QLType::Create(type)); |
734 | 24.8k | } |
735 | | |
736 | | Status SchemaBuilder::AddColumn(const std::string& name, |
737 | | DataType type, |
738 | | bool is_nullable, |
739 | | bool is_hash_key, |
740 | | bool is_static, |
741 | | bool is_counter, |
742 | | int32_t order, |
743 | 0 | yb::SortingType sorting_type) { |
744 | 0 | return AddColumn(name, QLType::Create(type), is_nullable, is_hash_key, is_static, is_counter, |
745 | 0 | order, sorting_type); |
746 | 0 | } |
747 | | |
748 | | Status SchemaBuilder::AddColumn(const string& name, |
749 | | const std::shared_ptr<QLType>& type, |
750 | | bool is_nullable, |
751 | | bool is_hash_key, |
752 | | bool is_static, |
753 | | bool is_counter, |
754 | | int32_t order, |
755 | 221k | SortingType sorting_type) { |
756 | 221k | return AddColumn(ColumnSchema(name, type, is_nullable, is_hash_key, is_static, is_counter, |
757 | 221k | order, sorting_type), false); |
758 | 221k | } |
759 | | |
760 | | |
761 | 2 | Status SchemaBuilder::AddNullableColumn(const std::string& name, DataType type) { |
762 | 2 | return AddNullableColumn(name, QLType::Create(type)); |
763 | 2 | } |
764 | | |
765 | 97 | Status SchemaBuilder::RemoveColumn(const string& name) { |
766 | 97 | unordered_set<string>::const_iterator it_names; |
767 | 97 | if ((it_names = col_names_.find(name)) == col_names_.end()) { |
768 | 0 | return STATUS(NotFound, "The specified column does not exist", name); |
769 | 0 | } |
770 | | |
771 | 97 | col_names_.erase(it_names); |
772 | 489 | for (size_t i = 0; i < cols_.size(); ++i) { |
773 | 489 | if (name == cols_[i].name()) { |
774 | 97 | cols_.erase(cols_.begin() + i); |
775 | 97 | col_ids_.erase(col_ids_.begin() + i); |
776 | 97 | if (i < num_key_columns_) { |
777 | 0 | num_key_columns_--; |
778 | 0 | } |
779 | 97 | return Status::OK(); |
780 | 97 | } |
781 | 489 | } |
782 | | |
783 | 0 | LOG(FATAL) << "Should not reach here"; |
784 | 0 | return STATUS(Corruption, "Unable to remove existing column"); |
785 | 97 | } |
786 | | |
787 | 17 | Status SchemaBuilder::RenameColumn(const string& old_name, const string& new_name) { |
788 | 17 | unordered_set<string>::const_iterator it_names; |
789 | | |
790 | | // check if 'new_name' is already in use |
791 | 17 | if ((it_names = col_names_.find(new_name)) != col_names_.end()) { |
792 | 0 | return STATUS(AlreadyPresent, "The column already exists", new_name); |
793 | 0 | } |
794 | | |
795 | | // check if the 'old_name' column exists |
796 | 17 | if ((it_names = col_names_.find(old_name)) == col_names_.end()) { |
797 | 0 | return STATUS(NotFound, "The specified column does not exist", old_name); |
798 | 0 | } |
799 | | |
800 | 17 | col_names_.erase(it_names); // TODO: Should this one stay and marked as alias? |
801 | 17 | col_names_.insert(new_name); |
802 | | |
803 | 45 | for (ColumnSchema& col_schema : cols_) { |
804 | 45 | if (old_name == col_schema.name()) { |
805 | 17 | col_schema.set_name(new_name); |
806 | 17 | return Status::OK(); |
807 | 17 | } |
808 | 45 | } |
809 | | |
810 | 0 | LOG(FATAL) << "Should not reach here"; |
811 | 0 | return STATUS(IllegalState, "Unable to rename existing column"); |
812 | 17 | } |
813 | | |
814 | 307k | Status SchemaBuilder::AddColumn(const ColumnSchema& column, bool is_key) { |
815 | 307k | if (ContainsKey(col_names_, column.name())) { |
816 | 0 | return STATUS(AlreadyPresent, "The column already exists", column.name()); |
817 | 0 | } |
818 | | |
819 | 307k | col_names_.insert(column.name()); |
820 | 307k | if (is_key) { |
821 | 85.8k | cols_.insert(cols_.begin() + num_key_columns_, column); |
822 | 85.8k | col_ids_.insert(col_ids_.begin() + num_key_columns_, next_id_); |
823 | 85.8k | num_key_columns_++; |
824 | 221k | } else { |
825 | 221k | cols_.push_back(column); |
826 | 221k | col_ids_.push_back(next_id_); |
827 | 221k | } |
828 | | |
829 | 307k | next_id_ = ColumnId(next_id_ + 1); |
830 | 307k | return Status::OK(); |
831 | 307k | } |
832 | | |
833 | 7 | Status SchemaBuilder::AlterProperties(const TablePropertiesPB& pb) { |
834 | 7 | table_properties_.AlterFromTablePropertiesPB(pb); |
835 | 7 | return Status::OK(); |
836 | 7 | } |
837 | | |
838 | | |
839 | 657 | Status DeletedColumn::FromPB(const DeletedColumnPB& col, DeletedColumn* ret) { |
840 | 657 | ret->id = col.column_id(); |
841 | 657 | ret->ht = HybridTime(col.deleted_hybrid_time()); |
842 | 657 | return Status::OK(); |
843 | 657 | } |
844 | | |
845 | 12.5k | void DeletedColumn::CopyToPB(DeletedColumnPB* pb) const { |
846 | 12.5k | pb->set_column_id(id); |
847 | 12.5k | pb->set_deleted_hybrid_time(ht.ToUint64()); |
848 | 12.5k | } |
849 | | |
850 | | } // namespace yb |