PostgreSQL Source Code  git master
hstore_subs.c
Go to the documentation of this file.
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-2021, 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
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  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))));
58 
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);
62 
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,
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))));
76 
77  /* ... and store the transformed subscript into the SubscriptRef node */
78  sbsref->refupperindexpr = list_make1(subexpr);
79  sbsref->reflowerindexpr = NIL;
80 
81  /* Determine the result type of the subscripting operation; always text */
82  sbsref->refrestype = TEXTOID;
83  sbsref->reftypmod = -1;
84 }
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
95  ExprEvalStep *op,
96  ExprContext *econtext)
97 {
98  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  if (sbsrefstate->upperindexnull[0])
110  {
111  *op->resnull = true;
112  return;
113  }
114 
115  /* OK, fetch/detoast the hstore and subscript */
116  hs = DatumGetHStoreP(*op->resvalue);
117  key = DatumGetTextPP(sbsrefstate->upperindex[0]);
118 
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));
123 
124  if (idx < 0 || HSTORE_VALISNULL(entries, idx))
125  {
126  *op->resnull = true;
127  return;
128  }
129 
130  out = cstring_to_text_with_len(HSTORE_VAL(entries, STRPTR(hs), idx),
131  HSTORE_VALLEN(entries, idx));
132 
133  *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
144  ExprEvalStep *op,
145  ExprContext *econtext)
146 {
147  SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
148  text *key;
149  Pairs p;
150  HStore *out;
151 
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")));
157 
158  /* OK, fetch/detoast the subscript */
159  key = DatumGetTextPP(sbsrefstate->upperindex[0]);
160 
161  /* Create a Pairs entry for subscript + replacement value */
162  p.needfree = false;
163  p.key = VARDATA_ANY(key);
165 
166  if (sbsrefstate->replacenull)
167  {
168  p.vallen = 0;
169  p.isnull = true;
170  }
171  else
172  {
173  text *val = DatumGetTextPP(sbsrefstate->replacevalue);
174 
175  p.val = VARDATA_ANY(val);
177  p.isnull = false;
178  }
179 
180  if (*op->resnull)
181  {
182  /* Just build a one-element hstore (cf. hstore_from_text) */
183  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  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;
202 
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);
208 
209  ps1 = STRPTR(hs);
210  bufd = pd = STRPTR(out);
211  es1 = ARRPTR(hs);
212  ed = ARRPTR(out);
213 
214  for (s1idx = s2idx = 0; s1idx < s1count || s2idx < 1; ++outcount)
215  {
216  int difference;
217 
218  if (s1idx >= s1count)
219  difference = 1;
220  else if (s2idx >= 1)
221  difference = -1;
222  else
223  {
224  int s1keylen = HSTORE_KEYLEN(es1, s1idx);
225  int s2keylen = p.keylen;
226 
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;
233  }
234 
235  if (difference >= 0)
236  {
237  HS_ADDITEM(ed, bufd, pd, p);
238  ++s2idx;
239  if (difference == 0)
240  ++s1idx;
241  }
242  else
243  {
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;
250  }
251  }
252 
253  HS_FINALIZE(out, outcount, bufd, pd);
254  }
255 
256  *op->resvalue = PointerGetDatum(out);
257  *op->resnull = false;
258 }
259 
260 /*
261  * Set up execution state for an hstore subscript operation.
262  */
263 static void
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  methods->sbs_check_subscripts = NULL;
277  methods->sbs_fetch_old = NULL;
278 }
279 
280 /*
281  * hstore_subscript_handler
282  * Subscripting handler for hstore.
283  */
285 Datum
287 {
288  static const SubscriptRoutines sbsroutines = {
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  PG_RETURN_POINTER(&sbsroutines);
297 }
#define hstorePairs
Definition: hstore_plperl.c:58
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:361
#define NIL
Definition: pg_list.h:65
Definition: hstore.h:44
#define DatumGetHStoreP(d)
Definition: hstore.h:152
#define VARDATA_ANY(PTR)
Definition: postgres.h:361
static void hstore_subscript_fetch(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
Definition: hstore_subs.c:94
int exprLocation(const Node *expr)
Definition: nodeFuncs.c:1250
Datum * resvalue
Definition: execExpr.h:273
#define VARSIZE(PTR)
Definition: postgres.h:316
#define PointerGetDatum(X)
Definition: postgres.h:600
ExecEvalSubroutine sbs_fetch
Definition: execExpr.h:710
#define HSTORE_KEYLEN(arr_, i_)
Definition: hstore.h:81
#define DatumGetTextPP(X)
Definition: fmgr.h:292
bool * resnull
Definition: execExpr.h:274
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:94
Definition: nodes.h:536
int errcode(int sqlerrcode)
Definition: elog.c:698
ExecEvalSubroutine sbs_fetch_old
Definition: execExpr.h:712
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
#define HSTORE_VALLEN(arr_, i_)
Definition: hstore.h:82
ExecEvalSubroutine sbs_assign
Definition: execExpr.h:711
size_t vallen
Definition: hstore.h:166
#define linitial_node(type, l)
Definition: pg_list.h:177
#define hstoreCheckValLen
Definition: hstore_plperl.c:60
List * refupperindexpr
Definition: primnodes.h:444
bool needfree
Definition: hstore.h:168
char * val
Definition: hstore.h:164
#define list_make1(x1)
Definition: pg_list.h:206
char * key
Definition: hstore.h:163
#define HSTORE_VALISNULL(arr_, i_)
Definition: hstore.h:83
bool is_slice
Definition: parsenodes.h:406
#define ERROR
Definition: elog.h:46
union ExprEvalStep::@49 d
int hstoreFindKey(HStore *hs, int *lowbound, char *key, int keylen)
Definition: hstore_op.c:36
Node * coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, Oid targettype, int32 targettypmod, CoercionContext ccontext, CoercionForm cformat, int location)
Definition: parse_coerce.c:78
text * cstring_to_text_with_len(const char *s, int len)
Definition: varlena.c:201
#define HS_COUNT(hsp_)
Definition: hstore.h:61
#define HS_ADDITEM(dent_, dbuf_, dptr_, pair_)
Definition: hstore.h:112
#define HSTORE_KEY(arr_, str_, i_)
Definition: hstore.h:79
#define HS_FINALIZE(hsp_, count_, buf_, ptr_)
Definition: hstore.h:129
Datum hstore_subscript_handler(PG_FUNCTION_ARGS)
Definition: hstore_subs.c:286
#define CALCDATASIZE(x, lenstr)
Definition: hstore.h:72
uintptr_t Datum
Definition: postgres.h:411
Datum difference(PG_FUNCTION_ARGS)
ParseExprKind p_expr_kind
Definition: parse_node.h:196
size_t keylen
Definition: hstore.h:165
#define ereport(elevel,...)
Definition: elog.h:157
#define Assert(condition)
Definition: c.h:804
Definition: regguts.h:317
bool isnull
Definition: hstore.h:167
#define hstoreCheckKeyLen
Definition: hstore_plperl.c:59
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:41
static int list_length(const List *l)
Definition: pg_list.h:149
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:111
Node * lidx
Definition: parsenodes.h:407
Definition: hstore.h:18
struct ExprEvalStep::@49::@77 sbsref
static void hstore_subscript_assign(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
Definition: hstore_subs.c:143
#define HSTORE_VAL(arr_, str_, i_)
Definition: hstore.h:80
#define VARSIZE_ANY_EXHDR(PTR)
Definition: postgres.h:354
void * palloc(Size size)
Definition: mcxt.c:1062
int errmsg(const char *fmt,...)
Definition: elog.c:909
SubscriptTransform transform
Definition: subscripting.h:160
#define STRPTR(x)
Definition: hstore.h:76
List * reflowerindexpr
Definition: primnodes.h:446
Node * uidx
Definition: parsenodes.h:408
Definition: c.h:621
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
#define HS_COPYITEM(dent_, dbuf_, dptr_, sptr_, klen_, vlen_, vnull_)
Definition: hstore.h:99
#define SET_VARSIZE(PTR, len)
Definition: postgres.h:342
#define ARRPTR(x)
Definition: cube.c:25
ExecEvalBoolSubroutine sbs_check_subscripts
Definition: execExpr.h:709
#define HS_SETCOUNT(hsp_, c_)
Definition: hstore.h:62
PG_FUNCTION_INFO_V1(hstore_subscript_handler)
Definition: hstore.h:161
Definition: pg_list.h:50
static void hstore_subscript_transform(SubscriptingRef *sbsref, List *indirection, ParseState *pstate, bool isSlice, bool isAssignment)
Definition: hstore_subs.c:42
long val
Definition: informix.c:664
static void hstore_exec_setup(const SubscriptingRef *sbsref, SubscriptingRefState *sbsrefstate, SubscriptExecSteps *methods)
Definition: hstore_subs.c:264