Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit da27c0a

Browse files
committed
Teach tid-scan code to make use of "ctid = ANY (array)" clauses, so that
"ctid IN (list)" will still work after we convert IN to ScalarArrayOpExpr. Make some minor efficiency improvements while at it, such as ensuring that multiple TIDs are fetched in physical heap order. And fix EXPLAIN so that it shows what's really going on for a TID scan.
1 parent a66e2c8 commit da27c0a

File tree

15 files changed

+378
-147
lines changed

15 files changed

+378
-147
lines changed

src/backend/commands/explain.c

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1994-5, Regents of the University of California
88
*
99
* IDENTIFICATION
10-
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.140 2005/11/22 18:17:09 momjian Exp $
10+
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.141 2005/11/26 22:14:56 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -724,7 +724,6 @@ explain_outNode(StringInfo str,
724724
str, indent, es);
725725
/* FALL THRU */
726726
case T_SeqScan:
727-
case T_TidScan:
728727
case T_SubqueryScan:
729728
case T_FunctionScan:
730729
show_scan_qual(plan->qual,
@@ -733,6 +732,28 @@ explain_outNode(StringInfo str,
733732
outer_plan,
734733
str, indent, es);
735734
break;
735+
case T_TidScan:
736+
{
737+
/*
738+
* The tidquals list has OR semantics, so be sure to show it
739+
* as an OR condition.
740+
*/
741+
List *tidquals = ((TidScan *) plan)->tidquals;
742+
743+
if (list_length(tidquals) > 1)
744+
tidquals = list_make1(make_orclause(tidquals));
745+
show_scan_qual(tidquals,
746+
"TID Cond",
747+
((Scan *) plan)->scanrelid,
748+
outer_plan,
749+
str, indent, es);
750+
show_scan_qual(plan->qual,
751+
"Filter",
752+
((Scan *) plan)->scanrelid,
753+
outer_plan,
754+
str, indent, es);
755+
}
756+
break;
736757
case T_NestLoop:
737758
show_upper_qual(((NestLoop *) plan)->join.joinqual,
738759
"Join Filter",

src/backend/executor/nodeTidscan.c

Lines changed: 163 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/executor/nodeTidscan.c,v 1.44 2005/11/25 04:24:48 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/executor/nodeTidscan.c,v 1.45 2005/11/26 22:14:56 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -24,54 +24,187 @@
2424
*/
2525
#include "postgres.h"
2626

27+
#include "access/heapam.h"
28+
#include "catalog/pg_type.h"
2729
#include "executor/execdebug.h"
2830
#include "executor/nodeTidscan.h"
29-
#include "access/heapam.h"
31+
#include "optimizer/clauses.h"
3032
#include "parser/parsetree.h"
33+
#include "utils/array.h"
3134

3235

36+
#define IsCTIDVar(node) \
37+
((node) != NULL && \
38+
IsA((node), Var) && \
39+
((Var *) (node))->varattno == SelfItemPointerAttributeNumber && \
40+
((Var *) (node))->varlevelsup == 0)
41+
3342
static void TidListCreate(TidScanState *tidstate);
43+
static int itemptr_comparator(const void *a, const void *b);
3444
static TupleTableSlot *TidNext(TidScanState *node);
3545

3646

3747
/*
3848
* Compute the list of TIDs to be visited, by evaluating the expressions
3949
* for them.
50+
*
51+
* (The result is actually an array, not a list.)
4052
*/
4153
static void
4254
TidListCreate(TidScanState *tidstate)
4355
{
44-
List *evalList = tidstate->tss_tideval;
56+
List *evalList = tidstate->tss_tidquals;
4557
ExprContext *econtext = tidstate->ss.ps.ps_ExprContext;
4658
ItemPointerData *tidList;
47-
int numTids = 0;
59+
int numAllocTids;
60+
int numTids;
4861
ListCell *l;
4962

63+
/*
64+
* We initialize the array with enough slots for the case that all
65+
* quals are simple OpExprs. If there's any ScalarArrayOpExprs,
66+
* we may have to enlarge the array.
67+
*/
68+
numAllocTids = list_length(evalList);
5069
tidList = (ItemPointerData *)
51-
palloc(list_length(tidstate->tss_tideval) * sizeof(ItemPointerData));
70+
palloc(numAllocTids * sizeof(ItemPointerData));
71+
numTids = 0;
5272

5373
foreach(l, evalList)
5474
{
75+
ExprState *exstate = (ExprState *) lfirst(l);
76+
Expr *expr = exstate->expr;
5577
ItemPointer itemptr;
5678
bool isNull;
5779

58-
itemptr = (ItemPointer)
59-
DatumGetPointer(ExecEvalExprSwitchContext(lfirst(l),
60-
econtext,
61-
&isNull,
62-
NULL));
63-
if (!isNull && itemptr && ItemPointerIsValid(itemptr))
80+
if (is_opclause(expr))
6481
{
65-
tidList[numTids] = *itemptr;
66-
numTids++;
82+
FuncExprState *fexstate = (FuncExprState *) exstate;
83+
Node *arg1;
84+
Node *arg2;
85+
86+
arg1 = get_leftop(expr);
87+
arg2 = get_rightop(expr);
88+
if (IsCTIDVar(arg1))
89+
exstate = (ExprState *) lsecond(fexstate->args);
90+
else if (IsCTIDVar(arg2))
91+
exstate = (ExprState *) linitial(fexstate->args);
92+
else
93+
elog(ERROR, "could not identify CTID variable");
94+
95+
itemptr = (ItemPointer)
96+
DatumGetPointer(ExecEvalExprSwitchContext(exstate,
97+
econtext,
98+
&isNull,
99+
NULL));
100+
if (!isNull && ItemPointerIsValid(itemptr))
101+
{
102+
if (numTids >= numAllocTids)
103+
{
104+
numAllocTids *= 2;
105+
tidList = (ItemPointerData *)
106+
repalloc(tidList,
107+
numAllocTids * sizeof(ItemPointerData));
108+
}
109+
tidList[numTids++] = *itemptr;
110+
}
67111
}
112+
else if (expr && IsA(expr, ScalarArrayOpExpr))
113+
{
114+
ScalarArrayOpExprState *saexstate = (ScalarArrayOpExprState *) exstate;
115+
Datum arraydatum;
116+
ArrayType *itemarray;
117+
Datum *ipdatums;
118+
bool *ipnulls;
119+
int ndatums;
120+
int i;
121+
122+
exstate = (ExprState *) lsecond(saexstate->fxprstate.args);
123+
arraydatum = ExecEvalExprSwitchContext(exstate,
124+
econtext,
125+
&isNull,
126+
NULL);
127+
if (isNull)
128+
continue;
129+
itemarray = DatumGetArrayTypeP(arraydatum);
130+
deconstruct_array(itemarray,
131+
TIDOID, SizeOfIptrData, false, 's',
132+
&ipdatums, &ipnulls, &ndatums);
133+
if (numTids + ndatums > numAllocTids)
134+
{
135+
numAllocTids = numTids + ndatums;
136+
tidList = (ItemPointerData *)
137+
repalloc(tidList,
138+
numAllocTids * sizeof(ItemPointerData));
139+
}
140+
for (i = 0; i < ndatums; i++)
141+
{
142+
if (!ipnulls[i])
143+
{
144+
itemptr = (ItemPointer) DatumGetPointer(ipdatums[i]);
145+
if (ItemPointerIsValid(itemptr))
146+
tidList[numTids++] = *itemptr;
147+
}
148+
}
149+
pfree(ipdatums);
150+
pfree(ipnulls);
151+
}
152+
else
153+
elog(ERROR, "could not identify CTID expression");
154+
}
155+
156+
/*
157+
* Sort the array of TIDs into order, and eliminate duplicates.
158+
* Eliminating duplicates is necessary since we want OR semantics
159+
* across the list. Sorting makes it easier to detect duplicates,
160+
* and as a bonus ensures that we will visit the heap in the most
161+
* efficient way.
162+
*/
163+
if (numTids > 1)
164+
{
165+
int lastTid;
166+
int i;
167+
168+
qsort((void *) tidList, numTids, sizeof(ItemPointerData),
169+
itemptr_comparator);
170+
lastTid = 0;
171+
for (i = 1; i < numTids; i++)
172+
{
173+
if (!ItemPointerEquals(&tidList[lastTid], &tidList[i]))
174+
tidList[++lastTid] = tidList[i];
175+
}
176+
numTids = lastTid + 1;
68177
}
69178

70179
tidstate->tss_TidList = tidList;
71180
tidstate->tss_NumTids = numTids;
72181
tidstate->tss_TidPtr = -1;
73182
}
74183

184+
/*
185+
* qsort comparator for ItemPointerData items
186+
*/
187+
static int
188+
itemptr_comparator(const void *a, const void *b)
189+
{
190+
const ItemPointerData *ipa = (const ItemPointerData *) a;
191+
const ItemPointerData *ipb = (const ItemPointerData *) b;
192+
BlockNumber ba = ItemPointerGetBlockNumber(ipa);
193+
BlockNumber bb = ItemPointerGetBlockNumber(ipb);
194+
OffsetNumber oa = ItemPointerGetOffsetNumber(ipa);
195+
OffsetNumber ob = ItemPointerGetOffsetNumber(ipb);
196+
197+
if (ba < bb)
198+
return -1;
199+
if (ba > bb)
200+
return 1;
201+
if (oa < ob)
202+
return -1;
203+
if (oa > ob)
204+
return 1;
205+
return 0;
206+
}
207+
75208
/* ----------------------------------------------------------------
76209
* TidNext
77210
*
@@ -94,7 +227,6 @@ TidNext(TidScanState *node)
94227
ItemPointerData *tidList;
95228
int numTids;
96229
bool bBackward;
97-
int tidNumber;
98230

99231
/*
100232
* extract necessary information from tid scan node
@@ -143,38 +275,35 @@ TidNext(TidScanState *node)
143275
tuple = &(node->tss_htup);
144276

145277
/*
146-
* ok, now that we have what we need, fetch an tid tuple. if scanning this
147-
* tid succeeded then return the appropriate heap tuple.. else return
148-
* NULL.
278+
* Initialize or advance scan position, depending on direction.
149279
*/
150280
bBackward = ScanDirectionIsBackward(direction);
151281
if (bBackward)
152282
{
153-
tidNumber = numTids - node->tss_TidPtr - 1;
154-
if (tidNumber < 0)
283+
if (node->tss_TidPtr < 0)
155284
{
156-
tidNumber = 0;
285+
/* initialize for backward scan */
157286
node->tss_TidPtr = numTids - 1;
158287
}
288+
else
289+
node->tss_TidPtr--;
159290
}
160291
else
161292
{
162-
if ((tidNumber = node->tss_TidPtr) < 0)
293+
if (node->tss_TidPtr < 0)
163294
{
164-
tidNumber = 0;
295+
/* initialize for forward scan */
165296
node->tss_TidPtr = 0;
166297
}
298+
else
299+
node->tss_TidPtr++;
167300
}
168-
while (tidNumber < numTids)
169-
{
170-
bool slot_is_valid = false;
171301

302+
while (node->tss_TidPtr >= 0 && node->tss_TidPtr < numTids)
303+
{
172304
tuple->t_self = tidList[node->tss_TidPtr];
173305
if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL))
174306
{
175-
bool prev_matches = false;
176-
int prev_tid;
177-
178307
/*
179308
* store the scanned tuple in the scan tuple slot of the scan
180309
* state. Eventually we will only do this and not return a tuple.
@@ -193,31 +322,13 @@ TidNext(TidScanState *node)
193322
*/
194323
ReleaseBuffer(buffer);
195324

196-
/*
197-
* We must check to see if the current tuple would have been
198-
* matched by an earlier tid, so we don't double report it.
199-
*/
200-
for (prev_tid = 0; prev_tid < node->tss_TidPtr;
201-
prev_tid++)
202-
{
203-
if (ItemPointerEquals(&tidList[prev_tid], &tuple->t_self))
204-
{
205-
prev_matches = true;
206-
break;
207-
}
208-
}
209-
if (!prev_matches)
210-
slot_is_valid = true;
211-
else
212-
ExecClearTuple(slot);
325+
return slot;
213326
}
214-
tidNumber++;
327+
/* Bad TID or failed snapshot qual; try next */
215328
if (bBackward)
216329
node->tss_TidPtr--;
217330
else
218331
node->tss_TidPtr++;
219-
if (slot_is_valid)
220-
return slot;
221332
}
222333

223334
/*
@@ -242,8 +353,7 @@ TidNext(TidScanState *node)
242353
* Initial States:
243354
* -- the relation indicated is opened for scanning so that the
244355
* "cursor" is positioned before the first qualifying tuple.
245-
* -- tidPtr points to the first tid.
246-
* -- state variable ruleFlag = nil.
356+
* -- tidPtr is -1.
247357
* ----------------------------------------------------------------
248358
*/
249359
TupleTableSlot *
@@ -362,7 +472,6 @@ TidScanState *
362472
ExecInitTidScan(TidScan *node, EState *estate)
363473
{
364474
TidScanState *tidstate;
365-
List *rangeTable;
366475
RangeTblEntry *rtentry;
367476
Oid relid;
368477
Oid reloid;
@@ -392,8 +501,8 @@ ExecInitTidScan(TidScan *node, EState *estate)
392501
ExecInitExpr((Expr *) node->scan.plan.qual,
393502
(PlanState *) tidstate);
394503

395-
tidstate->tss_tideval = (List *)
396-
ExecInitExpr((Expr *) node->tideval,
504+
tidstate->tss_tidquals = (List *)
505+
ExecInitExpr((Expr *) node->tidquals,
397506
(PlanState *) tidstate);
398507

399508
#define TIDSCAN_NSLOTS 2
@@ -411,19 +520,13 @@ ExecInitTidScan(TidScan *node, EState *estate)
411520
tidstate->tss_NumTids = 0;
412521
tidstate->tss_TidPtr = -1;
413522

414-
/*
415-
* get the range table and direction information from the execution state
416-
* (these are needed to open the relations).
417-
*/
418-
rangeTable = estate->es_range_table;
419-
420523
/*
421524
* open the base relation
422525
*
423526
* We acquire AccessShareLock for the duration of the scan.
424527
*/
425528
relid = node->scan.scanrelid;
426-
rtentry = rt_fetch(relid, rangeTable);
529+
rtentry = rt_fetch(relid, estate->es_range_table);
427530
reloid = rtentry->relid;
428531

429532
currentRelation = heap_open(reloid, AccessShareLock);

0 commit comments

Comments
 (0)