YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/yql/pggate/pg_dml_read.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_dml_read.h"
17
18
#include "yb/client/yb_op.h"
19
20
#include "yb/common/partition.h"
21
#include "yb/common/pg_system_attr.h"
22
#include "yb/common/row_mark.h"
23
#include "yb/common/schema.h"
24
25
#include "yb/docdb/doc_key.h"
26
#include "yb/docdb/primitive_value.h"
27
28
#include "yb/util/status_format.h"
29
30
#include "yb/yql/pggate/pg_select_index.h"
31
#include "yb/yql/pggate/pg_tools.h"
32
#include "yb/yql/pggate/util/pg_doc_data.h"
33
34
namespace yb {
35
namespace pggate {
36
37
namespace {
38
39
template<class Key, class Value, class CompatibleKey>
40
1.66k
auto Find(const boost::unordered_map<Key, Value>& map, const CompatibleKey& key) {
41
1.66k
  return map.find(key, boost::hash<CompatibleKey>(), std::equal_to<CompatibleKey>());
42
1.66k
}
43
44
using DocKeyBuilder = std::function<docdb::DocKey(const vector<docdb::PrimitiveValue>&)>;
45
46
Result<DocKeyBuilder> CreateDocKeyBuilder(
47
    const vector<docdb::PrimitiveValue>& hashed_components,
48
    const google::protobuf::RepeatedPtrField<PgsqlExpressionPB>& hashed_values,
49
6
    const PartitionSchema& partition_schema) {
50
51
6
  if (hashed_values.empty()) {
52
0
    return [](const auto& range_components) {
53
0
      return docdb::DocKey(range_components);
54
0
    };
55
0
  }
56
57
6
  string partition_key;
58
6
  RETURN_NOT_OK(partition_schema.EncodeKey(hashed_values, &partition_key));
59
6
  const auto hash = PartitionSchema::DecodeMultiColumnHashValue(partition_key);
60
61
12
  return [hash, &hashed_components](const auto& range_components) {
62
12
    return docdb::DocKey(hash, hashed_components, range_components);
63
12
  };
64
6
}
65
66
} // namespace
67
68
using std::make_shared;
69
70
//--------------------------------------------------------------------------------------------------
71
// PgDmlRead
72
//--------------------------------------------------------------------------------------------------
73
74
PgDmlRead::PgDmlRead(PgSession::ScopedRefPtr pg_session, const PgObjectId& table_id,
75
                     const PgObjectId& index_id, const PgPrepareParameters *prepare_params)
76
713k
    : PgDml(std::move(pg_session), table_id, index_id, prepare_params) {
77
713k
}
78
79
713k
PgDmlRead::~PgDmlRead() {
80
713k
}
81
82
713k
void PgDmlRead::PrepareBinds() {
83
713k
  if (!bind_) {
84
    // This statement doesn't have bindings.
85
151k
    return;
86
151k
  }
87
88
6.61M
  for (auto& col : bind_.columns()) {
89
6.61M
    col.AllocPrimaryBindPB(read_req_.get());
90
6.61M
  }
91
561k
}
92
93
678k
void PgDmlRead::SetForwardScan(const bool is_forward_scan) {
94
678k
  if (secondary_index_query_) {
95
151k
    return secondary_index_query_->SetForwardScan(is_forward_scan);
96
151k
  }
97
526k
  read_req_->set_is_forward_scan(is_forward_scan);
98
526k
}
99
100
//--------------------------------------------------------------------------------------------------
101
// DML support.
102
// TODO(neil) WHERE clause is not yet supported. Revisit this function when it is.
103
104
9.48k
PgsqlExpressionPB *PgDmlRead::AllocColumnBindPB(PgColumn *col) {
105
9.48k
  return col->AllocBindPB(read_req_.get());
106
9.48k
}
107
108
2.89k
PgsqlExpressionPB *PgDmlRead::AllocColumnBindConditionExprPB(PgColumn *col) {
109
2.89k
  return col->AllocBindConditionExprPB(read_req_.get());
110
2.89k
}
111
112
0
PgsqlExpressionPB *PgDmlRead::AllocColumnAssignPB(PgColumn *col) {
113
  // SELECT statement should not have an assign expression (SET clause).
114
0
  LOG(FATAL) << "Pure virtual function is being called";
115
0
  return nullptr;
116
0
}
117
118
7.54M
PgsqlExpressionPB *PgDmlRead::AllocTargetPB() {
119
7.54M
  return read_req_->add_targets();
120
7.54M
}
121
122
22
PgsqlExpressionPB *PgDmlRead::AllocQualPB() {
123
22
  return read_req_->add_where_clauses();
124
22
}
125
126
7.05M
PgsqlColRefPB *PgDmlRead::AllocColRefPB() {
127
7.05M
  return read_req_->add_col_refs();
128
7.05M
}
129
130
712k
void PgDmlRead::ClearColRefPBs() {
131
712k
  read_req_->clear_col_refs();
132
712k
}
133
134
//--------------------------------------------------------------------------------------------------
135
// RESULT SET SUPPORT.
136
// For now, selected expressions are just a list of column names (ref).
137
//   SELECT column_l, column_m, column_n FROM ...
138
139
712k
void PgDmlRead::SetColumnRefs() {
140
712k
  if (secondary_index_query_) {
141
0
    DCHECK(!has_aggregate_targets()) << "Aggregate pushdown should not happen with index";
142
151k
  }
143
712k
  read_req_->set_is_aggregate(has_aggregate_targets());
144
  // Populate column references in the read request
145
712k
  ColRefsToPB();
146
  // Compatibility: set column ids in a form that is expected by legacy nodes
147
712k
  ColumnRefsToPB(read_req_->mutable_column_refs());
148
712k
}
149
150
// Method removes empty primary binds and moves tailing non empty range primary binds
151
// which are following after empty binds into the 'condition_expr' field.
152
712k
Status PgDmlRead::ProcessEmptyPrimaryBinds() {
153
712k
  if (!bind_) {
154
    // This query does not have any binds.
155
151k
    read_req_->clear_partition_column_values();
156
151k
    read_req_->clear_range_column_values();
157
151k
    return Status::OK();
158
151k
  }
159
160
  // NOTE: ybctid is a system column and not processed as bind.
161
560k
  bool miss_partition_columns = false;
162
560k
  bool has_partition_columns = false;
163
164
757k
  for (size_t index = 0; index != bind_->num_hash_key_columns(); ++index) {
165
196k
    auto expr = bind_.ColumnForIndex(index).bind_pb();
166
    // For IN clause expr->has_condition() returns 'true'.
167
196k
    if (!expr || (!expr->has_condition() && (expr_binds_.find(expr) == expr_binds_.end()))) {
168
24.5k
      miss_partition_columns = true;
169
172k
    } else {
170
172k
      has_partition_columns = true;
171
172k
    }
172
196k
  }
173
174
560k
  SCHECK(!has_partition_columns || !miss_partition_columns, InvalidArgument,
175
560k
      "Partition key must be fully specified");
176
177
560k
  bool preceding_key_column_missed = false;
178
179
560k
  if (miss_partition_columns) {
180
0
    VLOG(1) << "Full scan is needed";
181
24.2k
    read_req_->clear_partition_column_values();
182
    // Reset binding of columns whose values has been deleted.
183
48.8k
    for (size_t i = 0, end = bind_->num_hash_key_columns(); i != end; ++i) {
184
24.5k
      bind_.ColumnForIndex(i).ResetBindPB();
185
24.5k
    }
186
187
    // Move all range column binds (if any) into the 'condition_expr' field.
188
24.2k
    preceding_key_column_missed = true;
189
24.2k
  }
190
191
560k
  int num_bound_range_columns = 0;
192
193
1.46M
  for (auto index = bind_->num_hash_key_columns(); index < bind_->num_key_columns(); ++index) {
194
908k
    auto& col = bind_.ColumnForIndex(index);
195
908k
    auto expr = col.bind_pb();
196
908k
    const auto expr_bind = expr ? expr_binds_.find(expr) : expr_binds_.end();
197
    // For IN clause expr->has_condition() returns 'true'.
198
908k
    if (expr && expr->has_condition()) {
199
216
      preceding_key_column_missed = true;
200
216
      RETURN_NOT_OK(MoveBoundKeyInOperator(&col, expr->condition()));
201
908k
    } else if (expr_bind == expr_binds_.end()) {
202
355k
      preceding_key_column_missed = true;
203
553k
    } else {
204
553k
      if (preceding_key_column_missed) {
205
        // Move current bind into the 'condition_expr' field.
206
2.54k
        PgsqlExpressionPB* condition_expr_pb = AllocColumnBindConditionExprPB(&col);
207
2.54k
        condition_expr_pb->mutable_condition()->set_op(QL_OP_EQUAL);
208
209
2.54k
        auto op1_pb = condition_expr_pb->mutable_condition()->add_operands();
210
2.54k
        auto op2_pb = condition_expr_pb->mutable_condition()->add_operands();
211
212
2.54k
        op1_pb->set_column_id(col.id());
213
214
2.54k
        auto attr_value = expr_bind->second;
215
2.54k
        RETURN_NOT_OK(attr_value->Eval(op2_pb->mutable_value()));
216
2.54k
        expr_binds_.erase(expr_bind);
217
550k
      } else {
218
550k
        ++num_bound_range_columns;
219
550k
      }
220
553k
    }
221
908k
  }
222
223
560k
  auto& range_column_values = *read_req_->mutable_range_column_values();
224
560k
  range_column_values.DeleteSubrange(
225
560k
      num_bound_range_columns, range_column_values.size() - num_bound_range_columns);
226
  // Reset binding of columns whose values has been deleted.
227
6.05M
  for (size_t i = num_bound_range_columns, end = bind_->num_columns(); i != end; ++i) {
228
5.49M
    bind_.ColumnForIndex(i).ResetBindPB();
229
5.49M
  }
230
560k
  return Status::OK();
231
560k
}
232
233
//--------------------------------------------------------------------------------------------------
234
235
215
bool PgDmlRead::IsConcreteRowRead() const {
236
  // Operation reads a concrete row at least one of the following conditions is met:
237
  // - ybctid is explicitly bound
238
  // - ybctid is used implicitly by using secondary index
239
  // - all hash and range key components are bound (Note: each key component can be bound only once)
240
215
  return has_doc_op() && bind_ &&
241
202
         (ybctid_bind_ ||
242
133
          (secondary_index_query_ && secondary_index_query_->has_doc_op()) ||
243
133
          (bind_->num_key_columns() ==
244
133
              static_cast<size_t>(read_req_->partition_column_values_size() +
245
133
                                  read_req_->range_column_values_size())));
246
215
}
247
248
712k
Status PgDmlRead::Exec(const PgExecParameters *exec_params) {
249
  // Save IN/OUT parameters from Postgres.
250
712k
  pg_exec_params_ = exec_params;
251
252
  // Set column references in protobuf and whether query is aggregate.
253
712k
  SetColumnRefs();
254
255
712k
  const auto row_mark_type = GetRowMarkType(exec_params);
256
712k
  if (has_doc_op() &&
257
561k
      !secondary_index_query_ &&
258
409k
      IsValidRowMarkType(row_mark_type) &&
259
6.98k
      CanBuildYbctidsFromPrimaryBinds()) {
260
6
    RETURN_NOT_OK(SubstitutePrimaryBindsWithYbctids(exec_params));
261
712k
  } else {
262
712k
    RETURN_NOT_OK(ProcessEmptyPrimaryBinds());
263
712k
    if (has_doc_op()) {
264
561k
      if (row_mark_type == RowMarkType::ROW_MARK_KEYSHARE && !IsConcreteRowRead()) {
265
        // ROW_MARK_KEYSHARE creates a weak read intent on DocDB side. As a result it is only
266
        // applicable when the read operation reads a concrete row (by using ybctid or by specifying
267
        // all primary key columns). In case some columns of the primary key are not specified,
268
        // a strong read intent is required to prevent rows from being deleted by another
269
        // transaction. For this purpose ROW_MARK_KEYSHARE must be replaced with ROW_MARK_SHARE.
270
69
        auto actual_exec_params = *exec_params;
271
69
        actual_exec_params.rowmark = RowMarkType::ROW_MARK_SHARE;
272
69
        RETURN_NOT_OK(doc_op_->ExecuteInit(&actual_exec_params));
273
561k
      } else {
274
561k
        RETURN_NOT_OK(doc_op_->ExecuteInit(exec_params));
275
561k
      }
276
561k
    }
277
712k
  }
278
279
  // First, process the secondary index request.
280
712k
  bool has_ybctid = VERIFY_RESULT(ProcessSecondaryIndexRequest(exec_params));
281
282
712k
  if (!has_ybctid && secondary_index_query_ && secondary_index_query_->has_doc_op()) {
283
    // No ybctid is found from the IndexScan. Instruct "doc_op_" to abandon the execution and not
284
    // querying any data from tablet server.
285
    //
286
    // Note: For system catalog (colocated table), the secondary_index_query_ won't send a separate
287
    // scan read request to DocDB.  For this case, the index request is embedded inside the SELECT
288
    // request (PgsqlReadRequestPB::index_request).
289
113
    doc_op_->AbandonExecution();
290
712k
  } else {
291
    // Update bind values for constants and placeholders.
292
712k
    RETURN_NOT_OK(UpdateBindPBs());
293
294
    // Execute select statement and prefetching data from DocDB.
295
    // Note: For SysTable, doc_op_ === null, IndexScan doesn't send separate request.
296
712k
    if (doc_op_) {
297
561k
      SCHECK_EQ(VERIFY_RESULT(doc_op_->Execute()), RequestSent::kTrue, IllegalState,
298
561k
                "YSQL read operation was not sent");
299
561k
    }
300
712k
  }
301
302
712k
  return Status::OK();
303
712k
}
304
305
138
Status PgDmlRead::BindColumnCondBetween(int attr_num, PgExpr *attr_value, PgExpr *attr_value_end) {
306
138
  if (secondary_index_query_) {
307
    // Bind by secondary key.
308
4
    return secondary_index_query_->BindColumnCondBetween(attr_num, attr_value, attr_value_end);
309
4
  }
310
311
0
  DCHECK(attr_num != static_cast<int>(PgSystemAttrNum::kYBTupleId))
312
0
    << "Operator BETWEEN cannot be applied to ROWID";
313
314
  // Find column.
315
134
  PgColumn& col = VERIFY_RESULT(bind_.ColumnForAttr(attr_num));
316
317
  // Check datatype.
318
134
  if (attr_value) {
319
115
    SCHECK_EQ(col.internal_type(), attr_value->internal_type(), Corruption,
320
115
              "Attribute value type does not match column type");
321
115
  }
322
323
134
  if (attr_value_end) {
324
112
    SCHECK_EQ(col.internal_type(), attr_value_end->internal_type(), Corruption,
325
112
              "Attribute value type does not match column type");
326
112
  }
327
328
0
  CHECK(!col.is_partition()) << "This method cannot be used for binding partition column!";
329
330
  // Alloc the protobuf.
331
134
  PgsqlExpressionPB *condition_expr_pb = AllocColumnBindConditionExprPB(&col);
332
333
134
  if (attr_value != nullptr) {
334
115
    if (attr_value_end != nullptr) {
335
93
      condition_expr_pb->mutable_condition()->set_op(QL_OP_BETWEEN);
336
337
93
      auto op1_pb = condition_expr_pb->mutable_condition()->add_operands();
338
93
      auto op2_pb = condition_expr_pb->mutable_condition()->add_operands();
339
93
      auto op3_pb = condition_expr_pb->mutable_condition()->add_operands();
340
341
93
      op1_pb->set_column_id(col.id());
342
343
93
      RETURN_NOT_OK(attr_value->Eval(op2_pb->mutable_value()));
344
93
      RETURN_NOT_OK(attr_value_end->Eval(op3_pb->mutable_value()));
345
22
    } else {
346
22
      condition_expr_pb->mutable_condition()->set_op(QL_OP_GREATER_THAN_EQUAL);
347
348
22
      auto op1_pb = condition_expr_pb->mutable_condition()->add_operands();
349
22
      auto op2_pb = condition_expr_pb->mutable_condition()->add_operands();
350
351
22
      op1_pb->set_column_id(col.id());
352
353
22
      RETURN_NOT_OK(attr_value->Eval(op2_pb->mutable_value()));
354
22
    }
355
19
  } else {
356
19
    if (attr_value_end != nullptr) {
357
19
      condition_expr_pb->mutable_condition()->set_op(QL_OP_LESS_THAN_EQUAL);
358
359
19
      auto op1_pb = condition_expr_pb->mutable_condition()->add_operands();
360
19
      auto op2_pb = condition_expr_pb->mutable_condition()->add_operands();
361
362
19
      op1_pb->set_column_id(col.id());
363
364
19
      RETURN_NOT_OK(attr_value_end->Eval(op2_pb->mutable_value()));
365
0
    } else {
366
      // Unreachable.
367
0
    }
368
19
  }
369
370
134
  return Status::OK();
371
134
}
372
373
45.6k
Status PgDmlRead::BindColumnCondIn(int attr_num, int n_attr_values, PgExpr **attr_values) {
374
45.6k
  if (secondary_index_query_) {
375
    // Bind by secondary key.
376
134
    return secondary_index_query_->BindColumnCondIn(attr_num, n_attr_values, attr_values);
377
134
  }
378
379
45.5k
  SCHECK(attr_num != static_cast<int>(PgSystemAttrNum::kYBTupleId),
380
45.5k
         InvalidArgument,
381
45.5k
         "Operator IN cannot be applied to ROWID");
382
383
  // Find column.
384
45.5k
  PgColumn& col = VERIFY_RESULT(bind_.ColumnForAttr(attr_num));
385
386
  // Check datatype.
387
  // TODO(neil) Current code combine TEXT and BINARY datatypes into ONE representation.  Once that
388
  // is fixed, we can remove the special if() check for BINARY type.
389
45.5k
  if (col.internal_type() != InternalType::kBinaryValue) {
390
1.37M
    for (int i = 0; i < n_attr_values; i++) {
391
1.32M
      if (attr_values[i]) {
392
1.32M
        SCHECK_EQ(col.internal_type(), attr_values[i]->internal_type(), Corruption,
393
1.32M
            "Attribute value type does not match column type");
394
1.32M
      }
395
1.32M
    }
396
45.4k
  }
397
398
45.5k
  if (col.is_primary()) {
399
    // Alloc the protobuf.
400
45.5k
    auto *bind_pb = col.bind_pb();
401
45.5k
    if (bind_pb == nullptr) {
402
0
      bind_pb = AllocColumnBindPB(&col);
403
45.5k
    } else {
404
45.5k
      if (expr_binds_.find(bind_pb) != expr_binds_.end()) {
405
0
        LOG(WARNING) << strings::Substitute("Column $0 is already bound to another value.",
406
0
                                            attr_num);
407
0
      }
408
45.5k
    }
409
410
45.5k
    bind_pb->mutable_condition()->set_op(QL_OP_IN);
411
45.5k
    bind_pb->mutable_condition()->add_operands()->set_column_id(col.id());
412
413
    // There's no "list of expressions" field so we simulate it with an artificial nested OR
414
    // with repeated operands, one per bind expression.
415
    // This is only used for operation unrolling in pg_doc_op and is not understood by DocDB.
416
45.5k
    auto op2_pb = bind_pb->mutable_condition()->add_operands();
417
45.5k
    op2_pb->mutable_condition()->set_op(QL_OP_OR);
418
419
1.37M
    for (int i = 0; i < n_attr_values; i++) {
420
1.32M
      auto *attr_pb = op2_pb->mutable_condition()->add_operands();
421
      // Link the expression and protobuf. During execution, expr will write result to the pb.
422
1.32M
      RETURN_NOT_OK(attr_values[i]->PrepareForRead(this, attr_pb));
423
424
1.32M
      expr_binds_[attr_pb] = attr_values[i];
425
1.32M
    }
426
18.4E
  } else {
427
    // Alloc the protobuf.
428
18.4E
    PgsqlExpressionPB *condition_expr_pb = AllocColumnBindConditionExprPB(&col);
429
430
18.4E
    condition_expr_pb->mutable_condition()->set_op(QL_OP_IN);
431
432
18.4E
    auto op1_pb = condition_expr_pb->mutable_condition()->add_operands();
433
18.4E
    auto op2_pb = condition_expr_pb->mutable_condition()->add_operands();
434
435
18.4E
    op1_pb->set_column_id(col.id());
436
437
18.4E
    for (int i = 0; i < n_attr_values; i++) {
438
      // Link the given expression "attr_value" with the allocated protobuf.
439
      // Note that except for constants and place_holders, all other expressions can be setup
440
      // just one time during prepare.
441
      // Examples:
442
      // - Bind values for primary columns in where clause.
443
      //     WHERE hash = ?
444
      // - Bind values for a column in INSERT statement.
445
      //     INSERT INTO a_table(hash, key, col) VALUES(?, ?, ?)
446
447
0
      if (attr_values[i]) {
448
0
        RETURN_NOT_OK(attr_values[i]->Eval(
449
0
            op2_pb->mutable_value()->mutable_list_value()->add_elems()));
450
0
      }
451
0
    }
452
18.4E
  }
453
45.5k
  return Status::OK();
454
45.5k
}
455
456
6
Status PgDmlRead::SubstitutePrimaryBindsWithYbctids(const PgExecParameters* exec_params) {
457
6
  const auto ybctids = VERIFY_RESULT(BuildYbctidsFromPrimaryBinds());
458
6
  std::vector<Slice> ybctids_as_slice;
459
6
  ybctids_as_slice.reserve(ybctids.size());
460
12
  for (const auto& ybctid : ybctids) {
461
12
    ybctids_as_slice.emplace_back(ybctid);
462
12
  }
463
6
  expr_binds_.clear();
464
6
  read_req_->clear_partition_column_values();
465
6
  read_req_->clear_range_column_values();
466
6
  RETURN_NOT_OK(doc_op_->ExecuteInit(exec_params));
467
6
  return doc_op_->PopulateDmlByYbctidOps(ybctids_as_slice);
468
6
}
469
470
// Function builds vector of ybctids from primary key binds.
471
// Required precondition that one and only one range key component has IN clause and all
472
// other key components are set must be checked by caller code.
473
6
Result<std::vector<std::string>> PgDmlRead::BuildYbctidsFromPrimaryBinds() {
474
6
  google::protobuf::RepeatedPtrField<PgsqlExpressionPB> hashed_values;
475
6
  vector<docdb::PrimitiveValue> hashed_components, range_components;
476
6
  hashed_components.reserve(bind_->num_hash_key_columns());
477
6
  range_components.reserve(bind_->num_key_columns() - bind_->num_hash_key_columns());
478
12
  for (size_t i = 0; i < bind_->num_hash_key_columns(); ++i) {
479
6
    auto& col = bind_.ColumnForIndex(i);
480
6
    hashed_components.push_back(VERIFY_RESULT(
481
6
        BuildKeyColumnValue(col, *col.bind_pb(), hashed_values.Add())));
482
6
  }
483
484
6
  auto dockey_builder = VERIFY_RESULT(CreateDocKeyBuilder(
485
6
      hashed_components, hashed_values, bind_->partition_schema()));
486
487
9
  for (size_t i = bind_->num_hash_key_columns(); i < bind_->num_key_columns(); ++i) {
488
9
    auto& col = bind_.ColumnForIndex(i);
489
9
    auto& expr = *col.bind_pb();
490
    // For IN clause expr->has_condition() returns 'true'.
491
9
    if (expr.has_condition()) {
492
6
      const auto prefix_len = range_components.size();
493
      // Form ybctid for each value in IN clause.
494
6
      std::vector<std::string> ybctids;
495
12
      for (const auto& in_exp : expr.condition().operands(1).condition().operands()) {
496
12
        range_components.push_back(VERIFY_RESULT(BuildKeyColumnValue(col, in_exp)));
497
        // Range key component has one and only one IN clause,
498
        // all remains components has explicit values. Add them as is.
499
18
        for (size_t j = i + 1; j < bind_->num_key_columns(); ++j) {
500
6
          auto& suffix_col = bind_.ColumnForIndex(j);
501
6
          range_components.push_back(VERIFY_RESULT(
502
6
              BuildKeyColumnValue(suffix_col, *suffix_col.bind_pb())));
503
6
        }
504
12
        const auto doc_key = dockey_builder(range_components);
505
12
        ybctids.push_back(doc_key.Encode().ToStringBuffer());
506
12
        range_components.resize(prefix_len);
507
12
      }
508
6
      return ybctids;
509
3
    } else {
510
3
      range_components.push_back(VERIFY_RESULT(BuildKeyColumnValue(col, expr)));
511
3
    }
512
9
  }
513
0
  return STATUS(IllegalState, "Can't build ybctids, bad preconditions");
514
6
}
515
516
// Function checks that one and only one range key component has IN clause
517
// and all other key components are set.
518
6.98k
bool PgDmlRead::CanBuildYbctidsFromPrimaryBinds() {
519
6.98k
  if (!bind_) {
520
0
    return false;
521
0
  }
522
523
6.98k
  size_t range_components_in_clause_count = 0;
524
525
7.15k
  for (size_t i = 0; i < bind_->num_key_columns(); ++i) {
526
7.06k
    auto& col = bind_.ColumnForIndex(i);
527
7.06k
    auto* expr = col.bind_pb();
528
    // For IN clause expr->has_condition() returns 'true'.
529
7.06k
    if (expr->has_condition()) {
530
6
      if ((i < bind_->num_hash_key_columns()) || (++range_components_in_clause_count > 1)) {
531
        // unsupported IN clause
532
0
        return false;
533
0
      }
534
7.06k
    } else if (expr_binds_.find(expr) == expr_binds_.end()) {
535
      // missing key component found
536
6.89k
      return false;
537
6.89k
    }
538
7.06k
  }
539
87
  return range_components_in_clause_count == 1;
540
6.98k
}
541
542
// Moves IN operator bound for range key component into 'condition_expr' field
543
216
Status PgDmlRead::MoveBoundKeyInOperator(PgColumn* col, const PgsqlConditionPB& in_operator) {
544
216
  auto* condition_expr_pb = AllocColumnBindConditionExprPB(col);
545
216
  condition_expr_pb->mutable_condition()->set_op(QL_OP_IN);
546
547
216
  auto op1_pb = condition_expr_pb->mutable_condition()->add_operands();
548
216
  op1_pb->set_column_id(col->id());
549
550
216
  auto op2_pb = condition_expr_pb->mutable_condition()->add_operands();
551
817
  for (const auto& expr : in_operator.operands(1).condition().operands()) {
552
817
    RETURN_NOT_OK(CopyBoundValue(
553
817
        *col, expr, op2_pb->mutable_value()->mutable_list_value()->add_elems()));
554
817
    expr_binds_.erase(Find(expr_binds_, &expr));
555
817
  }
556
216
  return Status::OK();
557
216
}
558
559
Status PgDmlRead::CopyBoundValue(
560
844
    const PgColumn& col, const PgsqlExpressionPB& src, QLValuePB* dest) const {
561
  // 'src' expression has no value yet,
562
  // it is used as the key to find actual source in 'expr_binds_'.
563
844
  const auto it = Find(expr_binds_, &src);
564
844
  if (it == expr_binds_.end()) {
565
0
    return STATUS_FORMAT(IllegalState, "Bind value not found for $0", col.id());
566
0
  }
567
844
  return it->second->Eval(dest);
568
844
}
569
570
Result<docdb::PrimitiveValue> PgDmlRead::BuildKeyColumnValue(
571
27
    const PgColumn& col, const PgsqlExpressionPB& src, PgsqlExpressionPB* dest) {
572
27
  RETURN_NOT_OK(CopyBoundValue(col, src, dest->mutable_value()));
573
27
  return docdb::PrimitiveValue::FromQLValuePB(dest->value(), col.desc().sorting_type());
574
27
}
575
576
Result<docdb::PrimitiveValue> PgDmlRead::BuildKeyColumnValue(
577
21
    const PgColumn& col, const PgsqlExpressionPB& src) {
578
21
  PgsqlExpressionPB temp_expr;
579
21
  return BuildKeyColumnValue(col, src, &temp_expr);
580
21
}
581
582
Status PgDmlRead::BindHashCode(bool start_valid, bool start_inclusive,
583
                                uint64_t start_hash_val, bool end_valid,
584
0
                                bool end_inclusive, uint64_t end_hash_val) {
585
0
  if (secondary_index_query_) {
586
0
    return secondary_index_query_->BindHashCode(start_valid, start_inclusive,
587
0
                                                  start_hash_val, end_valid,
588
0
                                                  end_inclusive, end_hash_val);
589
0
  }
590
0
  if (start_valid) {
591
0
    read_req_->mutable_lower_bound()
592
0
            ->set_key(PartitionSchema::EncodeMultiColumnHashValue
593
0
                      (start_hash_val));
594
0
    read_req_->mutable_lower_bound()->set_is_inclusive(start_inclusive);
595
0
  }
596
597
0
  if (end_valid) {
598
0
    read_req_->mutable_upper_bound()
599
0
              ->set_key(PartitionSchema::EncodeMultiColumnHashValue
600
0
                        (end_hash_val));
601
0
    read_req_->mutable_upper_bound()->set_is_inclusive(end_inclusive);
602
0
  }
603
0
  return Status::OK();
604
0
}
605
606
}  // namespace pggate
607
}  // namespace yb