YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/yql/cql/ql/ptree/pt_update.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 implementation for UPDATE statements.
16
//--------------------------------------------------------------------------------------------------
17
18
#include "yb/yql/cql/ql/ptree/pt_update.h"
19
20
#include "yb/common/common.pb.h"
21
#include "yb/common/ql_type.h"
22
23
#include "yb/gutil/casts.h"
24
25
#include "yb/yql/cql/ql/ptree/column_arg.h"
26
#include "yb/yql/cql/ql/ptree/column_desc.h"
27
#include "yb/yql/cql/ql/ptree/pt_dml_using_clause.h"
28
#include "yb/yql/cql/ql/ptree/pt_expr.h"
29
#include "yb/yql/cql/ql/ptree/sem_context.h"
30
#include "yb/yql/cql/ql/ptree/yb_location.h"
31
32
namespace yb {
33
namespace ql {
34
35
//--------------------------------------------------------------------------------------------------
36
37
PTAssign::PTAssign(MemoryContext *memctx,
38
                   YBLocationPtr loc,
39
                   const PTQualifiedName::SharedPtr& lhs,
40
                   const PTExprPtr& rhs,
41
                   const PTExprListNode::SharedPtr& subscript_args,
42
                   const PTExprListNode::SharedPtr& json_ops)
43
    : TreeNode(memctx, loc),
44
      lhs_(lhs),
45
      rhs_(rhs),
46
      subscript_args_(subscript_args),
47
      json_ops_(json_ops),
48
7.03k
      col_desc_(nullptr) {
49
7.03k
}
50
51
6.83k
PTAssign::~PTAssign() {
52
6.83k
}
53
54
7.00k
CHECKED_STATUS PTAssign::Analyze(SemContext *sem_context) {
55
7.00k
  SemState sem_state(sem_context);
56
57
7.00k
  sem_state.set_processing_assignee(true);
58
59
  // Analyze left value (column name).
60
7.00k
  RETURN_NOT_OK(lhs_->Analyze(sem_context));
61
7.00k
  if (!lhs_->IsSimpleName()) {
62
1
    return sem_context->Error(lhs_, "Qualified name not allowed for column reference",
63
1
                              ErrorCode::SQL_STATEMENT_INVALID);
64
1
  }
65
6.99k
  col_desc_ = sem_context->current_dml_stmt()->GetColumnDesc(sem_context, lhs_->last_name());
66
6.99k
  if (col_desc_ == nullptr) {
67
0
    return sem_context->Error(this, "Column doesn't exist", ErrorCode::UNDEFINED_COLUMN);
68
0
  }
69
70
6.99k
  std::shared_ptr<QLType> curr_ytype = col_desc_->ql_type();
71
6.99k
  InternalType curr_itype = col_desc_->internal_type();
72
73
6.99k
  if (has_subscripted_column()) {
74
90
    for (const auto &arg : subscript_args_->node_list()) {
75
90
      if (curr_ytype->keys_type() == nullptr) {
76
14
        return sem_context->Error(this, "Columns with elementary types cannot take arguments",
77
14
                                  ErrorCode::CQL_STATEMENT_INVALID);
78
14
      }
79
80
76
      sem_state.SetExprState(curr_ytype->keys_type(),
81
76
                             client::YBColumnSchema::ToInternalDataType(curr_ytype->keys_type()));
82
76
      RETURN_NOT_OK(arg->Analyze(sem_context));
83
84
74
      curr_ytype = curr_ytype->values_type();
85
74
      curr_itype = client::YBColumnSchema::ToInternalDataType(curr_ytype);
86
74
    }
87
    // For "UPDATE ... SET list[x] = ...", the list needs to be read first in order to set an
88
    // element in it.
89
74
    if (col_desc_->ql_type()->main() == DataType::LIST) {
90
25
      sem_context->current_dml_stmt()->AddColumnRef(*col_desc_);
91
25
    }
92
74
  }
93
94
6.98k
  if (has_json_ops()) {
95
191
    if (!col_desc_->ql_type()->IsJson()) {
96
1
      return sem_context->Error(this, "Json ops only supported for json columns",
97
1
                                ErrorCode::CQL_STATEMENT_INVALID);
98
1
    }
99
190
    RETURN_NOT_OK(json_ops_->Analyze(sem_context));
100
101
    // We need to perform a read-modify-write for json updates.
102
190
    sem_context->current_dml_stmt()->AddColumnRef(*col_desc_);
103
190
  }
104
105
6.98k
  sem_state.set_processing_assignee(false);
106
107
  // Setup the expected datatypes, and analyze the rhs value.
108
6.98k
  sem_state.SetExprState(curr_ytype, curr_itype, lhs_->bindvar_name(), col_desc_);
109
6.98k
  RETURN_NOT_OK(rhs_->Analyze(sem_context));
110
6.85k
  RETURN_NOT_OK(rhs_->CheckRhsExpr(sem_context));
111
112
6.85k
  return Status::OK();
113
6.85k
}
114
115
0
void PTAssign::PrintSemanticAnalysisResult(SemContext *sem_context) {
116
0
  VLOG(3) << "SEMANTIC ANALYSIS RESULT (" << *loc_ << "):\n" << "Not yet avail";
117
0
}
118
119
//--------------------------------------------------------------------------------------------------
120
121
PTUpdateStmt::PTUpdateStmt(MemoryContext *memctx,
122
                           YBLocation::SharedPtr loc,
123
                           PTTableRef::SharedPtr relation,
124
                           PTAssignListNode::SharedPtr set_clause,
125
                           PTExpr::SharedPtr where_clause,
126
                           PTExpr::SharedPtr if_clause,
127
                           const bool else_error,
128
                           PTDmlUsingClause::SharedPtr using_clause,
129
                           const bool return_status,
130
                           PTDmlWritePropertyListNode::SharedPtr update_properties)
131
    : PTDmlStmt(memctx, loc, where_clause, if_clause, else_error, using_clause, return_status),
132
      relation_(relation),
133
      set_clause_(set_clause),
134
3.33k
      update_properties_(update_properties) {
135
3.33k
}
136
137
3.21k
PTUpdateStmt::~PTUpdateStmt() {
138
3.21k
}
139
140
3.33k
CHECKED_STATUS PTUpdateStmt::Analyze(SemContext *sem_context) {
141
  // If use_cassandra_authentication is set, permissions are checked in PTDmlStmt::Analyze.
142
3.33k
  RETURN_NOT_OK(PTDmlStmt::Analyze(sem_context));
143
144
3.33k
  RETURN_NOT_OK(relation_->Analyze(sem_context));
145
146
  // Collect table's schema for semantic analysis.
147
3.33k
  RETURN_NOT_OK(LookupTable(sem_context));
148
149
3.31k
  SemState sem_state(sem_context);
150
  // Run error checking on USING clause. Need to run this before analyzing the SET clause, so the
151
  // user supplied timestamp is filled in.
152
3.31k
  RETURN_NOT_OK(AnalyzeUsingClause(sem_context));
153
154
  // Process set clause.
155
3.31k
  column_args_->resize(num_columns());
156
3.31k
  TreeNodePtrOperator<SemContext, PTAssign> analyze = std::bind(&PTUpdateStmt::AnalyzeSetExpr,
157
3.31k
                                                                this,
158
3.31k
                                                                std::placeholders::_1,
159
3.31k
                                                                std::placeholders::_2);
160
161
3.31k
  sem_state.set_processing_set_clause(true);
162
3.31k
  sem_state.set_allowing_column_refs(true);
163
3.31k
  RETURN_NOT_OK(set_clause_->Analyze(sem_context, analyze));
164
3.14k
  sem_state.ResetContextState();
165
166
  // Set clause can't have primary keys.
167
3.14k
  auto num_keys = num_key_columns();
168
14.7k
  for (size_t idx = 0; idx < num_keys; idx++) {
169
11.6k
    if (column_args_->at(idx).IsInitialized()) {
170
6
      return sem_context->Error(set_clause_, ErrorCode::INVALID_ARGUMENTS);
171
6
    }
172
11.6k
  }
173
174
  // Analyze column args to set if primary and/or static row is modified.
175
3.14k
  RETURN_NOT_OK(AnalyzeColumnArgs(sem_context));
176
177
  // Run error checking on the WHERE conditions.
178
3.14k
  RETURN_NOT_OK(AnalyzeWhereClause(sem_context));
179
180
  // Run error checking on the IF conditions.
181
3.10k
  RETURN_NOT_OK(AnalyzeIfClause(sem_context));
182
183
  // Analyze indexes for write operations.
184
3.08k
  RETURN_NOT_OK(AnalyzeIndexesForWrites(sem_context));
185
186
3.08k
  if (update_properties_ != nullptr) {
187
48
    RETURN_NOT_OK(update_properties_->Analyze(sem_context));
188
48
  }
189
  // If returning a status we always return back the whole row.
190
3.08k
  if (returns_status_) {
191
5
    AddRefForAllColumns();
192
5
  }
193
194
3.08k
  return Status::OK();
195
3.08k
}
196
197
namespace {
198
199
CHECKED_STATUS MultipleColumnSetError(const ColumnDesc* const col_desc,
200
                                      const PTAssign* const assign_expr,
201
5
                                      SemContext* sem_context) {
202
5
  return sem_context->Error(
203
5
      assign_expr,
204
5
      strings::Substitute("Multiple incompatible setting of column $0.",
205
5
                          col_desc->name()).c_str(),
206
5
      ErrorCode::INVALID_ARGUMENTS);
207
5
}
208
209
} // anonymous namespace
210
211
7.00k
CHECKED_STATUS PTUpdateStmt::AnalyzeSetExpr(PTAssign *assign_expr, SemContext *sem_context) {
212
  // Analyze the expression.
213
7.00k
  RETURN_NOT_OK(assign_expr->Analyze(sem_context));
214
215
6.85k
  if (assign_expr->col_desc()->ql_type()->IsCollection() &&
216
183
      using_clause_ != nullptr && using_clause_->has_user_timestamp_usec()) {
217
19
    return sem_context->Error(assign_expr, "UPDATE statement with collection and USING TIMESTAMP "
218
19
        "is not supported", ErrorCode::INVALID_ARGUMENTS);
219
19
  }
220
221
6.83k
  if (!require_column_read_ && assign_expr->require_column_read()) {
222
0
    require_column_read_ = true;
223
0
  }
224
225
  // Form the column args for protobuf.
226
6.83k
  const ColumnDesc *col_desc = assign_expr->col_desc();
227
6.83k
  if (assign_expr->has_subscripted_column()) {
228
    // Setting the same column twice, once with a subscripted arg and once as a regular set for the
229
    // entire column is not allowed.
230
54
    if (column_args_->at(col_desc->index()).IsInitialized()) {
231
0
      return MultipleColumnSetError(col_desc, assign_expr, sem_context);
232
0
    }
233
54
    subscripted_col_args_->emplace_back(col_desc,
234
54
                                        assign_expr->subscript_args(),
235
54
                                        assign_expr->rhs());
236
6.78k
  } else if (assign_expr->has_json_ops()) {
237
    // Setting the same column twice, once with a json arg and once as a regular set for the
238
    // entire column is not allowed.
239
186
    if (column_args_->at(col_desc->index()).IsInitialized()) {
240
0
      return MultipleColumnSetError(col_desc, assign_expr, sem_context);
241
0
    }
242
186
    json_col_args_->emplace_back(col_desc,
243
186
                                 assign_expr->json_ops(),
244
186
                                 assign_expr->rhs());
245
6.59k
  } else {
246
    // Setting the same column twice is not allowed.
247
2
    for (const auto& json_col_arg : *json_col_args_) {
248
2
      if (json_col_arg.desc()->id() == col_desc->id()) {
249
2
        return MultipleColumnSetError(col_desc, assign_expr, sem_context);
250
2
      }
251
2
    }
252
6.59k
    for (const auto& subscripted_col_arg : *subscripted_col_args_) {
253
2
      if (subscripted_col_arg.desc()->id() == col_desc->id()) {
254
2
        return MultipleColumnSetError(col_desc, assign_expr, sem_context);
255
2
      }
256
2
    }
257
6.59k
    if (column_args_->at(col_desc->index()).IsInitialized()) {
258
1
      return MultipleColumnSetError(col_desc, assign_expr, sem_context);
259
1
    }
260
6.59k
    column_args_->at(col_desc->index()).Init(col_desc, assign_expr->rhs());
261
6.59k
  }
262
6.83k
  return Status::OK();
263
6.83k
}
264
265
0
void PTUpdateStmt::PrintSemanticAnalysisResult(SemContext *sem_context) {
266
0
  VLOG(3) << "SEMANTIC ANALYSIS RESULT (" << *loc_ << "):\n" << "Not yet avail";
267
0
}
268
269
1
ExplainPlanPB PTUpdateStmt::AnalysisResultToPB() {
270
1
  ExplainPlanPB explain_plan;
271
1
  UpdatePlanPB *update_plan = explain_plan.mutable_update_plan();
272
1
  update_plan->set_update_type("Update on " + table_name().ToString());
273
1
  update_plan->set_scan_type("  ->  Primary Key Lookup on " + table_name().ToString());
274
1
  string key_conditions = "        Key Conditions: " +
275
1
      ConditionsToString<MCVector<ColumnOp>>(key_where_ops());
276
1
  update_plan->set_key_conditions(key_conditions);
277
1
  update_plan->set_output_width(narrow_cast<int32_t>(max({
278
1
    update_plan->update_type().length(),
279
1
    update_plan->scan_type().length(),
280
1
    update_plan->key_conditions().length()
281
1
  })));
282
1
  return explain_plan;
283
1
}
284
285
}  // namespace ql
286
}  // namespace yb