/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 |