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 | |