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

Commit 9e8da0f

Browse files
committed
Teach btree to handle ScalarArrayOpExpr quals natively.
This allows "indexedcol op ANY(ARRAY[...])" conditions to be used in plain indexscans, and particularly in index-only scans.
1 parent 0898d71 commit 9e8da0f

File tree

13 files changed

+725
-128
lines changed

13 files changed

+725
-128
lines changed

doc/src/sgml/catalogs.sgml

+7
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,13 @@
491491
for the first index column?</entry>
492492
</row>
493493

494+
<row>
495+
<entry><structfield>amsearcharray</structfield></entry>
496+
<entry><type>bool</type></entry>
497+
<entry></entry>
498+
<entry>Does the access method support <literal>ScalarArrayOpExpr</> searches?</entry>
499+
</row>
500+
494501
<row>
495502
<entry><structfield>amsearchnulls</structfield></entry>
496503
<entry><type>bool</type></entry>

src/backend/access/nbtree/nbtree.c

+100-49
Original file line numberDiff line numberDiff line change
@@ -276,39 +276,63 @@ btgettuple(PG_FUNCTION_ARGS)
276276
scan->xs_recheck = false;
277277

278278
/*
279-
* If we've already initialized this scan, we can just advance it in the
280-
* appropriate direction. If we haven't done so yet, we call a routine to
281-
* get the first item in the scan.
279+
* If we have any array keys, initialize them during first call for a
280+
* scan. We can't do this in btrescan because we don't know the scan
281+
* direction at that time.
282282
*/
283-
if (BTScanPosIsValid(so->currPos))
283+
if (so->numArrayKeys && !BTScanPosIsValid(so->currPos))
284+
{
285+
/* punt if we have any unsatisfiable array keys */
286+
if (so->numArrayKeys < 0)
287+
PG_RETURN_BOOL(false);
288+
289+
_bt_start_array_keys(scan, dir);
290+
}
291+
292+
/* This loop handles advancing to the next array elements, if any */
293+
do
284294
{
285295
/*
286-
* Check to see if we should kill the previously-fetched tuple.
296+
* If we've already initialized this scan, we can just advance it in
297+
* the appropriate direction. If we haven't done so yet, we call
298+
* _bt_first() to get the first item in the scan.
287299
*/
288-
if (scan->kill_prior_tuple)
300+
if (!BTScanPosIsValid(so->currPos))
301+
res = _bt_first(scan, dir);
302+
else
289303
{
290304
/*
291-
* Yes, remember it for later. (We'll deal with all such tuples
292-
* at once right before leaving the index page.) The test for
293-
* numKilled overrun is not just paranoia: if the caller reverses
294-
* direction in the indexscan then the same item might get entered
295-
* multiple times. It's not worth trying to optimize that, so we
296-
* don't detect it, but instead just forget any excess entries.
305+
* Check to see if we should kill the previously-fetched tuple.
297306
*/
298-
if (so->killedItems == NULL)
299-
so->killedItems = (int *)
300-
palloc(MaxIndexTuplesPerPage * sizeof(int));
301-
if (so->numKilled < MaxIndexTuplesPerPage)
302-
so->killedItems[so->numKilled++] = so->currPos.itemIndex;
307+
if (scan->kill_prior_tuple)
308+
{
309+
/*
310+
* Yes, remember it for later. (We'll deal with all such
311+
* tuples at once right before leaving the index page.) The
312+
* test for numKilled overrun is not just paranoia: if the
313+
* caller reverses direction in the indexscan then the same
314+
* item might get entered multiple times. It's not worth
315+
* trying to optimize that, so we don't detect it, but instead
316+
* just forget any excess entries.
317+
*/
318+
if (so->killedItems == NULL)
319+
so->killedItems = (int *)
320+
palloc(MaxIndexTuplesPerPage * sizeof(int));
321+
if (so->numKilled < MaxIndexTuplesPerPage)
322+
so->killedItems[so->numKilled++] = so->currPos.itemIndex;
323+
}
324+
325+
/*
326+
* Now continue the scan.
327+
*/
328+
res = _bt_next(scan, dir);
303329
}
304330

305-
/*
306-
* Now continue the scan.
307-
*/
308-
res = _bt_next(scan, dir);
309-
}
310-
else
311-
res = _bt_first(scan, dir);
331+
/* If we have a tuple, return it ... */
332+
if (res)
333+
break;
334+
/* ... otherwise see if we have more array keys to deal with */
335+
} while (so->numArrayKeys && _bt_advance_array_keys(scan, dir));
312336

313337
PG_RETURN_BOOL(res);
314338
}
@@ -325,35 +349,50 @@ btgetbitmap(PG_FUNCTION_ARGS)
325349
int64 ntids = 0;
326350
ItemPointer heapTid;
327351

328-
/* Fetch the first page & tuple. */
329-
if (!_bt_first(scan, ForwardScanDirection))
352+
/*
353+
* If we have any array keys, initialize them.
354+
*/
355+
if (so->numArrayKeys)
330356
{
331-
/* empty scan */
332-
PG_RETURN_INT64(0);
357+
/* punt if we have any unsatisfiable array keys */
358+
if (so->numArrayKeys < 0)
359+
PG_RETURN_INT64(ntids);
360+
361+
_bt_start_array_keys(scan, ForwardScanDirection);
333362
}
334-
/* Save tuple ID, and continue scanning */
335-
heapTid = &scan->xs_ctup.t_self;
336-
tbm_add_tuples(tbm, heapTid, 1, false);
337-
ntids++;
338363

339-
for (;;)
364+
/* This loop handles advancing to the next array elements, if any */
365+
do
340366
{
341-
/*
342-
* Advance to next tuple within page. This is the same as the easy
343-
* case in _bt_next().
344-
*/
345-
if (++so->currPos.itemIndex > so->currPos.lastItem)
367+
/* Fetch the first page & tuple */
368+
if (_bt_first(scan, ForwardScanDirection))
346369
{
347-
/* let _bt_next do the heavy lifting */
348-
if (!_bt_next(scan, ForwardScanDirection))
349-
break;
350-
}
370+
/* Save tuple ID, and continue scanning */
371+
heapTid = &scan->xs_ctup.t_self;
372+
tbm_add_tuples(tbm, heapTid, 1, false);
373+
ntids++;
351374

352-
/* Save tuple ID, and continue scanning */
353-
heapTid = &so->currPos.items[so->currPos.itemIndex].heapTid;
354-
tbm_add_tuples(tbm, heapTid, 1, false);
355-
ntids++;
356-
}
375+
for (;;)
376+
{
377+
/*
378+
* Advance to next tuple within page. This is the same as the
379+
* easy case in _bt_next().
380+
*/
381+
if (++so->currPos.itemIndex > so->currPos.lastItem)
382+
{
383+
/* let _bt_next do the heavy lifting */
384+
if (!_bt_next(scan, ForwardScanDirection))
385+
break;
386+
}
387+
388+
/* Save tuple ID, and continue scanning */
389+
heapTid = &so->currPos.items[so->currPos.itemIndex].heapTid;
390+
tbm_add_tuples(tbm, heapTid, 1, false);
391+
ntids++;
392+
}
393+
}
394+
/* Now see if we have more array keys to deal with */
395+
} while (so->numArrayKeys && _bt_advance_array_keys(scan, ForwardScanDirection));
357396

358397
PG_RETURN_INT64(ntids);
359398
}
@@ -383,6 +422,12 @@ btbeginscan(PG_FUNCTION_ARGS)
383422
so->keyData = (ScanKey) palloc(scan->numberOfKeys * sizeof(ScanKeyData));
384423
else
385424
so->keyData = NULL;
425+
426+
so->arrayKeyData = NULL; /* assume no array keys for now */
427+
so->numArrayKeys = 0;
428+
so->arrayKeys = NULL;
429+
so->arrayContext = NULL;
430+
386431
so->killedItems = NULL; /* until needed */
387432
so->numKilled = 0;
388433

@@ -460,6 +505,9 @@ btrescan(PG_FUNCTION_ARGS)
460505
scan->numberOfKeys * sizeof(ScanKeyData));
461506
so->numberOfKeys = 0; /* until _bt_preprocess_keys sets it */
462507

508+
/* If any keys are SK_SEARCHARRAY type, set up array-key info */
509+
_bt_preprocess_array_keys(scan);
510+
463511
PG_RETURN_VOID();
464512
}
465513

@@ -490,10 +538,13 @@ btendscan(PG_FUNCTION_ARGS)
490538
so->markItemIndex = -1;
491539

492540
/* Release storage */
493-
if (so->killedItems != NULL)
494-
pfree(so->killedItems);
495541
if (so->keyData != NULL)
496542
pfree(so->keyData);
543+
/* so->arrayKeyData and so->arrayKeys are in arrayContext */
544+
if (so->arrayContext != NULL)
545+
MemoryContextDelete(so->arrayContext);
546+
if (so->killedItems != NULL)
547+
pfree(so->killedItems);
497548
if (so->currTuples != NULL)
498549
pfree(so->currTuples);
499550
/* so->markTuples should not be pfree'd, see btrescan */

0 commit comments

Comments
 (0)