Fix a possibility of logical replication slot's restart_lsn going backwards.
[pgsql.git] / contrib / hstore / hstore_subs.c
blob1443d8634b29927e9860cfa52722f8ed7fa957bb
1 /*-------------------------------------------------------------------------
3 * hstore_subs.c
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
13 * do everything.
15 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
16 * Portions Copyright (c) 1994, Regents of the University of California
19 * IDENTIFICATION
20 * contrib/hstore/hstore_subs.c
22 *-------------------------------------------------------------------------
24 #include "postgres.h"
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"
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.
41 static void
42 hstore_subscript_transform(SubscriptingRef *sbsref,
43 List *indirection,
44 ParseState *pstate,
45 bool isSlice,
46 bool isAssignment)
48 A_Indices *ai;
49 Node *subexpr;
51 /* We support only single-subscript, non-slice cases */
52 if (isSlice || list_length(indirection) != 1)
53 ereport(ERROR,
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),
67 TEXTOID, -1,
68 COERCION_ASSIGNMENT,
69 COERCE_IMPLICIT_CAST,
70 -1);
71 if (subexpr == NULL)
72 ereport(ERROR,
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
91 * upperindex[] array.
93 static void
94 hstore_subscript_fetch(ExprState *state,
95 ExprEvalStep *op,
96 ExprContext *econtext)
98 SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
99 HStore *hs;
100 text *key;
101 HEntry *entries;
102 int idx;
103 text *out;
105 /* Should not get here if source hstore is null */
106 Assert(!(*op->resnull));
108 /* Check for null subscript */
109 if (sbsrefstate->upperindexnull[0])
111 *op->resnull = true;
112 return;
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))
126 *op->resnull = true;
127 return;
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.
142 static void
143 hstore_subscript_assign(ExprState *state,
144 ExprEvalStep *op,
145 ExprContext *econtext)
147 SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
148 text *key;
149 Pairs p;
150 HStore *out;
152 /* Check for null subscript */
153 if (sbsrefstate->upperindexnull[0])
154 ereport(ERROR,
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 */
162 p.needfree = false;
163 p.key = VARDATA_ANY(key);
164 p.keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key));
166 if (sbsrefstate->replacenull)
168 p.vallen = 0;
169 p.isnull = true;
171 else
173 text *val = DatumGetTextPP(sbsrefstate->replacevalue);
175 p.val = VARDATA_ANY(val);
176 p.vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(val));
177 p.isnull = false;
180 if (*op->resnull)
182 /* Just build a one-element hstore (cf. hstore_from_text) */
183 out = hstorePairs(&p, 1, p.keylen + p.vallen);
185 else
188 * Otherwise, merge the new key into the hstore. Based on
189 * hstore_concat.
191 HStore *hs = DatumGetHStoreP(*op->resvalue);
192 int s1count = HS_COUNT(hs);
193 int outcount = 0;
194 int vsize;
195 char *ps1,
196 *bufd,
197 *pd;
198 HEntry *es1,
199 *ed;
200 int s1idx;
201 int s2idx;
203 /* Allocate result without considering possibility of duplicate */
204 vsize = CALCDATASIZE(s1count + 1, VARSIZE(hs) + p.keylen + p.vallen);
205 out = palloc(vsize);
206 SET_VARSIZE(out, vsize);
207 HS_SETCOUNT(out, s1count + 1);
209 ps1 = STRPTR(hs);
210 bufd = pd = STRPTR(out);
211 es1 = ARRPTR(hs);
212 ed = ARRPTR(out);
214 for (s1idx = s2idx = 0; s1idx < s1count || s2idx < 1; ++outcount)
216 int difference;
218 if (s1idx >= s1count)
219 difference = 1;
220 else if (s2idx >= 1)
221 difference = -1;
222 else
224 int s1keylen = HSTORE_KEYLEN(es1, s1idx);
225 int s2keylen = p.keylen;
227 if (s1keylen == s2keylen)
228 difference = memcmp(HSTORE_KEY(es1, ps1, s1idx),
229 p.key,
230 s1keylen);
231 else
232 difference = (s1keylen > s2keylen) ? 1 : -1;
235 if (difference >= 0)
237 HS_ADDITEM(ed, bufd, pd, p);
238 ++s2idx;
239 if (difference == 0)
240 ++s1idx;
242 else
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));
249 ++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.
263 static void
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);
285 Datum
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);