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