YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/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
namespace {
38
39
//--------------------------------------------------------------------------------------------------
40
// Constants used for the sequences data table.
41
//--------------------------------------------------------------------------------------------------
42
static constexpr const char* const kPgSequencesNamespaceName = "system_postgres";
43
static constexpr const char* const kPgSequencesDataTableName = "sequences_data";
44
45
// Columns names and ids.
46
static constexpr const char* const kPgSequenceDbOidColName = "db_oid";
47
48
static constexpr const char* const kPgSequenceSeqOidColName = "seq_oid";
49
50
static constexpr const char* const kPgSequenceLastValueColName = "last_value";
51
52
static constexpr const char* const kPgSequenceIsCalledColName = "is_called";
53
54
} // namespace
55
56
5.05k
PgCreateTable::PgCreateTable(const PgCreateTableRequestPB& req) : req_(req) {
57
5.05k
}
58
59
5.05k
Status PgCreateTable::Prepare() {
60
5.05k
  table_name_ = client::YBTableName(
61
5.05k
    YQL_DATABASE_PGSQL, GetPgsqlNamespaceId(req_.table_id().database_oid()),
62
5.05k
    req_.database_name(), req_.table_name());
63
5.05k
  indexed_table_id_ = PgObjectId::FromPB(req_.base_table_id());
64
65
13.9k
  for (const auto& create_column : req_.create_columns()) {
66
13.9k
    RETURN_NOT_OK(AddColumn(create_column));
67
13.9k
  }
68
69
5.05k
  EnsureYBbasectidColumnCreated();
70
71
5.05k
  if (!req_.split_bounds().empty()) {
72
85
    if (hash_schema_.is_initialized()) {
73
1
      return STATUS(InvalidArgument,
74
1
                    "SPLIT AT option is not yet supported for hash partitioned tables");
75
1
    }
76
85
  }
77
78
5.05k
  return Status::OK();
79
5.05k
}
80
81
Status PgCreateTable::Exec(
82
    client::YBClient* client, const TransactionMetadata* transaction_metadata,
83
5.05k
    CoarseTimePoint deadline) {
84
  // Construct schema.
85
5.05k
  client::YBSchema schema;
86
87
5.05k
  const char* pg_txn_enabled_env_var = getenv("YB_PG_TRANSACTIONS_ENABLED");
88
5.05k
  const bool transactional =
89
5.05k
      !pg_txn_enabled_env_var || 
strcmp(pg_txn_enabled_env_var, "1") == 00
;
90
5.05k
  LOG(INFO) << Format(
91
5.05k
      "PgCreateTable: creating a $0 $1: $2/$3",
92
5.05k
      transactional ? "transactional" : 
"non-transactional"0
,
93
5.05k
      indexed_table_id_.IsValid() ? 
"index"862
:
"table"4.19k
,
94
5.05k
      table_name_, PgObjectId::FromPB(req_.table_id()));
95
5.05k
  TableProperties table_properties;
96
5.05k
  bool set_table_properties = false;
97
5.05k
  if (transactional) {
98
5.05k
    table_properties.SetTransactional(true);
99
5.05k
    set_table_properties = true;
100
5.05k
  }
101
5.05k
  if (req_.num_tablets() > 0) {
102
352
    table_properties.SetNumTablets(req_.num_tablets());
103
352
    set_table_properties = true;
104
352
  }
105
5.05k
  if (set_table_properties) {
106
5.05k
    schema_builder_.SetTableProperties(table_properties);
107
5.05k
  }
108
5.05k
  if (!req_.schema_name().empty()) {
109
5.05k
    schema_builder_.SetSchemaName(req_.schema_name());
110
5.05k
  }
111
112
5.05k
  RETURN_NOT_OK(schema_builder_.Build(&schema));
113
5.05k
  const auto split_rows = 
VERIFY_RESULT5.05k
(BuildSplitRows(schema));5.05k
114
115
  // Create table.
116
0
  auto table_creator = client->NewTableCreator();
117
5.05k
  table_creator->table_name(table_name_).table_type(client::YBTableType::PGSQL_TABLE_TYPE)
118
5.05k
                .table_id(PgObjectId::GetYbTableIdFromPB(req_.table_id()))
119
5.05k
                .schema(&schema)
120
5.05k
                .colocated(req_.colocated());
121
5.05k
  if (req_.is_pg_catalog_table()) {
122
313
    table_creator->is_pg_catalog_table();
123
313
  }
124
5.05k
  if (req_.is_shared_table()) {
125
50
    table_creator->is_pg_shared_table();
126
50
  }
127
5.05k
  if (hash_schema_) {
128
4.32k
    table_creator->hash_schema(*hash_schema_);
129
4.32k
  } else 
if (728
!req_.is_pg_catalog_table()728
) {
130
415
    table_creator->set_range_partition_columns(range_columns_, split_rows);
131
415
  }
132
133
5.05k
  auto tablegroup_oid = PgObjectId::FromPB(req_.tablegroup_oid());
134
5.05k
  if (tablegroup_oid.IsValid()) {
135
89
    table_creator->tablegroup_id(tablegroup_oid.GetYbTablegroupId());
136
89
  }
137
138
5.05k
  if (req_.optional_colocation_id_case() !=
139
5.05k
      PgCreateTableRequestPB::OptionalColocationIdCase::OPTIONAL_COLOCATION_ID_NOT_SET) {
140
24
    table_creator->colocation_id(req_.colocation_id());
141
24
  }
142
143
5.05k
  auto tablespace_oid = PgObjectId::FromPB(req_.tablespace_oid());
144
5.05k
  if (tablespace_oid.IsValid()) {
145
114
    table_creator->tablespace_id(tablespace_oid.GetYbTablespaceId());
146
114
  }
147
148
5.05k
  auto matview_pg_table_oid = PgObjectId::FromPB(req_.matview_pg_table_oid());
149
5.05k
  if (matview_pg_table_oid.IsValid()) {
150
24
    table_creator->matview_pg_table_id(matview_pg_table_oid.GetYbTableId());
151
24
  }
152
153
  // For index, set indexed (base) table id.
154
5.05k
  if (indexed_table_id_.IsValid()) {
155
862
    table_creator->indexed_table_id(indexed_table_id_.GetYbTableId());
156
862
    if (req_.is_unique_index()) {
157
365
      table_creator->is_unique_index(true);
158
365
    }
159
862
    if (req_.skip_index_backfill()) {
160
307
      table_creator->skip_index_backfill(true);
161
307
    }
162
862
  }
163
164
5.05k
  if (transaction_metadata) {
165
4.78k
    table_creator->part_of_transaction(transaction_metadata);
166
4.78k
  }
167
168
5.05k
  table_creator->timeout(deadline - CoarseMonoClock::now());
169
170
5.05k
  const Status s = table_creator->Create();
171
5.05k
  if (PREDICT_FALSE(!s.ok())) {
172
20
    if (s.IsAlreadyPresent()) {
173
0
      if (req_.if_not_exist()) {
174
0
        return Status::OK();
175
0
      }
176
0
      return STATUS(InvalidArgument, "Duplicate table");
177
0
    }
178
20
    if (s.IsNotFound()) {
179
0
      return STATUS(InvalidArgument, "Database not found", table_name_.namespace_name());
180
0
    }
181
20
    return STATUS_FORMAT(
182
20
        InvalidArgument, "Invalid table definition: $0",
183
20
        s.ToString(false /* include_file_and_line */, false /* include_code */));
184
20
  }
185
186
5.03k
  return Status::OK();
187
5.05k
}
188
189
13.9k
Status PgCreateTable::AddColumn(const PgCreateColumnPB& req) {
190
13.9k
  auto yb_type = QLType::Create(static_cast<DataType>(req.attr_ybtype()));
191
13.9k
  if (!req.is_hash() && 
!req.is_range()9.57k
) {
192
8.20k
    EnsureYBbasectidColumnCreated();
193
8.20k
  }
194
13.9k
  client::YBColumnSpec* col = schema_builder_.AddColumn(req.attr_name())->Type(yb_type);
195
13.9k
  col->Order(req.attr_num());
196
13.9k
  auto sorting_type = static_cast<SortingType>(req.sorting_type());
197
13.9k
  if (req.is_hash()) {
198
4.37k
    if (!range_columns_.empty()) {
199
0
      return STATUS(InvalidArgument, "Hash column not allowed after an ASC/DESC column");
200
0
    }
201
4.37k
    if (sorting_type != SortingType::kNotSpecified) {
202
0
      return STATUS(InvalidArgument, "Hash column can't have sorting order");
203
0
    }
204
4.37k
    col->HashPrimaryKey();
205
4.37k
    hash_schema_ = YBHashSchema::kPgsqlHash;
206
9.57k
  } else if (req.is_range()) {
207
1.37k
    col->PrimaryKey();
208
1.37k
    range_columns_.emplace_back(req.attr_name());
209
1.37k
  }
210
13.9k
  col->SetSortingType(sorting_type);
211
13.9k
  col->PgTypeOid(req.attr_pgoid());
212
13.9k
  return Status::OK();
213
13.9k
}
214
215
13.2k
void PgCreateTable::EnsureYBbasectidColumnCreated() {
216
13.2k
  if (ybbasectid_added_ || 
!indexed_table_id_.IsValid()13.2k
) {
217
12.4k
    return;
218
12.4k
  }
219
220
862
  auto yb_type = QLType::Create(DataType::BINARY);
221
222
  // Add YBUniqueIdxKeySuffix column to store key suffix for handling multiple NULL values in
223
  // column with unique index.
224
  // Value of this column is set to ybctid (same as ybbasectid) for index row in case index
225
  // is unique and at least one of its key column is NULL.
226
  // In all other case value of this column is NULL.
227
862
  if (req_.is_unique_index()) {
228
365
    auto name = "ybuniqueidxkeysuffix";
229
365
    client::YBColumnSpec* col = schema_builder_.AddColumn(name)->Type(yb_type);
230
365
    col->Order(to_underlying(PgSystemAttrNum::kYBUniqueIdxKeySuffix));
231
365
    col->PrimaryKey();
232
365
    range_columns_.emplace_back(name);
233
365
  }
234
235
  // Add ybbasectid column to store the ybctid of the rows in the indexed table. It should be
236
  // added at the end of the primary key of the index, i.e. either before any non-primary-key
237
  // column if any or before exec() below.
238
862
  auto name = "ybidxbasectid";
239
862
  client::YBColumnSpec* col = schema_builder_.AddColumn(name)->Type(yb_type);
240
862
  col->Order(to_underlying(PgSystemAttrNum::kYBIdxBaseTupleId));
241
862
  if (!req_.is_unique_index()) {
242
497
    col->PrimaryKey();
243
497
    range_columns_.emplace_back(name);
244
497
  }
245
246
862
  ybbasectid_added_ = true;
247
862
}
248
249
5.05k
Result<std::vector<std::string>> PgCreateTable::BuildSplitRows(const client::YBSchema& schema) {
250
5.05k
  std::vector<std::string> rows;
251
5.05k
  rows.reserve(req_.split_bounds().size());
252
5.05k
  docdb::DocKey prev_doc_key;
253
5.05k
  for (const auto& bounds : req_.split_bounds()) {
254
175
    const auto& row = bounds.values();
255
175
    SCHECK_EQ(
256
175
        implicit_cast<size_t>(row.size()),
257
175
        PrimaryKeyRangeColumnCount() - (ybbasectid_added_ ? 1 : 0),
258
175
        IllegalState,
259
175
        "Number of split row values must be equal to number of primary key columns");
260
175
    std::vector<docdb::PrimitiveValue> range_components;
261
175
    range_components.reserve(row.size());
262
175
    bool compare_columns = true;
263
272
    for (const auto& row_value : row) {
264
272
      const auto column_index = range_components.size();
265
272
      range_components.push_back(row_value.value_case() == QLValuePB::VALUE_NOT_SET
266
272
        ? 
docdb::PrimitiveValue(docdb::ValueType::kLowest)0
267
272
        : docdb::PrimitiveValue::FromQLValuePB(
268
272
            row_value,
269
272
            schema.Column(schema.FindColumn(range_columns_[column_index])).sorting_type()));
270
271
      // Validate that split rows honor column ordering.
272
272
      if (compare_columns && 
!prev_doc_key.empty()227
) {
273
112
        const auto& prev_value = prev_doc_key.range_group()[column_index];
274
112
        const auto compare = prev_value.CompareTo(range_components.back());
275
112
        if (compare > 0) {
276
5
          return STATUS(InvalidArgument, "Split rows ordering does not match column ordering");
277
107
        } else if (compare < 0) {
278
          // Don't need to compare further columns
279
85
          compare_columns = false;
280
85
        }
281
112
      }
282
272
    }
283
170
    prev_doc_key = docdb::DocKey(std::move(range_components));
284
170
    const auto keybytes = prev_doc_key.Encode();
285
286
    // Validate that there are no duplicate split rows.
287
170
    if (rows.size() > 0 && 
keybytes.AsSlice() == Slice(rows.back())86
) {
288
1
      return STATUS(InvalidArgument, "Cannot have duplicate split rows");
289
1
    }
290
169
    rows.push_back(keybytes.ToStringBuffer());
291
169
  }
292
5.05k
  return rows;
293
5.05k
}
294
295
175
size_t PgCreateTable::PrimaryKeyRangeColumnCount() const {
296
175
  return range_columns_.size();
297
175
}
298
299
92
Status CreateSequencesDataTable(client::YBClient* client, CoarseTimePoint deadline) {
300
92
  const client::YBTableName table_name(YQL_DATABASE_PGSQL,
301
92
                                       kPgSequencesDataNamespaceId,
302
92
                                       kPgSequencesNamespaceName,
303
92
                                       kPgSequencesDataTableName);
304
92
  RETURN_NOT_OK(client->CreateNamespaceIfNotExists(kPgSequencesNamespaceName,
305
92
                                                   YQLDatabase::YQL_DATABASE_PGSQL,
306
92
                                                   "" /* creator_role_name */,
307
92
                                                   kPgSequencesDataNamespaceId));
308
309
  // Set up the schema.
310
92
  client::YBSchemaBuilder schemaBuilder;
311
92
  schemaBuilder.AddColumn(kPgSequenceDbOidColName)->HashPrimaryKey()->Type(yb::INT64)->NotNull();
312
92
  schemaBuilder.AddColumn(kPgSequenceSeqOidColName)->HashPrimaryKey()->Type(yb::INT64)->NotNull();
313
92
  schemaBuilder.AddColumn(kPgSequenceLastValueColName)->Type(yb::INT64)->NotNull();
314
92
  schemaBuilder.AddColumn(kPgSequenceIsCalledColName)->Type(yb::BOOL)->NotNull();
315
92
  client::YBSchema schema;
316
92
  CHECK_OK(schemaBuilder.Build(&schema));
317
318
  // Generate the table id.
319
92
  PgObjectId oid(kPgSequencesDataDatabaseOid, kPgSequencesDataTableOid);
320
321
  // Try to create the table.
322
92
  auto table_creator(client->NewTableCreator());
323
324
92
  auto status = table_creator->table_name(table_name)
325
92
      .schema(&schema)
326
92
      .table_type(client::YBTableType::PGSQL_TABLE_TYPE)
327
92
      .table_id(oid.GetYbTableId())
328
92
      .hash_schema(YBHashSchema::kPgsqlHash)
329
92
      .timeout(deadline - CoarseMonoClock::now())
330
92
      .Create();
331
  // If we could create it, then all good!
332
92
  if (status.ok()) {
333
92
    LOG(INFO) << "Table '" << table_name.ToString() << "' created.";
334
    // If the table was already there, also not an error...
335
92
  } else 
if (0
status.IsAlreadyPresent()0
) {
336
0
    LOG(INFO) << "Table '" << table_name.ToString() << "' already exists";
337
0
  } else {
338
    // If any other error, report that!
339
0
    LOG(ERROR) << "Error creating table '" << table_name.ToString() << "': " << status;
340
0
    return status;
341
0
  }
342
92
  return Status::OK();
343
92
}
344
345
}  // namespace tserver
346
}  // namespace yb