/Users/deen/code/yugabyte-db/src/yb/yql/cql/ql/ptree/pt_delete.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 DELETE statements. |
16 | | //-------------------------------------------------------------------------------------------------- |
17 | | |
18 | | #include "yb/yql/cql/ql/ptree/pt_delete.h" |
19 | | |
20 | | #include "yb/common/common.pb.h" |
21 | | |
22 | | #include "yb/gutil/casts.h" |
23 | | |
24 | | #include "yb/yql/cql/ql/ptree/column_arg.h" |
25 | | #include "yb/yql/cql/ql/ptree/column_desc.h" |
26 | | #include "yb/yql/cql/ql/ptree/pt_dml_using_clause.h" |
27 | | #include "yb/yql/cql/ql/ptree/pt_expr.h" |
28 | | #include "yb/yql/cql/ql/ptree/sem_context.h" |
29 | | #include "yb/yql/cql/ql/ptree/sem_state.h" |
30 | | #include "yb/yql/cql/ql/ptree/yb_location.h" |
31 | | |
32 | | namespace yb { |
33 | | namespace ql { |
34 | | |
35 | | //-------------------------------------------------------------------------------------------------- |
36 | | |
37 | | PTDeleteStmt::PTDeleteStmt(MemoryContext *memctx, |
38 | | YBLocation::SharedPtr loc, |
39 | | PTExprListNode::SharedPtr target, |
40 | | PTTableRef::SharedPtr relation, |
41 | | PTDmlUsingClausePtr using_clause, |
42 | | PTExprPtr where_clause, |
43 | | PTExprPtr if_clause, |
44 | | const bool else_error, |
45 | | const bool returns_status) |
46 | | : PTDmlStmt(memctx, loc, where_clause, if_clause, else_error, using_clause, returns_status), |
47 | | target_(target), |
48 | 815 | relation_(relation) { |
49 | 815 | } |
50 | | |
51 | 749 | PTDeleteStmt::~PTDeleteStmt() { |
52 | 749 | } |
53 | | |
54 | 810 | CHECKED_STATUS PTDeleteStmt::Analyze(SemContext *sem_context) { |
55 | | // If use_cassandra_authentication is set, permissions are checked in PTDmlStmt::Analyze. |
56 | 810 | RETURN_NOT_OK(PTDmlStmt::Analyze(sem_context)); |
57 | | |
58 | 810 | RETURN_NOT_OK(relation_->Analyze(sem_context)); |
59 | | |
60 | | // Collect table's schema for semantic analysis. |
61 | 810 | RETURN_NOT_OK(LookupTable(sem_context)); |
62 | | |
63 | | // Analyze the target columns. |
64 | 809 | if (target_) { |
65 | 68 | column_args_->resize(num_columns()); |
66 | 68 | TreeNodePtrOperator<SemContext> analyze = |
67 | 68 | std::bind(&PTDeleteStmt::AnalyzeTarget, this, std::placeholders::_1, std::placeholders::_2); |
68 | 68 | RETURN_NOT_OK(target_->Analyze(sem_context, analyze)); |
69 | 68 | } |
70 | | |
71 | | // Analyze column args to set if primary and/or static row is modified. |
72 | 803 | RETURN_NOT_OK(AnalyzeColumnArgs(sem_context)); |
73 | | |
74 | | // Run error checking on the WHERE conditions. |
75 | 803 | RETURN_NOT_OK(AnalyzeWhereClause(sem_context)); |
76 | 765 | bool range_key_missing = key_where_ops_.size() < num_key_columns(); |
77 | | |
78 | | // If target columns are given, range key can be omitted only if all columns targeted for |
79 | | // deletions are static. Then we must also check there are no extra conditions on the range |
80 | | // columns (e.g. inequality conditions). |
81 | | // Otherwise, (if no target columns are given) range key can omitted (implying a range delete) |
82 | | // only if there is no 'IF' clause (not allowed for range deletes). |
83 | 765 | if (target_) { |
84 | 62 | if (range_key_missing) { |
85 | 7 | if (!StaticColumnArgsOnly()) { |
86 | 4 | return sem_context->Error(this, |
87 | 4 | "DELETE statement must give the entire primary key if specifying non-static columns", |
88 | 4 | ErrorCode::CQL_STATEMENT_INVALID); |
89 | 4 | } |
90 | 3 | if (!where_ops_.empty()) { |
91 | 2 | return sem_context->Error(this, |
92 | 2 | "DELETE statement cannot specify both target columns and range condition", |
93 | 2 | ErrorCode::CQL_STATEMENT_INVALID); |
94 | 2 | } |
95 | 3 | } |
96 | 703 | } else if (range_key_missing) { |
97 | 26 | if (if_clause_ != nullptr) { |
98 | 2 | return sem_context->Error(this, |
99 | 2 | "DELETE statement must specify the entire primary key to use an IF clause", |
100 | 2 | ErrorCode::CQL_STATEMENT_INVALID); |
101 | 2 | } |
102 | | // This is a range delete, affecting an entire hash key. |
103 | 24 | modifies_multiple_rows_ = true; |
104 | 24 | } |
105 | | |
106 | | // Run error checking on the IF conditions. |
107 | 757 | RETURN_NOT_OK(AnalyzeIfClause(sem_context)); |
108 | | |
109 | | // Run error checking on USING clause. |
110 | 754 | RETURN_NOT_OK(AnalyzeUsingClause(sem_context)); |
111 | | |
112 | | // Analyze indexes for write operations. |
113 | 754 | RETURN_NOT_OK(AnalyzeIndexesForWrites(sem_context)); |
114 | | |
115 | 754 | if (using_clause_ != nullptr && using_clause_->has_ttl_seconds()16 ) { |
116 | | // Delete only supports TIMESTAMP as part of the using clause. |
117 | 2 | return sem_context->Error(this, "DELETE statement cannot have TTL", |
118 | 2 | ErrorCode::CQL_STATEMENT_INVALID); |
119 | 2 | } |
120 | | |
121 | | // If returning a status we always return back the whole row. |
122 | 752 | if (returns_status_) { |
123 | 5 | AddRefForAllColumns(); |
124 | 5 | } |
125 | | |
126 | 752 | return Status::OK(); |
127 | 754 | } |
128 | | |
129 | 83 | CHECKED_STATUS PTDeleteStmt::AnalyzeTarget(TreeNode *target, SemContext *sem_context) { |
130 | | // Walking through the target expressions and collect all columns. Currently, CQL doesn't allow |
131 | | // any expression except for references to table column. |
132 | 83 | if (target->opcode() != TreeNodeOpcode::kPTRef) { |
133 | 2 | return sem_context->Error(target, "Deleting expression is not allowed in CQL", |
134 | 2 | ErrorCode::CQL_STATEMENT_INVALID); |
135 | 2 | } |
136 | | |
137 | 81 | PTRef *ref = static_cast<PTRef *>(target); |
138 | | |
139 | 81 | if (ref->name() == nullptr) { // This ref is pointing to the whole table (DELETE *) |
140 | 0 | return sem_context->Error(target, "Deleting '*' is not allowed in this context", |
141 | 0 | ErrorCode::CQL_STATEMENT_INVALID); |
142 | 81 | } else { // Add the column descriptor to column_args. |
143 | 81 | SemState sem_state(sem_context); |
144 | 81 | RETURN_NOT_OK(ref->Analyze(sem_context)); |
145 | 79 | const ColumnDesc *col_desc = ref->desc(); |
146 | 79 | if (col_desc->is_primary()) { |
147 | 2 | return sem_context->Error(target, "Delete target cannot be part of primary key", |
148 | 2 | ErrorCode::INVALID_ARGUMENTS); |
149 | 2 | } |
150 | | |
151 | | // Set rhs expr to nullptr, since it is delete. |
152 | 77 | column_args_->at(col_desc->index()).Init(col_desc, nullptr); |
153 | 77 | } |
154 | 77 | return Status::OK(); |
155 | 81 | } |
156 | | |
157 | 0 | void PTDeleteStmt::PrintSemanticAnalysisResult(SemContext *sem_context) { |
158 | 0 | VLOG(3) << "SEMANTIC ANALYSIS RESULT (" << *loc_ << "):\n" << "Not yet avail"; |
159 | 0 | } |
160 | | |
161 | 2 | ExplainPlanPB PTDeleteStmt::AnalysisResultToPB() { |
162 | 2 | ExplainPlanPB explain_plan; |
163 | 2 | DeletePlanPB *delete_plan = explain_plan.mutable_delete_plan(); |
164 | 2 | delete_plan->set_delete_type("Delete on " + table_name().ToString()); |
165 | 2 | if (modifies_multiple_rows_) { |
166 | 2 | delete_plan->set_scan_type(" -> Range Scan on " + table_name().ToString()); |
167 | 2 | } else { |
168 | 0 | delete_plan->set_scan_type(" -> Primary Key Lookup on " + table_name().ToString()); |
169 | 0 | } |
170 | 2 | std::string key_conditions = " Key Conditions: " + ConditionsToString(key_where_ops()); |
171 | 2 | delete_plan->set_key_conditions(key_conditions); |
172 | 2 | if (!where_ops().empty()) { |
173 | 1 | std::string filter = " Filter: " + ConditionsToString(where_ops()); |
174 | 1 | delete_plan->set_filter(filter); |
175 | 1 | } |
176 | 2 | delete_plan->set_output_width(narrow_cast<int32_t>(max({ |
177 | 2 | delete_plan->delete_type().length(), |
178 | 2 | delete_plan->scan_type().length(), |
179 | 2 | delete_plan->key_conditions().length(), |
180 | 2 | delete_plan->filter().length() |
181 | 2 | }))); |
182 | 2 | return explain_plan; |
183 | 2 | } |
184 | | |
185 | | } // namespace ql |
186 | | } // namespace yb |