/Users/deen/code/yugabyte-db/src/yb/client/table_creator.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) YugaByte, Inc. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
4 | | // in compliance with the License. You may obtain a copy of the License at |
5 | | // |
6 | | // http://www.apache.org/licenses/LICENSE-2.0 |
7 | | // |
8 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
9 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
10 | | // or implied. See the License for the specific language governing permissions and limitations |
11 | | // under the License. |
12 | | // |
13 | | |
14 | | #include "yb/client/table_creator.h" |
15 | | |
16 | | #include "yb/client/client-internal.h" |
17 | | #include "yb/client/client.h" |
18 | | #include "yb/client/table_info.h" |
19 | | |
20 | | #include "yb/common/schema.h" |
21 | | #include "yb/common/transaction.h" |
22 | | #include "yb/common/wire_protocol.h" |
23 | | |
24 | | #include "yb/master/master_ddl.pb.h" |
25 | | |
26 | | #include "yb/util/result.h" |
27 | | #include "yb/util/status_format.h" |
28 | | |
29 | | #include "yb/yql/redis/redisserver/redis_constants.h" |
30 | | |
31 | | DECLARE_bool(client_suppress_created_logs); |
32 | | |
33 | | namespace yb { |
34 | | namespace client { |
35 | | |
36 | | YBTableCreator::YBTableCreator(YBClient* client) |
37 | 3.20k | : client_(client), partition_schema_(new PartitionSchemaPB), index_info_(new IndexInfoPB) { |
38 | 3.20k | } |
39 | | |
40 | 3.19k | YBTableCreator::~YBTableCreator() { |
41 | 3.19k | } |
42 | | |
43 | 3.20k | YBTableCreator& YBTableCreator::table_name(const YBTableName& name) { |
44 | 3.20k | table_name_ = name; |
45 | 3.20k | return *this; |
46 | 3.20k | } |
47 | | |
48 | 3.12k | YBTableCreator& YBTableCreator::table_type(YBTableType table_type) { |
49 | 3.12k | table_type_ = ClientToPBTableType(table_type); |
50 | 3.12k | return *this; |
51 | 3.12k | } |
52 | | |
53 | 1.59k | YBTableCreator& YBTableCreator::creator_role_name(const RoleName& creator_role_name) { |
54 | 1.59k | creator_role_name_ = creator_role_name; |
55 | 1.59k | return *this; |
56 | 1.59k | } |
57 | | |
58 | 1.44k | YBTableCreator& YBTableCreator::table_id(const std::string& table_id) { |
59 | 1.44k | table_id_ = table_id; |
60 | 1.44k | return *this; |
61 | 1.44k | } |
62 | | |
63 | 0 | YBTableCreator& YBTableCreator::is_pg_catalog_table() { |
64 | 0 | is_pg_catalog_table_ = true; |
65 | 0 | return *this; |
66 | 0 | } |
67 | | |
68 | 0 | YBTableCreator& YBTableCreator::is_pg_shared_table() { |
69 | 0 | is_pg_shared_table_ = true; |
70 | 0 | return *this; |
71 | 0 | } |
72 | | |
73 | 1.36k | YBTableCreator& YBTableCreator::hash_schema(YBHashSchema hash_schema) { |
74 | 1.36k | switch (hash_schema) { |
75 | 30 | case YBHashSchema::kMultiColumnHash: |
76 | 30 | partition_schema_->set_hash_schema(PartitionSchemaPB::MULTI_COLUMN_HASH_SCHEMA); |
77 | 30 | break; |
78 | 0 | case YBHashSchema::kRedisHash: |
79 | 0 | partition_schema_->set_hash_schema(PartitionSchemaPB::REDIS_HASH_SCHEMA); |
80 | 0 | break; |
81 | 1.33k | case YBHashSchema::kPgsqlHash: |
82 | 1.33k | partition_schema_->set_hash_schema(PartitionSchemaPB::PGSQL_HASH_SCHEMA); |
83 | 1.33k | break; |
84 | 1.36k | } |
85 | 1.36k | return *this; |
86 | 1.36k | } |
87 | | |
88 | 132 | YBTableCreator& YBTableCreator::num_tablets(int32_t count) { |
89 | 132 | num_tablets_ = count; |
90 | 132 | return *this; |
91 | 132 | } |
92 | | |
93 | 1.42k | YBTableCreator& YBTableCreator::colocated(const bool colocated) { |
94 | 1.42k | colocated_ = colocated; |
95 | 1.42k | return *this; |
96 | 1.42k | } |
97 | | |
98 | 7 | YBTableCreator& YBTableCreator::tablegroup_id(const std::string& tablegroup_id) { |
99 | 7 | tablegroup_id_ = tablegroup_id; |
100 | 7 | return *this; |
101 | 7 | } |
102 | | |
103 | 0 | YBTableCreator& YBTableCreator::tablespace_id(const std::string& tablespace_id) { |
104 | 0 | tablespace_id_ = tablespace_id; |
105 | 0 | return *this; |
106 | 0 | } |
107 | | |
108 | 0 | YBTableCreator& YBTableCreator::matview_pg_table_id(const std::string& matview_pg_table_id) { |
109 | 0 | matview_pg_table_id_ = matview_pg_table_id; |
110 | 0 | return *this; |
111 | 0 | } |
112 | | |
113 | 3.20k | YBTableCreator& YBTableCreator::schema(const YBSchema* schema) { |
114 | 3.20k | schema_ = schema; |
115 | 3.20k | return *this; |
116 | 3.20k | } |
117 | | |
118 | 1.41k | YBTableCreator& YBTableCreator::part_of_transaction(const TransactionMetadata* txn) { |
119 | 1.41k | txn_ = txn; |
120 | 1.41k | return *this; |
121 | 1.41k | } |
122 | | |
123 | 2 | YBTableCreator &YBTableCreator::add_partition(const Partition& partition) { |
124 | 2 | partitions_.push_back(partition); |
125 | 2 | return *this; |
126 | 2 | } |
127 | | |
128 | | YBTableCreator& YBTableCreator::add_hash_partitions(const std::vector<std::string>& columns, |
129 | 0 | int32_t num_buckets) { |
130 | 0 | return add_hash_partitions(columns, num_buckets, 0); |
131 | 0 | } |
132 | | |
133 | | YBTableCreator& YBTableCreator::add_hash_partitions(const std::vector<std::string>& columns, |
134 | 0 | int32_t num_buckets, int32_t seed) { |
135 | 0 | PartitionSchemaPB::HashBucketSchemaPB* bucket_schema = |
136 | 0 | partition_schema_->add_hash_bucket_schemas(); |
137 | 0 | for (const string& col_name : columns) { |
138 | 0 | bucket_schema->add_columns()->set_name(col_name); |
139 | 0 | } |
140 | 0 | bucket_schema->set_num_buckets(num_buckets); |
141 | 0 | bucket_schema->set_seed(seed); |
142 | 0 | return *this; |
143 | 0 | } |
144 | | |
145 | | YBTableCreator& YBTableCreator::set_range_partition_columns( |
146 | | const std::vector<std::string>& columns, |
147 | 103 | const std::vector<std::string>& split_rows) { |
148 | 103 | PartitionSchemaPB::RangeSchemaPB* range_schema = |
149 | 103 | partition_schema_->mutable_range_schema(); |
150 | 103 | range_schema->Clear(); |
151 | 152 | for (const string& col_name : columns) { |
152 | 152 | range_schema->add_columns()->set_name(col_name); |
153 | 152 | } |
154 | | |
155 | 15 | for (const auto& row : split_rows) { |
156 | 15 | range_schema->add_splits()->set_column_bounds(row); |
157 | 15 | } |
158 | 103 | return *this; |
159 | 103 | } |
160 | | |
161 | 1 | YBTableCreator& YBTableCreator::replication_info(const master::ReplicationInfoPB& ri) { |
162 | 1 | replication_info_ = std::make_unique<master::ReplicationInfoPB>(ri); |
163 | 1 | return *this; |
164 | 1 | } |
165 | | |
166 | 609 | YBTableCreator& YBTableCreator::indexed_table_id(const std::string& id) { |
167 | 609 | index_info_->set_indexed_table_id(id); |
168 | 609 | return *this; |
169 | 609 | } |
170 | | |
171 | 440 | YBTableCreator& YBTableCreator::is_local_index(bool is_local_index) { |
172 | 440 | index_info_->set_is_local(is_local_index); |
173 | 440 | return *this; |
174 | 440 | } |
175 | | |
176 | 492 | YBTableCreator& YBTableCreator::is_unique_index(bool is_unique_index) { |
177 | 492 | index_info_->set_is_unique(is_unique_index); |
178 | 492 | return *this; |
179 | 492 | } |
180 | | |
181 | 440 | YBTableCreator& YBTableCreator::is_backfill_deferred(bool is_backfill_deferred) { |
182 | 440 | index_info_->set_is_backfill_deferred(is_backfill_deferred); |
183 | 440 | return *this; |
184 | 440 | } |
185 | | |
186 | 61 | YBTableCreator& YBTableCreator::skip_index_backfill(const bool skip_index_backfill) { |
187 | 61 | skip_index_backfill_ = skip_index_backfill; |
188 | 61 | return *this; |
189 | 61 | } |
190 | | |
191 | 0 | YBTableCreator& YBTableCreator::use_mangled_column_name(bool value) { |
192 | 0 | index_info_->set_use_mangled_column_name(value); |
193 | 0 | return *this; |
194 | 0 | } |
195 | | |
196 | 1.48k | YBTableCreator& YBTableCreator::timeout(const MonoDelta& timeout) { |
197 | 1.48k | timeout_ = timeout; |
198 | 1.48k | return *this; |
199 | 1.48k | } |
200 | | |
201 | 40 | YBTableCreator& YBTableCreator::wait(bool wait) { |
202 | 40 | wait_ = wait; |
203 | 40 | return *this; |
204 | 40 | } |
205 | | |
206 | 18 | YBTableCreator& YBTableCreator::TEST_use_old_style_create_request() { |
207 | 18 | TEST_use_old_style_create_request_ = true; |
208 | 18 | return *this; |
209 | 18 | } |
210 | | |
211 | 2.93k | Status YBTableCreator::Create() { |
212 | 2.38k | const char *object_type = index_info_->has_indexed_table_id() ? "index" : "table"; |
213 | 2.93k | if (table_name_.table_name().empty()) { |
214 | 0 | return STATUS_SUBSTITUTE(InvalidArgument, "Missing $0 name", object_type); |
215 | 0 | } |
216 | | // For a redis table, no external schema is passed to TableCreator, we make a unique schema |
217 | | // and manage its memory withing here. |
218 | 2.93k | std::unique_ptr<YBSchema> redis_schema; |
219 | | // We create dummy schema for transaction status table, redis schema is quite lightweight for |
220 | | // this purpose. |
221 | 2.93k | if (table_type_ == TableType::REDIS_TABLE_TYPE || |
222 | 2.93k | table_type_ == TableType::TRANSACTION_STATUS_TABLE_TYPE) { |
223 | 0 | CHECK(!schema_) << "Schema should not be set for redis table creation"; |
224 | 0 | redis_schema.reset(new YBSchema()); |
225 | 0 | YBSchemaBuilder b; |
226 | 0 | b.AddColumn(kRedisKeyColumnName)->Type(BINARY)->NotNull()->HashPrimaryKey(); |
227 | 0 | RETURN_NOT_OK(b.Build(redis_schema.get())); |
228 | 0 | schema(redis_schema.get()); |
229 | 0 | } |
230 | 2.93k | if (!schema_) { |
231 | 0 | return STATUS(InvalidArgument, "Missing schema"); |
232 | 0 | } |
233 | | |
234 | | // Build request. |
235 | 2.93k | master::CreateTableRequestPB req; |
236 | 2.93k | req.set_name(table_name_.table_name()); |
237 | 2.93k | table_name_.SetIntoNamespaceIdentifierPB(req.mutable_namespace_()); |
238 | 2.93k | req.set_table_type(table_type_); |
239 | 2.93k | req.set_colocated(colocated_); |
240 | | |
241 | 2.93k | if (!creator_role_name_.empty()) { |
242 | 242 | req.set_creator_role_name(creator_role_name_); |
243 | 242 | } |
244 | | |
245 | 2.93k | if (!table_id_.empty()) { |
246 | 1.43k | req.set_table_id(table_id_); |
247 | 1.43k | } |
248 | 2.93k | if (is_pg_catalog_table_) { |
249 | 0 | req.set_is_pg_catalog_table(*is_pg_catalog_table_); |
250 | 0 | } |
251 | 2.93k | if (is_pg_shared_table_) { |
252 | 0 | req.set_is_pg_shared_table(*is_pg_shared_table_); |
253 | 0 | } |
254 | | |
255 | 2.93k | if (!tablegroup_id_.empty()) { |
256 | 7 | req.set_tablegroup_id(tablegroup_id_); |
257 | 7 | } |
258 | | |
259 | 2.93k | if (!tablespace_id_.empty()) { |
260 | 0 | req.set_tablespace_id(tablespace_id_); |
261 | 0 | } |
262 | | |
263 | 2.93k | if (!matview_pg_table_id_.empty()) { |
264 | 0 | req.set_matview_pg_table_id(matview_pg_table_id_); |
265 | 0 | } |
266 | | |
267 | | // Note that the check that the sum of min_num_replicas for each placement block being less or |
268 | | // equal than the overall placement info num_replicas is done on the master side and an error is |
269 | | // naturally returned if you try to create a table and the numbers mismatch. As such, it is the |
270 | | // responsibility of the client to ensure that does not happen. |
271 | 2.93k | if (replication_info_) { |
272 | 0 | req.mutable_replication_info()->CopyFrom(*replication_info_); |
273 | 0 | } |
274 | | |
275 | 2.93k | SchemaToPB(internal::GetSchema(*schema_), req.mutable_schema()); |
276 | | |
277 | 2.93k | if (txn_) { |
278 | 1.41k | txn_->ToPB(req.mutable_transaction()); |
279 | 1.41k | } |
280 | | |
281 | | // Setup the number splits (i.e. number of splits). |
282 | 2.93k | if (num_tablets_ > 0) { |
283 | 0 | VLOG(1) << "num_tablets: number of tablets explicitly specified: " << num_tablets_; |
284 | 2.93k | } else if (schema_->table_properties().num_tablets() > 0) { |
285 | 0 | VLOG(1) << "num_tablets: number of tablets specified by user: " |
286 | 0 | << schema_->table_properties().num_tablets(); |
287 | 178 | num_tablets_ = schema_->table_properties().num_tablets(); |
288 | 2.75k | } else { |
289 | 2.75k | if (table_name_.is_system()) { |
290 | 0 | num_tablets_ = 1; |
291 | 0 | VLOG(1) << "num_tablets=1: using one tablet for a system table"; |
292 | 2.75k | } else { |
293 | 2.75k | num_tablets_ = VERIFY_RESULT(client_->NumTabletsForUserTable(table_type_)); |
294 | 2.75k | } |
295 | 2.75k | } |
296 | 2.93k | req.set_num_tablets(num_tablets_); |
297 | | |
298 | 2.93k | req.mutable_partition_schema()->CopyFrom(*partition_schema_); |
299 | | |
300 | 2.93k | if (!partitions_.empty()) { |
301 | 0 | for (const auto& p : partitions_) { |
302 | 0 | auto * np = req.add_partitions(); |
303 | 0 | p.ToPB(np); |
304 | 0 | } |
305 | 0 | } |
306 | | |
307 | | // Index mapping with data-table being indexed. |
308 | 2.93k | if (index_info_->has_indexed_table_id()) { |
309 | 548 | if (!TEST_use_old_style_create_request_) { |
310 | 548 | req.mutable_index_info()->CopyFrom(*index_info_); |
311 | 548 | } |
312 | | |
313 | | // For compatibility reasons, set the old fields just in case we have new clients talking to |
314 | | // old master server during rolling upgrade. |
315 | 548 | req.set_indexed_table_id(index_info_->indexed_table_id()); |
316 | 548 | req.set_is_local_index(index_info_->is_local()); |
317 | 548 | req.set_is_unique_index(index_info_->is_unique()); |
318 | 548 | req.set_skip_index_backfill(skip_index_backfill_); |
319 | 548 | req.set_is_backfill_deferred(index_info_->is_backfill_deferred()); |
320 | 548 | } |
321 | | |
322 | 2.93k | auto deadline = CoarseMonoClock::Now() + |
323 | 1.49k | (timeout_.Initialized() ? timeout_ : client_->default_admin_operation_timeout()); |
324 | | |
325 | 2.93k | auto s = client_->data_->CreateTable( |
326 | 2.93k | client_, req, *schema_, deadline, &table_id_); |
327 | | |
328 | 2.93k | if (!s.ok() && !s.IsAlreadyPresent()) { |
329 | 8 | RETURN_NOT_OK_PREPEND(s, strings::Substitute("Error creating $0 $1 on the master", |
330 | 0 | object_type, table_name_.ToString())); |
331 | 0 | } |
332 | | |
333 | | // We are here because the create request succeeded or we received an IsAlreadyPresent error. |
334 | | // Although the table is already in the catalog manager, it doesn't mean that the table is |
335 | | // ready to receive requests. So we will call WaitForCreateTableToFinish to ensure that once |
336 | | // this request returns, the client can send operations without receiving a "Table Not Found" |
337 | | // error. |
338 | | |
339 | | // Spin until the table is fully created, if requested. |
340 | 2.92k | if (wait_) { |
341 | 2.92k | RETURN_NOT_OK(client_->data_->WaitForCreateTableToFinish( |
342 | 2.92k | client_, YBTableName(), table_id_, deadline)); |
343 | 2.92k | } |
344 | | |
345 | 2.92k | if (s.ok() && !FLAGS_client_suppress_created_logs) { |
346 | 2.92k | LOG(INFO) << "Created " << object_type << " " << table_name_.ToString() |
347 | 2.92k | << " of type " << TableType_Name(table_type_); |
348 | 2.92k | } |
349 | | |
350 | 2.92k | return s; |
351 | 2.92k | } |
352 | | |
353 | | } // namespace client |
354 | | } // namespace yb |