YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/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