/Users/deen/code/yugabyte-db/src/yb/yql/pggate/pg_ddl.cc
Line | Count | Source (jump to first uncovered line) |
1 | | //-------------------------------------------------------------------------------------------------- |
2 | | // Copyright (c) YugaByte, Inc. |
3 | | // |
4 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
5 | | // in compliance with the License. You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
10 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
11 | | // or implied. See the License for the specific language governing permissions and limitations |
12 | | // under the License. |
13 | | // |
14 | | //-------------------------------------------------------------------------------------------------- |
15 | | |
16 | | #include "yb/yql/pggate/pg_ddl.h" |
17 | | |
18 | | #include "yb/client/yb_op.h" |
19 | | #include "yb/client/yb_table_name.h" |
20 | | |
21 | | #include "yb/common/common.pb.h" |
22 | | #include "yb/common/entity_ids.h" |
23 | | #include "yb/common/pg_system_attr.h" |
24 | | |
25 | | #include "yb/util/flag_tags.h" |
26 | | #include "yb/util/status_format.h" |
27 | | #include "yb/util/status_log.h" |
28 | | |
29 | | #include "yb/yql/pggate/pg_client.h" |
30 | | |
31 | | DEFINE_test_flag(int32, user_ddl_operation_timeout_sec, 0, |
32 | | "Adjusts the timeout for a DDL operation from the YBClient default, if non-zero."); |
33 | | |
34 | | DECLARE_int32(max_num_tablets_for_table); |
35 | | |
36 | | namespace yb { |
37 | | namespace pggate { |
38 | | |
39 | | using std::make_shared; |
40 | | using std::shared_ptr; |
41 | | using std::string; |
42 | | using namespace std::literals; // NOLINT |
43 | | |
44 | | using client::YBClient; |
45 | | using client::YBSession; |
46 | | using client::YBMetaDataCache; |
47 | | |
48 | | // TODO(neil) This should be derived from a GFLAGS. |
49 | | static MonoDelta kSessionTimeout = 60s; |
50 | | |
51 | | namespace { |
52 | | |
53 | 1.62k | CoarseTimePoint DdlDeadline() { |
54 | 1.62k | auto timeout = MonoDelta::FromSeconds(FLAGS_TEST_user_ddl_operation_timeout_sec); |
55 | 1.62k | if (timeout == MonoDelta::kZero) { |
56 | | // TODO(PG_CLIENT) |
57 | 1.62k | timeout = 120s; |
58 | 1.62k | } |
59 | 1.62k | return CoarseMonoClock::now() + timeout; |
60 | 1.62k | } |
61 | | |
62 | | } // namespace |
63 | | |
64 | | //-------------------------------------------------------------------------------------------------- |
65 | | // PgCreateDatabase |
66 | | //-------------------------------------------------------------------------------------------------- |
67 | | |
68 | | PgCreateDatabase::PgCreateDatabase(PgSession::ScopedRefPtr pg_session, |
69 | | const char *database_name, |
70 | | const PgOid database_oid, |
71 | | const PgOid source_database_oid, |
72 | | const PgOid next_oid, |
73 | | const bool colocated) |
74 | 22 | : PgDdl(std::move(pg_session)) { |
75 | 22 | req_.set_database_name(database_name); |
76 | 22 | req_.set_database_oid(database_oid); |
77 | 22 | req_.set_source_database_oid(source_database_oid); |
78 | 22 | req_.set_next_oid(next_oid); |
79 | 22 | req_.set_colocated(colocated); |
80 | 22 | } |
81 | | |
82 | 22 | PgCreateDatabase::~PgCreateDatabase() { |
83 | 22 | } |
84 | | |
85 | 22 | Status PgCreateDatabase::Exec() { |
86 | 22 | return pg_session_->pg_client().CreateDatabase(&req_, DdlDeadline()); |
87 | 22 | } |
88 | | |
89 | | PgDropDatabase::PgDropDatabase(PgSession::ScopedRefPtr pg_session, |
90 | | const char *database_name, |
91 | | PgOid database_oid) |
92 | | : PgDdl(pg_session), |
93 | | database_name_(database_name), |
94 | 21 | database_oid_(database_oid) { |
95 | 21 | } |
96 | | |
97 | 21 | PgDropDatabase::~PgDropDatabase() { |
98 | 21 | } |
99 | | |
100 | 21 | Status PgDropDatabase::Exec() { |
101 | 21 | return pg_session_->DropDatabase(database_name_, database_oid_); |
102 | 21 | } |
103 | | |
104 | | PgAlterDatabase::PgAlterDatabase(PgSession::ScopedRefPtr pg_session, |
105 | | const char *database_name, |
106 | | PgOid database_oid) |
107 | 0 | : PgDdl(pg_session) { |
108 | 0 | req_.set_database_name(database_name); |
109 | 0 | req_.set_database_oid(database_oid); |
110 | 0 | } |
111 | | |
112 | 0 | PgAlterDatabase::~PgAlterDatabase() { |
113 | 0 | } |
114 | | |
115 | 0 | Status PgAlterDatabase::Exec() { |
116 | 0 | return pg_session_->pg_client().AlterDatabase(&req_, DdlDeadline()); |
117 | 0 | } |
118 | | |
119 | 0 | void PgAlterDatabase::RenameDatabase(const char *newname) { |
120 | 0 | req_.set_new_name(newname); |
121 | 0 | } |
122 | | |
123 | | //-------------------------------------------------------------------------------------------------- |
124 | | // PgCreateTablegroup / PgDropTablegroup |
125 | | //-------------------------------------------------------------------------------------------------- |
126 | | |
127 | | PgCreateTablegroup::PgCreateTablegroup(PgSession::ScopedRefPtr pg_session, |
128 | | const char *database_name, |
129 | | const PgOid database_oid, |
130 | | const PgOid tablegroup_oid, |
131 | | const PgOid tablespace_oid) |
132 | 1 | : PgDdl(pg_session) { |
133 | 1 | req_.set_database_name(database_name); |
134 | 1 | PgObjectId(database_oid, tablegroup_oid).ToPB(req_.mutable_tablegroup_id()); |
135 | 1 | PgObjectId(database_oid, tablespace_oid).ToPB(req_.mutable_tablespace_id()); |
136 | 1 | } |
137 | | |
138 | 1 | PgCreateTablegroup::~PgCreateTablegroup() { |
139 | 1 | } |
140 | | |
141 | 1 | Status PgCreateTablegroup::Exec() { |
142 | 1 | return pg_session_->pg_client().CreateTablegroup(&req_, DdlDeadline()); |
143 | 1 | } |
144 | | |
145 | | PgDropTablegroup::PgDropTablegroup(PgSession::ScopedRefPtr pg_session, |
146 | | const PgOid database_oid, |
147 | | const PgOid tablegroup_oid) |
148 | 1 | : PgDdl(pg_session) { |
149 | 1 | PgObjectId(database_oid, tablegroup_oid).ToPB(req_.mutable_tablegroup_id()); |
150 | 1 | } |
151 | | |
152 | 1 | PgDropTablegroup::~PgDropTablegroup() { |
153 | 1 | } |
154 | | |
155 | 1 | Status PgDropTablegroup::Exec() { |
156 | 1 | return pg_session_->pg_client().DropTablegroup(&req_, DdlDeadline()); |
157 | 1 | } |
158 | | |
159 | | //-------------------------------------------------------------------------------------------------- |
160 | | // PgCreateTable |
161 | | //-------------------------------------------------------------------------------------------------- |
162 | | |
163 | | PgCreateTable::PgCreateTable(PgSession::ScopedRefPtr pg_session, |
164 | | const char *database_name, |
165 | | const char *schema_name, |
166 | | const char *table_name, |
167 | | const PgObjectId& table_id, |
168 | | bool is_shared_table, |
169 | | bool if_not_exist, |
170 | | bool add_primary_key, |
171 | | const bool colocated, |
172 | | const PgObjectId& tablegroup_oid, |
173 | | const PgObjectId& tablespace_oid, |
174 | | const PgObjectId& matview_pg_table_oid) |
175 | 1.42k | : PgDdl(pg_session) { |
176 | 1.42k | table_id.ToPB(req_.mutable_table_id()); |
177 | 1.42k | req_.set_database_name(database_name); |
178 | 1.42k | req_.set_table_name(table_name); |
179 | 1.42k | req_.set_num_tablets(-1); |
180 | 1.42k | req_.set_is_pg_catalog_table(strcmp(schema_name, "pg_catalog") == 0 || |
181 | 1.42k | strcmp(schema_name, "information_schema") == 0); |
182 | 1.42k | req_.set_is_shared_table(is_shared_table); |
183 | 1.42k | req_.set_if_not_exist(if_not_exist); |
184 | 1.42k | req_.set_colocated(colocated); |
185 | 1.42k | req_.set_schema_name(schema_name); |
186 | 1.42k | tablegroup_oid.ToPB(req_.mutable_tablegroup_oid()); |
187 | 1.42k | tablespace_oid.ToPB(req_.mutable_tablespace_oid()); |
188 | 1.42k | matview_pg_table_oid.ToPB(req_.mutable_matview_pg_table_oid()); |
189 | | |
190 | | // Add internal primary key column to a Postgres table without a user-specified primary key. |
191 | 1.42k | if (add_primary_key) { |
192 | | // For regular user table, ybrowid should be a hash key because ybrowid is a random uuid. |
193 | | // For colocated or sys catalog table, ybrowid should be a range key because they are |
194 | | // unpartitioned tables in a single tablet. |
195 | 570 | bool is_hash = !(req_.is_pg_catalog_table() || colocated || tablegroup_oid.IsValid()); |
196 | 570 | CHECK_OK(AddColumn("ybrowid", static_cast<int32_t>(PgSystemAttrNum::kYBRowId), |
197 | 570 | YB_YQL_DATA_TYPE_BINARY, is_hash, true /* is_range */)); |
198 | 570 | } |
199 | 1.42k | } |
200 | | |
201 | | Status PgCreateTable::AddColumnImpl(const char *attr_name, |
202 | | int attr_num, |
203 | | int attr_ybtype, |
204 | | int pg_type_oid, |
205 | | bool is_hash, |
206 | | bool is_range, |
207 | 3.61k | SortingType sorting_type) { |
208 | 3.61k | auto& column = *req_.mutable_create_columns()->Add(); |
209 | 3.61k | column.set_attr_name(attr_name); |
210 | 3.61k | column.set_attr_num(attr_num); |
211 | 3.61k | column.set_attr_ybtype(attr_ybtype); |
212 | 3.61k | column.set_is_hash(is_hash); |
213 | 3.61k | column.set_is_range(is_range); |
214 | 3.61k | column.set_sorting_type(sorting_type); |
215 | 3.61k | column.set_attr_pgoid(pg_type_oid); |
216 | 3.61k | return Status::OK(); |
217 | 3.61k | } |
218 | | |
219 | 162 | Status PgCreateTable::SetNumTablets(int32_t num_tablets) { |
220 | 162 | if (num_tablets < 0) { |
221 | 0 | return STATUS(InvalidArgument, "num_tablets cannot be less than zero"); |
222 | 0 | } |
223 | 162 | if (num_tablets > FLAGS_max_num_tablets_for_table) { |
224 | 0 | return STATUS(InvalidArgument, "num_tablets exceeds system limit"); |
225 | 0 | } |
226 | | |
227 | 162 | req_.set_num_tablets(num_tablets); |
228 | 162 | return Status::OK(); |
229 | 162 | } |
230 | | |
231 | 15 | Status PgCreateTable::AddSplitBoundary(PgExpr **exprs, int expr_count) { |
232 | 15 | auto* values = req_.mutable_split_bounds()->Add()->mutable_values(); |
233 | 42 | for (int i = 0; i < expr_count; ++i) { |
234 | 27 | RETURN_NOT_OK(exprs[i]->Eval(values->Add())); |
235 | 27 | } |
236 | 15 | return Status::OK(); |
237 | 15 | } |
238 | | |
239 | 1.41k | Status PgCreateTable::Exec() { |
240 | 1.41k | RETURN_NOT_OK(pg_session_->pg_client().CreateTable(&req_, DdlDeadline())); |
241 | 1.41k | auto base_table_id = PgObjectId::FromPB(req_.base_table_id()); |
242 | 1.41k | if (base_table_id.IsValid()) { |
243 | 150 | pg_session_->InvalidateTableCache(base_table_id, InvalidateOnPgClient::kFalse); |
244 | 150 | } |
245 | 1.41k | return Status::OK(); |
246 | 1.41k | } |
247 | | |
248 | | void PgCreateTable::SetupIndex( |
249 | 151 | const PgObjectId& base_table_id, bool is_unique_index, bool skip_index_backfill) { |
250 | 151 | base_table_id.ToPB(req_.mutable_base_table_id()); |
251 | 151 | req_.set_is_unique_index(is_unique_index); |
252 | 151 | req_.set_skip_index_backfill(skip_index_backfill); |
253 | 151 | } |
254 | | |
255 | 4.64k | StmtOp PgCreateTable::stmt_op() const { |
256 | 4.64k | return PgObjectId::FromPB(req_.base_table_id()).IsValid() |
257 | 4.28k | ? StmtOp::STMT_CREATE_INDEX : StmtOp::STMT_CREATE_TABLE; |
258 | 4.64k | } |
259 | | |
260 | | //-------------------------------------------------------------------------------------------------- |
261 | | // PgDropTable |
262 | | //-------------------------------------------------------------------------------------------------- |
263 | | |
264 | | PgDropTable::PgDropTable(PgSession::ScopedRefPtr pg_session, |
265 | | const PgObjectId& table_id, |
266 | | bool if_exist) |
267 | | : PgDdl(pg_session), |
268 | | table_id_(table_id), |
269 | 1.17k | if_exist_(if_exist) { |
270 | 1.17k | } |
271 | | |
272 | 1.17k | PgDropTable::~PgDropTable() { |
273 | 1.17k | } |
274 | | |
275 | 1.03k | Status PgDropTable::Exec() { |
276 | 1.03k | Status s = pg_session_->DropTable(table_id_); |
277 | 1.03k | pg_session_->InvalidateTableCache(table_id_, InvalidateOnPgClient::kFalse); |
278 | 1.03k | if (s.ok() || (s.IsNotFound() && if_exist_)) { |
279 | 1.03k | return Status::OK(); |
280 | 1.03k | } |
281 | 1 | return s; |
282 | 1 | } |
283 | | |
284 | | //-------------------------------------------------------------------------------------------------- |
285 | | // PgTruncateTable |
286 | | //-------------------------------------------------------------------------------------------------- |
287 | | |
288 | | PgTruncateTable::PgTruncateTable(PgSession::ScopedRefPtr pg_session, |
289 | | const PgObjectId& table_id) |
290 | 31 | : PgDdl(pg_session) { |
291 | 31 | table_id.ToPB(req_.mutable_table_id()); |
292 | 31 | } |
293 | | |
294 | 31 | PgTruncateTable::~PgTruncateTable() { |
295 | 31 | } |
296 | | |
297 | 31 | Status PgTruncateTable::Exec() { |
298 | 31 | return pg_session_->pg_client().TruncateTable(&req_, DdlDeadline()); |
299 | 31 | } |
300 | | |
301 | | //-------------------------------------------------------------------------------------------------- |
302 | | // PgDropIndex |
303 | | //-------------------------------------------------------------------------------------------------- |
304 | | |
305 | | PgDropIndex::PgDropIndex(PgSession::ScopedRefPtr pg_session, |
306 | | const PgObjectId& index_id, |
307 | | bool if_exist) |
308 | 141 | : PgDropTable(pg_session, index_id, if_exist) { |
309 | 141 | } |
310 | | |
311 | 141 | PgDropIndex::~PgDropIndex() { |
312 | 141 | } |
313 | | |
314 | 141 | Status PgDropIndex::Exec() { |
315 | 141 | client::YBTableName indexed_table_name; |
316 | 141 | Status s = pg_session_->DropIndex(table_id_, &indexed_table_name); |
317 | 141 | if (s.ok() || (s.IsNotFound() && if_exist_)) { |
318 | 140 | RSTATUS_DCHECK(!indexed_table_name.empty(), Uninitialized, "indexed_table_name uninitialized"); |
319 | 140 | PgObjectId indexed_table_id(indexed_table_name.table_id()); |
320 | | |
321 | 140 | pg_session_->InvalidateTableCache(table_id_, InvalidateOnPgClient::kFalse); |
322 | 140 | pg_session_->InvalidateTableCache(indexed_table_id, InvalidateOnPgClient::kFalse); |
323 | 140 | return Status::OK(); |
324 | 140 | } |
325 | 1 | return s; |
326 | 1 | } |
327 | | |
328 | | //-------------------------------------------------------------------------------------------------- |
329 | | // PgAlterTable |
330 | | //-------------------------------------------------------------------------------------------------- |
331 | | |
332 | | PgAlterTable::PgAlterTable(PgSession::ScopedRefPtr pg_session, |
333 | | const PgObjectId& table_id) |
334 | 661 | : PgDdl(pg_session) { |
335 | 661 | table_id.ToPB(req_.mutable_table_id()); |
336 | 661 | } |
337 | | |
338 | | Status PgAlterTable::AddColumn(const char *name, |
339 | | const YBCPgTypeEntity *attr_type, |
340 | 69 | int order) { |
341 | 69 | auto& col = *req_.mutable_add_columns()->Add(); |
342 | 69 | col.set_attr_name(name); |
343 | 69 | col.set_attr_ybtype(attr_type->yb_type); |
344 | 69 | col.set_attr_num(order); |
345 | 69 | col.set_attr_pgoid(attr_type->type_oid); |
346 | 69 | return Status::OK(); |
347 | 69 | } |
348 | | |
349 | 1 | Status PgAlterTable::RenameColumn(const char *oldname, const char *newname) { |
350 | 1 | auto& rename = *req_.mutable_rename_columns()->Add(); |
351 | 1 | rename.set_old_name(oldname); |
352 | 1 | rename.set_new_name(newname); |
353 | 1 | return Status::OK(); |
354 | 1 | } |
355 | | |
356 | 116 | Status PgAlterTable::DropColumn(const char *name) { |
357 | 116 | req_.mutable_drop_columns()->Add(name); |
358 | 116 | return Status::OK(); |
359 | 116 | } |
360 | | |
361 | 42 | Status PgAlterTable::RenameTable(const char *db_name, const char *newname) { |
362 | 42 | auto& rename = *req_.mutable_rename_table(); |
363 | 42 | rename.set_database_name(db_name); |
364 | 42 | rename.set_table_name(newname); |
365 | 42 | return Status::OK(); |
366 | 42 | } |
367 | | |
368 | 155 | Status PgAlterTable::Exec() { |
369 | 155 | RETURN_NOT_OK(pg_session_->pg_client().AlterTable(&req_, DdlDeadline())); |
370 | 154 | pg_session_->InvalidateTableCache( |
371 | 154 | PgObjectId::FromPB(req_.table_id()), InvalidateOnPgClient::kFalse); |
372 | 154 | return Status::OK(); |
373 | 155 | } |
374 | | |
375 | 661 | PgAlterTable::~PgAlterTable() { |
376 | 661 | } |
377 | | |
378 | | } // namespace pggate |
379 | | } // namespace yb |