YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/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