YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/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