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 } SortShimExtra; 32 33 #define SizeForSortShimExtra(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 *extra = (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 *extra; 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