diff --git a/contrib/btree_gist/btree_date.c b/contrib/btree_gist/btree_date.c index 68a4107dbf..1941459025 100644 --- a/contrib/btree_gist/btree_date.c +++ b/contrib/btree_gist/btree_date.c @@ -178,6 +178,21 @@ gbt_date_distance(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); DateADT query = PG_GETARG_DATEADT(1); + StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + + switch (strategy) + { + case RTLessStrategyNumber: + query = DATEVAL_NOBEGIN; + break; + + case RTGreaterStrategyNumber: + query = DATEVAL_NOEND; + break; + + default: + break; + } /* Oid subtype = PG_GETARG_OID(3); */ dateKEY *kkk = (dateKEY *) DatumGetPointer(entry->key); diff --git a/contrib/btree_gist/btree_gist--1.6--1.7.sql b/contrib/btree_gist/btree_gist--1.6--1.7.sql index 1085216501..e1cc256e01 100644 --- a/contrib/btree_gist/btree_gist--1.6--1.7.sql +++ b/contrib/btree_gist/btree_gist--1.6--1.7.sql @@ -75,3 +75,29 @@ AS FUNCTION 7 gbt_bool_same (gbtreekey2, gbtreekey2, internal), FUNCTION 9 gbt_bool_fetch (internal), STORAGE gbtreekey2; + + +-- Add ORDER BY support for int2, int4, int8, date, ts, time +ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD + OPERATOR 20 < (int2, int2) FOR ORDER BY pg_catalog.integer_ops, + OPERATOR 22 > (int2, int2) FOR ORDER BY pg_catalog.integer_ops; + +ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD + OPERATOR 20 < (int4, int4) FOR ORDER BY pg_catalog.integer_ops, + OPERATOR 22 > (int4, int4) FOR ORDER BY pg_catalog.integer_ops; + +ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD + OPERATOR 20 < (int8, int8) FOR ORDER BY pg_catalog.integer_ops, + OPERATOR 22 > (int8, int8) FOR ORDER BY pg_catalog.integer_ops; + +ALTER OPERATOR FAMILY gist_date_ops USING gist ADD + OPERATOR 20 < (date, date) FOR ORDER BY pg_catalog.integer_ops, + OPERATOR 22 > (date, date) FOR ORDER BY pg_catalog.integer_ops; + +ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD + OPERATOR 20 < (timestamp, timestamp) FOR ORDER BY pg_catalog.interval_ops, + OPERATOR 22 > (timestamp, timestamp) FOR ORDER BY pg_catalog.interval_ops; + +ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD + OPERATOR 20 < (timestamptz, timestamptz) FOR ORDER BY pg_catalog.interval_ops, + OPERATOR 22 > (timestamptz, timestamptz) FOR ORDER BY pg_catalog.interval_ops; diff --git a/contrib/btree_gist/btree_int2.c b/contrib/btree_gist/btree_int2.c index fdbf156586..760a83ca4d 100644 --- a/contrib/btree_gist/btree_int2.c +++ b/contrib/btree_gist/btree_int2.c @@ -160,6 +160,19 @@ gbt_int2_distance(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); int16 query = PG_GETARG_INT16(1); + StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + + switch (strategy) + { + case RTLessStrategyNumber: + query = PG_INT16_MIN; + break; + case RTGreaterStrategyNumber: + query = PG_INT16_MAX; + break; + default: + break; + } /* Oid subtype = PG_GETARG_OID(3); */ int16KEY *kkk = (int16KEY *) DatumGetPointer(entry->key); diff --git a/contrib/btree_gist/btree_int4.c b/contrib/btree_gist/btree_int4.c index 8915fb5d08..8f072e55c0 100644 --- a/contrib/btree_gist/btree_int4.c +++ b/contrib/btree_gist/btree_int4.c @@ -161,6 +161,19 @@ gbt_int4_distance(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); int32 query = PG_GETARG_INT32(1); + StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + + switch (strategy) + { + case RTLessStrategyNumber: + query = PG_INT32_MIN; + break; + case RTGreaterStrategyNumber: + query = PG_INT32_MAX; + break; + default: + break; + } /* Oid subtype = PG_GETARG_OID(3); */ int32KEY *kkk = (int32KEY *) DatumGetPointer(entry->key); diff --git a/contrib/btree_gist/btree_int8.c b/contrib/btree_gist/btree_int8.c index 7c63a5b6dc..ac2c35f059 100644 --- a/contrib/btree_gist/btree_int8.c +++ b/contrib/btree_gist/btree_int8.c @@ -161,6 +161,21 @@ gbt_int8_distance(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); int64 query = PG_GETARG_INT64(1); + StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + + switch (strategy) + { + case RTLessStrategyNumber: + query = PG_INT64_MIN; + break; + + case RTGreaterStrategyNumber: + query = PG_INT64_MAX; + break; + + default: + break; + } /* Oid subtype = PG_GETARG_OID(3); */ int64KEY *kkk = (int64KEY *) DatumGetPointer(entry->key); diff --git a/contrib/btree_gist/btree_time.c b/contrib/btree_gist/btree_time.c index d89401c0f5..e06efc0ceb 100644 --- a/contrib/btree_gist/btree_time.c +++ b/contrib/btree_gist/btree_time.c @@ -225,6 +225,21 @@ gbt_time_distance(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); TimeADT query = PG_GETARG_TIMEADT(1); + StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + + switch (strategy) + { + case RTLessStrategyNumber: + query = PG_INT64_MIN; + break; + + case RTGreaterStrategyNumber: + query = PG_INT64_MAX; + break; + + default: + break; + } /* Oid subtype = PG_GETARG_OID(3); */ timeKEY *kkk = (timeKEY *) DatumGetPointer(entry->key); diff --git a/contrib/btree_gist/btree_ts.c b/contrib/btree_gist/btree_ts.c index 3f5ba91891..b2b019a35c 100644 --- a/contrib/btree_gist/btree_ts.c +++ b/contrib/btree_gist/btree_ts.c @@ -274,6 +274,21 @@ gbt_ts_distance(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); Timestamp query = PG_GETARG_TIMESTAMP(1); + StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + + switch (strategy) + { + case RTLessStrategyNumber: + query = TIMESTAMP_MINUS_INFINITY; + break; + + case RTGreaterStrategyNumber: + query = TIMESTAMP_INFINITY; + break; + + default: + break; + } /* Oid subtype = PG_GETARG_OID(3); */ tsKEY *kkk = (tsKEY *) DatumGetPointer(entry->key); diff --git a/src/backend/executor/nodeBitmapIndexscan.c b/src/backend/executor/nodeBitmapIndexscan.c index 7cf8532bc9..9db905f36e 100644 --- a/src/backend/executor/nodeBitmapIndexscan.c +++ b/src/backend/executor/nodeBitmapIndexscan.c @@ -269,7 +269,7 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags) ExecIndexBuildScanKeys((PlanState *) indexstate, indexstate->biss_RelationDesc, node->indexqual, - false, + NULL, &indexstate->biss_ScanKeys, &indexstate->biss_NumScanKeys, &indexstate->biss_RuntimeKeys, diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index f1db35665c..7ba350c3fa 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -563,7 +563,7 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) ExecIndexBuildScanKeys((PlanState *) indexstate, indexstate->ioss_RelationDesc, node->indexqual, - false, + NULL, &indexstate->ioss_ScanKeys, &indexstate->ioss_NumScanKeys, &indexstate->ioss_RuntimeKeys, @@ -577,7 +577,7 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) ExecIndexBuildScanKeys((PlanState *) indexstate, indexstate->ioss_RelationDesc, node->indexorderby, - true, + node->indexorderbyops, &indexstate->ioss_OrderByKeys, &indexstate->ioss_NumOrderByKeys, &indexstate->ioss_RuntimeKeys, diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 14b9c00217..fb8057c446 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -968,7 +968,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) ExecIndexBuildScanKeys((PlanState *) indexstate, indexstate->iss_RelationDesc, node->indexqual, - false, + NULL, &indexstate->iss_ScanKeys, &indexstate->iss_NumScanKeys, &indexstate->iss_RuntimeKeys, @@ -982,7 +982,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) ExecIndexBuildScanKeys((PlanState *) indexstate, indexstate->iss_RelationDesc, node->indexorderby, - true, + node->indexorderbyops, &indexstate->iss_OrderByKeys, &indexstate->iss_NumOrderByKeys, &indexstate->iss_RuntimeKeys, @@ -1134,12 +1134,13 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) */ void ExecIndexBuildScanKeys(PlanState *planstate, Relation index, - List *quals, bool isorderby, + List *quals, List* orderbyops, ScanKey *scanKeys, int *numScanKeys, IndexRuntimeKeyInfo **runtimeKeys, int *numRuntimeKeys, IndexArrayKeyInfo **arrayKeys, int *numArrayKeys) { ListCell *qual_cell; + ListCell *orderbyop_cell; ScanKey scan_keys; IndexRuntimeKeyInfo *runtime_keys; IndexArrayKeyInfo *array_keys; @@ -1148,6 +1149,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, int max_runtime_keys; int n_array_keys; int j; + bool isorderby = (orderbyops != NULL); /* Allocate array for ScanKey structs: one per qual */ n_scan_keys = list_length(quals); @@ -1168,12 +1170,19 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, palloc0(n_scan_keys * sizeof(IndexArrayKeyInfo)); n_array_keys = 0; + /* + * Make sure forboth below works in all cases + */ + if (!isorderby) + { + orderbyops = quals; + } /* * for each opclause in the given qual, convert the opclause into a single * scan key */ j = 0; - foreach(qual_cell, quals) + forboth(qual_cell, quals, orderbyop_cell, orderbyops) { Expr *clause = (Expr *) lfirst(qual_cell); ScanKey this_scan_key = &scan_keys[j++]; @@ -1596,8 +1605,30 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, InvalidOid, /* no reg proc for this */ (Datum) 0); /* constant */ } + else if (isorderby && IsA(clause, Var)) + { + Var *var = (Var *) clause; + int flags = SK_ORDER_BY; + varattno = var->varattno; + opfamily = index->rd_opfamily[varattno - 1]; + opno = lfirst(orderbyop_cell); + + get_op_opfamily_properties(opno, opfamily, isorderby, + &op_strategy, + &op_lefttype, + &op_righttype); + + ScanKeyEntryInitialize(this_scan_key, + flags, + varattno, /* attribute number to scan */ + op_strategy, /* op's strategy */ + InvalidOid, /* strategy subtype */ + var->varcollid, /* collation */ + get_opcode(opno), /* reg proc to use */ + (Datum) 0); /* constant */ + } else - elog(ERROR, "unsupported indexqual type: %d", + elog(ERROR, "nodeIndexscan unsupported indexqual type: %d", (int) nodeTag(clause)); } diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 03a5fbdc6d..0855f917ea 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -188,7 +188,8 @@ static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys, List **orderby_clauses_p, List **clause_columns_p); static Expr *match_clause_to_ordering_op(IndexOptInfo *index, - int indexcol, Expr *clause, Oid pk_opfamily); + int indexcol, Expr *clause, + Oid pk_opfamily, int pk_strategy); static bool ec_member_matches_indexcol(PlannerInfo *root, RelOptInfo *rel, EquivalenceClass *ec, EquivalenceMember *em, void *arg); @@ -3092,8 +3093,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys, /* Pathkey must request default sort order for the target opfamily */ - if (pathkey->pk_strategy != BTLessStrategyNumber || - pathkey->pk_nulls_first) + /* Fixme !!! */ + if (!(pathkey->pk_strategy == BTLessStrategyNumber && !pathkey->pk_nulls_first || pathkey->pk_strategy == BTGreaterStrategyNumber && pathkey->pk_nulls_first)) return; /* If eclass is volatile, no hope of using an indexscan */ @@ -3132,7 +3133,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys, expr = match_clause_to_ordering_op(index, indexcol, member->em_expr, - pathkey->pk_opfamily); + pathkey->pk_opfamily, + pathkey->pk_strategy); if (expr) { *orderby_clauses_p = lappend(*orderby_clauses_p, expr); @@ -3184,7 +3186,8 @@ static Expr * match_clause_to_ordering_op(IndexOptInfo *index, int indexcol, Expr *clause, - Oid pk_opfamily) + Oid pk_opfamily, + int pk_strategy) { Oid opfamily; Oid idxcollation; @@ -3200,6 +3203,46 @@ match_clause_to_ordering_op(IndexOptInfo *index, opfamily = index->opfamily[indexcol]; idxcollation = index->indexcollations[indexcol]; + /* + * If clause is a Var and matches the index then + * try to find an ordering operator + * registered as ordering operator in index operator family + */ + if (clause && IsA(clause, Var) && match_index_to_operand(clause, indexcol, index)) + { + Var *var = (Var *) clause; + /* + * Copied from createplan.c - find ordering operator + * There is an opportunity for refactoring + * to avoid double cache lookup here and in creatplan + */ + Oid sortop = get_opfamily_member(pk_opfamily, + var->vartype, + var->vartype, + pk_strategy); + /* + * This should not happen??? + * For now just return no-match + * Alternative is to err + */ + if (sortop == InvalidOid) + return NULL; + + /* + * Make sure + * 1) ordering operator is registered in pg_amop for index am + * 2) sort family is the same as requested ordering op family (BTree) + */ + Oid sortfamily = get_op_opfamily_sortfamily(sortop, opfamily); + if (sortfamily == pk_opfamily) + return clause; + + /* + * No match + */ + return NULL; + } + /* * Clause must be a binary opclause. */ diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 34ca6d4ac2..b364419d08 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -189,6 +189,7 @@ static IndexOnlyScan *make_indexonlyscan(List *qptlist, List *qpqual, Index scanrelid, Oid indexid, List *indexqual, List *recheckqual, List *indexorderby, + List *indexorderbyops, List *indextlist, ScanDirection indexscandir); static BitmapIndexScan *make_bitmap_indexscan(Index scanrelid, Oid indexid, @@ -3170,6 +3171,7 @@ create_indexscan_plan(PlannerInfo *root, fixed_indexquals, stripped_indexquals, fixed_indexorderbys, + indexorderbyops, indexinfo->indextlist, best_path->indexscandir); else @@ -5114,6 +5116,10 @@ fix_indexqual_clause(PlannerInfo *root, IndexOptInfo *index, int indexcol, index, indexcol); } + else if (IsA(clause, Var)) + { + clause = fix_indexqual_operand(clause, index, indexcol); + } else elog(ERROR, "unsupported indexqual type: %d", (int) nodeTag(clause)); @@ -5551,6 +5557,7 @@ make_indexonlyscan(List *qptlist, List *indexqual, List *recheckqual, List *indexorderby, + List *indexorderbyops, List *indextlist, ScanDirection indexscandir) { @@ -5566,6 +5573,7 @@ make_indexonlyscan(List *qptlist, node->indexqual = indexqual; node->recheckqual = recheckqual; node->indexorderby = indexorderby; + node->indexorderbyops = indexorderbyops; node->indextlist = indextlist; node->indexorderdir = indexscandir; diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index c4fcd0076e..2233a213c1 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -6414,6 +6414,10 @@ index_other_operands_eval_cost(PlannerInfo *root, List *indexquals) { other_operand = NULL; } + else if (IsA(clause, Var)) + { + other_operand = (Node *) clause; + } else { elog(ERROR, "unsupported indexqual type: %d", diff --git a/src/include/executor/nodeIndexscan.h b/src/include/executor/nodeIndexscan.h index 8ee7969792..de6d77f1c3 100644 --- a/src/include/executor/nodeIndexscan.h +++ b/src/include/executor/nodeIndexscan.h @@ -34,7 +34,8 @@ extern void ExecIndexScanInitializeWorker(IndexScanState *node, * nodeBitmapIndexscan.c */ extern void ExecIndexBuildScanKeys(PlanState *planstate, Relation index, - List *quals, bool isorderby, + List *quals, + List *orderbyops, ScanKey *scanKeys, int *numScanKeys, IndexRuntimeKeyInfo **runtimeKeys, int *numRuntimeKeys, IndexArrayKeyInfo **arrayKeys, int *numArrayKeys); diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 24d46c76dc..350dede16e 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -495,6 +495,7 @@ typedef struct IndexOnlyScan List *indexqual; /* list of index quals (usually OpExprs) */ List *recheckqual; /* index quals in recheckable form */ List *indexorderby; /* list of index ORDER BY exprs */ + List *indexorderbyops;/* OIDs of sort ops for ORDER BY exprs */ List *indextlist; /* TargetEntry list describing index's cols */ ScanDirection indexorderdir; /* forward or backward or don't care */ } IndexOnlyScan;