Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * sortsupport.c
4 : * Support routines for accelerated sorting.
5 : *
6 : *
7 : * Portions Copyright (c) 1996-2025, 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 "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 124493448 : comparison_shim(Datum x, Datum y, SortSupport ssup)
44 : {
45 124493448 : SortShimExtra *extra = (SortShimExtra *) ssup->ssup_extra;
46 : Datum result;
47 :
48 124493448 : extra->fcinfo.args[0].value = x;
49 124493448 : extra->fcinfo.args[1].value = y;
50 :
51 : /* just for paranoia's sake, we reset isnull each time */
52 124493448 : extra->fcinfo.isnull = false;
53 :
54 124493448 : result = FunctionCallInvoke(&extra->fcinfo);
55 :
56 : /* Check for null result, since caller is clearly not expecting one */
57 124493448 : if (extra->fcinfo.isnull)
58 0 : elog(ERROR, "function %u returned NULL", extra->flinfo.fn_oid);
59 :
60 124493448 : 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 42994 : PrepareSortSupportComparisonShim(Oid cmpFunc, SortSupport ssup)
69 : {
70 : SortShimExtra *extra;
71 :
72 42994 : extra = (SortShimExtra *) MemoryContextAlloc(ssup->ssup_cxt,
73 : SizeForSortShimExtra(2));
74 :
75 : /* Lookup the comparison function */
76 42994 : fmgr_info_cxt(cmpFunc, &extra->flinfo, ssup->ssup_cxt);
77 :
78 : /* We can initialize the callinfo just once and re-use it */
79 42994 : InitFunctionCallInfoData(extra->fcinfo, &extra->flinfo, 2,
80 : ssup->ssup_collation, NULL, NULL);
81 42994 : extra->fcinfo.args[0].isnull = false;
82 42994 : extra->fcinfo.args[1].isnull = false;
83 :
84 42994 : ssup->ssup_extra = extra;
85 42994 : ssup->comparator = comparison_shim;
86 42994 : }
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 481016 : FinishSortSupportFunction(Oid opfamily, Oid opcintype, SortSupport ssup)
95 : {
96 : Oid sortSupportFunction;
97 :
98 : /* Look for a sort support function */
99 481016 : sortSupportFunction = get_opfamily_proc(opfamily, opcintype, opcintype,
100 : BTSORTSUPPORT_PROC);
101 481016 : 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 438540 : OidFunctionCall1(sortSupportFunction, PointerGetDatum(ssup));
108 : }
109 :
110 481004 : if (ssup->comparator == NULL)
111 : {
112 : Oid sortFunction;
113 :
114 42476 : sortFunction = get_opfamily_proc(opfamily, opcintype, opcintype,
115 : BTORDER_PROC);
116 :
117 42476 : if (!OidIsValid(sortFunction))
118 0 : 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 42476 : PrepareSortSupportComparisonShim(sortFunction, ssup);
123 : }
124 481004 : }
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 333140 : 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 333140 : if (!get_ordering_op_properties(orderingOp, &opfamily, &opcintype,
144 : &strategy))
145 0 : elog(ERROR, "operator %u is not a valid ordering operator",
146 : orderingOp);
147 333140 : ssup->ssup_reverse = (strategy == BTGreaterStrategyNumber);
148 :
149 333140 : FinishSortSupportFunction(opfamily, opcintype, ssup);
150 333128 : }
151 :
152 : /*
153 : * Fill in SortSupport given an index relation and attribute.
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 argument), as well as the
158 : * comparator function pointer.
159 : */
160 : void
161 147876 : PrepareSortSupportFromIndexRel(Relation indexRel, bool reverse,
162 : SortSupport ssup)
163 : {
164 147876 : Oid opfamily = indexRel->rd_opfamily[ssup->ssup_attno - 1];
165 147876 : Oid opcintype = indexRel->rd_opcintype[ssup->ssup_attno - 1];
166 :
167 : Assert(ssup->comparator == NULL);
168 :
169 147876 : if (!indexRel->rd_indam->amcanorder)
170 0 : elog(ERROR, "unexpected non-amcanorder AM: %u", indexRel->rd_rel->relam);
171 147876 : ssup->ssup_reverse = reverse;
172 :
173 147876 : FinishSortSupportFunction(opfamily, opcintype, ssup);
174 147876 : }
175 :
176 : /*
177 : * Fill in SortSupport given a GiST index relation
178 : *
179 : * Caller must previously have zeroed the SortSupportData structure and then
180 : * filled in ssup_cxt, ssup_attno, ssup_collation, and ssup_nulls_first. This
181 : * will fill in ssup_reverse (always false for GiST index build), as well as
182 : * the comparator function pointer.
183 : */
184 : void
185 150 : PrepareSortSupportFromGistIndexRel(Relation indexRel, SortSupport ssup)
186 : {
187 150 : Oid opfamily = indexRel->rd_opfamily[ssup->ssup_attno - 1];
188 150 : Oid opcintype = indexRel->rd_opcintype[ssup->ssup_attno - 1];
189 : Oid sortSupportFunction;
190 :
191 : Assert(ssup->comparator == NULL);
192 :
193 150 : if (indexRel->rd_rel->relam != GIST_AM_OID)
194 0 : elog(ERROR, "unexpected non-gist AM: %u", indexRel->rd_rel->relam);
195 150 : ssup->ssup_reverse = false;
196 :
197 : /*
198 : * Look up the sort support function. This is simpler than for B-tree
199 : * indexes because we don't support the old-style btree comparators.
200 : */
201 150 : sortSupportFunction = get_opfamily_proc(opfamily, opcintype, opcintype,
202 : GIST_SORTSUPPORT_PROC);
203 150 : if (!OidIsValid(sortSupportFunction))
204 0 : elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
205 : GIST_SORTSUPPORT_PROC, opcintype, opcintype, opfamily);
206 150 : OidFunctionCall1(sortSupportFunction, PointerGetDatum(ssup));
207 150 : }
|