Skip to content

Commit a7490ab

Browse files
committed
Clean up code and docs.
1 parent 2f27ecd commit a7490ab

File tree

6 files changed

+62
-58
lines changed

6 files changed

+62
-58
lines changed

python/multicorn/__init__.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -214,16 +214,20 @@ def can_sort(self, sortkeys):
214214

215215
def can_limit(self, limit, offset):
216216
"""
217-
Method called from the planner to ask the FDW whether it supports LIMIT pushdown.
218-
This method is only called if the rest of the query can be pushed down including the sort and quals. For example,
219-
if the query has a GROUP BY clause, this method will not be called.
217+
Method called from the planner to ask the FDW whether it supports LIMIT and OFFSET pushdown.
218+
219+
This method is only called if the entire query can be pushed down. For example, if the query has
220+
a GROUP BY clause, this method will not be called. Or, if only part of the sort is pushed down,
221+
this method will not be called and limit/offset will not be pushed down.
222+
223+
Currently, we do not support pushing down limit/offset if the query includes a WHERE clause (quals).
220224
221225
Args:
222-
limit (int or None): The limit to apply to the query.
223-
offset (int or None): The offset to apply to the query.
226+
limit (int or None): The limit to apply to the query, if any.
227+
offset (int or None): The offset to apply to the query, if any.
224228
225229
Return:
226-
True if the FDW can support both LIMIT and OFFSET pushdown, Falseotherwise.
230+
True if the FDW can support both LIMIT and OFFSET pushdown, False otherwise.
227231
"""
228232
return False
229233

@@ -328,8 +332,8 @@ def execute(self, quals, columns, sortkeys=None, limit=None, offset=None):
328332
should be in the sequence.
329333
sortkeys (list): A list of :class:`SortKey`
330334
that the FDW said it can enforce.
331-
limit (int): The limit to apply to the query.
332-
offset (int): The offset to apply to the query.
335+
limit (int or None): The limit to apply to the query, if any.
336+
offset (int or None): The offset to apply to the query, if any.
333337
334338
Returns:
335339
An iterable of python objects which can be converted back to PostgreSQL.

python/multicorn/testfdw.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,10 @@ def get_path_keys(self):
133133
return []
134134

135135
def can_sort(self, sortkeys):
136-
# assume sort pushdown ok for all cols, in any order, any collation
136+
# assume sort pushdown ok only for first sort key
137137
if not self.cansort:
138138
return []
139-
return sortkeys
139+
return sortkeys[:1]
140140

141141
def can_limit(self, limit, offset):
142142
return self.canlimit

src/multicorn.c

Lines changed: 20 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ multicornGetForeignPaths(PlannerInfo *root,
375375
/* Try to find parameterized paths */
376376
pathes = findPaths(root, baserel, possiblePaths, planstate->startupCost,
377377
planstate, apply_pathkeys, deparsed_pathkeys);
378-
378+
379379
/* Add a simple default path */
380380
pathes = lappend(pathes, create_foreignscan_path(root, baserel,
381381
NULL, /* default pathtarget */
@@ -406,6 +406,10 @@ multicornGetForeignPaths(PlannerInfo *root,
406406
}
407407
}
408408

409+
/* Determine if the sort is completely pushed down and store the results to be used in the upper paths */
410+
/* Regardless, store the deparsed pathkeys to be used in the upper paths */
411+
planstate->sort_pushed_down = pathkeys_contained_in(root->sort_pathkeys, apply_pathkeys);
412+
409413
/* Add each ForeignPath previously found */
410414
foreach(lc, pathes)
411415
{
@@ -419,11 +423,13 @@ multicornGetForeignPaths(PlannerInfo *root,
419423
{
420424
ForeignPath *newpath;
421425

422-
MulticornPathState *pathstate = (MulticornPathState *)calloc(1, sizeof(MulticornPathState));
426+
MulticornPathState *pathstate = (MulticornPathState *)palloc0(sizeof(MulticornPathState));
423427
pathstate->pathkeys = deparsed_pathkeys;
424428
pathstate->limit = -1;
425429
pathstate->offset = -1;
426430

431+
planstate->pathkeys = deparsed_pathkeys;
432+
427433
newpath = create_foreignscan_path(root, baserel,
428434
NULL, /* default pathtarget */
429435
path->path.rows,
@@ -458,23 +464,21 @@ static void multicornGetForeignUpperPaths(PlannerInfo *root,
458464
RelOptInfo *output_rel,
459465
void *extra)
460466
{
461-
// elog(WARNING, "Got input_rel private: %p", input_rel->fdw_private);
462-
// elog(WARNING, "Got output_rel private: %p", output_rel->fdw_private);
463-
464-
// If the input_rel has no private, then pushdown wasn't supported for the previous stage
465-
// Which means we can't pushdown anything for the the current stage (as least this is true for limit/offset)
467+
// If the input_rel has no private, then pushdown wasn't supported for the previous stage.
468+
// Therefore we can't pushdown anything for the the current stage (as least this is true for limit/offset)
466469
if (!input_rel->fdw_private)
467470
return;
468471

469-
// elog(WARNING, "Got stage: %d", stage);
470472
switch (stage)
471473
{
472474
case UPPERREL_ORDERED:
473475
add_foreign_ordered_paths(root, input_rel, output_rel, (FinalPathExtraData *)extra);
474476
break;
477+
475478
case UPPERREL_FINAL:
476479
add_foreign_final_paths(root, input_rel, output_rel, (FinalPathExtraData *)extra);
477480
break;
481+
478482
default:
479483
break;
480484
}
@@ -484,51 +488,17 @@ static void multicornGetForeignUpperPaths(PlannerInfo *root,
484488
* add_foreign_ordered_paths
485489
* Add foreign paths for performing the sort processing remotely.
486490
*
487-
* Given input_rel contains the source-data Paths. The paths are added to the
488-
* given final_rel.
489-
*
490491
* Note: Since sorts are already taken care of in the base rel, we only check for pushdown here.
491492
*/
492493
static void
493494
add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel,
494495
RelOptInfo *final_rel,
495496
FinalPathExtraData *extra)
496497
{
497-
List *applied_pathkeys = NIL;
498-
ListCell *lc;
499498
MulticornPlanState *planstate = input_rel->fdw_private;
500-
ForeignPath *cheapest_path = (ForeignPath *)input_rel->cheapest_total_path;
501-
if ( planstate )
502-
{
503-
if (cheapest_path && IsA(cheapest_path, ForeignPath))
504-
{
505-
MulticornPathState *pathstate = (MulticornPathState *)cheapest_path->fdw_private;
506-
if ( pathstate )
507-
{
508-
planstate->pathkeys = pathstate->pathkeys;
509-
}
510-
}
511-
512-
/* Extract the pathkeys from the input_rel */
513-
foreach(lc, input_rel->pathlist)
514-
{
515-
Path *path = (Path *) lfirst(lc);
516-
if (IsA(path, ForeignPath))
517-
{
518-
ForeignPath *fpath = (ForeignPath *) path;
519-
if (fpath->path.pathkeys != NIL)
520-
{
521-
applied_pathkeys = fpath->path.pathkeys;
522-
break;
523-
}
524-
}
525-
}
526-
527-
/* We only support limit/offset if the sort is completely pushed down */
528-
/* By bailing here, input_rel for the next state will not have planstate, which will cause no more pushdowns */
529-
if (!pathkeys_contained_in(root->sort_pathkeys, applied_pathkeys))
530-
return;
531499

500+
if ( planstate && planstate->sort_pushed_down )
501+
{
532502
planstate->input_rel = input_rel;
533503
final_rel->fdw_private = planstate;
534504
}
@@ -584,7 +554,7 @@ static void multicornGetForeignUpperPaths(PlannerInfo *root,
584554
if (parse->limitOffset)
585555
limitOffset = DatumGetInt32(((Const *)parse->limitOffset)->constvalue);
586556

587-
/* Get the current input_rel and it's planstate */
557+
/* Get the current planstate and its input_rel */
588558
planstate = input_rel->fdw_private;
589559
if ( planstate->input_rel )
590560
input_rel = planstate->input_rel;
@@ -593,11 +563,13 @@ static void multicornGetForeignUpperPaths(PlannerInfo *root,
593563
if (!canLimit(planstate, limitCount, limitOffset))
594564
return;
595565

596-
/* Create foreign final path with the correct number of rows, and include state for limit/offset pushdown */
597-
pathstate = (MulticornPathState *)calloc(1, sizeof(MulticornPathState));
566+
/* Include pathkeys and limit/offset in pathstate */
567+
pathstate = (MulticornPathState *)palloc(sizeof(MulticornPathState));
598568
pathstate->pathkeys = planstate->pathkeys;
599569
pathstate->limit = limitCount;
600570
pathstate->offset = limitOffset;
571+
572+
/* Create foreign final path with the correct number of rows and cost. */
601573
final_path = create_foreign_upper_path(root,
602574
input_rel,
603575
root->upper_targets[UPPERREL_FINAL],
@@ -1369,7 +1341,7 @@ serializePlanState(MulticornPlanState * state)
13691341
List *result = NULL;
13701342

13711343
result = lappend(result, makeConst(INT4OID,
1372-
-1, InvalidOid, 4, Int32GetDatum(state->numattrs), false, true));
1344+
-1, InvalidOid, 4, Int32GetDatum(state->numattrs), false, true));
13731345
result = lappend(result, makeConst(INT4OID,
13741346
-1, InvalidOid, 4, Int32GetDatum(state->foreigntableid), false, true));
13751347
result = lappend(result, state->target_list);

src/multicorn.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ typedef struct MulticornPlanState
9393
int offset;
9494
int limit;
9595

96+
/* For tracking if the sort is completely pushed down */
97+
bool sort_pushed_down;
98+
9699
/* Used for tracking the input_rel for upper plans*/
97100
RelOptInfo *input_rel;
98101

test-3.9/expected/multicorn_test_limit.out

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,27 @@ NOTICE: ['test1', 'test2']
121121
01-01-2011 | Sun Jan 02 14:30:25 2011
122122
(1 row)
123123

124+
-- Verify limit and offset are not pushed when sort is partially pushed down
125+
EXPLAIN SELECT * FROM testmulticorn ORDER BY test1, test2 LIMIT 1 OFFSET 1;
126+
QUERY PLAN
127+
----------------------------------------------------------------------------------
128+
Limit (cost=48.08..66.65 rows=1 width=12)
129+
-> Incremental Sort (cost=29.51..400.90 rows=20 width=12)
130+
Sort Key: test1, test2
131+
Presorted Key: test1
132+
-> Foreign Scan on testmulticorn (cost=10.00..400.00 rows=20 width=20)
133+
(5 rows)
134+
135+
SELECT * FROM testmulticorn ORDER BY test1, test2 LIMIT 1 OFFSET 1;
136+
NOTICE: []
137+
NOTICE: ['test1', 'test2']
138+
NOTICE: requested sort(s):
139+
NOTICE: SortKey(attname='test1', attnum=1, is_reversed=False, nulls_first=False, collate=None)
140+
test1 | test2
141+
------------+--------------------------
142+
01-01-2011 | Sun Jan 02 14:30:25 2011
143+
(1 row)
144+
124145
-- Verify limit and offset are not pushed down with where (which we may eventually support in the future)
125146
EXPLAIN SELECT * FROM testmulticorn WHERE test1 = '02-03-2011' LIMIT 1 OFFSET 1;
126147
QUERY PLAN

test-3.9/sql/multicorn_test_limit.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ SELECT * FROM testmulticorn ORDER BY test1 LIMIT 1 OFFSET 1;
5555
EXPLAIN SELECT * FROM testmulticorn_nosort ORDER BY test1 LIMIT 1 OFFSET 1;
5656
SELECT * FROM testmulticorn_nosort ORDER BY test1 LIMIT 1 OFFSET 1;
5757

58+
-- Verify limit and offset are not pushed when sort is partially pushed down
59+
EXPLAIN SELECT * FROM testmulticorn ORDER BY test1, test2 LIMIT 1 OFFSET 1;
60+
SELECT * FROM testmulticorn ORDER BY test1, test2 LIMIT 1 OFFSET 1;
61+
5862
-- Verify limit and offset are not pushed down with where (which we may eventually support in the future)
5963
EXPLAIN SELECT * FROM testmulticorn WHERE test1 = '02-03-2011' LIMIT 1 OFFSET 1;
6064
SELECT * FROM testmulticorn WHERE test1 = '02-03-2011' LIMIT 1 OFFSET 1;

0 commit comments

Comments
 (0)