Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * hstore_subs.c
4 : * Subscripting support functions for hstore.
5 : *
6 : * This is a great deal simpler than array_subs.c, because the result of
7 : * subscripting an hstore is just a text string (the value for the key).
8 : * We do not need to support array slicing notation, nor multiple subscripts.
9 : * Less obviously, because the subscript result is never a SQL container
10 : * type, there will never be any nested-assignment scenarios, so we do not
11 : * need a fetch_old function. In turn, that means we can drop the
12 : * check_subscripts function and just let the fetch and assign functions
13 : * do everything.
14 : *
15 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
16 : * Portions Copyright (c) 1994, Regents of the University of California
17 : *
18 : *
19 : * IDENTIFICATION
20 : * contrib/hstore/hstore_subs.c
21 : *
22 : *-------------------------------------------------------------------------
23 : */
24 : #include "postgres.h"
25 :
26 : #include "executor/execExpr.h"
27 : #include "hstore.h"
28 : #include "nodes/nodeFuncs.h"
29 : #include "nodes/subscripting.h"
30 : #include "parser/parse_coerce.h"
31 : #include "parser/parse_expr.h"
32 : #include "utils/builtins.h"
33 :
34 :
35 : /*
36 : * Finish parse analysis of a SubscriptingRef expression for hstore.
37 : *
38 : * Verify there's just one subscript, coerce it to text,
39 : * and set the result type of the SubscriptingRef node.
40 : */
41 : static void
42 18 : hstore_subscript_transform(SubscriptingRef *sbsref,
43 : List *indirection,
44 : ParseState *pstate,
45 : bool isSlice,
46 : bool isAssignment)
47 : {
48 : A_Indices *ai;
49 : Node *subexpr;
50 :
51 : /* We support only single-subscript, non-slice cases */
52 18 : if (isSlice || list_length(indirection) != 1)
53 4 : ereport(ERROR,
54 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
55 : errmsg("hstore allows only one subscript"),
56 : parser_errposition(pstate,
57 : exprLocation((Node *) indirection))));
58 :
59 : /* Transform the subscript expression to type text */
60 14 : ai = linitial_node(A_Indices, indirection);
61 : Assert(ai->uidx != NULL && ai->lidx == NULL && !ai->is_slice);
62 :
63 14 : subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
64 : /* If it's not text already, try to coerce */
65 14 : subexpr = coerce_to_target_type(pstate,
66 : subexpr, exprType(subexpr),
67 : TEXTOID, -1,
68 : COERCION_ASSIGNMENT,
69 : COERCE_IMPLICIT_CAST,
70 : -1);
71 14 : if (subexpr == NULL)
72 0 : ereport(ERROR,
73 : (errcode(ERRCODE_DATATYPE_MISMATCH),
74 : errmsg("hstore subscript must have type text"),
75 : parser_errposition(pstate, exprLocation(ai->uidx))));
76 :
77 : /* ... and store the transformed subscript into the SubscriptRef node */
78 14 : sbsref->refupperindexpr = list_make1(subexpr);
79 14 : sbsref->reflowerindexpr = NIL;
80 :
81 : /* Determine the result type of the subscripting operation; always text */
82 14 : sbsref->refrestype = TEXTOID;
83 14 : sbsref->reftypmod = -1;
84 14 : }
85 :
86 : /*
87 : * Evaluate SubscriptingRef fetch for hstore.
88 : *
89 : * Source container is in step's result variable (it's known not NULL, since
90 : * we set fetch_strict to true), and the subscript expression is in the
91 : * upperindex[] array.
92 : */
93 : static void
94 14 : hstore_subscript_fetch(ExprState *state,
95 : ExprEvalStep *op,
96 : ExprContext *econtext)
97 : {
98 14 : SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
99 : HStore *hs;
100 : text *key;
101 : HEntry *entries;
102 : int idx;
103 : text *out;
104 :
105 : /* Should not get here if source hstore is null */
106 : Assert(!(*op->resnull));
107 :
108 : /* Check for null subscript */
109 14 : if (sbsrefstate->upperindexnull[0])
110 : {
111 0 : *op->resnull = true;
112 0 : return;
113 : }
114 :
115 : /* OK, fetch/detoast the hstore and subscript */
116 14 : hs = DatumGetHStoreP(*op->resvalue);
117 14 : key = DatumGetTextPP(sbsrefstate->upperindex[0]);
118 :
119 : /* The rest is basically the same as hstore_fetchval() */
120 14 : entries = ARRPTR(hs);
121 28 : idx = hstoreFindKey(hs, NULL,
122 28 : VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
123 :
124 14 : if (idx < 0 || HSTORE_VALISNULL(entries, idx))
125 : {
126 4 : *op->resnull = true;
127 4 : return;
128 : }
129 :
130 10 : out = cstring_to_text_with_len(HSTORE_VAL(entries, STRPTR(hs), idx),
131 10 : HSTORE_VALLEN(entries, idx));
132 :
133 10 : *op->resvalue = PointerGetDatum(out);
134 : }
135 :
136 : /*
137 : * Evaluate SubscriptingRef assignment for hstore.
138 : *
139 : * Input container (possibly null) is in result area, replacement value is in
140 : * SubscriptingRefState's replacevalue/replacenull.
141 : */
142 : static void
143 14 : hstore_subscript_assign(ExprState *state,
144 : ExprEvalStep *op,
145 : ExprContext *econtext)
146 : {
147 14 : SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
148 : text *key;
149 : Pairs p;
150 : HStore *out;
151 :
152 : /* Check for null subscript */
153 14 : if (sbsrefstate->upperindexnull[0])
154 0 : ereport(ERROR,
155 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
156 : errmsg("hstore subscript in assignment must not be null")));
157 :
158 : /* OK, fetch/detoast the subscript */
159 14 : key = DatumGetTextPP(sbsrefstate->upperindex[0]);
160 :
161 : /* Create a Pairs entry for subscript + replacement value */
162 14 : p.needfree = false;
163 14 : p.key = VARDATA_ANY(key);
164 14 : p.keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key));
165 :
166 14 : if (sbsrefstate->replacenull)
167 : {
168 2 : p.vallen = 0;
169 2 : p.isnull = true;
170 : }
171 : else
172 : {
173 12 : text *val = DatumGetTextPP(sbsrefstate->replacevalue);
174 :
175 12 : p.val = VARDATA_ANY(val);
176 12 : p.vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(val));
177 12 : p.isnull = false;
178 : }
179 :
180 14 : if (*op->resnull)
181 : {
182 : /* Just build a one-element hstore (cf. hstore_from_text) */
183 4 : out = hstorePairs(&p, 1, p.keylen + p.vallen);
184 : }
185 : else
186 : {
187 : /*
188 : * Otherwise, merge the new key into the hstore. Based on
189 : * hstore_concat.
190 : */
191 10 : HStore *hs = DatumGetHStoreP(*op->resvalue);
192 10 : int s1count = HS_COUNT(hs);
193 10 : int outcount = 0;
194 : int vsize;
195 : char *ps1,
196 : *bufd,
197 : *pd;
198 : HEntry *es1,
199 : *ed;
200 : int s1idx;
201 : int s2idx;
202 :
203 : /* Allocate result without considering possibility of duplicate */
204 10 : vsize = CALCDATASIZE(s1count + 1, VARSIZE(hs) + p.keylen + p.vallen);
205 10 : out = palloc(vsize);
206 10 : SET_VARSIZE(out, vsize);
207 10 : HS_SETCOUNT(out, s1count + 1);
208 :
209 10 : ps1 = STRPTR(hs);
210 10 : bufd = pd = STRPTR(out);
211 10 : es1 = ARRPTR(hs);
212 10 : ed = ARRPTR(out);
213 :
214 74 : for (s1idx = s2idx = 0; s1idx < s1count || s2idx < 1; ++outcount)
215 : {
216 : int difference;
217 :
218 64 : if (s1idx >= s1count)
219 2 : difference = 1;
220 62 : else if (s2idx >= 1)
221 20 : difference = -1;
222 : else
223 : {
224 42 : int s1keylen = HSTORE_KEYLEN(es1, s1idx);
225 42 : int s2keylen = p.keylen;
226 :
227 42 : if (s1keylen == s2keylen)
228 38 : difference = memcmp(HSTORE_KEY(es1, ps1, s1idx),
229 38 : p.key,
230 : s1keylen);
231 : else
232 4 : difference = (s1keylen > s2keylen) ? 1 : -1;
233 : }
234 :
235 64 : if (difference >= 0)
236 : {
237 10 : HS_ADDITEM(ed, bufd, pd, p);
238 10 : ++s2idx;
239 10 : if (difference == 0)
240 4 : ++s1idx;
241 : }
242 : else
243 : {
244 54 : HS_COPYITEM(ed, bufd, pd,
245 : HSTORE_KEY(es1, ps1, s1idx),
246 : HSTORE_KEYLEN(es1, s1idx),
247 : HSTORE_VALLEN(es1, s1idx),
248 : HSTORE_VALISNULL(es1, s1idx));
249 54 : ++s1idx;
250 : }
251 : }
252 :
253 10 : HS_FINALIZE(out, outcount, bufd, pd);
254 : }
255 :
256 14 : *op->resvalue = PointerGetDatum(out);
257 14 : *op->resnull = false;
258 14 : }
259 :
260 : /*
261 : * Set up execution state for an hstore subscript operation.
262 : */
263 : static void
264 14 : hstore_exec_setup(const SubscriptingRef *sbsref,
265 : SubscriptingRefState *sbsrefstate,
266 : SubscriptExecSteps *methods)
267 : {
268 : /* Assert we are dealing with one subscript */
269 : Assert(sbsrefstate->numlower == 0);
270 : Assert(sbsrefstate->numupper == 1);
271 : /* We can't check upperprovided[0] here, but it must be true */
272 :
273 : /* Pass back pointers to appropriate step execution functions */
274 14 : methods->sbs_check_subscripts = NULL;
275 14 : methods->sbs_fetch = hstore_subscript_fetch;
276 14 : methods->sbs_assign = hstore_subscript_assign;
277 14 : methods->sbs_fetch_old = NULL;
278 14 : }
279 :
280 : /*
281 : * hstore_subscript_handler
282 : * Subscripting handler for hstore.
283 : */
284 16 : PG_FUNCTION_INFO_V1(hstore_subscript_handler);
285 : Datum
286 32 : hstore_subscript_handler(PG_FUNCTION_ARGS)
287 : {
288 : static const SubscriptRoutines sbsroutines = {
289 : .transform = hstore_subscript_transform,
290 : .exec_setup = hstore_exec_setup,
291 : .fetch_strict = true, /* fetch returns NULL for NULL inputs */
292 : .fetch_leakproof = true, /* fetch returns NULL for bad subscript */
293 : .store_leakproof = false /* ... but assignment throws error */
294 : };
295 :
296 32 : PG_RETURN_POINTER(&sbsroutines);
297 : }
|