/Users/deen/code/yugabyte-db/src/yb/yql/cql/ql/ptree/pt_bcall.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 | | // Treenode definitions for expressions. |
16 | | //-------------------------------------------------------------------------------------------------- |
17 | | |
18 | | #include "yb/yql/cql/ql/ptree/pt_bcall.h" |
19 | | |
20 | | #include "yb/bfql/bfql.h" |
21 | | |
22 | | #include "yb/client/schema.h" |
23 | | #include "yb/client/table.h" |
24 | | |
25 | | #include "yb/common/types.h" |
26 | | |
27 | | #include "yb/yql/cql/ql/ptree/column_desc.h" |
28 | | #include "yb/yql/cql/ql/ptree/pt_dml.h" |
29 | | #include "yb/yql/cql/ql/ptree/sem_context.h" |
30 | | |
31 | | namespace yb { |
32 | | namespace ql { |
33 | | |
34 | | using std::vector; |
35 | | using std::shared_ptr; |
36 | | using std::make_shared; |
37 | | using strings::Substitute; |
38 | | |
39 | | using yb::bfql::BFOpcode; |
40 | | using yb::bfql::BFOPCODE_NOOP; |
41 | | using yb::bfql::TSOpcode; |
42 | | using yb::bfql::BFDecl; |
43 | | |
44 | | using yb::client::YBColumnSchema; |
45 | | |
46 | | using BfuncCompile = yb::bfql::BFCompileApi<PTExpr, PTExpr>; |
47 | | |
48 | | //-------------------------------------------------------------------------------------------------- |
49 | | |
50 | | PTBcall::PTBcall(MemoryContext *memctx, |
51 | | YBLocationPtr loc, |
52 | | const MCSharedPtr<MCString>& name, |
53 | | PTExprListNode::SharedPtr args) |
54 | | : PTExpr(memctx, loc, ExprOperator::kBcall, QLOperator::QL_OP_NOOP), |
55 | | name_(name), |
56 | | args_(args), |
57 | | is_server_operator_(false), |
58 | | bfopcode_(static_cast<int32_t>(BFOPCODE_NOOP)), |
59 | | cast_ops_(memctx), |
60 | 1.15k | result_cast_op_(BFOPCODE_NOOP) { |
61 | 1.15k | } |
62 | | |
63 | 1.00k | PTBcall::~PTBcall() { |
64 | 1.00k | } |
65 | | |
66 | 13 | void PTBcall::CollectReferencedIndexColnames(MCSet<string> *col_names) const { |
67 | 19 | for (auto arg : args_->node_list()) { |
68 | 19 | arg->CollectReferencedIndexColnames(col_names); |
69 | 19 | } |
70 | 13 | } |
71 | | |
72 | 1.45k | string PTBcall::QLName(QLNameOption option) const { |
73 | 1.45k | string arg_names; |
74 | 1.45k | string keyspace; |
75 | | |
76 | | // cql_cast() is displayed as "cast(<col> as <type>)". |
77 | 1.45k | if (strcmp(name_->c_str(), bfql::kCqlCastFuncName) == 0) { |
78 | 118 | CHECK_GE(args_->size(), 2); |
79 | 118 | const string column_name = args_->element(0)->QLName(option); |
80 | 118 | const string type = QLType::ToCQLString(args_->element(1)->ql_type()->type_info()->type); |
81 | 118 | return strings::Substitute("cast($0 as $1)", column_name, type); |
82 | 118 | } |
83 | | |
84 | 1.33k | for (auto arg : args_->node_list()) { |
85 | 1.33k | if (!arg_names.empty()) { |
86 | 0 | arg_names += ", "; |
87 | 0 | } |
88 | 1.33k | arg_names += arg->QLName(option); |
89 | 1.33k | } |
90 | 1.33k | if (IsAggregateCall()) { |
91 | | // count(*) is displayed as "count". |
92 | 334 | if (arg_names.empty()) { |
93 | 116 | return name_->c_str(); |
94 | 116 | } |
95 | 218 | keyspace += "system."; |
96 | 218 | } |
97 | 1.22k | return strings::Substitute("$0$1($2)", keyspace, name_->c_str(), arg_names); |
98 | 1.33k | } |
99 | | |
100 | 1.68k | bool PTBcall::IsAggregateCall() const { |
101 | 1.68k | return is_server_operator_ && BFDecl::is_aggregate_op(static_cast<TSOpcode>(bfopcode_))1.41k ; |
102 | 1.68k | } |
103 | | |
104 | 1.38k | CHECKED_STATUS PTBcall::Analyze(SemContext *sem_context) { |
105 | | // Before traversing the expression, check if this whole expression is actually a column. |
106 | 1.38k | if (CheckIndexColumn(sem_context)) { |
107 | 0 | return Status::OK(); |
108 | 0 | } |
109 | | |
110 | 1.38k | RETURN_NOT_OK(CheckOperator(sem_context)); |
111 | | |
112 | | // Analyze arguments of the function call. |
113 | | // - Because of function overloading, we cannot determine the expected type before reading the |
114 | | // argument. At this point, the expected type is set to unknown, which is compatible with |
115 | | // all data type. |
116 | | // - If the datatype of an argument (such as bind variable) is not determined, and the overloaded |
117 | | // function call cannot be resolved by the rest of the arguments, the parser should raised |
118 | | // error for multiple matches. |
119 | 1.38k | SemState sem_state(sem_context, QLType::Create(UNKNOWN_DATA), InternalType::VALUE_NOT_SET); |
120 | | |
121 | 1.38k | int pindex = 0; |
122 | 1.38k | const MCList<PTExpr::SharedPtr>& exprs = args_->node_list(); |
123 | 1.38k | vector<PTExpr::SharedPtr> params(exprs.size()); |
124 | 1.80k | for (const PTExpr::SharedPtr& expr : exprs) { |
125 | 1.80k | RETURN_NOT_OK(expr->Analyze(sem_context)); |
126 | 1.78k | RETURN_NOT_OK(expr->CheckRhsExpr(sem_context)); |
127 | | |
128 | 1.76k | params[pindex] = expr; |
129 | 1.76k | pindex++; |
130 | 1.76k | } |
131 | | |
132 | 1.35k | RETURN_NOT_OK(CheckOperatorAfterArgAnalyze(sem_context)); |
133 | | |
134 | | // Reset the semantics state after analyzing the arguments. |
135 | 1.35k | sem_state.ResetContextState(); |
136 | | |
137 | | // Type check the builtin call. |
138 | 1.35k | BFOpcode bfopcode; |
139 | 1.35k | const BFDecl *bfdecl; |
140 | 1.35k | PTExpr::SharedPtr pt_result = PTConstArg::MakeShared(sem_context->PTempMem(), loc_, nullptr); |
141 | 1.35k | Status s = BfuncCompile::FindQLOpcode(name_->c_str(), params, &bfopcode, &bfdecl, pt_result); |
142 | 1.35k | if (!s.ok()) { |
143 | 85 | std::string err_msg = string("Failed calling '") + name_->c_str(); |
144 | 85 | bool got_first_arg = false; |
145 | 85 | err_msg += "("; |
146 | 118 | for (auto param : params) { |
147 | 118 | if (got_first_arg) { |
148 | 34 | err_msg += ","; |
149 | 34 | } |
150 | 118 | err_msg += param->ql_type()->ToString(); |
151 | 118 | got_first_arg = true; |
152 | 118 | } |
153 | 85 | err_msg += ")'. "; |
154 | 85 | err_msg += s.ToUserMessage(); |
155 | 85 | LOG(INFO) << err_msg; |
156 | 85 | return sem_context->Error(this, err_msg.c_str(), ErrorCode::INVALID_FUNCTION_CALL); |
157 | 85 | } |
158 | | |
159 | 1.26k | if (!bfdecl->is_server_operator()) { |
160 | | // Use the builtin-function opcode since this is a regular builtin call. |
161 | 925 | bfopcode_ = static_cast<int32_t>(bfopcode); |
162 | | |
163 | 925 | if (*name_ == "cql_cast" || *name_ == "tojson"790 ) { |
164 | | // Argument must be of primitive type for these operators. |
165 | 307 | for (const PTExpr::SharedPtr &expr : exprs) { |
166 | 307 | if (expr->expr_op() == ExprOperator::kCollection) { |
167 | 0 | return sem_context->Error(expr, "Input argument must be of primitive type", |
168 | 0 | ErrorCode::INVALID_ARGUMENTS); |
169 | 0 | } |
170 | 307 | } |
171 | 172 | } |
172 | 925 | } else { |
173 | | // Use the server opcode since this is a server operator. Ignore the BFOpcode. |
174 | 344 | is_server_operator_ = true; |
175 | 344 | TSOpcode tsop = bfdecl->tsopcode(); |
176 | 344 | bfopcode_ = static_cast<int32_t>(tsop); |
177 | | |
178 | | // Check error for special cases. |
179 | | // TODO(neil) This should be part of the builtin table. Each entry in builtin table should have |
180 | | // a function pointer for CheckRequirement(). During analysis, QL will all apply these function |
181 | | // pointers to check for any special requirement of an entry. |
182 | 344 | if (bfql::IsAggregateOpcode(tsop)) { |
183 | 184 | if (!sem_context->allowing_aggregate()) { |
184 | 8 | string errmsg = |
185 | 8 | Substitute("Aggregate function $0() cannot be called in this context", name_->c_str()); |
186 | 8 | return sem_context->Error(loc(), errmsg.c_str(), ErrorCode::INVALID_ARGUMENTS); |
187 | 8 | } |
188 | 176 | const PTExpr::SharedPtr& expr = exprs.front(); |
189 | 176 | if (expr->expr_op() != ExprOperator::kRef && |
190 | 176 | (66 !expr->IsDummyStar()66 || tsop != TSOpcode::kCount58 )) { |
191 | 8 | string errmsg = Substitute("Input argument for $0 must be a column", name_->c_str()); |
192 | 8 | return sem_context->Error(expr->loc(), errmsg.c_str(), ErrorCode::INVALID_ARGUMENTS); |
193 | 8 | } |
194 | 168 | if (tsop == TSOpcode::kMin || tsop == TSOpcode::kMax143 || tsop == TSOpcode::kAvg117 ) { |
195 | 71 | if (pt_result->ql_type()->IsAnyType()) { |
196 | 51 | pt_result->set_ql_type(args_->element(0)->ql_type()); |
197 | 51 | } |
198 | 71 | } |
199 | 168 | } else if (160 tsop == TSOpcode::kTtl160 || tsop == TSOpcode::kWriteTime137 ) { |
200 | 59 | const PTExpr::SharedPtr& expr = exprs.front(); |
201 | 59 | if (expr->expr_op() != ExprOperator::kRef) { |
202 | 0 | string errmsg = Substitute("Input argument for $0 must be a column", name_->c_str()); |
203 | 0 | return sem_context->Error(expr->loc(), errmsg.c_str(), ErrorCode::INVALID_ARGUMENTS); |
204 | 0 | } |
205 | 59 | const PTRef *ref = static_cast<const PTRef *>(expr.get()); |
206 | 59 | if (ref->desc()->is_primary()) { |
207 | 1 | string errmsg = Substitute("Input argument for $0 cannot be primary key", name_->c_str()); |
208 | 1 | return sem_context->Error(expr->loc(), errmsg.c_str(), ErrorCode::INVALID_ARGUMENTS); |
209 | 1 | } |
210 | 58 | if (ref->desc()->ql_type()->IsParametric()) { |
211 | 1 | string errmsg = Substitute("Input argument for $0 is of wrong datatype", name_->c_str()); |
212 | 1 | return sem_context->Error(expr->loc(), errmsg.c_str(), ErrorCode::INVALID_ARGUMENTS); |
213 | 1 | } |
214 | | |
215 | 101 | } else if (bfdecl->is_collection_bcall()) { |
216 | | // Collection operations require special handling during type analysis |
217 | | // 1. Casting check is not needed since conversion between collection types is not allowed |
218 | | // 2. Additional type inference is needed for the parameter types of the collections |
219 | 90 | DCHECK(pt_result->ql_type()->IsParametric()); |
220 | 90 | cast_ops_.resize(exprs.size(), BFOPCODE_NOOP); |
221 | | |
222 | 90 | if (!sem_context->processing_set_clause()) { |
223 | 0 | return sem_context->Error(this, "Collection operations only allowed in set clause", |
224 | 0 | ErrorCode::INVALID_FUNCTION_CALL); |
225 | 0 | } |
226 | | |
227 | 90 | auto it = exprs.begin(); |
228 | 90 | auto col_ref = *it; |
229 | 90 | auto arg = *(++it); |
230 | | |
231 | | // list addition allows column ref to be second argument |
232 | 90 | if (bfopcode == BFOpcode::OPCODE_LIST_PREPEND && col_ref->expr_op() != ExprOperator::kRef9 ) { |
233 | 9 | arg = col_ref; |
234 | 9 | col_ref = *it; |
235 | 9 | } |
236 | | |
237 | 90 | if (col_ref->expr_op() != ExprOperator::kRef) { |
238 | 2 | return sem_context->Error(col_ref.get(), |
239 | 2 | "Expected reference to column of a collection datatype", |
240 | 2 | ErrorCode::INVALID_FUNCTION_CALL); |
241 | 2 | } |
242 | | |
243 | 88 | PTRef *pt_ref = static_cast<PTRef *>(col_ref.get()); |
244 | 88 | if (sem_context->lhs_col() != pt_ref->desc()) { |
245 | 0 | return sem_context->Error( |
246 | 0 | this, |
247 | 0 | "Expected main argument for collection operation to reference the column being set", |
248 | 0 | ErrorCode::INVALID_FUNCTION_CALL); |
249 | 0 | } |
250 | | |
251 | 88 | if (arg->expr_op() != ExprOperator::kCollection && arg->expr_op() != ExprOperator::kBindVar19 ) { |
252 | 12 | return sem_context->Error( |
253 | 12 | this, |
254 | 12 | "Expected auxiliary argument for collection operations to be collection literal", |
255 | 12 | ErrorCode::INVALID_FUNCTION_CALL); |
256 | 12 | } |
257 | | |
258 | 76 | SemState state(sem_context); |
259 | | // Argument type is same as type of the referenced column (except for subtracting from MAP) |
260 | 76 | auto arg_ytype = col_ref->ql_type(); |
261 | 76 | auto arg_itype = col_ref->internal_type(); |
262 | | // Subtraction from MAP takes keys to be removed as param so arg type for MAP<A,B> is SET<A> |
263 | 76 | if (bfopcode == BFOpcode::OPCODE_MAP_REMOVE) { |
264 | 8 | arg_ytype = QLType::CreateTypeSet(col_ref->ql_type()->param_type(0)); |
265 | 8 | arg_itype = InternalType::kSetValue; |
266 | 8 | } |
267 | 76 | state.SetExprState(arg_ytype, arg_itype); |
268 | 76 | state.set_bindvar_name(pt_ref->desc()->name()); |
269 | 76 | RETURN_NOT_OK(arg->Analyze(sem_context)); |
270 | | |
271 | | // Type resolution (for map/set): (cref + <expr>) should have same type as (cref). |
272 | 58 | ql_type_ = col_ref->ql_type(); |
273 | 58 | internal_type_ = arg_itype; |
274 | | |
275 | | // return type is same as type of the referenced column |
276 | 58 | state.SetExprState(col_ref->ql_type(), col_ref->internal_type()); |
277 | 58 | RETURN_NOT_OK(CheckExpectedTypeCompatibility(sem_context)); |
278 | | |
279 | | // For "UPDATE ... SET list = list - x ..." , the list needs to be read first in order to |
280 | | // subtract (remove) elements from it. |
281 | 58 | if (bfopcode == BFOpcode::OPCODE_LIST_REMOVE) { |
282 | 7 | sem_context->current_dml_stmt()->AddColumnRef(*pt_ref->desc()); |
283 | 7 | } |
284 | | |
285 | 58 | return Status::OK(); |
286 | 58 | } |
287 | 344 | } |
288 | | |
289 | | // Find the cast operator if arguments' ql_type_id are not an exact match with signature. |
290 | 1.16k | pindex = 0; |
291 | 1.16k | cast_ops_.resize(exprs.size(), BFOPCODE_NOOP); |
292 | 1.16k | const std::vector<DataType> &formal_types = bfdecl->param_types(); |
293 | 1.32k | for (const auto &expr : exprs) { |
294 | 1.32k | if (formal_types[pindex] == DataType::TYPEARGS) { |
295 | | // For variadic functions, accept all arguments without casting. |
296 | 399 | break; |
297 | 399 | } |
298 | | |
299 | | // Converting or casting arguments to expected type for the function call. |
300 | | // - If argument and formal datatypes are the same, no conversion is needed. It's a NOOP. |
301 | | // - Currently, we only allowed constant expressions which would be folded to the correct type |
302 | | // by the QL engine before creating protobuf messages. |
303 | | // - When we support more complex expressions, CAST would be used for the conversion. It'd be |
304 | | // a bug if casting failed. |
305 | 922 | if (expr->expr_op() == ExprOperator::kConst || expr->expr_op() == ExprOperator::kBindVar512 ) { |
306 | | // Check if constant folding is possible between the two datatype. |
307 | 412 | SemState arg_state(sem_context, |
308 | 412 | QLType::Create(formal_types[pindex]), |
309 | 412 | YBColumnSchema::ToInternalDataType(QLType::Create(formal_types[pindex])), |
310 | 412 | sem_context->bindvar_name()); |
311 | | |
312 | 412 | RETURN_NOT_OK(expr->Analyze(sem_context)); |
313 | | |
314 | 510 | } else if (expr->ql_type_id() != formal_types[pindex]) { |
315 | | // Future usage: Need to cast from argument type to formal type. |
316 | 342 | s = BfuncCompile::FindCastOpcode(expr->ql_type_id(), formal_types[pindex], |
317 | 342 | &cast_ops_[pindex]); |
318 | 342 | if (!s.ok()) { |
319 | 4 | LOG(ERROR) << "Arguments to builtin call " << name_->c_str() << "() is compatible with " |
320 | 4 | << "its signature but converting the argument to the desired type failed"; |
321 | 4 | string err_msg = (s.ToUserMessage() + "CAST " + expr->ql_type()->ToString() + " AS " + |
322 | 4 | QLType::ToCQLString(formal_types[pindex]) + " failed"); |
323 | 4 | return sem_context->Error(this, err_msg.c_str(), ErrorCode::INVALID_FUNCTION_CALL); |
324 | 4 | } |
325 | 342 | } |
326 | 918 | pindex++; |
327 | 918 | } |
328 | | |
329 | | // TODO(neil) Not just BCALL, but each expression should have a "result_cast_op_". At execution |
330 | | // time if the cast is present, the result of the expression must be casted to the correct type. |
331 | | // Find the correct casting op for the result if expected type is not the same as return type. |
332 | 1.15k | if (!sem_context->expr_expected_ql_type()->IsUnknown()) { |
333 | 307 | s = BfuncCompile::FindCastOpcode(pt_result->ql_type()->main(), |
334 | 307 | sem_context->expr_expected_ql_type()->main(), |
335 | 307 | &result_cast_op_); |
336 | 307 | if (!s.ok()) { |
337 | 30 | string err_msg = Substitute("Cannot cast builtin call return type '$0' to expected type '$1'", |
338 | 30 | pt_result->ql_type()->ToString(), |
339 | 30 | sem_context->expr_expected_ql_type()->ToString()); |
340 | 30 | return sem_context->Error(this, err_msg.c_str(), ErrorCode::DATATYPE_MISMATCH); |
341 | 30 | } |
342 | | |
343 | 277 | ql_type_ = sem_context->expr_expected_ql_type(); |
344 | 850 | } else { |
345 | 850 | ql_type_ = pt_result->ql_type(); |
346 | 850 | } |
347 | | |
348 | 1.12k | internal_type_ = yb::client::YBColumnSchema::ToInternalDataType(ql_type_); |
349 | 1.12k | return CheckExpectedTypeCompatibility(sem_context); |
350 | 1.15k | } |
351 | | |
352 | 28 | bool PTBcall::HaveColumnRef() const { |
353 | 28 | const MCList<PTExpr::SharedPtr>& exprs = args_->node_list(); |
354 | 28 | vector<PTExpr::SharedPtr> params(exprs.size()); |
355 | 28 | for (const PTExpr::SharedPtr& expr : exprs) { |
356 | 28 | if (expr->HaveColumnRef()) { |
357 | 7 | return true; |
358 | 7 | } |
359 | 28 | } |
360 | | |
361 | 21 | return false; |
362 | 28 | } |
363 | | |
364 | 990 | CHECKED_STATUS PTBcall::CheckOperator(SemContext *sem_context) { |
365 | 990 | if (sem_context->processing_set_clause() && sem_context->lhs_col() != nullptr187 ) { |
366 | 187 | if (sem_context->lhs_col()->ql_type()->IsCollection()) { |
367 | | // Only increment ("+") and decrement ("-") operators are allowed for collections. |
368 | 124 | const string type_name = QLType::ToCQLString(sem_context->lhs_col()->ql_type()->main()); |
369 | 124 | if (*name_ == "+" || *name_ == "-"52 ) { |
370 | 124 | if (args_->element(0)->opcode() == TreeNodeOpcode::kPTRef) { |
371 | 93 | name_->insert(0, type_name.c_str()); |
372 | 93 | } else { |
373 | 31 | name_->append(type_name.c_str()); |
374 | 31 | } |
375 | 124 | } |
376 | 124 | } else if (63 sem_context->lhs_col()->is_counter()63 ) { |
377 | | // Only increment ("+") and decrement ("-") operators are allowed for counters. |
378 | 27 | if (strcmp(name_->c_str(), "+") == 0 || strcmp(name_->c_str(), "-") == 07 ) { |
379 | 27 | name_->insert(0, "counter"); |
380 | 27 | } else if (0 strcmp(name_->c_str(), "counter+") != 00 && |
381 | 0 | strcmp(name_->c_str(), "counter-") != 0) { |
382 | 0 | return sem_context->Error(this, ErrorCode::INVALID_COUNTING_EXPR); |
383 | 0 | } |
384 | 27 | } |
385 | 187 | } |
386 | | |
387 | 990 | return Status::OK(); |
388 | 990 | } |
389 | | |
390 | 27 | CHECKED_STATUS PTBcall::CheckCounterUpdateSupport(SemContext *sem_context) const { |
391 | 27 | PTExpr::SharedPtr arg1 = args_->element(0); |
392 | 27 | if (arg1->expr_op() != ExprOperator::kRef) { |
393 | 0 | return sem_context->Error(arg1, "Right and left arguments must reference the same counter", |
394 | 0 | ErrorCode::INVALID_COUNTING_EXPR); |
395 | 0 | } |
396 | | |
397 | 27 | const PTRef *ref = static_cast<const PTRef*>(arg1.get()); |
398 | 27 | if (ref->desc() != sem_context->lhs_col()) { |
399 | 1 | return sem_context->Error(arg1, "Right and left arguments must reference the same counter", |
400 | 1 | ErrorCode::INVALID_COUNTING_EXPR); |
401 | 1 | } |
402 | | |
403 | 26 | return Status::OK(); |
404 | 27 | } |
405 | | |
406 | 1.35k | CHECKED_STATUS PTBcall::CheckOperatorAfterArgAnalyze(SemContext *sem_context) { |
407 | 1.35k | if (*name_ == "tojson") { |
408 | | // The arguments must be analyzed and correct types must be set. |
409 | 48 | const QLType::SharedPtr type = args_->element(0)->ql_type(); |
410 | 48 | DCHECK(!type->IsUnknown()); |
411 | | |
412 | 48 | if (type->main() == TUPLE) { |
413 | | // https://github.com/YugaByte/yugabyte-db/issues/936 |
414 | 0 | return sem_context->Error(args_->element(0), |
415 | 0 | "Tuple type not implemented yet", ErrorCode::FEATURE_NOT_YET_IMPLEMENTED); |
416 | 0 | } |
417 | | |
418 | 48 | if (type->Contains(FROZEN) || type->Contains(USER_DEFINED_TYPE)39 ) { |
419 | | // Only the server side implementation allows complex types unwrapping based on the schema. |
420 | 11 | name_->insert(0, "server_"); |
421 | 11 | } |
422 | 48 | } |
423 | | |
424 | 1.35k | return Status::OK(); |
425 | 1.35k | } |
426 | | |
427 | 1.10k | void PTBcall::rscol_type_PB(QLTypePB *pb_type) const { |
428 | 1.10k | if (aggregate_opcode() == bfql::TSOpcode::kAvg) { |
429 | | // Tablets return a map of (count, sum), |
430 | | // so that the average can be calculated across all tablets. |
431 | 20 | QLType::CreateTypeMap(INT64, args_->node_list().front()->ql_type()->main()) |
432 | 20 | ->ToQLTypePB(pb_type); |
433 | 20 | return; |
434 | 20 | } |
435 | 1.08k | ql_type()->ToQLTypePB(pb_type); |
436 | 1.08k | } |
437 | | |
438 | 1.26k | yb::bfql::TSOpcode PTBcall::aggregate_opcode() const { |
439 | 1.26k | return is_server_operator_ ? static_cast<yb::bfql::TSOpcode>(bfopcode_)1.13k |
440 | 1.26k | : yb::bfql::TSOpcode::kNoOp125 ; |
441 | 1.26k | } |
442 | | |
443 | | //-------------------------------------------------------------------------------------------------- |
444 | | |
445 | | // Collection constants. |
446 | 399 | CHECKED_STATUS PTToken::Analyze(SemContext *sem_context) { |
447 | | |
448 | 399 | RETURN_NOT_OK(PTBcall::Analyze(sem_context)); |
449 | | |
450 | | // Analyzing the arguments: their types need to be inferred based on table schema (hash columns). |
451 | 399 | size_t size = sem_context->current_dml_stmt()->table()->schema().num_hash_key_columns(); |
452 | 399 | if (args().size() != size) { |
453 | 0 | return sem_context->Error( |
454 | 0 | this, |
455 | 0 | Substitute("Invalid $0 call, wrong number of arguments", func_name()).c_str(), |
456 | 0 | ErrorCode::CQL_STATEMENT_INVALID); |
457 | 0 | } |
458 | | |
459 | | // Check if reference to partition key. |
460 | 399 | if (args().front()->expr_op() == ExprOperator::kRef) { |
461 | 377 | size_t index = 0; |
462 | 489 | for (const PTExpr::SharedPtr &arg : args()) { |
463 | 489 | if (arg->expr_op() != ExprOperator::kRef) { |
464 | 0 | return sem_context->Error(arg, |
465 | 0 | Substitute("Invalid $0 call, all arguments must be either column references or " |
466 | 0 | "literals", func_name()).c_str(), |
467 | 0 | ErrorCode::CQL_STATEMENT_INVALID); |
468 | 0 | } |
469 | | |
470 | 489 | PTRef *col_ref = static_cast<PTRef *>(arg.get()); |
471 | 489 | RETURN_NOT_OK(col_ref->Analyze(sem_context)); |
472 | 489 | if (col_ref->desc()->index() != index) { |
473 | 0 | return sem_context->Error( |
474 | 0 | col_ref, |
475 | 0 | Substitute("Invalid $0 call, found reference to unexpected column", |
476 | 0 | func_name()).c_str(), |
477 | 0 | ErrorCode::CQL_STATEMENT_INVALID); |
478 | 0 | } |
479 | 489 | index++; |
480 | 489 | } |
481 | 377 | is_partition_key_ref_ = true; |
482 | 377 | return CheckExpectedTypeCompatibility(sem_context); |
483 | 377 | } |
484 | | |
485 | | // Otherwise, it could be a call with literal values to be evaluated. |
486 | 22 | size_t index = 0; |
487 | 22 | auto& schema = sem_context->current_dml_stmt()->table()->schema(); |
488 | 42 | for (const PTExpr::SharedPtr &arg : args()) { |
489 | 42 | if (arg->expr_op() != ExprOperator::kConst && |
490 | 42 | arg->expr_op() != ExprOperator::kUMinus28 && |
491 | 42 | arg->expr_op() != ExprOperator::kCollection28 && |
492 | 42 | arg->expr_op() != ExprOperator::kBindVar28 ) { |
493 | 0 | return sem_context->Error(arg, |
494 | 0 | Substitute("Invalid $0 call, all arguments must be either column references or " |
495 | 0 | "literals", func_name()).c_str(), |
496 | 0 | ErrorCode::CQL_STATEMENT_INVALID); |
497 | 0 | } |
498 | 42 | SemState sem_state(sem_context); |
499 | 42 | sem_state.SetExprState(schema.Column(index).type(), |
500 | 42 | YBColumnSchema::ToInternalDataType(schema.Column(index).type())); |
501 | 42 | if (arg->expr_op() == ExprOperator::kBindVar) { |
502 | 28 | sem_state.set_bindvar_name(PTBindVar::bcall_arg_bindvar_name(func_name(), index)); |
503 | 28 | } |
504 | | |
505 | | // All arguments are literals and are folded to the corresponding column type during analysis. |
506 | | // Therefore, we don't need an explicit cast to guarantee the correct types during execution. |
507 | 42 | RETURN_NOT_OK(arg->Analyze(sem_context)); |
508 | 42 | index++; |
509 | 42 | } |
510 | 22 | is_partition_key_ref_ = false; |
511 | 22 | return CheckExpectedTypeCompatibility(sem_context); |
512 | 22 | } |
513 | | |
514 | 399 | CHECKED_STATUS PTToken::CheckOperator(SemContext *sem_context) { |
515 | | // Nothing to do. |
516 | 399 | return Status::OK(); |
517 | 399 | } |
518 | | |
519 | | } // namespace ql |
520 | | } // namespace yb |