YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/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
1.34M
  Private() {
35
    // Memory context to store things that are needed for executor lifetime, like column references
36
    // or deserialized expressions.
37
1.34M
    YbgCreateMemoryContext(nullptr, "DocPg Expression Context", &mem_ctx_);
38
1.34M
  }
39
40
1.34M
  ~Private() {
41
    // If row_ctx_ was created it is deleted with mem_ctx_, as mem_ctx_ is the parent
42
1.34M
    YbgSetCurrentMemoryContext(mem_ctx_, nullptr);
43
1.34M
    YbgDeleteMemoryContext();
44
1.34M
  }
45
46
  // Process a column reference
47
  CHECKED_STATUS AddColumnRef(const PgsqlColRefPB& column_ref,
48
8.02M
                              const Schema *schema) {
49
8.02M
    DCHECK(expr_ctx_ == nullptr);
50
    // Get DocDB column identifier
51
8.02M
    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
8.02M
    if (!column_ref.has_typid()) {
55
18.4E
      VLOG(1) << "Column reference " << col_id << " has no type information, skipping";
56
8.01M
      return Status::OK();
57
8.01M
    }
58
    // Find column in the schema
59
4.85k
    VLOG(1) << "Column lookup " << col_id;
60
9.14k
    auto column = schema->column_by_id(col_id);
61
9.14k
    SCHECK(column.ok(), InternalError, "Invalid Schema");
62
9.14k
    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
9.14k
    return DocPgAddVarRef(col_id,
67
9.14k
                          column_ref.attno(),
68
9.14k
                          column_ref.typid(),
69
4.86k
                          column_ref.has_typmod() ? column_ref.typmod() : -1,
70
4.86k
                          column_ref.has_collid() ? column_ref.collid() : 0,
71
9.14k
                          &var_map_);
72
9.14k
  }
73
74
  // Process a where clause expression
75
  CHECKED_STATUS PreparePgWhereExpr(const PgsqlExpressionPB& ql_expr,
76
368
                                    const Schema *schema) {
77
368
    YbgPreparedExpr expr;
78
    // Deserialize Postgres expression. Expression type is known to be boolean
79
368
    RETURN_NOT_OK(prepare_pg_expr_call(ql_expr, schema, &expr, nullptr));
80
    // Store the Postgres expression in the list
81
368
    where_clause_.push_back(expr);
82
2
    VLOG(1) << "A condition has been added";
83
368
    return Status::OK();
84
368
  }
85
86
  // Process a target expression
87
  CHECKED_STATUS PreparePgTargetExpr(const PgsqlExpressionPB& ql_expr,
88
5.74k
                                     const Schema *schema) {
89
5.74k
    YbgPreparedExpr expr;
90
5.74k
    DocPgVarRef expr_type;
91
    // Deserialize Postgres expression. Get type information to convert evaluation results to
92
    // DocDB format
93
5.74k
    RETURN_NOT_OK(prepare_pg_expr_call(ql_expr, schema, &expr, &expr_type));
94
    // Store the Postgres expression in the list
95
5.74k
    targets_.emplace_back(expr, expr_type);
96
0
    VLOG(1) << "A target expression has been added";
97
5.74k
    return Status::OK();
98
5.74k
  }
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
6.11k
                                      DocPgVarRef *expr_type) {
105
6.11k
    YbgMemoryContext old;
106
    // Presence of row_ctx_ indicates that execution was started we do not allow to modify
107
    // the executor dynamically.
108
6.11k
    SCHECK(!row_ctx_, InternalError, "Can not add expression, execution has started");
109
6.11k
    SCHECK_EQ(
110
6.11k
        ql_expr.expr_case(), PgsqlExpressionPB::ExprCase::kTscall, InternalError,
111
6.11k
        "Unexpected expression code");
112
6.11k
    const PgsqlBCallPB& tscall = ql_expr.tscall();
113
6.11k
    SCHECK(
114
6.11k
        static_cast<bfpg::TSOpcode>(tscall.opcode()) == bfpg::TSOpcode::kPgEvalExprCall,
115
6.11k
        InternalError, "Serialized Postgres expression is expected");
116
6.11k
    SCHECK_EQ(tscall.operands_size(), 1, InternalError, "Invalid serialized Postgres expression");
117
    // Retrieve string representing the expression
118
6.11k
    const std::string& expr_str = tscall.operands(0).value().string_value();
119
    // Make sure expression is in the right memory context
120
6.11k
    YbgSetCurrentMemoryContext(mem_ctx_, &old);
121
    // Perform deserialization and get result data type info
122
6.11k
    const Status s = DocPgPrepareExpr(expr_str, expr, expr_type);
123
    // Restore previous memory context
124
6.11k
    YbgSetCurrentMemoryContext(old, nullptr);
125
6.11k
    return s;
126
6.11k
  }
127
128
  // Retrieve expressions from the row according to the added column references
129
20.2M
  CHECKED_STATUS PreparePgRowData(const QLTableRow& table_row) {
130
20.2M
    YbgMemoryContext old;
131
20.2M
    Status s = Status::OK();
132
20.2M
    if (row_ctx_ == nullptr) {
133
      // The first row, prepare memory context for per row allocations
134
1.01M
      YbgCreateMemoryContext(mem_ctx_, "DocPg Row Context", &row_ctx_);
135
1.01M
      YbgSetCurrentMemoryContext(row_ctx_, &old);
136
19.1M
    } else {
137
      // Clean up memory allocations that may be still around after previous row was processed
138
19.1M
      YbgSetCurrentMemoryContext(row_ctx_, &old);
139
19.1M
      YbgResetMemoryContext();
140
19.1M
    }
141
    // If there are no column references the expression context will not be used
142
20.2M
    if (!var_map_.empty()) {
143
57.3k
      s = ensure_expr_context();
144
      // Transfer referenced row values to the expr_ctx_ container
145
58.7k
      if (s.ok()) {
146
58.7k
        s = DocPgPrepareExprCtx(table_row, var_map_, expr_ctx_);
147
58.7k
      }
148
57.3k
    }
149
150
    // Restore previous memory context
151
20.2M
    YbgSetCurrentMemoryContext(old, nullptr);
152
20.2M
    return s;
153
20.2M
  }
154
155
  // Create the expression context if does not exist
156
57.2k
  CHECKED_STATUS ensure_expr_context() {
157
57.2k
    if (expr_ctx_ == nullptr) {
158
4.28k
      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
4.28k
      YbgSetCurrentMemoryContext(mem_ctx_, &old);
162
4.28k
      RETURN_NOT_OK(DocPgCreateExprCtx(var_map_, &expr_ctx_));
163
4.28k
      YbgSetCurrentMemoryContext(old, nullptr);
164
4.28k
    }
165
57.2k
    return Status::OK();
166
57.2k
  }
167
168
  // Evaluate where clause expressions
169
20.1M
  CHECKED_STATUS EvalWhereExprCalls(bool *result) {
170
20.1M
    YbgMemoryContext old;
171
20.1M
    uint64_t datum;
172
20.1M
    bool is_null;
173
20.1M
    YbgSetCurrentMemoryContext(row_ctx_, &old);
174
    // If where_clause_ is empty or all the expressions yield true, the result will remain true
175
20.1M
    *result = true;
176
56.5k
    for (auto expr : where_clause_) {
177
      // Evaluate expression
178
56.5k
      RETURN_NOT_OK(DocPgEvalExpr(expr, expr_ctx_, &datum, &is_null));
179
      // Stop iteration and return false if expression does not yield true
180
56.5k
      if (is_null || !datum) {
181
53.8k
        *result = false;
182
53.8k
        break;
183
53.8k
      }
184
56.5k
    }
185
    // Restore previous context
186
20.1M
    YbgSetCurrentMemoryContext(old, nullptr);
187
20.1M
    return Status::OK();
188
20.1M
  }
189
190
  // Evaluate target expressions and write results into provided vector elements
191
20.1M
  CHECKED_STATUS EvalTargetExprCalls(std::vector<QLExprResult>* results) {
192
20.1M
    YbgMemoryContext old;
193
    // Shortcut if there is nothing to evaluate
194
20.1M
    if (targets_.empty()) {
195
20.1M
      return Status::OK();
196
20.1M
    }
197
1.75k
    SCHECK_GE(
198
1.75k
        results->size(), targets_.size(), InternalError,
199
1.75k
        "Provided results storage is insufficient");
200
201
    // Output element's index
202
1.75k
    int i = 0;
203
    // Use per row memory context for allocations
204
1.75k
    YbgSetCurrentMemoryContext(row_ctx_, &old);
205
5.74k
    for (const DocPgEvalExprData& target : targets_) {
206
      // Container for the DocDB result
207
5.74k
      QLExprResult &result = results->at(i++);
208
      // Containers for Postgres result
209
5.74k
      uint64_t datum;
210
5.74k
      bool is_null;
211
      // Evaluate the expression
212
5.74k
      RETURN_NOT_OK(DocPgEvalExpr(target.first, expr_ctx_, &datum, &is_null));
213
      // Convert Postgres result to DocDB
214
5.74k
      RETURN_NOT_OK(
215
5.74k
          PgValueToPB(target.second.var_type, datum, is_null, &result.Writer().NewValue()));
216
5.74k
    }
217
    // Restore previous context
218
1.75k
    YbgSetCurrentMemoryContext(old, nullptr);
219
1.75k
    return Status::OK();
220
1.75k
  }
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
1.34M
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
1.34M
  delete ptr;
245
1.34M
}
246
247
//--------------------------------------------------------------------------------------------------
248
249
8.02M
CHECKED_STATUS DocPgExprExecutor::AddColumnRef(const PgsqlColRefPB& column_ref) {
250
8.02M
  if (private_.get() == nullptr) {
251
1.34M
    private_.reset(new Private());
252
1.34M
  }
253
8.02M
  return private_->AddColumnRef(column_ref, schema_);
254
8.02M
}
255
256
366
CHECKED_STATUS DocPgExprExecutor::AddWhereExpression(const PgsqlExpressionPB& ql_expr) {
257
366
  if (private_.get() == nullptr) {
258
0
    private_.reset(new Private());
259
0
  }
260
366
  return private_->PreparePgWhereExpr(ql_expr, schema_);
261
366
}
262
263
5.74k
CHECKED_STATUS DocPgExprExecutor::AddTargetExpression(const PgsqlExpressionPB& ql_expr) {
264
5.74k
  if (private_.get() == nullptr) {
265
3.92k
    private_.reset(new Private());
266
3.92k
  }
267
5.74k
  return private_->PreparePgTargetExpr(ql_expr, schema_);
268
5.74k
}
269
270
CHECKED_STATUS DocPgExprExecutor::Exec(const QLTableRow& table_row,
271
                                       std::vector<QLExprResult>* results,
272
22.1M
                                       bool* match) {
273
22.1M
  if (private_.get() == nullptr) {
274
1.93M
    return Status::OK();
275
1.93M
  }
276
277
20.1M
  RETURN_NOT_OK(private_->PreparePgRowData(table_row));
278
20.1M
  RETURN_NOT_OK(private_->EvalWhereExprCalls(match));
279
20.1M
  if (*match)
280
20.1M
    RETURN_NOT_OK(private_->EvalTargetExprCalls(results));
281
20.1M
  return Status::OK();
282
20.1M
}
283
284
}  // namespace docdb
285
}  // namespace yb