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