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