/Users/deen/code/yugabyte-db/src/yb/docdb/doc_pg_expr.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) Yugabyte, Inc. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
4 | | // in compliance with the License. You may obtain a copy of the License at |
5 | | // |
6 | | // http://www.apache.org/licenses/LICENSE-2.0 |
7 | | // |
8 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
9 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
10 | | // or implied. See the License for the specific language governing permissions and limitations |
11 | | // under the License. |
12 | | // |
13 | | |
14 | | #include <list> |
15 | | |
16 | | #include "yb/docdb/doc_pg_expr.h" |
17 | | #include "yb/docdb/docdb_pgapi.h" |
18 | | #include "yb/util/logging.h" |
19 | | #include "yb/util/result.h" |
20 | | #include "yb/yql/pggate/pg_value.h" |
21 | | |
22 | | using yb::pggate::PgValueToPB; |
23 | | |
24 | | namespace yb { |
25 | | namespace docdb { |
26 | | |
27 | | //-------------------------------------------------------------------------------------------------- |
28 | | |
29 | | // Deserialized Postgres expression paired with type information to convert results to DocDB format |
30 | | typedef std::pair<YbgPreparedExpr, DocPgVarRef> DocPgEvalExprData; |
31 | | |
32 | | class DocPgExprExecutor::Private { |
33 | | public: |
34 | 2.99M | Private() { |
35 | | // Memory context to store things that are needed for executor lifetime, like column references |
36 | | // or deserialized expressions. |
37 | 2.99M | YbgCreateMemoryContext(nullptr, "DocPg Expression Context", &mem_ctx_); |
38 | 2.99M | } |
39 | | |
40 | 2.99M | ~Private() { |
41 | | // If row_ctx_ was created it is deleted with mem_ctx_, as mem_ctx_ is the parent |
42 | 2.99M | YbgSetCurrentMemoryContext(mem_ctx_, nullptr); |
43 | 2.99M | YbgDeleteMemoryContext(); |
44 | 2.99M | } |
45 | | |
46 | | // Process a column reference |
47 | | CHECKED_STATUS AddColumnRef(const PgsqlColRefPB& column_ref, |
48 | 20.7M | const Schema *schema) { |
49 | 20.7M | DCHECK(expr_ctx_ == nullptr); |
50 | | // Get DocDB column identifier |
51 | 20.7M | ColumnId col_id = ColumnId(column_ref.column_id()); |
52 | | // Column references without Postgres type info are not used for expression evaluation |
53 | | // they may still be used for something else |
54 | 20.7M | if (!column_ref.has_typid()) { |
55 | 18.4E | VLOG(1) << "Column reference " << col_id << " has no type information, skipping"; |
56 | 20.6M | return Status::OK(); |
57 | 20.6M | } |
58 | | // Find column in the schema |
59 | 24.5k | VLOG(1) << "Column lookup " << col_id12.2k ; |
60 | 24.5k | auto column = schema->column_by_id(col_id); |
61 | 24.5k | SCHECK(column.ok(), InternalError, "Invalid Schema"); |
62 | 24.5k | SCHECK_EQ(column->order(), column_ref.attno(), InternalError, "Invalid Schema"); |
63 | | // Prepare DocPgVarRef object and store it in the var_map_ using the attribute number as a key. |
64 | | // The DocPgVarRef object encapsulates info needed to extract DocDB from a row and convert it |
65 | | // to Postgres format. |
66 | 24.5k | return DocPgAddVarRef(col_id, |
67 | 24.5k | column_ref.attno(), |
68 | 24.5k | column_ref.typid(), |
69 | 24.5k | column_ref.has_typmod() ? column_ref.typmod()12.2k : -112.2k , |
70 | 24.5k | column_ref.has_collid() ? column_ref.collid()12.2k : 012.2k , |
71 | 24.5k | &var_map_); |
72 | 24.5k | } |
73 | | |
74 | | // Process a where clause expression |
75 | | CHECKED_STATUS PreparePgWhereExpr(const PgsqlExpressionPB& ql_expr, |
76 | 369 | const Schema *schema) { |
77 | 369 | YbgPreparedExpr expr; |
78 | | // Deserialize Postgres expression. Expression type is known to be boolean |
79 | 369 | RETURN_NOT_OK(prepare_pg_expr_call(ql_expr, schema, &expr, nullptr)); |
80 | | // Store the Postgres expression in the list |
81 | 369 | where_clause_.push_back(expr); |
82 | 18.4E | VLOG(1) << "A condition has been added"; |
83 | 369 | return Status::OK(); |
84 | 369 | } |
85 | | |
86 | | // Process a target expression |
87 | | CHECKED_STATUS PreparePgTargetExpr(const PgsqlExpressionPB& ql_expr, |
88 | 16.7k | const Schema *schema) { |
89 | 16.7k | YbgPreparedExpr expr; |
90 | 16.7k | DocPgVarRef expr_type; |
91 | | // Deserialize Postgres expression. Get type information to convert evaluation results to |
92 | | // DocDB format |
93 | 16.7k | RETURN_NOT_OK(prepare_pg_expr_call(ql_expr, schema, &expr, &expr_type)); |
94 | | // Store the Postgres expression in the list |
95 | 16.7k | targets_.emplace_back(expr, expr_type); |
96 | 16.7k | VLOG(1) << "A target expression has been added"0 ; |
97 | 16.7k | return Status::OK(); |
98 | 16.7k | } |
99 | | |
100 | | // Deserialize a Postgres expression and optionally determine its result data type info |
101 | | CHECKED_STATUS prepare_pg_expr_call(const PgsqlExpressionPB& ql_expr, |
102 | | const Schema *schema, |
103 | | YbgPreparedExpr *expr, |
104 | 17.1k | DocPgVarRef *expr_type) { |
105 | 17.1k | YbgMemoryContext old; |
106 | | // Presence of row_ctx_ indicates that execution was started we do not allow to modify |
107 | | // the executor dynamically. |
108 | 17.1k | SCHECK(!row_ctx_, InternalError, "Can not add expression, execution has started"); |
109 | 17.1k | SCHECK_EQ( |
110 | 17.1k | ql_expr.expr_case(), PgsqlExpressionPB::ExprCase::kTscall, InternalError, |
111 | 17.1k | "Unexpected expression code"); |
112 | 17.1k | const PgsqlBCallPB& tscall = ql_expr.tscall(); |
113 | 17.1k | SCHECK( |
114 | 17.1k | static_cast<bfpg::TSOpcode>(tscall.opcode()) == bfpg::TSOpcode::kPgEvalExprCall, |
115 | 17.1k | InternalError, "Serialized Postgres expression is expected"); |
116 | 17.1k | SCHECK_EQ(tscall.operands_size(), 1, InternalError, "Invalid serialized Postgres expression"); |
117 | | // Retrieve string representing the expression |
118 | 17.1k | const std::string& expr_str = tscall.operands(0).value().string_value(); |
119 | | // Make sure expression is in the right memory context |
120 | 17.1k | YbgSetCurrentMemoryContext(mem_ctx_, &old); |
121 | | // Perform deserialization and get result data type info |
122 | 17.1k | const Status s = DocPgPrepareExpr(expr_str, expr, expr_type); |
123 | | // Restore previous memory context |
124 | 17.1k | YbgSetCurrentMemoryContext(old, nullptr); |
125 | 17.1k | return s; |
126 | 17.1k | } |
127 | | |
128 | | // Retrieve expressions from the row according to the added column references |
129 | 52.0M | CHECKED_STATUS PreparePgRowData(const QLTableRow& table_row) { |
130 | 52.0M | YbgMemoryContext old; |
131 | 52.0M | Status s = Status::OK(); |
132 | 52.0M | if (row_ctx_ == nullptr) { |
133 | | // The first row, prepare memory context for per row allocations |
134 | 2.26M | YbgCreateMemoryContext(mem_ctx_, "DocPg Row Context", &row_ctx_); |
135 | 2.26M | YbgSetCurrentMemoryContext(row_ctx_, &old); |
136 | 49.7M | } else { |
137 | | // Clean up memory allocations that may be still around after previous row was processed |
138 | 49.7M | YbgSetCurrentMemoryContext(row_ctx_, &old); |
139 | 49.7M | YbgResetMemoryContext(); |
140 | 49.7M | } |
141 | | // If there are no column references the expression context will not be used |
142 | 52.0M | if (!var_map_.empty()) { |
143 | 64.9k | s = ensure_expr_context(); |
144 | | // Transfer referenced row values to the expr_ctx_ container |
145 | 66.6k | if (s.ok()64.9k ) { |
146 | 66.6k | s = DocPgPrepareExprCtx(table_row, var_map_, expr_ctx_); |
147 | 66.6k | } |
148 | 64.9k | } |
149 | | |
150 | | // Restore previous memory context |
151 | 52.0M | YbgSetCurrentMemoryContext(old, nullptr); |
152 | 52.0M | return s; |
153 | 52.0M | } |
154 | | |
155 | | // Create the expression context if does not exist |
156 | 64.8k | CHECKED_STATUS ensure_expr_context() { |
157 | 64.8k | if (expr_ctx_ == nullptr) { |
158 | 12.2k | YbgMemoryContext old; |
159 | | // While contents of the expression container is updated per row, the container itself |
160 | | // should persist. So make sure that mem_ctx_ is curent during the creation. |
161 | 12.2k | YbgSetCurrentMemoryContext(mem_ctx_, &old); |
162 | 12.2k | RETURN_NOT_OK(DocPgCreateExprCtx(var_map_, &expr_ctx_)); |
163 | 12.2k | YbgSetCurrentMemoryContext(old, nullptr); |
164 | 12.2k | } |
165 | 64.8k | return Status::OK(); |
166 | 64.8k | } |
167 | | |
168 | | // Evaluate where clause expressions |
169 | 52.0M | CHECKED_STATUS EvalWhereExprCalls(bool *result) { |
170 | 52.0M | YbgMemoryContext old; |
171 | 52.0M | uint64_t datum; |
172 | 52.0M | bool is_null; |
173 | 52.0M | YbgSetCurrentMemoryContext(row_ctx_, &old); |
174 | | // If where_clause_ is empty or all the expressions yield true, the result will remain true |
175 | 52.0M | *result = true; |
176 | 52.0M | for (auto expr : where_clause_) { |
177 | | // Evaluate expression |
178 | 56.9k | RETURN_NOT_OK(DocPgEvalExpr(expr, expr_ctx_, &datum, &is_null)); |
179 | | // Stop iteration and return false if expression does not yield true |
180 | 56.9k | if (is_null || !datum54.1k ) { |
181 | 53.5k | *result = false; |
182 | 53.5k | break; |
183 | 53.5k | } |
184 | 56.9k | } |
185 | | // Restore previous context |
186 | 52.0M | YbgSetCurrentMemoryContext(old, nullptr); |
187 | 52.0M | return Status::OK(); |
188 | 52.0M | } |
189 | | |
190 | | // Evaluate target expressions and write results into provided vector elements |
191 | 51.9M | CHECKED_STATUS EvalTargetExprCalls(std::vector<QLExprResult>* results) { |
192 | 51.9M | YbgMemoryContext old; |
193 | | // Shortcut if there is nothing to evaluate |
194 | 51.9M | if (targets_.empty()51.9M ) { |
195 | 51.9M | return Status::OK(); |
196 | 51.9M | } |
197 | 18.4E | SCHECK_GE( |
198 | 18.4E | results->size(), targets_.size(), InternalError, |
199 | 18.4E | "Provided results storage is insufficient"); |
200 | | |
201 | | // Output element's index |
202 | 18.4E | int i = 0; |
203 | | // Use per row memory context for allocations |
204 | 18.4E | YbgSetCurrentMemoryContext(row_ctx_, &old); |
205 | 18.4E | for (const DocPgEvalExprData& target : targets_) { |
206 | | // Container for the DocDB result |
207 | 16.7k | QLExprResult &result = results->at(i++); |
208 | | // Containers for Postgres result |
209 | 16.7k | uint64_t datum; |
210 | 16.7k | bool is_null; |
211 | | // Evaluate the expression |
212 | 16.7k | RETURN_NOT_OK(DocPgEvalExpr(target.first, expr_ctx_, &datum, &is_null)); |
213 | | // Convert Postgres result to DocDB |
214 | 16.7k | RETURN_NOT_OK( |
215 | 16.7k | PgValueToPB(target.second.var_type, datum, is_null, &result.Writer().NewValue())); |
216 | 16.7k | } |
217 | | // Restore previous context |
218 | 18.4E | YbgSetCurrentMemoryContext(old, nullptr); |
219 | 18.4E | return Status::OK(); |
220 | 18.4E | } |
221 | | |
222 | | private: |
223 | | // Memory context for permanent allocations. Exists for executor's lifetime. |
224 | | YbgMemoryContext mem_ctx_ = nullptr; |
225 | | // Memory context for per row allocations. Reset with every new row. |
226 | | YbgMemoryContext row_ctx_ = nullptr; |
227 | | // Container for Postgres-format data retrieved from the DocDB row. |
228 | | // Provides fast access to is_nulls and datums by index(attribute number). |
229 | | YbgExprContext expr_ctx_ = nullptr; |
230 | | // List of where clause expressions |
231 | | std::list<YbgPreparedExpr> where_clause_; |
232 | | // List of target expressions with their type info |
233 | | std::list<DocPgEvalExprData> targets_; |
234 | | // Storage for column references. Key is the attribute number, value is basically DocDB column id |
235 | | // and type info. |
236 | | // There are couple minor benefits of using ordered map here. First, we tolerate duplicate column |
237 | | // references, second is that we iterate over columns in their schema order, hopefully this speeds |
238 | | // up access to data. |
239 | | std::map<int, const DocPgVarRef> var_map_; |
240 | | }; |
241 | | |
242 | 2.99M | void DocPgExprExecutor::private_deleter::operator()(DocPgExprExecutor::Private* ptr) const { |
243 | | // DocPgExprExecutor::Private is a complete class in this module, so it can be simply deleted |
244 | 2.99M | delete ptr; |
245 | 2.99M | } |
246 | | |
247 | | //-------------------------------------------------------------------------------------------------- |
248 | | |
249 | 20.7M | CHECKED_STATUS DocPgExprExecutor::AddColumnRef(const PgsqlColRefPB& column_ref) { |
250 | 20.7M | if (private_.get() == nullptr) { |
251 | 2.98M | private_.reset(new Private()); |
252 | 2.98M | } |
253 | 20.7M | return private_->AddColumnRef(column_ref, schema_); |
254 | 20.7M | } |
255 | | |
256 | 371 | CHECKED_STATUS DocPgExprExecutor::AddWhereExpression(const PgsqlExpressionPB& ql_expr) { |
257 | 371 | if (private_.get() == nullptr) { |
258 | 0 | private_.reset(new Private()); |
259 | 0 | } |
260 | 371 | return private_->PreparePgWhereExpr(ql_expr, schema_); |
261 | 371 | } |
262 | | |
263 | 16.7k | CHECKED_STATUS DocPgExprExecutor::AddTargetExpression(const PgsqlExpressionPB& ql_expr) { |
264 | 16.7k | if (private_.get() == nullptr) { |
265 | 11.8k | private_.reset(new Private()); |
266 | 11.8k | } |
267 | 16.7k | return private_->PreparePgTargetExpr(ql_expr, schema_); |
268 | 16.7k | } |
269 | | |
270 | | CHECKED_STATUS DocPgExprExecutor::Exec(const QLTableRow& table_row, |
271 | | std::vector<QLExprResult>* results, |
272 | 56.7M | bool* match) { |
273 | 56.7M | if (private_.get() == nullptr) { |
274 | 4.78M | return Status::OK(); |
275 | 4.78M | } |
276 | | |
277 | 52.0M | RETURN_NOT_OK(private_->PreparePgRowData(table_row)); |
278 | 52.0M | RETURN_NOT_OK(private_->EvalWhereExprCalls(match)); |
279 | 52.0M | if (*match) |
280 | 51.9M | RETURN_NOT_OK(private_->EvalTargetExprCalls(results)); |
281 | 52.0M | return Status::OK(); |
282 | 52.0M | } |
283 | | |
284 | | } // namespace docdb |
285 | | } // namespace yb |