YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/Users/deen/code/yugabyte-db/src/postgres/src/backend/executor/nodeTidscan.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * nodeTidscan.c
4
 *    Routines to support direct tid scans of relations
5
 *
6
 * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
7
 * Portions Copyright (c) 1994, Regents of the University of California
8
 *
9
 *
10
 * IDENTIFICATION
11
 *    src/backend/executor/nodeTidscan.c
12
 *
13
 *-------------------------------------------------------------------------
14
 */
15
/*
16
 * INTERFACE ROUTINES
17
 *
18
 *    ExecTidScan     scans a relation using tids
19
 *    ExecInitTidScan   creates and initializes state info.
20
 *    ExecReScanTidScan rescans the tid relation.
21
 *    ExecEndTidScan    releases all storage.
22
 */
23
#include "postgres.h"
24
25
#include "access/sysattr.h"
26
#include "catalog/pg_type.h"
27
#include "executor/execdebug.h"
28
#include "executor/nodeTidscan.h"
29
#include "miscadmin.h"
30
#include "optimizer/clauses.h"
31
#include "storage/bufmgr.h"
32
#include "utils/array.h"
33
#include "utils/rel.h"
34
35
36
#define IsCTIDVar(node)  \
37
0
  ((node) != NULL && \
38
0
   IsA((node), Var) && \
39
0
   ((Var *) (node))->varattno == SelfItemPointerAttributeNumber && \
40
0
   ((Var *) (node))->varlevelsup == 0)
41
42
/* one element in tss_tidexprs */
43
typedef struct TidExpr
44
{
45
  ExprState  *exprstate;    /* ExprState for a TID-yielding subexpr */
46
  bool    isarray;    /* if true, it yields tid[] not just tid */
47
  CurrentOfExpr *cexpr;   /* alternatively, we can have CURRENT OF */
48
} TidExpr;
49
50
static void TidExprListCreate(TidScanState *tidstate);
51
static void TidListEval(TidScanState *tidstate);
52
static int  itemptr_comparator(const void *a, const void *b);
53
static TupleTableSlot *TidNext(TidScanState *node);
54
55
56
/*
57
 * Extract the qual subexpressions that yield TIDs to search for,
58
 * and compile them into ExprStates if they're ordinary expressions.
59
 *
60
 * CURRENT OF is a special case that we can't compile usefully;
61
 * just drop it into the TidExpr list as-is.
62
 */
63
static void
64
TidExprListCreate(TidScanState *tidstate)
65
0
{
66
0
  TidScan    *node = (TidScan *) tidstate->ss.ps.plan;
67
0
  ListCell   *l;
68
69
0
  tidstate->tss_tidexprs = NIL;
70
0
  tidstate->tss_isCurrentOf = false;
71
72
0
  foreach(l, node->tidquals)
73
0
  {
74
0
    Expr     *expr = (Expr *) lfirst(l);
75
0
    TidExpr    *tidexpr = (TidExpr *) palloc0(sizeof(TidExpr));
76
77
0
    if (is_opclause(expr))
78
0
    {
79
0
      Node     *arg1;
80
0
      Node     *arg2;
81
82
0
      arg1 = get_leftop(expr);
83
0
      arg2 = get_rightop(expr);
84
0
      if (IsCTIDVar(arg1))
85
0
        tidexpr->exprstate = ExecInitExpr((Expr *) arg2,
86
0
                          &tidstate->ss.ps);
87
0
      else if (IsCTIDVar(arg2))
88
0
        tidexpr->exprstate = ExecInitExpr((Expr *) arg1,
89
0
                          &tidstate->ss.ps);
90
0
      else
91
0
        elog(ERROR, "could not identify CTID variable");
92
0
      tidexpr->isarray = false;
93
0
    }
94
0
    else if (expr && IsA(expr, ScalarArrayOpExpr))
95
0
    {
96
0
      ScalarArrayOpExpr *saex = (ScalarArrayOpExpr *) expr;
97
98
0
      Assert(IsCTIDVar(linitial(saex->args)));
99
0
      tidexpr->exprstate = ExecInitExpr(lsecond(saex->args),
100
0
                        &tidstate->ss.ps);
101
0
      tidexpr->isarray = true;
102
0
    }
103
0
    else if (expr && IsA(expr, CurrentOfExpr))
104
0
    {
105
0
      CurrentOfExpr *cexpr = (CurrentOfExpr *) expr;
106
107
0
      tidexpr->cexpr = cexpr;
108
0
      tidstate->tss_isCurrentOf = true;
109
0
    }
110
0
    else
111
0
      elog(ERROR, "could not identify CTID expression");
112
113
0
    tidstate->tss_tidexprs = lappend(tidstate->tss_tidexprs, tidexpr);
114
0
  }
115
116
  /* CurrentOfExpr could never appear OR'd with something else */
117
0
  Assert(list_length(tidstate->tss_tidexprs) == 1 ||
118
0
       !tidstate->tss_isCurrentOf);
119
0
}
120
121
/*
122
 * Compute the list of TIDs to be visited, by evaluating the expressions
123
 * for them.
124
 *
125
 * (The result is actually an array, not a list.)
126
 */
127
static void
128
TidListEval(TidScanState *tidstate)
129
0
{
130
0
  ExprContext *econtext = tidstate->ss.ps.ps_ExprContext;
131
0
  BlockNumber nblocks;
132
0
  ItemPointerData *tidList;
133
0
  int     numAllocTids;
134
0
  int     numTids;
135
0
  ListCell   *l;
136
137
  /*
138
   * We silently discard any TIDs that are out of range at the time of scan
139
   * start.  (Since we hold at least AccessShareLock on the table, it won't
140
   * be possible for someone to truncate away the blocks we intend to
141
   * visit.)
142
   */
143
0
  nblocks = RelationGetNumberOfBlocks(tidstate->ss.ss_currentRelation);
144
145
  /*
146
   * We initialize the array with enough slots for the case that all quals
147
   * are simple OpExprs or CurrentOfExprs.  If there are any
148
   * ScalarArrayOpExprs, we may have to enlarge the array.
149
   */
150
0
  numAllocTids = list_length(tidstate->tss_tidexprs);
151
0
  tidList = (ItemPointerData *)
152
0
    palloc(numAllocTids * sizeof(ItemPointerData));
153
0
  numTids = 0;
154
155
0
  foreach(l, tidstate->tss_tidexprs)
156
0
  {
157
0
    TidExpr    *tidexpr = (TidExpr *) lfirst(l);
158
0
    ItemPointer itemptr;
159
0
    bool    isNull;
160
161
0
    if (tidexpr->exprstate && !tidexpr->isarray)
162
0
    {
163
0
      itemptr = (ItemPointer)
164
0
        DatumGetPointer(ExecEvalExprSwitchContext(tidexpr->exprstate,
165
0
                              econtext,
166
0
                              &isNull));
167
0
      if (!isNull &&
168
0
        ItemPointerIsValid(itemptr) &&
169
0
        ItemPointerGetBlockNumber(itemptr) < nblocks)
170
0
      {
171
0
        if (numTids >= numAllocTids)
172
0
        {
173
0
          numAllocTids *= 2;
174
0
          tidList = (ItemPointerData *)
175
0
            repalloc(tidList,
176
0
                 numAllocTids * sizeof(ItemPointerData));
177
0
        }
178
0
        tidList[numTids++] = *itemptr;
179
0
      }
180
0
    }
181
0
    else if (tidexpr->exprstate && tidexpr->isarray)
182
0
    {
183
0
      Datum   arraydatum;
184
0
      ArrayType  *itemarray;
185
0
      Datum    *ipdatums;
186
0
      bool     *ipnulls;
187
0
      int     ndatums;
188
0
      int     i;
189
190
0
      arraydatum = ExecEvalExprSwitchContext(tidexpr->exprstate,
191
0
                           econtext,
192
0
                           &isNull);
193
0
      if (isNull)
194
0
        continue;
195
0
      itemarray = DatumGetArrayTypeP(arraydatum);
196
0
      deconstruct_array(itemarray,
197
0
                TIDOID, sizeof(ItemPointerData), false, 's',
198
0
                &ipdatums, &ipnulls, &ndatums);
199
0
      if (numTids + ndatums > numAllocTids)
200
0
      {
201
0
        numAllocTids = numTids + ndatums;
202
0
        tidList = (ItemPointerData *)
203
0
          repalloc(tidList,
204
0
               numAllocTids * sizeof(ItemPointerData));
205
0
      }
206
0
      for (i = 0; i < ndatums; i++)
207
0
      {
208
0
        if (!ipnulls[i])
209
0
        {
210
0
          itemptr = (ItemPointer) DatumGetPointer(ipdatums[i]);
211
0
          if (ItemPointerIsValid(itemptr) &&
212
0
            ItemPointerGetBlockNumber(itemptr) < nblocks)
213
0
            tidList[numTids++] = *itemptr;
214
0
        }
215
0
      }
216
0
      pfree(ipdatums);
217
0
      pfree(ipnulls);
218
0
    }
219
0
    else
220
0
    {
221
0
      ItemPointerData cursor_tid;
222
223
0
      Assert(tidexpr->cexpr);
224
0
      if (execCurrentOf(tidexpr->cexpr, econtext,
225
0
                RelationGetRelid(tidstate->ss.ss_currentRelation),
226
0
                &cursor_tid))
227
0
      {
228
0
        if (numTids >= numAllocTids)
229
0
        {
230
0
          numAllocTids *= 2;
231
0
          tidList = (ItemPointerData *)
232
0
            repalloc(tidList,
233
0
                 numAllocTids * sizeof(ItemPointerData));
234
0
        }
235
0
        tidList[numTids++] = cursor_tid;
236
0
      }
237
0
    }
238
0
  }
239
240
  /*
241
   * Sort the array of TIDs into order, and eliminate duplicates.
242
   * Eliminating duplicates is necessary since we want OR semantics across
243
   * the list.  Sorting makes it easier to detect duplicates, and as a bonus
244
   * ensures that we will visit the heap in the most efficient way.
245
   */
246
0
  if (numTids > 1)
247
0
  {
248
0
    int     lastTid;
249
0
    int     i;
250
251
    /* CurrentOfExpr could never appear OR'd with something else */
252
0
    Assert(!tidstate->tss_isCurrentOf);
253
254
0
    qsort((void *) tidList, numTids, sizeof(ItemPointerData),
255
0
        itemptr_comparator);
256
0
    lastTid = 0;
257
0
    for (i = 1; i < numTids; i++)
258
0
    {
259
0
      if (!ItemPointerEquals(&tidList[lastTid], &tidList[i]))
260
0
        tidList[++lastTid] = tidList[i];
261
0
    }
262
0
    numTids = lastTid + 1;
263
0
  }
264
265
0
  tidstate->tss_TidList = tidList;
266
0
  tidstate->tss_NumTids = numTids;
267
0
  tidstate->tss_TidPtr = -1;
268
0
}
269
270
/*
271
 * qsort comparator for ItemPointerData items
272
 */
273
static int
274
itemptr_comparator(const void *a, const void *b)
275
0
{
276
0
  const ItemPointerData *ipa = (const ItemPointerData *) a;
277
0
  const ItemPointerData *ipb = (const ItemPointerData *) b;
278
0
  BlockNumber ba = ItemPointerGetBlockNumber(ipa);
279
0
  BlockNumber bb = ItemPointerGetBlockNumber(ipb);
280
0
  OffsetNumber oa = ItemPointerGetOffsetNumber(ipa);
281
0
  OffsetNumber ob = ItemPointerGetOffsetNumber(ipb);
282
283
0
  if (ba < bb)
284
0
    return -1;
285
0
  if (ba > bb)
286
0
    return 1;
287
0
  if (oa < ob)
288
0
    return -1;
289
0
  if (oa > ob)
290
0
    return 1;
291
0
  return 0;
292
0
}
293
294
/* ----------------------------------------------------------------
295
 *    TidNext
296
 *
297
 *    Retrieve a tuple from the TidScan node's currentRelation
298
 *    using the tids in the TidScanState information.
299
 *
300
 * ----------------------------------------------------------------
301
 */
302
static TupleTableSlot *
303
TidNext(TidScanState *node)
304
0
{
305
0
  EState     *estate;
306
0
  ScanDirection direction;
307
0
  Snapshot  snapshot;
308
0
  Relation  heapRelation;
309
0
  HeapTuple tuple;
310
0
  TupleTableSlot *slot;
311
0
  Buffer    buffer = InvalidBuffer;
312
0
  ItemPointerData *tidList;
313
0
  int     numTids;
314
0
  bool    bBackward;
315
316
  /*
317
   * extract necessary information from tid scan node
318
   */
319
0
  estate = node->ss.ps.state;
320
0
  direction = estate->es_direction;
321
0
  snapshot = estate->es_snapshot;
322
0
  heapRelation = node->ss.ss_currentRelation;
323
0
  slot = node->ss.ss_ScanTupleSlot;
324
325
  /*
326
   * First time through, compute the list of TIDs to be visited
327
   */
328
0
  if (node->tss_TidList == NULL)
329
0
    TidListEval(node);
330
331
0
  tidList = node->tss_TidList;
332
0
  numTids = node->tss_NumTids;
333
334
  /*
335
   * We use node->tss_htup as the tuple pointer; note this can't just be a
336
   * local variable here, as the scan tuple slot will keep a pointer to it.
337
   */
338
0
  tuple = &(node->tss_htup);
339
340
  /*
341
   * Initialize or advance scan position, depending on direction.
342
   */
343
0
  bBackward = ScanDirectionIsBackward(direction);
344
0
  if (bBackward)
345
0
  {
346
0
    if (node->tss_TidPtr < 0)
347
0
    {
348
      /* initialize for backward scan */
349
0
      node->tss_TidPtr = numTids - 1;
350
0
    }
351
0
    else
352
0
      node->tss_TidPtr--;
353
0
  }
354
0
  else
355
0
  {
356
0
    if (node->tss_TidPtr < 0)
357
0
    {
358
      /* initialize for forward scan */
359
0
      node->tss_TidPtr = 0;
360
0
    }
361
0
    else
362
0
      node->tss_TidPtr++;
363
0
  }
364
365
0
  while (node->tss_TidPtr >= 0 && node->tss_TidPtr < numTids)
366
0
  {
367
0
    tuple->t_self = tidList[node->tss_TidPtr];
368
369
    /*
370
     * For WHERE CURRENT OF, the tuple retrieved from the cursor might
371
     * since have been updated; if so, we should fetch the version that is
372
     * current according to our snapshot.
373
     */
374
0
    if (node->tss_isCurrentOf)
375
0
      heap_get_latest_tid(heapRelation, snapshot, &tuple->t_self);
376
377
0
    if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL))
378
0
    {
379
      /*
380
       * Store the scanned tuple in the scan tuple slot of the scan
381
       * state.  Eventually we will only do this and not return a tuple.
382
       */
383
0
      ExecStoreBufferHeapTuple(tuple, /* tuple to store */
384
0
                   slot,  /* slot to store in */
385
0
                   buffer); /* buffer associated with
386
                         * tuple */
387
388
      /*
389
       * At this point we have an extra pin on the buffer, because
390
       * ExecStoreHeapTuple incremented the pin count. Drop our local
391
       * pin.
392
       */
393
0
      ReleaseBuffer(buffer);
394
395
0
      return slot;
396
0
    }
397
    /* Bad TID or failed snapshot qual; try next */
398
0
    if (bBackward)
399
0
      node->tss_TidPtr--;
400
0
    else
401
0
      node->tss_TidPtr++;
402
403
0
    CHECK_FOR_INTERRUPTS();
404
0
  }
405
406
  /*
407
   * if we get here it means the tid scan failed so we are at the end of the
408
   * scan..
409
   */
410
0
  return ExecClearTuple(slot);
411
0
}
412
413
/*
414
 * TidRecheck -- access method routine to recheck a tuple in EvalPlanQual
415
 */
416
static bool
417
TidRecheck(TidScanState *node, TupleTableSlot *slot)
418
0
{
419
  /*
420
   * XXX shouldn't we check here to make sure tuple matches TID list? In
421
   * runtime-key case this is not certain, is it?  However, in the WHERE
422
   * CURRENT OF case it might not match anyway ...
423
   */
424
0
  return true;
425
0
}
426
427
428
/* ----------------------------------------------------------------
429
 *    ExecTidScan(node)
430
 *
431
 *    Scans the relation using tids and returns
432
 *       the next qualifying tuple in the direction specified.
433
 *    We call the ExecScan() routine and pass it the appropriate
434
 *    access method functions.
435
 *
436
 *    Conditions:
437
 *      -- the "cursor" maintained by the AMI is positioned at the tuple
438
 *       returned previously.
439
 *
440
 *    Initial States:
441
 *      -- the relation indicated is opened for scanning so that the
442
 *       "cursor" is positioned before the first qualifying tuple.
443
 *      -- tidPtr is -1.
444
 * ----------------------------------------------------------------
445
 */
446
static TupleTableSlot *
447
ExecTidScan(PlanState *pstate)
448
0
{
449
0
  TidScanState *node = castNode(TidScanState, pstate);
450
451
0
  return ExecScan(&node->ss,
452
0
          (ExecScanAccessMtd) TidNext,
453
0
          (ExecScanRecheckMtd) TidRecheck);
454
0
}
455
456
/* ----------------------------------------------------------------
457
 *    ExecReScanTidScan(node)
458
 * ----------------------------------------------------------------
459
 */
460
void
461
ExecReScanTidScan(TidScanState *node)
462
0
{
463
0
  if (node->tss_TidList)
464
0
    pfree(node->tss_TidList);
465
0
  node->tss_TidList = NULL;
466
0
  node->tss_NumTids = 0;
467
0
  node->tss_TidPtr = -1;
468
469
0
  ExecScanReScan(&node->ss);
470
0
}
471
472
/* ----------------------------------------------------------------
473
 *    ExecEndTidScan
474
 *
475
 *    Releases any storage allocated through C routines.
476
 *    Returns nothing.
477
 * ----------------------------------------------------------------
478
 */
479
void
480
ExecEndTidScan(TidScanState *node)
481
0
{
482
  /*
483
   * Free the exprcontext
484
   */
485
0
  ExecFreeExprContext(&node->ss.ps);
486
487
  /*
488
   * clear out tuple table slots
489
   */
490
0
  if (node->ss.ps.ps_ResultTupleSlot)
491
0
    ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
492
0
  ExecClearTuple(node->ss.ss_ScanTupleSlot);
493
494
  /*
495
   * close the heap relation.
496
   */
497
0
  ExecCloseScanRelation(node->ss.ss_currentRelation);
498
0
}
499
500
/* ----------------------------------------------------------------
501
 *    ExecInitTidScan
502
 *
503
 *    Initializes the tid scan's state information, creates
504
 *    scan keys, and opens the base and tid relations.
505
 *
506
 *    Parameters:
507
 *      node: TidNode node produced by the planner.
508
 *      estate: the execution state initialized in InitPlan.
509
 * ----------------------------------------------------------------
510
 */
511
TidScanState *
512
ExecInitTidScan(TidScan *node, EState *estate, int eflags)
513
0
{
514
0
  TidScanState *tidstate;
515
0
  Relation  currentRelation;
516
517
  /*
518
   * create state structure
519
   */
520
0
  tidstate = makeNode(TidScanState);
521
0
  tidstate->ss.ps.plan = (Plan *) node;
522
0
  tidstate->ss.ps.state = estate;
523
0
  tidstate->ss.ps.ExecProcNode = ExecTidScan;
524
525
  /*
526
   * Miscellaneous initialization
527
   *
528
   * create expression context for node
529
   */
530
0
  ExecAssignExprContext(estate, &tidstate->ss.ps);
531
532
  /*
533
   * mark tid list as not computed yet
534
   */
535
0
  tidstate->tss_TidList = NULL;
536
0
  tidstate->tss_NumTids = 0;
537
0
  tidstate->tss_TidPtr = -1;
538
539
  /*
540
   * open the base relation and acquire appropriate lock on it.
541
   */
542
0
  currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
543
544
0
  tidstate->ss.ss_currentRelation = currentRelation;
545
0
  tidstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */
546
547
  /*
548
   * get the scan type from the relation descriptor.
549
   */
550
0
  ExecInitScanTupleSlot(estate, &tidstate->ss,
551
0
              RelationGetDescr(currentRelation));
552
553
  /*
554
   * Initialize result type and projection.
555
   */
556
0
  ExecInitResultTypeTL(&tidstate->ss.ps);
557
0
  ExecAssignScanProjectionInfo(&tidstate->ss);
558
559
  /*
560
   * initialize child expressions
561
   */
562
0
  tidstate->ss.ps.qual =
563
0
    ExecInitQual(node->scan.plan.qual, (PlanState *) tidstate);
564
565
0
  TidExprListCreate(tidstate);
566
567
  /*
568
   * all done.
569
   */
570
0
  return tidstate;
571
0
}