/Users/deen/code/yugabyte-db/src/yb/tserver/pg_create_table.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/tserver/pg_create_table.h" |
15 | | |
16 | | #include "yb/client/client.h" |
17 | | #include "yb/client/table.h" |
18 | | #include "yb/client/table_creator.h" |
19 | | |
20 | | #include "yb/common/pg_system_attr.h" |
21 | | #include "yb/common/ql_type.h" |
22 | | #include "yb/common/schema.h" |
23 | | |
24 | | #include "yb/docdb/doc_key.h" |
25 | | #include "yb/docdb/value_type.h" |
26 | | |
27 | | #include "yb/gutil/casts.h" |
28 | | |
29 | | #include "yb/tserver/pg_client.pb.h" |
30 | | |
31 | | #include "yb/util/result.h" |
32 | | #include "yb/util/status_format.h" |
33 | | |
34 | | namespace yb { |
35 | | namespace tserver { |
36 | | |
37 | 1.41k | PgCreateTable::PgCreateTable(const PgCreateTableRequestPB& req) : req_(req) { |
38 | 1.41k | } |
39 | | |
40 | 1.41k | Status PgCreateTable::Prepare() { |
41 | 1.41k | table_name_ = client::YBTableName( |
42 | 1.41k | YQL_DATABASE_PGSQL, GetPgsqlNamespaceId(req_.table_id().database_oid()), |
43 | 1.41k | req_.database_name(), req_.table_name()); |
44 | 1.41k | indexed_table_id_ = PgObjectId::FromPB(req_.base_table_id()); |
45 | | |
46 | 3.60k | for (const auto& create_column : req_.create_columns()) { |
47 | 3.60k | RETURN_NOT_OK(AddColumn(create_column)); |
48 | 3.60k | } |
49 | | |
50 | 1.41k | EnsureYBbasectidColumnCreated(); |
51 | | |
52 | 1.41k | if (!req_.split_bounds().empty()) { |
53 | 6 | if (hash_schema_.is_initialized()) { |
54 | 0 | return STATUS(InvalidArgument, |
55 | 0 | "SPLIT AT option is not yet supported for hash partitioned tables"); |
56 | 0 | } |
57 | 1.41k | } |
58 | | |
59 | 1.41k | return Status::OK(); |
60 | 1.41k | } |
61 | | |
62 | | Status PgCreateTable::Exec( |
63 | | client::YBClient* client, const TransactionMetadata* transaction_metadata, |
64 | 1.41k | CoarseTimePoint deadline) { |
65 | | // Construct schema. |
66 | 1.41k | client::YBSchema schema; |
67 | | |
68 | 1.41k | const char* pg_txn_enabled_env_var = getenv("YB_PG_TRANSACTIONS_ENABLED"); |
69 | 1.41k | const bool transactional = |
70 | 1.41k | !pg_txn_enabled_env_var || strcmp(pg_txn_enabled_env_var, "1") == 0; |
71 | 1.41k | LOG(INFO) << Format( |
72 | 1.41k | "PgCreateTable: creating a $0 $1: $2/$3", |
73 | 1.41k | transactional ? "transactional" : "non-transactional", |
74 | 1.26k | indexed_table_id_.IsValid() ? "index" : "table", |
75 | 1.41k | table_name_, PgObjectId::FromPB(req_.table_id())); |
76 | 1.41k | TableProperties table_properties; |
77 | 1.41k | bool set_table_properties = false; |
78 | 1.41k | if (transactional) { |
79 | 1.41k | table_properties.SetTransactional(true); |
80 | 1.41k | set_table_properties = true; |
81 | 1.41k | } |
82 | 1.41k | if (req_.num_tablets() > 0) { |
83 | 162 | table_properties.SetNumTablets(req_.num_tablets()); |
84 | 162 | set_table_properties = true; |
85 | 162 | } |
86 | 1.41k | if (set_table_properties) { |
87 | 1.41k | schema_builder_.SetTableProperties(table_properties); |
88 | 1.41k | } |
89 | 1.41k | if (!req_.schema_name().empty()) { |
90 | 1.41k | schema_builder_.SetSchemaName(req_.schema_name()); |
91 | 1.41k | } |
92 | | |
93 | 1.41k | RETURN_NOT_OK(schema_builder_.Build(&schema)); |
94 | 1.41k | const auto split_rows = VERIFY_RESULT(BuildSplitRows(schema)); |
95 | | |
96 | | // Create table. |
97 | 1.41k | auto table_creator = client->NewTableCreator(); |
98 | 1.41k | table_creator->table_name(table_name_).table_type(client::YBTableType::PGSQL_TABLE_TYPE) |
99 | 1.41k | .table_id(PgObjectId::GetYBTableIdFromPB(req_.table_id())) |
100 | 1.41k | .schema(&schema) |
101 | 1.41k | .colocated(req_.colocated()); |
102 | 1.41k | if (req_.is_pg_catalog_table()) { |
103 | 0 | table_creator->is_pg_catalog_table(); |
104 | 0 | } |
105 | 1.41k | if (req_.is_shared_table()) { |
106 | 0 | table_creator->is_pg_shared_table(); |
107 | 0 | } |
108 | 1.41k | if (hash_schema_) { |
109 | 1.31k | table_creator->hash_schema(*hash_schema_); |
110 | 103 | } else if (!req_.is_pg_catalog_table()) { |
111 | 103 | table_creator->set_range_partition_columns(range_columns_, split_rows); |
112 | 103 | } |
113 | | |
114 | 1.41k | auto tablegroup_oid = PgObjectId::FromPB(req_.tablegroup_oid()); |
115 | 1.41k | if (tablegroup_oid.IsValid()) { |
116 | 7 | table_creator->tablegroup_id(tablegroup_oid.GetYBTablegroupId()); |
117 | 7 | } |
118 | | |
119 | 1.41k | auto tablespace_oid = PgObjectId::FromPB(req_.tablespace_oid()); |
120 | 1.41k | if (tablespace_oid.IsValid()) { |
121 | 0 | table_creator->tablespace_id(tablespace_oid.GetYBTablespaceId()); |
122 | 0 | } |
123 | | |
124 | 1.41k | auto matview_pg_table_oid = PgObjectId::FromPB(req_.matview_pg_table_oid()); |
125 | 1.41k | if (matview_pg_table_oid.IsValid()) { |
126 | 0 | table_creator->matview_pg_table_id(matview_pg_table_oid.GetYBTableId()); |
127 | 0 | } |
128 | | |
129 | | // For index, set indexed (base) table id. |
130 | 1.41k | if (indexed_table_id_.IsValid()) { |
131 | 151 | table_creator->indexed_table_id(indexed_table_id_.GetYBTableId()); |
132 | 151 | if (req_.is_unique_index()) { |
133 | 52 | table_creator->is_unique_index(true); |
134 | 52 | } |
135 | 151 | if (req_.skip_index_backfill()) { |
136 | 61 | table_creator->skip_index_backfill(true); |
137 | 61 | } |
138 | 151 | } |
139 | | |
140 | 1.41k | if (transaction_metadata) { |
141 | 1.41k | table_creator->part_of_transaction(transaction_metadata); |
142 | 1.41k | } |
143 | | |
144 | 1.41k | table_creator->timeout(deadline - CoarseMonoClock::now()); |
145 | | |
146 | 1.41k | const Status s = table_creator->Create(); |
147 | 1.41k | if (PREDICT_FALSE(!s.ok())) { |
148 | 4 | if (s.IsAlreadyPresent()) { |
149 | 0 | if (req_.if_not_exist()) { |
150 | 0 | return Status::OK(); |
151 | 0 | } |
152 | 0 | return STATUS(InvalidArgument, "Duplicate table"); |
153 | 0 | } |
154 | 4 | if (s.IsNotFound()) { |
155 | 0 | return STATUS(InvalidArgument, "Database not found", table_name_.namespace_name()); |
156 | 0 | } |
157 | 4 | return STATUS_FORMAT( |
158 | 4 | InvalidArgument, "Invalid table definition: $0", |
159 | 4 | s.ToString(false /* include_file_and_line */, false /* include_code */)); |
160 | 4 | } |
161 | | |
162 | 1.41k | return Status::OK(); |
163 | 1.41k | } |
164 | | |
165 | 3.60k | Status PgCreateTable::AddColumn(const PgCreateColumnPB& req) { |
166 | 3.60k | auto yb_type = QLType::Create(static_cast<DataType>(req.attr_ybtype())); |
167 | 3.60k | if (!req.is_hash() && !req.is_range()) { |
168 | 2.05k | EnsureYBbasectidColumnCreated(); |
169 | 2.05k | } |
170 | 3.60k | client::YBColumnSpec* col = schema_builder_.AddColumn(req.attr_name())->Type(yb_type); |
171 | 3.60k | col->Order(req.attr_num()); |
172 | 3.60k | auto sorting_type = static_cast<SortingType>(req.sorting_type()); |
173 | 3.60k | if (req.is_hash()) { |
174 | 1.32k | if (!range_columns_.empty()) { |
175 | 0 | return STATUS(InvalidArgument, "Hash column not allowed after an ASC/DESC column"); |
176 | 0 | } |
177 | 1.32k | if (sorting_type != SortingType::kNotSpecified) { |
178 | 0 | return STATUS(InvalidArgument, "Hash column can't have sorting order"); |
179 | 0 | } |
180 | 1.32k | col->HashPrimaryKey(); |
181 | 1.32k | hash_schema_ = YBHashSchema::kPgsqlHash; |
182 | 2.28k | } else if (req.is_range()) { |
183 | 232 | col->PrimaryKey(); |
184 | 232 | range_columns_.emplace_back(req.attr_name()); |
185 | 232 | } |
186 | 3.60k | col->SetSortingType(sorting_type); |
187 | 3.60k | col->PgTypeOid(req.attr_pgoid()); |
188 | 3.60k | return Status::OK(); |
189 | 3.60k | } |
190 | | |
191 | 3.47k | void PgCreateTable::EnsureYBbasectidColumnCreated() { |
192 | 3.47k | if (ybbasectid_added_ || !indexed_table_id_.IsValid()) { |
193 | 3.32k | return; |
194 | 3.32k | } |
195 | | |
196 | 151 | auto yb_type = QLType::Create(DataType::BINARY); |
197 | | |
198 | | // Add YBUniqueIdxKeySuffix column to store key suffix for handling multiple NULL values in |
199 | | // column with unique index. |
200 | | // Value of this column is set to ybctid (same as ybbasectid) for index row in case index |
201 | | // is unique and at least one of its key column is NULL. |
202 | | // In all other case value of this column is NULL. |
203 | 151 | if (req_.is_unique_index()) { |
204 | 52 | auto name = "ybuniqueidxkeysuffix"; |
205 | 52 | client::YBColumnSpec* col = schema_builder_.AddColumn(name)->Type(yb_type); |
206 | 52 | col->Order(to_underlying(PgSystemAttrNum::kYBUniqueIdxKeySuffix)); |
207 | 52 | col->PrimaryKey(); |
208 | 52 | range_columns_.emplace_back(name); |
209 | 52 | } |
210 | | |
211 | | // Add ybbasectid column to store the ybctid of the rows in the indexed table. It should be |
212 | | // added at the end of the primary key of the index, i.e. either before any non-primary-key |
213 | | // column if any or before exec() below. |
214 | 151 | auto name = "ybidxbasectid"; |
215 | 151 | client::YBColumnSpec* col = schema_builder_.AddColumn(name)->Type(yb_type); |
216 | 151 | col->Order(to_underlying(PgSystemAttrNum::kYBIdxBaseTupleId)); |
217 | 151 | if (!req_.is_unique_index()) { |
218 | 99 | col->PrimaryKey(); |
219 | 99 | range_columns_.emplace_back(name); |
220 | 99 | } |
221 | | |
222 | 151 | ybbasectid_added_ = true; |
223 | 151 | } |
224 | | |
225 | 1.41k | Result<std::vector<std::string>> PgCreateTable::BuildSplitRows(const client::YBSchema& schema) { |
226 | 1.41k | std::vector<std::string> rows; |
227 | 1.41k | rows.reserve(req_.split_bounds().size()); |
228 | 1.41k | docdb::DocKey prev_doc_key; |
229 | 15 | for (const auto& bounds : req_.split_bounds()) { |
230 | 15 | const auto& row = bounds.values(); |
231 | 15 | SCHECK_EQ( |
232 | 15 | implicit_cast<size_t>(row.size()), |
233 | 15 | PrimaryKeyRangeColumnCount() - (ybbasectid_added_ ? 1 : 0), |
234 | 15 | IllegalState, |
235 | 15 | "Number of split row values must be equal to number of primary key columns"); |
236 | 15 | std::vector<docdb::PrimitiveValue> range_components; |
237 | 15 | range_components.reserve(row.size()); |
238 | 15 | bool compare_columns = true; |
239 | 27 | for (const auto& row_value : row) { |
240 | 27 | const auto column_index = range_components.size(); |
241 | 27 | range_components.push_back(row_value.value_case() == QLValuePB::VALUE_NOT_SET |
242 | 0 | ? docdb::PrimitiveValue(docdb::ValueType::kLowest) |
243 | 27 | : docdb::PrimitiveValue::FromQLValuePB( |
244 | 27 | row_value, |
245 | 27 | schema.Column(schema.FindColumn(range_columns_[column_index])).sorting_type())); |
246 | | |
247 | | // Validate that split rows honor column ordering. |
248 | 27 | if (compare_columns && !prev_doc_key.empty()) { |
249 | 13 | const auto& prev_value = prev_doc_key.range_group()[column_index]; |
250 | 13 | const auto compare = prev_value.CompareTo(range_components.back()); |
251 | 13 | if (compare > 0) { |
252 | 0 | return STATUS(InvalidArgument, "Split rows ordering does not match column ordering"); |
253 | 13 | } else if (compare < 0) { |
254 | | // Don't need to compare further columns |
255 | 9 | compare_columns = false; |
256 | 9 | } |
257 | 13 | } |
258 | 27 | } |
259 | 15 | prev_doc_key = docdb::DocKey(std::move(range_components)); |
260 | 15 | const auto keybytes = prev_doc_key.Encode(); |
261 | | |
262 | | // Validate that there are no duplicate split rows. |
263 | 15 | if (rows.size() > 0 && keybytes.AsSlice() == Slice(rows.back())) { |
264 | 0 | return STATUS(InvalidArgument, "Cannot have duplicate split rows"); |
265 | 0 | } |
266 | 15 | rows.push_back(keybytes.ToStringBuffer()); |
267 | 15 | } |
268 | 1.41k | return rows; |
269 | 1.41k | } |
270 | | |
271 | 15 | size_t PgCreateTable::PrimaryKeyRangeColumnCount() const { |
272 | 15 | return range_columns_.size(); |
273 | 15 | } |
274 | | |
275 | | } // namespace tserver |
276 | | } // namespace yb |