YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/postgres/src/backend/executor/ybcExpr.c
Line
Count
Source (jump to first uncovered line)
1
/*--------------------------------------------------------------------------------------------------
2
 * ybcExpr.c
3
 *        Routines to construct YBC expression tree.
4
 *
5
 * Copyright (c) YugaByte, Inc.
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
8
 * in compliance with the License.  You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software distributed under the License
13
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
14
 * or implied.  See the License for the specific language governing permissions and limitations
15
 * under the License.
16
 *
17
 * IDENTIFICATION
18
 *        src/backend/executor/ybcExpr.c
19
 *--------------------------------------------------------------------------------------------------
20
 */
21
22
#include <inttypes.h>
23
24
#include "postgres.h"
25
26
#include "access/htup_details.h"
27
#include "catalog/pg_collation.h"
28
#include "catalog/pg_type.h"
29
#include "catalog/pg_proc.h"
30
#include "nodes/makefuncs.h"
31
#include "nodes/nodeFuncs.h"
32
#include "utils/datum.h"
33
#include "utils/relcache.h"
34
#include "utils/rel.h"
35
#include "parser/parse_type.h"
36
#include "utils/lsyscache.h"
37
#include "commands/dbcommands.h"
38
#include "executor/tuptable.h"
39
#include "miscadmin.h"
40
#include "utils/syscache.h"
41
#include "utils/builtins.h"
42
43
#include "pg_yb_utils.h"
44
#include "executor/ybcExpr.h"
45
#include "catalog/yb_type.h"
46
47
Node *yb_expr_instantiate_params_mutator(Node *node, ParamListInfo paramLI);
48
bool yb_pushdown_walker(Node *node, List **params);
49
bool yb_can_pushdown_func(Oid funcid);
50
51
YBCPgExpr YBCNewColumnRef(YBCPgStatement ybc_stmt, int16_t attr_num,
52
              int attr_typid, int attr_collation,
53
7.55M
              const YBCPgTypeAttrs *type_attrs) {
54
7.55M
  YBCPgExpr expr = NULL;
55
7.55M
  const YBCPgTypeEntity *type_entity = YbDataTypeFromOidMod(attr_num, attr_typid);
56
7.55M
  YBCPgCollationInfo collation_info;
57
7.55M
  YBGetCollationInfo(attr_collation, type_entity, 0 /* datum */, true /* is_null */,
58
7.55M
             &collation_info);
59
7.55M
  HandleYBStatus(YBCPgNewColumnRef(ybc_stmt, attr_num, type_entity,
60
7.55M
                   collation_info.collate_is_valid_non_c,
61
7.55M
                   type_attrs, &expr));
62
7.55M
  return expr;
63
7.55M
}
64
65
YBCPgExpr YBCNewConstant(YBCPgStatement ybc_stmt, Oid type_id, Oid collation_id,
66
11.2M
             Datum datum, bool is_null) {
67
11.2M
  YBCPgExpr expr = NULL;
68
11.2M
  const YBCPgTypeEntity *type_entity = YbDataTypeFromOidMod(InvalidAttrNumber, type_id);
69
11.2M
  YBCPgCollationInfo collation_info;
70
11.2M
  YBGetCollationInfo(collation_id, type_entity, datum, is_null, &collation_info);
71
11.2M
  HandleYBStatus(YBCPgNewConstant(ybc_stmt, type_entity,
72
11.2M
                  collation_info.collate_is_valid_non_c,
73
11.2M
                  collation_info.sortkey,
74
11.2M
                  datum, is_null, &expr));
75
11.2M
  return expr;
76
11.2M
}
77
78
8
YBCPgExpr YBCNewConstantVirtual(YBCPgStatement ybc_stmt, Oid type_id, YBCPgDatumKind kind) {
79
8
  YBCPgExpr expr = NULL;
80
8
  const YBCPgTypeEntity *type_entity = YbDataTypeFromOidMod(InvalidAttrNumber, type_id);
81
8
  HandleYBStatus(YBCPgNewConstantVirtual(ybc_stmt, type_entity, kind, &expr));
82
8
  return expr;
83
8
}
84
85
/*
86
 * yb_expr_instantiate_params_mutator
87
 *
88
 *    Expression mutator used internally by YbExprInstantiateParams
89
 */
90
Node *yb_expr_instantiate_params_mutator(Node *node, ParamListInfo paramLI)
91
0
{
92
0
  if (node == NULL)
93
0
    return NULL;
94
95
0
  if (IsA(node, Param))
96
0
  {
97
0
    Param *param = castNode(Param, node);
98
0
    ParamExternData *prm = NULL;
99
0
    ParamExternData prmdata;
100
0
    if (paramLI->paramFetch != NULL)
101
0
      prm = paramLI->paramFetch(paramLI, param->paramid,
102
0
                    true, &prmdata);
103
0
    else
104
0
      prm = &paramLI->params[param->paramid - 1];
105
106
0
    if (!OidIsValid(prm->ptype) ||
107
0
      prm->ptype != param->paramtype)
108
0
    {
109
      /* Planner should ensure this does not happen */
110
0
      elog(ERROR, "Invalid parameter: %s", nodeToString(param));
111
0
    }
112
0
    int16   typLen = 0;
113
0
    bool    typByVal = false;
114
0
    Datum   pval = 0;
115
116
0
    get_typlenbyval(param->paramtype, &typLen, &typByVal);
117
0
    if (prm->isnull || typByVal)
118
0
      pval = prm->value;
119
0
    else
120
0
      pval = datumCopy(prm->value, typByVal, typLen);
121
122
0
    return (Node *) makeConst(param->paramtype,
123
0
                  param->paramtypmod,
124
0
                  param->paramcollid,
125
0
                  (int) typLen,
126
0
                  pval,
127
0
                  prm->isnull,
128
0
                  typByVal);
129
0
  }
130
0
  return expression_tree_mutator(node,
131
0
                   yb_expr_instantiate_params_mutator,
132
0
                   (void *) paramLI);
133
0
}
134
135
/*
136
 * YbExprInstantiateParams
137
 *
138
 *    Replace the Param nodes of the expression tree with Const nodes carrying
139
 *    current parameter values before pushing the expression down to DocDB
140
 */
141
Expr *YbExprInstantiateParams(Expr* expr, ParamListInfo paramLI)
142
22
{
143
  /* Fast-path if there are no params. */
144
22
  if (paramLI == NULL)
145
22
    return expr;
146
147
0
  return (Expr *) expression_tree_mutator((Node *) expr,
148
0
                      yb_expr_instantiate_params_mutator,
149
0
                      (void *) paramLI);
150
0
}
151
152
/*
153
 * yb_can_pushdown_func
154
 *
155
 *    Determine if the function can be pushed down to DocDB
156
 *    Since catalog access is not currently available in DocDB, only built in
157
 *    functions are pushable. The lack of catalog access imposes also other
158
 *    limitations:
159
 *     - Only immutable functions are pushable. Stable and volatile functions
160
 *       are permitted to access the catalog;
161
 *     - DocDB must support conversion of parameter and result values between
162
 *       DocDB and Postgres formats, so there should be conversion functions;
163
 *     - Typically functions with polymorfic parameters or result need catalog
164
 *       access to determine runtime data types, so they are not pushed down.
165
 */
166
bool yb_can_pushdown_func(Oid funcid)
167
22
{
168
22
  HeapTuple   tuple;
169
22
  Form_pg_proc  pg_proc;
170
22
  bool      result;
171
172
  /* Quick check if the function is builtin */
173
22
  if (!is_builtin_func(funcid))
174
0
  {
175
0
    return false;
176
0
  }
177
178
  /*
179
   * Check whether this function is on a list of hand-picked functions
180
   * safe for pushdown.
181
   */
182
44
  for (int i = 0; i < yb_funcs_safe_for_pushdown_count; ++i)
183
22
  {
184
22
    if (funcid == yb_funcs_safe_for_pushdown[i])
185
0
      return true;
186
22
  }
187
188
  /* Examine misc function attributes that may affect pushability */
189
22
  tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
190
22
  if (!HeapTupleIsValid(tuple))
191
0
    elog(ERROR, "cache lookup failed for function %u", funcid);
192
22
  pg_proc = (Form_pg_proc) GETSTRUCT(tuple);
193
22
  result = true;
194
22
  if (pg_proc->provolatile != PROVOLATILE_IMMUTABLE)
195
0
  {
196
0
    result = false;
197
0
  }
198
22
  if (result &&
199
22
    (!YBCPgFindTypeEntity(pg_proc->prorettype) ||
200
22
     IsPolymorphicType(pg_proc->prorettype)))
201
0
  {
202
0
    result = false;
203
0
  }
204
22
  if (result)
205
22
  {
206
66
    for (int i = 0; i < pg_proc->pronargs; i++)
207
44
    {
208
44
      Oid typid = pg_proc->proargtypes.values[i];
209
44
      if (!YBCPgFindTypeEntity(typid) || IsPolymorphicType(typid))
210
0
      {
211
0
        result = false;
212
0
        break;
213
0
      }
214
44
    }
215
22
  }
216
22
  ReleaseSysCache(tuple);
217
22
  return result;
218
22
}
219
220
/*
221
 * yb_pushdown_walker
222
 *
223
 *    Expression walker used internally by YbCanPushdownExpr
224
 */
225
bool yb_pushdown_walker(Node *node, List **params)
226
66
{
227
66
  if (node == NULL)
228
0
    return false;
229
66
  switch (node->type)
230
66
  {
231
22
    case T_Var:
232
22
    {
233
22
      Var      *var_expr = castNode(Var, node);
234
22
      AttrNumber  attno = var_expr->varattno;
235
      /* DocDB is not aware of Postgres virtual attributes */
236
22
      if (!AttrNumberIsForUserDefinedAttr(attno))
237
0
      {
238
0
        return true;
239
0
      }
240
      /* Need to convert values between DocDB and Postgres formats */
241
22
      if (!YBCPgFindTypeEntity(var_expr->vartype))
242
0
      {
243
0
        return true;
244
0
      }
245
      /* Collect column reference */
246
22
      if (params)
247
22
      {
248
22
        ListCell   *lc;
249
22
        bool    found = false;
250
251
        /* Check if the column reference has already been collected */
252
22
        foreach(lc, *params)
253
0
        {
254
0
          YbExprParamDesc *param = (YbExprParamDesc *) lfirst(lc);
255
0
          if (param->attno == attno)
256
0
          {
257
0
            found = true;
258
0
            break;
259
0
          }
260
0
        }
261
262
22
        if (!found)
263
22
        {
264
          /* Add new column reference to the list */
265
22
          YbExprParamDesc *new_param = makeNode(YbExprParamDesc);
266
22
          new_param->attno = attno;
267
22
          new_param->typid = var_expr->vartype;
268
22
          new_param->typmod = var_expr->vartypmod;
269
22
          new_param->collid = var_expr->varcollid;
270
22
          *params = lappend(*params, new_param);
271
22
        }
272
22
      }
273
22
      break;
274
22
    }
275
0
    case T_FuncExpr:
276
0
    {
277
0
      FuncExpr *func_expr = castNode(FuncExpr, node);
278
      /* DocDB executor does not expand variadic argument */
279
0
      if (func_expr->funcvariadic)
280
0
      {
281
0
        return true;
282
0
      }
283
      /* Check if the function is pushable */
284
0
      if (!yb_can_pushdown_func(func_expr->funcid))
285
0
      {
286
0
        return true;
287
0
      }
288
0
      break;
289
0
    }
290
22
    case T_OpExpr:
291
22
    {
292
22
      OpExpr *op_expr = castNode(OpExpr, node);
293
22
      if (!yb_can_pushdown_func(op_expr->opfuncid))
294
0
      {
295
0
        return true;
296
0
      }
297
22
      break;
298
22
    }
299
0
    case T_CaseExpr:
300
0
    {
301
0
      CaseExpr *case_expr = castNode(CaseExpr, node);
302
      /*
303
       * Support for implicit equality comparison would require catalog
304
       * lookup to find equality operation for the argument data type.
305
       */
306
0
      if (case_expr->arg)
307
0
      {
308
0
        return true;
309
0
      }
310
0
      break;
311
0
    }
312
0
    case T_Param:
313
0
    {
314
0
      Param *p = castNode(Param, node);
315
0
      if (p->paramkind != PARAM_EXTERN ||
316
0
          !YBCPgFindTypeEntity(p->paramtype))
317
0
      {
318
0
        return true;
319
0
      }
320
0
      break;
321
0
    }
322
22
    case T_Const:
323
22
    {
324
22
      Const *c = castNode(Const, node);
325
      /*
326
      * Constant value may need to be converted to DocDB format, but
327
      * DocDB does not support arbitrary types.
328
      */
329
22
      if (!YBCPgFindTypeEntity(c->consttype))
330
0
      {
331
0
        return true;
332
0
      }
333
22
      break;
334
22
    }
335
0
    case T_RelabelType:
336
0
    case T_NullTest:
337
0
    case T_BoolExpr:
338
0
    case T_CaseWhen:
339
0
      break;
340
0
    default:
341
0
      return true;
342
66
  }
343
66
  return expression_tree_walker(node, yb_pushdown_walker, (void *) params);
344
66
}
345
346
/*
347
 * YbCanPushdownExpr
348
 *
349
 *    Determine if the expression is pushable.
350
 *    In general, expression tree is pushable if DocDB knows how to execute
351
 *    all its nodes, in other words, it should be handeled by the evalExpr()
352
 *    function defined in the ybgate_api.c. In addition, external paremeter
353
 *    references of supported data types are also pushable, since these
354
 *    references are replaced with constants by YbExprInstantiateParams before
355
 *    the DocDB request is sent.
356
 *
357
 *    If the params parameter is provided, function also collects column
358
 *    references represented by Var nodes in the expression tree. The params
359
 *    list may be initially empty (NIL) or already contain some YbExprParamDesc
360
 *    entries. That allows to collect column references from multiple
361
 *    expressions into single list. The function avoids adding duplicate
362
 *    references, however it does not remove duplecates if they are already
363
 *    present in the params list.
364
 *
365
 *    To add support for another expression node type it should be added to the
366
 *    yb_pushdown_walker where it should check node attributes that may affect
367
 *    pushability, and implement evaluation of that node type instance in the
368
 *    evalExpr() function.
369
 */
370
bool YbCanPushdownExpr(Expr *pg_expr, List **params)
371
28.4k
{
372
  /* respond with false if pushdown disabled in GUC */
373
28.4k
  if (!yb_enable_expression_pushdown)
374
28.4k
    return false;
375
376
22
  return !yb_pushdown_walker((Node *) pg_expr, params);
377
22
}
378
379
/*
380
 * yb_transactional_walker
381
 *
382
 *    Expression tree walker for the YbIsTransactionalExpr function.
383
 *    As of initial version, it may be too optimistic, needs revisit.
384
 */
385
bool yb_transactional_walker(Node *node, void *context)
386
1.04M
{
387
1.04M
  if (node == NULL)
388
120k
    return false;
389
929k
  switch (node->type)
390
929k
  {
391
1.01k
    case T_FuncExpr:
392
1.01k
    {
393
      /*
394
       * Built-in functions should be safe. If we learn of functions
395
       * that are unsafe we may need a blacklist here.
396
       * User defined function may be everything, unless it is immutable
397
       * By definition, immutable functions can not access database.
398
       * Otherwise safely assume that not immutable function would needs
399
       * a distributed transaction.
400
       */
401
1.01k
      FuncExpr     *func_expr = castNode(FuncExpr, node);
402
1.01k
      Oid       funcid = func_expr->funcid;
403
1.01k
      HeapTuple   tuple;
404
1.01k
      Form_pg_proc  pg_proc;
405
1.01k
      if (is_builtin_func(funcid))
406
1.01k
      {
407
1.01k
        break;
408
1.01k
      }
409
0
      tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
410
0
      if (!HeapTupleIsValid(tuple))
411
0
        elog(ERROR, "cache lookup failed for function %u", funcid);
412
0
      pg_proc = (Form_pg_proc) GETSTRUCT(tuple);
413
0
      if (pg_proc->provolatile != PROVOLATILE_IMMUTABLE)
414
0
      {
415
0
        ReleaseSysCache(tuple);
416
0
        return true;
417
0
      }
418
0
      ReleaseSysCache(tuple);
419
0
      break;
420
0
    }
421
    /*
422
     * The list of the expression types below was built by scrolling over
423
     * expression_tree_walker function and selecting those looking like they
424
     * do suspiciously transactional thing like running a subquery.
425
     */
426
2
    case T_NextValueExpr:
427
2
    case T_RangeTblRef:
428
2
    case T_SubLink:
429
2
    case T_SubPlan:
430
2
    case T_AlternativeSubPlan:
431
2
    case T_Query:
432
2
    case T_CommonTableExpr:
433
2
    case T_FromExpr:
434
2
    case T_JoinExpr:
435
2
    case T_AppendRelInfo:
436
2
    case T_RangeTblFunction:
437
2
    case T_TableSampleClause:
438
2
    case T_TableFunc:
439
2
      return true;
440
    /*
441
     * Optimistically assume all other expression types do not
442
     * require a distributed transaction.
443
     */
444
928k
    default:
445
928k
      break;
446
929k
  }
447
929k
  return expression_tree_walker(node, yb_transactional_walker, context);
448
929k
}
449
450
/*
451
 * YbIsTransactionalExpr
452
 *
453
 *    Determine if the expression may need distributed transaction.
454
 *    One shard modify table queries (INSERT, UPDATE, DELETE) running in
455
 *    autocommit mode may skip starting distributed transactions. Without
456
 *    distributed transaction overhead those statements perform much better.
457
 *    However, certain expression may need to run a subquery or otherwise
458
 *    access multiple nodes transactionally. This function checks if that
459
 *    might be the case for given expression, and therefore distributed
460
 *    transaction should be used for parent statement.
461
 *    Historically the same function was used to determine if an expression
462
 *    is pushable or if expression is (not) transactional, out of consideration
463
 *    that if expression is simple enough to be pushable, it is not
464
 *    transactional. That is generally true, pushable expression are not
465
 *    transactional, however there are many not pushable expressions, which are
466
 *    not transactional at the same time, so we can still benefit from higher
467
 *    performing one shard queries even if they use not pushable expressions.
468
 *    Besides, expression pushdown may be turned off with a GUC parameter.
469
 *    If this function misdetermine transactional expression as not
470
 *    transactional distributed transaction may be forced by surrounding the
471
 *    statement with BEGIN; ... COMMIT;
472
 *    Opposite misdetermination causes performance overhead only.
473
 */
474
bool YbIsTransactionalExpr(Node *pg_expr)
475
239k
{
476
239k
  return yb_transactional_walker(pg_expr, NULL);
477
239k
}
478
479
/*
480
 * YBCNewEvalExprCall
481
 *
482
 *    Serialize the Postgres expression tree and associate it with the
483
 *    DocDB statement. Caller is supposed to ensure that expression is pushable
484
 *    so DocDB can handle it.
485
 */
486
YBCPgExpr YBCNewEvalExprCall(YBCPgStatement ybc_stmt, Expr *pg_expr)
487
5.76k
{
488
5.76k
  YBCPgExpr ybc_expr;
489
5.76k
  YBCPgCollationInfo collation_info;
490
5.76k
  const YBCPgTypeEntity *type_ent;
491
5.76k
  type_ent = YbDataTypeFromOidMod(InvalidAttrNumber,
492
5.76k
                  exprType((Node *) pg_expr));
493
5.76k
  YBGetCollationInfo(exprCollation((Node *) pg_expr),
494
5.76k
             type_ent,
495
5.76k
             0 /* Datum */,
496
5.76k
             true /* is_null */,
497
5.76k
             &collation_info);
498
5.76k
  HandleYBStatus(YBCPgNewOperator(ybc_stmt,
499
5.76k
                  "eval_expr_call",
500
5.76k
                  type_ent,
501
5.76k
                  collation_info.collate_is_valid_non_c,
502
5.76k
                  &ybc_expr));
503
504
5.76k
  Datum expr_datum = CStringGetDatum(nodeToString(pg_expr));
505
5.76k
  YBCPgExpr expr = YBCNewConstant(ybc_stmt, CSTRINGOID, C_COLLATION_OID,
506
5.76k
                  expr_datum , /* IsNull */ false);
507
5.76k
  HandleYBStatus(YBCPgOperatorAppendArg(ybc_expr, expr));
508
5.76k
  return ybc_expr;
509
5.76k
}
510
511
/* ------------------------------------------------------------------------- */
512
/*  Execution output parameter from Yugabyte */
513
YbPgExecOutParam *YbCreateExecOutParam()
514
257
{
515
257
  YbPgExecOutParam *param = makeNode(YbPgExecOutParam);
516
257
  param->bfoutput = makeStringInfo();
517
518
  /* Not yet used */
519
257
  param->status = makeStringInfo();
520
257
  param->status_code = 0;
521
522
257
  return param;
523
257
}
524
525
257
void YbWriteExecOutParam(YbPgExecOutParam *param, const YbcPgExecOutParamValue *value) {
526
257
  appendStringInfoString(param->bfoutput, value->bfoutput);
527
528
  /* Not yet used */
529
257
  if (value->status)
530
0
  {
531
0
    appendStringInfoString(param->status, value->status);
532
0
    param->status_code = value->status_code;
533
0
  }
534
257
}