| 1 | /*------------------------------------------------------------------------- |
| 2 | * |
| 3 | * sortsupport.c |
| 4 | * Support routines for accelerated sorting. |
| 5 | * |
| 6 | * |
| 7 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
| 8 | * Portions Copyright (c) 1994, Regents of the University of California |
| 9 | * |
| 10 | * IDENTIFICATION |
| 11 | * src/backend/utils/sort/sortsupport.c |
| 12 | * |
| 13 | *------------------------------------------------------------------------- |
| 14 | */ |
| 15 | |
| 16 | #include "postgres.h" |
| 17 | |
| 18 | #include "access/nbtree.h" |
| 19 | #include "catalog/pg_am.h" |
| 20 | #include "fmgr.h" |
| 21 | #include "utils/lsyscache.h" |
| 22 | #include "utils/rel.h" |
| 23 | #include "utils/sortsupport.h" |
| 24 | |
| 25 | |
| 26 | /* Info needed to use an old-style comparison function as a sort comparator */ |
| 27 | typedef struct |
| 28 | { |
| 29 | FmgrInfo flinfo; /* lookup data for comparison function */ |
| 30 | FunctionCallInfoBaseData fcinfo; /* reusable callinfo structure */ |
| 31 | } ; |
| 32 | |
| 33 | #define (nargs) (offsetof(SortShimExtra, fcinfo) + SizeForFunctionCallInfo(nargs)) |
| 34 | |
| 35 | /* |
| 36 | * Shim function for calling an old-style comparator |
| 37 | * |
| 38 | * This is essentially an inlined version of FunctionCall2Coll(), except |
| 39 | * we assume that the FunctionCallInfoBaseData was already mostly set up by |
| 40 | * PrepareSortSupportComparisonShim. |
| 41 | */ |
| 42 | static int |
| 43 | comparison_shim(Datum x, Datum y, SortSupport ssup) |
| 44 | { |
| 45 | SortShimExtra * = (SortShimExtra *) ssup->ssup_extra; |
| 46 | Datum result; |
| 47 | |
| 48 | extra->fcinfo.args[0].value = x; |
| 49 | extra->fcinfo.args[1].value = y; |
| 50 | |
| 51 | /* just for paranoia's sake, we reset isnull each time */ |
| 52 | extra->fcinfo.isnull = false; |
| 53 | |
| 54 | result = FunctionCallInvoke(&extra->fcinfo); |
| 55 | |
| 56 | /* Check for null result, since caller is clearly not expecting one */ |
| 57 | if (extra->fcinfo.isnull) |
| 58 | elog(ERROR, "function %u returned NULL" , extra->flinfo.fn_oid); |
| 59 | |
| 60 | return result; |
| 61 | } |
| 62 | |
| 63 | /* |
| 64 | * Set up a shim function to allow use of an old-style btree comparison |
| 65 | * function as if it were a sort support comparator. |
| 66 | */ |
| 67 | void |
| 68 | PrepareSortSupportComparisonShim(Oid cmpFunc, SortSupport ssup) |
| 69 | { |
| 70 | SortShimExtra *; |
| 71 | |
| 72 | extra = (SortShimExtra *) MemoryContextAlloc(ssup->ssup_cxt, |
| 73 | SizeForSortShimExtra(2)); |
| 74 | |
| 75 | /* Lookup the comparison function */ |
| 76 | fmgr_info_cxt(cmpFunc, &extra->flinfo, ssup->ssup_cxt); |
| 77 | |
| 78 | /* We can initialize the callinfo just once and re-use it */ |
| 79 | InitFunctionCallInfoData(extra->fcinfo, &extra->flinfo, 2, |
| 80 | ssup->ssup_collation, NULL, NULL); |
| 81 | extra->fcinfo.args[0].isnull = false; |
| 82 | extra->fcinfo.args[1].isnull = false; |
| 83 | |
| 84 | ssup->ssup_extra = extra; |
| 85 | ssup->comparator = comparison_shim; |
| 86 | } |
| 87 | |
| 88 | /* |
| 89 | * Look up and call sortsupport function to setup SortSupport comparator; |
| 90 | * or if no such function exists or it declines to set up the appropriate |
| 91 | * state, prepare a suitable shim. |
| 92 | */ |
| 93 | static void |
| 94 | FinishSortSupportFunction(Oid opfamily, Oid opcintype, SortSupport ssup) |
| 95 | { |
| 96 | Oid sortSupportFunction; |
| 97 | |
| 98 | /* Look for a sort support function */ |
| 99 | sortSupportFunction = get_opfamily_proc(opfamily, opcintype, opcintype, |
| 100 | BTSORTSUPPORT_PROC); |
| 101 | if (OidIsValid(sortSupportFunction)) |
| 102 | { |
| 103 | /* |
| 104 | * The sort support function can provide a comparator, but it can also |
| 105 | * choose not to so (e.g. based on the selected collation). |
| 106 | */ |
| 107 | OidFunctionCall1(sortSupportFunction, PointerGetDatum(ssup)); |
| 108 | } |
| 109 | |
| 110 | if (ssup->comparator == NULL) |
| 111 | { |
| 112 | Oid sortFunction; |
| 113 | |
| 114 | sortFunction = get_opfamily_proc(opfamily, opcintype, opcintype, |
| 115 | BTORDER_PROC); |
| 116 | |
| 117 | if (!OidIsValid(sortFunction)) |
| 118 | elog(ERROR, "missing support function %d(%u,%u) in opfamily %u" , |
| 119 | BTORDER_PROC, opcintype, opcintype, opfamily); |
| 120 | |
| 121 | /* We'll use a shim to call the old-style btree comparator */ |
| 122 | PrepareSortSupportComparisonShim(sortFunction, ssup); |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | /* |
| 127 | * Fill in SortSupport given an ordering operator (btree "<" or ">" operator). |
| 128 | * |
| 129 | * Caller must previously have zeroed the SortSupportData structure and then |
| 130 | * filled in ssup_cxt, ssup_collation, and ssup_nulls_first. This will fill |
| 131 | * in ssup_reverse as well as the comparator function pointer. |
| 132 | */ |
| 133 | void |
| 134 | PrepareSortSupportFromOrderingOp(Oid orderingOp, SortSupport ssup) |
| 135 | { |
| 136 | Oid opfamily; |
| 137 | Oid opcintype; |
| 138 | int16 strategy; |
| 139 | |
| 140 | Assert(ssup->comparator == NULL); |
| 141 | |
| 142 | /* Find the operator in pg_amop */ |
| 143 | if (!get_ordering_op_properties(orderingOp, &opfamily, &opcintype, |
| 144 | &strategy)) |
| 145 | elog(ERROR, "operator %u is not a valid ordering operator" , |
| 146 | orderingOp); |
| 147 | ssup->ssup_reverse = (strategy == BTGreaterStrategyNumber); |
| 148 | |
| 149 | FinishSortSupportFunction(opfamily, opcintype, ssup); |
| 150 | } |
| 151 | |
| 152 | /* |
| 153 | * Fill in SortSupport given an index relation, attribute, and strategy. |
| 154 | * |
| 155 | * Caller must previously have zeroed the SortSupportData structure and then |
| 156 | * filled in ssup_cxt, ssup_attno, ssup_collation, and ssup_nulls_first. This |
| 157 | * will fill in ssup_reverse (based on the supplied strategy), as well as the |
| 158 | * comparator function pointer. |
| 159 | */ |
| 160 | void |
| 161 | PrepareSortSupportFromIndexRel(Relation indexRel, int16 strategy, |
| 162 | SortSupport ssup) |
| 163 | { |
| 164 | Oid opfamily = indexRel->rd_opfamily[ssup->ssup_attno - 1]; |
| 165 | Oid opcintype = indexRel->rd_opcintype[ssup->ssup_attno - 1]; |
| 166 | |
| 167 | Assert(ssup->comparator == NULL); |
| 168 | |
| 169 | if (indexRel->rd_rel->relam != BTREE_AM_OID) |
| 170 | elog(ERROR, "unexpected non-btree AM: %u" , indexRel->rd_rel->relam); |
| 171 | if (strategy != BTGreaterStrategyNumber && |
| 172 | strategy != BTLessStrategyNumber) |
| 173 | elog(ERROR, "unexpected sort support strategy: %d" , strategy); |
| 174 | ssup->ssup_reverse = (strategy == BTGreaterStrategyNumber); |
| 175 | |
| 176 | FinishSortSupportFunction(opfamily, opcintype, ssup); |
| 177 | } |
| 178 | |