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