1 /*-------------------------------------------------------------------------
4 * Subscripting support functions for hstore.
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
15 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
16 * Portions Copyright (c) 1994, Regents of the University of California
20 * contrib/hstore/hstore_subs.c
22 *-------------------------------------------------------------------------
26 #include "executor/execExpr.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"
36 * Finish parse analysis of a SubscriptingRef expression for hstore.
38 * Verify there's just one subscript, coerce it to text,
39 * and set the result type of the SubscriptingRef node.
42 hstore_subscript_transform(SubscriptingRef
*sbsref
,
51 /* We support only single-subscript, non-slice cases */
52 if (isSlice
|| list_length(indirection
) != 1)
54 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
55 errmsg("hstore allows only one subscript"),
56 parser_errposition(pstate
,
57 exprLocation((Node
*) indirection
))));
59 /* Transform the subscript expression to type text */
60 ai
= linitial_node(A_Indices
, indirection
);
61 Assert(ai
->uidx
!= NULL
&& ai
->lidx
== NULL
&& !ai
->is_slice
);
63 subexpr
= transformExpr(pstate
, ai
->uidx
, pstate
->p_expr_kind
);
64 /* If it's not text already, try to coerce */
65 subexpr
= coerce_to_target_type(pstate
,
66 subexpr
, exprType(subexpr
),
73 (errcode(ERRCODE_DATATYPE_MISMATCH
),
74 errmsg("hstore subscript must have type text"),
75 parser_errposition(pstate
, exprLocation(ai
->uidx
))));
77 /* ... and store the transformed subscript into the SubscriptRef node */
78 sbsref
->refupperindexpr
= list_make1(subexpr
);
79 sbsref
->reflowerindexpr
= NIL
;
81 /* Determine the result type of the subscripting operation; always text */
82 sbsref
->refrestype
= TEXTOID
;
83 sbsref
->reftypmod
= -1;
87 * Evaluate SubscriptingRef fetch for hstore.
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
94 hstore_subscript_fetch(ExprState
*state
,
96 ExprContext
*econtext
)
98 SubscriptingRefState
*sbsrefstate
= op
->d
.sbsref
.state
;
105 /* Should not get here if source hstore is null */
106 Assert(!(*op
->resnull
));
108 /* Check for null subscript */
109 if (sbsrefstate
->upperindexnull
[0])
115 /* OK, fetch/detoast the hstore and subscript */
116 hs
= DatumGetHStoreP(*op
->resvalue
);
117 key
= DatumGetTextPP(sbsrefstate
->upperindex
[0]);
119 /* The rest is basically the same as hstore_fetchval() */
120 entries
= ARRPTR(hs
);
121 idx
= hstoreFindKey(hs
, NULL
,
122 VARDATA_ANY(key
), VARSIZE_ANY_EXHDR(key
));
124 if (idx
< 0 || HSTORE_VALISNULL(entries
, idx
))
130 out
= cstring_to_text_with_len(HSTORE_VAL(entries
, STRPTR(hs
), idx
),
131 HSTORE_VALLEN(entries
, idx
));
133 *op
->resvalue
= PointerGetDatum(out
);
137 * Evaluate SubscriptingRef assignment for hstore.
139 * Input container (possibly null) is in result area, replacement value is in
140 * SubscriptingRefState's replacevalue/replacenull.
143 hstore_subscript_assign(ExprState
*state
,
145 ExprContext
*econtext
)
147 SubscriptingRefState
*sbsrefstate
= op
->d
.sbsref
.state
;
152 /* Check for null subscript */
153 if (sbsrefstate
->upperindexnull
[0])
155 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED
),
156 errmsg("hstore subscript in assignment must not be null")));
158 /* OK, fetch/detoast the subscript */
159 key
= DatumGetTextPP(sbsrefstate
->upperindex
[0]);
161 /* Create a Pairs entry for subscript + replacement value */
163 p
.key
= VARDATA_ANY(key
);
164 p
.keylen
= hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key
));
166 if (sbsrefstate
->replacenull
)
173 text
*val
= DatumGetTextPP(sbsrefstate
->replacevalue
);
175 p
.val
= VARDATA_ANY(val
);
176 p
.vallen
= hstoreCheckValLen(VARSIZE_ANY_EXHDR(val
));
182 /* Just build a one-element hstore (cf. hstore_from_text) */
183 out
= hstorePairs(&p
, 1, p
.keylen
+ p
.vallen
);
188 * Otherwise, merge the new key into the hstore. Based on
191 HStore
*hs
= DatumGetHStoreP(*op
->resvalue
);
192 int s1count
= HS_COUNT(hs
);
203 /* Allocate result without considering possibility of duplicate */
204 vsize
= CALCDATASIZE(s1count
+ 1, VARSIZE(hs
) + p
.keylen
+ p
.vallen
);
206 SET_VARSIZE(out
, vsize
);
207 HS_SETCOUNT(out
, s1count
+ 1);
210 bufd
= pd
= STRPTR(out
);
214 for (s1idx
= s2idx
= 0; s1idx
< s1count
|| s2idx
< 1; ++outcount
)
218 if (s1idx
>= s1count
)
224 int s1keylen
= HSTORE_KEYLEN(es1
, s1idx
);
225 int s2keylen
= p
.keylen
;
227 if (s1keylen
== s2keylen
)
228 difference
= memcmp(HSTORE_KEY(es1
, ps1
, s1idx
),
232 difference
= (s1keylen
> s2keylen
) ? 1 : -1;
237 HS_ADDITEM(ed
, bufd
, pd
, p
);
244 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
));
253 HS_FINALIZE(out
, outcount
, bufd
, pd
);
256 *op
->resvalue
= PointerGetDatum(out
);
257 *op
->resnull
= false;
261 * Set up execution state for an hstore subscript operation.
264 hstore_exec_setup(const SubscriptingRef
*sbsref
,
265 SubscriptingRefState
*sbsrefstate
,
266 SubscriptExecSteps
*methods
)
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 */
273 /* Pass back pointers to appropriate step execution functions */
274 methods
->sbs_check_subscripts
= NULL
;
275 methods
->sbs_fetch
= hstore_subscript_fetch
;
276 methods
->sbs_assign
= hstore_subscript_assign
;
277 methods
->sbs_fetch_old
= NULL
;
281 * hstore_subscript_handler
282 * Subscripting handler for hstore.
284 PG_FUNCTION_INFO_V1(hstore_subscript_handler
);
286 hstore_subscript_handler(PG_FUNCTION_ARGS
)
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 */
296 PG_RETURN_POINTER(&sbsroutines
);