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-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
1220 tgl@sss.pgh.pa.us 42 :CBC 9 : 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 [ + + + + ]: 9 : if (isSlice || list_length(indirection) != 1)
53 [ + - ]: 2 : 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 : 7 : ai = linitial_node(A_Indices, indirection);
61 [ + - + - : 7 : Assert(ai->uidx != NULL && ai->lidx == NULL && !ai->is_slice);
- + ]
62 : :
63 : 7 : subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
64 : : /* If it's not text already, try to coerce */
65 : 7 : subexpr = coerce_to_target_type(pstate,
66 : : subexpr, exprType(subexpr),
67 : : TEXTOID, -1,
68 : : COERCION_ASSIGNMENT,
69 : : COERCE_IMPLICIT_CAST,
70 : : -1);
71 [ - + ]: 7 : if (subexpr == NULL)
1220 tgl@sss.pgh.pa.us 72 [ # # ]:UBC 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 */
1220 tgl@sss.pgh.pa.us 78 :CBC 7 : sbsref->refupperindexpr = list_make1(subexpr);
79 : 7 : sbsref->reflowerindexpr = NIL;
80 : :
81 : : /* Determine the result type of the subscripting operation; always text */
82 : 7 : sbsref->refrestype = TEXTOID;
83 : 7 : sbsref->reftypmod = -1;
84 : 7 : }
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 : 7 : hstore_subscript_fetch(ExprState *state,
95 : : ExprEvalStep *op,
96 : : ExprContext *econtext)
97 : : {
98 : 7 : 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 [ - + ]: 7 : Assert(!(*op->resnull));
107 : :
108 : : /* Check for null subscript */
109 [ - + ]: 7 : if (sbsrefstate->upperindexnull[0])
110 : : {
1220 tgl@sss.pgh.pa.us 111 :UBC 0 : *op->resnull = true;
112 : 0 : return;
113 : : }
114 : :
115 : : /* OK, fetch/detoast the hstore and subscript */
1220 tgl@sss.pgh.pa.us 116 :CBC 7 : hs = DatumGetHStoreP(*op->resvalue);
117 : 7 : key = DatumGetTextPP(sbsrefstate->upperindex[0]);
118 : :
119 : : /* The rest is basically the same as hstore_fetchval() */
120 : 7 : entries = ARRPTR(hs);
1220 tgl@sss.pgh.pa.us 121 :UBC 0 : idx = hstoreFindKey(hs, NULL,
1220 tgl@sss.pgh.pa.us 122 [ - + - - :CBC 14 : VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
- - - - -
+ - + ]
123 : :
124 [ + + - + ]: 7 : if (idx < 0 || HSTORE_VALISNULL(entries, idx))
125 : : {
126 : 2 : *op->resnull = true;
127 : 2 : return;
128 : : }
129 : :
130 [ + - ]: 5 : out = cstring_to_text_with_len(HSTORE_VAL(entries, STRPTR(hs), idx),
131 [ - + ]: 5 : HSTORE_VALLEN(entries, idx));
132 : :
133 : 5 : *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 : 7 : hstore_subscript_assign(ExprState *state,
144 : : ExprEvalStep *op,
145 : : ExprContext *econtext)
146 : : {
147 : 7 : SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
148 : : text *key;
149 : : Pairs p;
150 : : HStore *out;
151 : :
152 : : /* Check for null subscript */
153 [ - + ]: 7 : if (sbsrefstate->upperindexnull[0])
1220 tgl@sss.pgh.pa.us 154 [ # # ]:UBC 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 */
1220 tgl@sss.pgh.pa.us 159 :CBC 7 : key = DatumGetTextPP(sbsrefstate->upperindex[0]);
160 : :
161 : : /* Create a Pairs entry for subscript + replacement value */
162 : 7 : p.needfree = false;
163 [ - + ]: 7 : p.key = VARDATA_ANY(key);
164 [ - + - - : 7 : p.keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key));
- - - - -
+ ]
165 : :
166 [ + + ]: 7 : if (sbsrefstate->replacenull)
167 : : {
168 : 1 : p.vallen = 0;
169 : 1 : p.isnull = true;
170 : : }
171 : : else
172 : : {
173 : 6 : text *val = DatumGetTextPP(sbsrefstate->replacevalue);
174 : :
175 [ - + ]: 6 : p.val = VARDATA_ANY(val);
176 [ - + - - : 6 : p.vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(val));
- - - - -
+ ]
177 : 6 : p.isnull = false;
178 : : }
179 : :
180 [ + + ]: 7 : if (*op->resnull)
181 : : {
182 : : /* Just build a one-element hstore (cf. hstore_from_text) */
183 : 2 : 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 : 5 : HStore *hs = DatumGetHStoreP(*op->resvalue);
192 : 5 : int s1count = HS_COUNT(hs);
193 : 5 : 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 : 5 : vsize = CALCDATASIZE(s1count + 1, VARSIZE(hs) + p.keylen + p.vallen);
205 : 5 : out = palloc(vsize);
206 : 5 : SET_VARSIZE(out, vsize);
207 : 5 : HS_SETCOUNT(out, s1count + 1);
208 : :
209 : 5 : ps1 = STRPTR(hs);
210 : 5 : bufd = pd = STRPTR(out);
211 : 5 : es1 = ARRPTR(hs);
212 : 5 : ed = ARRPTR(out);
213 : :
214 [ + + + + ]: 37 : for (s1idx = s2idx = 0; s1idx < s1count || s2idx < 1; ++outcount)
215 : : {
216 : : int difference;
217 : :
218 [ + + ]: 32 : if (s1idx >= s1count)
219 : 1 : difference = 1;
220 [ + + ]: 31 : else if (s2idx >= 1)
221 : 10 : difference = -1;
222 : : else
223 : : {
224 [ + + ]: 21 : int s1keylen = HSTORE_KEYLEN(es1, s1idx);
225 : 21 : int s2keylen = p.keylen;
226 : :
227 [ + + ]: 21 : if (s1keylen == s2keylen)
228 : 19 : difference = memcmp(HSTORE_KEY(es1, ps1, s1idx),
229 [ + + ]: 19 : p.key,
230 : : s1keylen);
231 : : else
232 [ + - ]: 2 : difference = (s1keylen > s2keylen) ? 1 : -1;
233 : : }
234 : :
235 [ + + ]: 32 : if (difference >= 0)
236 : : {
237 [ - + ]: 5 : HS_ADDITEM(ed, bufd, pd, p);
238 : 5 : ++s2idx;
239 [ + + ]: 5 : if (difference == 0)
240 : 2 : ++s1idx;
241 : : }
242 : : else
243 : : {
244 [ + + - + : 27 : 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 : 27 : ++s1idx;
250 : : }
251 : : }
252 : :
253 [ + - + + ]: 5 : HS_FINALIZE(out, outcount, bufd, pd);
254 : : }
255 : :
256 : 7 : *op->resvalue = PointerGetDatum(out);
257 : 7 : *op->resnull = false;
258 : 7 : }
259 : :
260 : : /*
261 : : * Set up execution state for an hstore subscript operation.
262 : : */
263 : : static void
264 : 7 : hstore_exec_setup(const SubscriptingRef *sbsref,
265 : : SubscriptingRefState *sbsrefstate,
266 : : SubscriptExecSteps *methods)
267 : : {
268 : : /* Assert we are dealing with one subscript */
269 [ - + ]: 7 : Assert(sbsrefstate->numlower == 0);
270 [ - + ]: 7 : 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 : 7 : methods->sbs_check_subscripts = NULL;
275 : 7 : methods->sbs_fetch = hstore_subscript_fetch;
276 : 7 : methods->sbs_assign = hstore_subscript_assign;
277 : 7 : methods->sbs_fetch_old = NULL;
278 : 7 : }
279 : :
280 : : /*
281 : : * hstore_subscript_handler
282 : : * Subscripting handler for hstore.
283 : : */
284 : 8 : PG_FUNCTION_INFO_V1(hstore_subscript_handler);
285 : : Datum
286 : 16 : 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 : 16 : PG_RETURN_POINTER(&sbsroutines);
297 : : }
|