Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * execExprInterp.c
4 : : * Interpreted evaluation of an expression step list.
5 : : *
6 : : * This file provides either a "direct threaded" (for gcc, clang and
7 : : * compatible) or a "switch threaded" (for all compilers) implementation of
8 : : * expression evaluation. The former is amongst the fastest known methods
9 : : * of interpreting programs without resorting to assembly level work, or
10 : : * just-in-time compilation, but it requires support for computed gotos.
11 : : * The latter is amongst the fastest approaches doable in standard C.
12 : : *
13 : : * In either case we use ExprEvalStep->opcode to dispatch to the code block
14 : : * within ExecInterpExpr() that implements the specific opcode type.
15 : : *
16 : : * Switch-threading uses a plain switch() statement to perform the
17 : : * dispatch. This has the advantages of being plain C and allowing the
18 : : * compiler to warn if implementation of a specific opcode has been forgotten.
19 : : * The disadvantage is that dispatches will, as commonly implemented by
20 : : * compilers, happen from a single location, requiring more jumps and causing
21 : : * bad branch prediction.
22 : : *
23 : : * In direct threading, we use gcc's label-as-values extension - also adopted
24 : : * by some other compilers - to replace ExprEvalStep->opcode with the address
25 : : * of the block implementing the instruction. Dispatch to the next instruction
26 : : * is done by a "computed goto". This allows for better branch prediction
27 : : * (as the jumps are happening from different locations) and fewer jumps
28 : : * (as no preparatory jump to a common dispatch location is needed).
29 : : *
30 : : * When using direct threading, ExecReadyInterpretedExpr will replace
31 : : * each step's opcode field with the address of the relevant code block and
32 : : * ExprState->flags will contain EEO_FLAG_DIRECT_THREADED to remember that
33 : : * that's been done.
34 : : *
35 : : * For very simple instructions the overhead of the full interpreter
36 : : * "startup", as minimal as it is, is noticeable. Therefore
37 : : * ExecReadyInterpretedExpr will choose to implement certain simple
38 : : * opcode patterns using special fast-path routines (ExecJust*).
39 : : *
40 : : * Complex or uncommon instructions are not implemented in-line in
41 : : * ExecInterpExpr(), rather we call out to a helper function appearing later
42 : : * in this file. For one reason, there'd not be a noticeable performance
43 : : * benefit, but more importantly those complex routines are intended to be
44 : : * shared between different expression evaluation approaches. For instance
45 : : * a JIT compiler would generate calls to them. (This is why they are
46 : : * exported rather than being "static" in this file.)
47 : : *
48 : : *
49 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
50 : : * Portions Copyright (c) 1994, Regents of the University of California
51 : : *
52 : : * IDENTIFICATION
53 : : * src/backend/executor/execExprInterp.c
54 : : *
55 : : *-------------------------------------------------------------------------
56 : : */
57 : : #include "postgres.h"
58 : :
59 : : #include "access/heaptoast.h"
60 : : #include "catalog/pg_type.h"
61 : : #include "commands/sequence.h"
62 : : #include "executor/execExpr.h"
63 : : #include "executor/nodeSubplan.h"
64 : : #include "funcapi.h"
65 : : #include "miscadmin.h"
66 : : #include "nodes/miscnodes.h"
67 : : #include "nodes/nodeFuncs.h"
68 : : #include "pgstat.h"
69 : : #include "utils/array.h"
70 : : #include "utils/builtins.h"
71 : : #include "utils/date.h"
72 : : #include "utils/datum.h"
73 : : #include "utils/expandedrecord.h"
74 : : #include "utils/json.h"
75 : : #include "utils/jsonfuncs.h"
76 : : #include "utils/jsonpath.h"
77 : : #include "utils/lsyscache.h"
78 : : #include "utils/memutils.h"
79 : : #include "utils/timestamp.h"
80 : : #include "utils/typcache.h"
81 : : #include "utils/xml.h"
82 : :
83 : : /*
84 : : * Use computed-goto-based opcode dispatch when computed gotos are available.
85 : : * But use a separate symbol so that it's easy to adjust locally in this file
86 : : * for development and testing.
87 : : */
88 : : #ifdef HAVE_COMPUTED_GOTO
89 : : #define EEO_USE_COMPUTED_GOTO
90 : : #endif /* HAVE_COMPUTED_GOTO */
91 : :
92 : : /*
93 : : * Macros for opcode dispatch.
94 : : *
95 : : * EEO_SWITCH - just hides the switch if not in use.
96 : : * EEO_CASE - labels the implementation of named expression step type.
97 : : * EEO_DISPATCH - jump to the implementation of the step type for 'op'.
98 : : * EEO_OPCODE - compute opcode required by used expression evaluation method.
99 : : * EEO_NEXT - increment 'op' and jump to correct next step type.
100 : : * EEO_JUMP - jump to the specified step number within the current expression.
101 : : */
102 : : #if defined(EEO_USE_COMPUTED_GOTO)
103 : :
104 : : /* struct for jump target -> opcode lookup table */
105 : : typedef struct ExprEvalOpLookup
106 : : {
107 : : const void *opcode;
108 : : ExprEvalOp op;
109 : : } ExprEvalOpLookup;
110 : :
111 : : /* to make dispatch_table accessible outside ExecInterpExpr() */
112 : : static const void **dispatch_table = NULL;
113 : :
114 : : /* jump target -> opcode lookup table */
115 : : static ExprEvalOpLookup reverse_dispatch_table[EEOP_LAST];
116 : :
117 : : #define EEO_SWITCH()
118 : : #define EEO_CASE(name) CASE_##name:
119 : : #define EEO_DISPATCH() goto *((void *) op->opcode)
120 : : #define EEO_OPCODE(opcode) ((intptr_t) dispatch_table[opcode])
121 : :
122 : : #else /* !EEO_USE_COMPUTED_GOTO */
123 : :
124 : : #define EEO_SWITCH() starteval: switch ((ExprEvalOp) op->opcode)
125 : : #define EEO_CASE(name) case name:
126 : : #define EEO_DISPATCH() goto starteval
127 : : #define EEO_OPCODE(opcode) (opcode)
128 : :
129 : : #endif /* EEO_USE_COMPUTED_GOTO */
130 : :
131 : : #define EEO_NEXT() \
132 : : do { \
133 : : op++; \
134 : : EEO_DISPATCH(); \
135 : : } while (0)
136 : :
137 : : #define EEO_JUMP(stepno) \
138 : : do { \
139 : : op = &state->steps[stepno]; \
140 : : EEO_DISPATCH(); \
141 : : } while (0)
142 : :
143 : :
144 : : static Datum ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull);
145 : : static void ExecInitInterpreter(void);
146 : :
147 : : /* support functions */
148 : : static void CheckVarSlotCompatibility(TupleTableSlot *slot, int attnum, Oid vartype);
149 : : static void CheckOpSlotCompatibility(ExprEvalStep *op, TupleTableSlot *slot);
150 : : static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod,
151 : : ExprEvalRowtypeCache *rowcache,
152 : : bool *changed);
153 : : static void ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op,
154 : : ExprContext *econtext, bool checkisnull);
155 : :
156 : : /* fast-path evaluation functions */
157 : : static Datum ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull);
158 : : static Datum ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull);
159 : : static Datum ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull);
160 : : static Datum ExecJustAssignInnerVar(ExprState *state, ExprContext *econtext, bool *isnull);
161 : : static Datum ExecJustAssignOuterVar(ExprState *state, ExprContext *econtext, bool *isnull);
162 : : static Datum ExecJustAssignScanVar(ExprState *state, ExprContext *econtext, bool *isnull);
163 : : static Datum ExecJustApplyFuncToCase(ExprState *state, ExprContext *econtext, bool *isnull);
164 : : static Datum ExecJustConst(ExprState *state, ExprContext *econtext, bool *isnull);
165 : : static Datum ExecJustInnerVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
166 : : static Datum ExecJustOuterVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
167 : : static Datum ExecJustScanVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
168 : : static Datum ExecJustAssignInnerVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
169 : : static Datum ExecJustAssignOuterVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
170 : : static Datum ExecJustAssignScanVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
171 : :
172 : : /* execution helper functions */
173 : : static pg_attribute_always_inline void ExecAggPlainTransByVal(AggState *aggstate,
174 : : AggStatePerTrans pertrans,
175 : : AggStatePerGroup pergroup,
176 : : ExprContext *aggcontext,
177 : : int setno);
178 : : static pg_attribute_always_inline void ExecAggPlainTransByRef(AggState *aggstate,
179 : : AggStatePerTrans pertrans,
180 : : AggStatePerGroup pergroup,
181 : : ExprContext *aggcontext,
182 : : int setno);
183 : : static char *ExecGetJsonValueItemString(JsonbValue *item, bool *resnull);
184 : :
185 : : /*
186 : : * ScalarArrayOpExprHashEntry
187 : : * Hash table entry type used during EEOP_HASHED_SCALARARRAYOP
188 : : */
189 : : typedef struct ScalarArrayOpExprHashEntry
190 : : {
191 : : Datum key;
192 : : uint32 status; /* hash status */
193 : : uint32 hash; /* hash value (cached) */
194 : : } ScalarArrayOpExprHashEntry;
195 : :
196 : : #define SH_PREFIX saophash
197 : : #define SH_ELEMENT_TYPE ScalarArrayOpExprHashEntry
198 : : #define SH_KEY_TYPE Datum
199 : : #define SH_SCOPE static inline
200 : : #define SH_DECLARE
201 : : #include "lib/simplehash.h"
202 : :
203 : : static bool saop_hash_element_match(struct saophash_hash *tb, Datum key1,
204 : : Datum key2);
205 : : static uint32 saop_element_hash(struct saophash_hash *tb, Datum key);
206 : :
207 : : /*
208 : : * ScalarArrayOpExprHashTable
209 : : * Hash table for EEOP_HASHED_SCALARARRAYOP
210 : : */
211 : : typedef struct ScalarArrayOpExprHashTable
212 : : {
213 : : saophash_hash *hashtab; /* underlying hash table */
214 : : struct ExprEvalStep *op;
215 : : FmgrInfo hash_finfo; /* function's lookup data */
216 : : FunctionCallInfoBaseData hash_fcinfo_data; /* arguments etc */
217 : : } ScalarArrayOpExprHashTable;
218 : :
219 : : /* Define parameters for ScalarArrayOpExpr hash table code generation. */
220 : : #define SH_PREFIX saophash
221 : : #define SH_ELEMENT_TYPE ScalarArrayOpExprHashEntry
222 : : #define SH_KEY_TYPE Datum
223 : : #define SH_KEY key
224 : : #define SH_HASH_KEY(tb, key) saop_element_hash(tb, key)
225 : : #define SH_EQUAL(tb, a, b) saop_hash_element_match(tb, a, b)
226 : : #define SH_SCOPE static inline
227 : : #define SH_STORE_HASH
228 : : #define SH_GET_HASH(tb, a) a->hash
229 : : #define SH_DEFINE
230 : : #include "lib/simplehash.h"
231 : :
232 : : /*
233 : : * Prepare ExprState for interpreted execution.
234 : : */
235 : : void
2588 andres@anarazel.de 236 :CBC 1119584 : ExecReadyInterpretedExpr(ExprState *state)
237 : : {
238 : : /* Ensure one-time interpreter setup has been done */
239 : 1119584 : ExecInitInterpreter();
240 : :
241 : : /* Simple validity checks on expression */
242 [ - + ]: 1119584 : Assert(state->steps_len >= 1);
243 [ - + ]: 1119584 : Assert(state->steps[state->steps_len - 1].opcode == EEOP_DONE);
244 : :
245 : : /*
246 : : * Don't perform redundant initialization. This is unreachable in current
247 : : * cases, but might be hit if there's additional expression evaluation
248 : : * methods that rely on interpreted execution to work.
249 : : */
250 [ - + ]: 1119584 : if (state->flags & EEO_FLAG_INTERPRETER_INITIALIZED)
2588 andres@anarazel.de 251 :UBC 0 : return;
252 : :
253 : : /*
254 : : * First time through, check whether attribute matches Var. Might not be
255 : : * ok anymore, due to schema changes. We do that by setting up a callback
256 : : * that does checking on the first call, which then sets the evalfunc
257 : : * callback to the actual method of execution.
258 : : */
2298 andres@anarazel.de 259 :CBC 1119584 : state->evalfunc = ExecInterpExprStillValid;
260 : :
261 : : /* DIRECT_THREADED should not already be set */
2588 262 [ - + ]: 1119584 : Assert((state->flags & EEO_FLAG_DIRECT_THREADED) == 0);
263 : :
264 : : /*
265 : : * There shouldn't be any errors before the expression is fully
266 : : * initialized, and even if so, it'd lead to the expression being
267 : : * abandoned. So we can set the flag now and save some code.
268 : : */
269 : 1119584 : state->flags |= EEO_FLAG_INTERPRETER_INITIALIZED;
270 : :
271 : : /*
272 : : * Select fast-path evalfuncs for very simple expressions. "Starting up"
273 : : * the full interpreter is a measurable overhead for these, and these
274 : : * patterns occur often enough to be worth optimizing.
275 : : */
276 [ + + ]: 1119584 : if (state->steps_len == 3)
277 : : {
278 : 201577 : ExprEvalOp step0 = state->steps[0].opcode;
279 : 201577 : ExprEvalOp step1 = state->steps[1].opcode;
280 : :
281 [ + + + + ]: 201577 : if (step0 == EEOP_INNER_FETCHSOME &&
282 : : step1 == EEOP_INNER_VAR)
283 : : {
2298 284 : 3547 : state->evalfunc_private = (void *) ExecJustInnerVar;
2588 285 : 3547 : return;
286 : : }
287 [ + + + + ]: 198030 : else if (step0 == EEOP_OUTER_FETCHSOME &&
288 : : step1 == EEOP_OUTER_VAR)
289 : : {
2298 290 : 15675 : state->evalfunc_private = (void *) ExecJustOuterVar;
2588 291 : 15675 : return;
292 : : }
293 [ + + + + ]: 182355 : else if (step0 == EEOP_SCAN_FETCHSOME &&
294 : : step1 == EEOP_SCAN_VAR)
295 : : {
2298 296 : 11521 : state->evalfunc_private = (void *) ExecJustScanVar;
2588 297 : 11521 : return;
298 : : }
299 [ + + + - ]: 170834 : else if (step0 == EEOP_INNER_FETCHSOME &&
300 : : step1 == EEOP_ASSIGN_INNER_VAR)
301 : : {
2298 302 : 2855 : state->evalfunc_private = (void *) ExecJustAssignInnerVar;
2588 303 : 2855 : return;
304 : : }
305 [ + + + + ]: 167979 : else if (step0 == EEOP_OUTER_FETCHSOME &&
306 : : step1 == EEOP_ASSIGN_OUTER_VAR)
307 : : {
2298 308 : 3701 : state->evalfunc_private = (void *) ExecJustAssignOuterVar;
2588 309 : 3701 : return;
310 : : }
311 [ + + + - ]: 164278 : else if (step0 == EEOP_SCAN_FETCHSOME &&
312 : : step1 == EEOP_ASSIGN_SCAN_VAR)
313 : : {
2298 314 : 19838 : state->evalfunc_private = (void *) ExecJustAssignScanVar;
2588 315 : 19838 : return;
316 : : }
2388 tgl@sss.pgh.pa.us 317 [ + + + + ]: 144440 : else if (step0 == EEOP_CASE_TESTVAL &&
318 : 278 : step1 == EEOP_FUNCEXPR_STRICT &&
319 [ + + ]: 278 : state->steps[0].d.casetest.value)
320 : : {
2298 andres@anarazel.de 321 : 178 : state->evalfunc_private = (void *) ExecJustApplyFuncToCase;
2388 tgl@sss.pgh.pa.us 322 : 178 : return;
323 : : }
324 : : }
1658 andres@anarazel.de 325 [ + + ]: 918007 : else if (state->steps_len == 2)
326 : : {
327 : 434295 : ExprEvalOp step0 = state->steps[0].opcode;
328 : :
329 [ + + ]: 434295 : if (step0 == EEOP_CONST)
330 : : {
331 : 172444 : state->evalfunc_private = (void *) ExecJustConst;
332 : 172444 : return;
333 : : }
334 [ + + ]: 261851 : else if (step0 == EEOP_INNER_VAR)
335 : : {
336 : 230 : state->evalfunc_private = (void *) ExecJustInnerVarVirt;
337 : 230 : return;
338 : : }
339 [ + + ]: 261621 : else if (step0 == EEOP_OUTER_VAR)
340 : : {
341 : 26057 : state->evalfunc_private = (void *) ExecJustOuterVarVirt;
342 : 26057 : return;
343 : : }
344 [ + + ]: 235564 : else if (step0 == EEOP_SCAN_VAR)
345 : : {
346 : 36 : state->evalfunc_private = (void *) ExecJustScanVarVirt;
347 : 36 : return;
348 : : }
349 [ + + ]: 235528 : else if (step0 == EEOP_ASSIGN_INNER_VAR)
350 : : {
351 : 163 : state->evalfunc_private = (void *) ExecJustAssignInnerVarVirt;
352 : 163 : return;
353 : : }
354 [ + + ]: 235365 : else if (step0 == EEOP_ASSIGN_OUTER_VAR)
355 : : {
356 : 2189 : state->evalfunc_private = (void *) ExecJustAssignOuterVarVirt;
357 : 2189 : return;
358 : : }
359 [ + + ]: 233176 : else if (step0 == EEOP_ASSIGN_SCAN_VAR)
360 : : {
361 : 1205 : state->evalfunc_private = (void *) ExecJustAssignScanVarVirt;
362 : 1205 : return;
363 : : }
364 : : }
365 : :
366 : : #if defined(EEO_USE_COMPUTED_GOTO)
367 : :
368 : : /*
369 : : * In the direct-threaded implementation, replace each opcode with the
370 : : * address to jump to. (Use ExecEvalStepOp() to get back the opcode.)
371 : : */
1529 372 [ + + ]: 5623664 : for (int off = 0; off < state->steps_len; off++)
373 : : {
374 : 4763719 : ExprEvalStep *op = &state->steps[off];
375 : :
376 : 4763719 : op->opcode = EEO_OPCODE(op->opcode);
377 : : }
378 : :
379 : 859945 : state->flags |= EEO_FLAG_DIRECT_THREADED;
380 : : #endif /* EEO_USE_COMPUTED_GOTO */
381 : :
2298 382 : 859945 : state->evalfunc_private = (void *) ExecInterpExpr;
383 : : }
384 : :
385 : :
386 : : /*
387 : : * Evaluate expression identified by "state" in the execution context
388 : : * given by "econtext". *isnull is set to the is-null flag for the result,
389 : : * and the Datum value is the function result.
390 : : *
391 : : * As a special case, return the dispatch table's address if state is NULL.
392 : : * This is used by ExecInitInterpreter to set up the dispatch_table global.
393 : : * (Only applies when EEO_USE_COMPUTED_GOTO is defined.)
394 : : */
395 : : static Datum
2588 396 : 71335821 : ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
397 : : {
398 : : ExprEvalStep *op;
399 : : TupleTableSlot *resultslot;
400 : : TupleTableSlot *innerslot;
401 : : TupleTableSlot *outerslot;
402 : : TupleTableSlot *scanslot;
403 : :
404 : : /*
405 : : * This array has to be in the same order as enum ExprEvalOp.
406 : : */
407 : : #if defined(EEO_USE_COMPUTED_GOTO)
408 : : static const void *const dispatch_table[] = {
409 : : &&CASE_EEOP_DONE,
410 : : &&CASE_EEOP_INNER_FETCHSOME,
411 : : &&CASE_EEOP_OUTER_FETCHSOME,
412 : : &&CASE_EEOP_SCAN_FETCHSOME,
413 : : &&CASE_EEOP_INNER_VAR,
414 : : &&CASE_EEOP_OUTER_VAR,
415 : : &&CASE_EEOP_SCAN_VAR,
416 : : &&CASE_EEOP_INNER_SYSVAR,
417 : : &&CASE_EEOP_OUTER_SYSVAR,
418 : : &&CASE_EEOP_SCAN_SYSVAR,
419 : : &&CASE_EEOP_WHOLEROW,
420 : : &&CASE_EEOP_ASSIGN_INNER_VAR,
421 : : &&CASE_EEOP_ASSIGN_OUTER_VAR,
422 : : &&CASE_EEOP_ASSIGN_SCAN_VAR,
423 : : &&CASE_EEOP_ASSIGN_TMP,
424 : : &&CASE_EEOP_ASSIGN_TMP_MAKE_RO,
425 : : &&CASE_EEOP_CONST,
426 : : &&CASE_EEOP_FUNCEXPR,
427 : : &&CASE_EEOP_FUNCEXPR_STRICT,
428 : : &&CASE_EEOP_FUNCEXPR_FUSAGE,
429 : : &&CASE_EEOP_FUNCEXPR_STRICT_FUSAGE,
430 : : &&CASE_EEOP_BOOL_AND_STEP_FIRST,
431 : : &&CASE_EEOP_BOOL_AND_STEP,
432 : : &&CASE_EEOP_BOOL_AND_STEP_LAST,
433 : : &&CASE_EEOP_BOOL_OR_STEP_FIRST,
434 : : &&CASE_EEOP_BOOL_OR_STEP,
435 : : &&CASE_EEOP_BOOL_OR_STEP_LAST,
436 : : &&CASE_EEOP_BOOL_NOT_STEP,
437 : : &&CASE_EEOP_QUAL,
438 : : &&CASE_EEOP_JUMP,
439 : : &&CASE_EEOP_JUMP_IF_NULL,
440 : : &&CASE_EEOP_JUMP_IF_NOT_NULL,
441 : : &&CASE_EEOP_JUMP_IF_NOT_TRUE,
442 : : &&CASE_EEOP_NULLTEST_ISNULL,
443 : : &&CASE_EEOP_NULLTEST_ISNOTNULL,
444 : : &&CASE_EEOP_NULLTEST_ROWISNULL,
445 : : &&CASE_EEOP_NULLTEST_ROWISNOTNULL,
446 : : &&CASE_EEOP_BOOLTEST_IS_TRUE,
447 : : &&CASE_EEOP_BOOLTEST_IS_NOT_TRUE,
448 : : &&CASE_EEOP_BOOLTEST_IS_FALSE,
449 : : &&CASE_EEOP_BOOLTEST_IS_NOT_FALSE,
450 : : &&CASE_EEOP_PARAM_EXEC,
451 : : &&CASE_EEOP_PARAM_EXTERN,
452 : : &&CASE_EEOP_PARAM_CALLBACK,
453 : : &&CASE_EEOP_CASE_TESTVAL,
454 : : &&CASE_EEOP_MAKE_READONLY,
455 : : &&CASE_EEOP_IOCOERCE,
456 : : &&CASE_EEOP_IOCOERCE_SAFE,
457 : : &&CASE_EEOP_DISTINCT,
458 : : &&CASE_EEOP_NOT_DISTINCT,
459 : : &&CASE_EEOP_NULLIF,
460 : : &&CASE_EEOP_SQLVALUEFUNCTION,
461 : : &&CASE_EEOP_CURRENTOFEXPR,
462 : : &&CASE_EEOP_NEXTVALUEEXPR,
463 : : &&CASE_EEOP_ARRAYEXPR,
464 : : &&CASE_EEOP_ARRAYCOERCE,
465 : : &&CASE_EEOP_ROW,
466 : : &&CASE_EEOP_ROWCOMPARE_STEP,
467 : : &&CASE_EEOP_ROWCOMPARE_FINAL,
468 : : &&CASE_EEOP_MINMAX,
469 : : &&CASE_EEOP_FIELDSELECT,
470 : : &&CASE_EEOP_FIELDSTORE_DEFORM,
471 : : &&CASE_EEOP_FIELDSTORE_FORM,
472 : : &&CASE_EEOP_SBSREF_SUBSCRIPTS,
473 : : &&CASE_EEOP_SBSREF_OLD,
474 : : &&CASE_EEOP_SBSREF_ASSIGN,
475 : : &&CASE_EEOP_SBSREF_FETCH,
476 : : &&CASE_EEOP_DOMAIN_TESTVAL,
477 : : &&CASE_EEOP_DOMAIN_NOTNULL,
478 : : &&CASE_EEOP_DOMAIN_CHECK,
479 : : &&CASE_EEOP_CONVERT_ROWTYPE,
480 : : &&CASE_EEOP_SCALARARRAYOP,
481 : : &&CASE_EEOP_HASHED_SCALARARRAYOP,
482 : : &&CASE_EEOP_XMLEXPR,
483 : : &&CASE_EEOP_JSON_CONSTRUCTOR,
484 : : &&CASE_EEOP_IS_JSON,
485 : : &&CASE_EEOP_JSONEXPR_PATH,
486 : : &&CASE_EEOP_JSONEXPR_COERCION,
487 : : &&CASE_EEOP_JSONEXPR_COERCION_FINISH,
488 : : &&CASE_EEOP_AGGREF,
489 : : &&CASE_EEOP_GROUPING_FUNC,
490 : : &&CASE_EEOP_WINDOW_FUNC,
491 : : &&CASE_EEOP_MERGE_SUPPORT_FUNC,
492 : : &&CASE_EEOP_SUBPLAN,
493 : : &&CASE_EEOP_AGG_STRICT_DESERIALIZE,
494 : : &&CASE_EEOP_AGG_DESERIALIZE,
495 : : &&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
496 : : &&CASE_EEOP_AGG_STRICT_INPUT_CHECK_NULLS,
497 : : &&CASE_EEOP_AGG_PLAIN_PERGROUP_NULLCHECK,
498 : : &&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL,
499 : : &&CASE_EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL,
500 : : &&CASE_EEOP_AGG_PLAIN_TRANS_BYVAL,
501 : : &&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF,
502 : : &&CASE_EEOP_AGG_PLAIN_TRANS_STRICT_BYREF,
503 : : &&CASE_EEOP_AGG_PLAIN_TRANS_BYREF,
504 : : &&CASE_EEOP_AGG_PRESORTED_DISTINCT_SINGLE,
505 : : &&CASE_EEOP_AGG_PRESORTED_DISTINCT_MULTI,
506 : : &&CASE_EEOP_AGG_ORDERED_TRANS_DATUM,
507 : : &&CASE_EEOP_AGG_ORDERED_TRANS_TUPLE,
508 : : &&CASE_EEOP_LAST
509 : : };
510 : :
511 : : StaticAssertDecl(lengthof(dispatch_table) == EEOP_LAST + 1,
512 : : "dispatch_table out of whack with ExprEvalOp");
513 : :
514 [ + + ]: 71335821 : if (unlikely(state == NULL))
515 : 9927 : return PointerGetDatum(dispatch_table);
516 : : #else
517 : : Assert(state != NULL);
518 : : #endif /* EEO_USE_COMPUTED_GOTO */
519 : :
520 : : /* setup state */
521 : 71325894 : op = state->steps;
522 : 71325894 : resultslot = state->resultslot;
523 : 71325894 : innerslot = econtext->ecxt_innertuple;
524 : 71325894 : outerslot = econtext->ecxt_outertuple;
525 : 71325894 : scanslot = econtext->ecxt_scantuple;
526 : :
527 : : #if defined(EEO_USE_COMPUTED_GOTO)
528 : 71325894 : EEO_DISPATCH();
529 : : #endif
530 : :
531 : : EEO_SWITCH()
532 : : {
533 : 71317063 : EEO_CASE(EEOP_DONE)
534 : : {
535 : 71317063 : goto out;
536 : : }
537 : :
538 : 15198311 : EEO_CASE(EEOP_INNER_FETCHSOME)
539 : : {
1977 540 : 15198311 : CheckOpSlotCompatibility(op, innerslot);
541 : :
2588 542 : 15198311 : slot_getsomeattrs(innerslot, op->d.fetch.last_var);
543 : :
544 : 15198311 : EEO_NEXT();
545 : : }
546 : :
547 : 14816652 : EEO_CASE(EEOP_OUTER_FETCHSOME)
548 : : {
1977 549 : 14816652 : CheckOpSlotCompatibility(op, outerslot);
550 : :
2588 551 : 14816652 : slot_getsomeattrs(outerslot, op->d.fetch.last_var);
552 : :
553 : 14816652 : EEO_NEXT();
554 : : }
555 : :
556 : 32472045 : EEO_CASE(EEOP_SCAN_FETCHSOME)
557 : : {
1977 558 : 32472045 : CheckOpSlotCompatibility(op, scanslot);
559 : :
2588 560 : 32472045 : slot_getsomeattrs(scanslot, op->d.fetch.last_var);
561 : :
562 : 32472045 : EEO_NEXT();
563 : : }
564 : :
565 : 15700735 : EEO_CASE(EEOP_INNER_VAR)
566 : : {
567 : 15700735 : int attnum = op->d.var.attnum;
568 : :
569 : : /*
570 : : * Since we already extracted all referenced columns from the
571 : : * tuple with a FETCHSOME step, we can just grab the value
572 : : * directly out of the slot's decomposed-data arrays. But let's
573 : : * have an Assert to check that that did happen.
574 : : */
575 [ + - - + ]: 15700735 : Assert(attnum >= 0 && attnum < innerslot->tts_nvalid);
576 : 15700735 : *op->resvalue = innerslot->tts_values[attnum];
577 : 15700735 : *op->resnull = innerslot->tts_isnull[attnum];
578 : :
579 : 15700735 : EEO_NEXT();
580 : : }
581 : :
582 : 25284946 : EEO_CASE(EEOP_OUTER_VAR)
583 : : {
584 : 25284946 : int attnum = op->d.var.attnum;
585 : :
586 : : /* See EEOP_INNER_VAR comments */
587 : :
588 [ + - - + ]: 25284946 : Assert(attnum >= 0 && attnum < outerslot->tts_nvalid);
589 : 25284946 : *op->resvalue = outerslot->tts_values[attnum];
590 : 25284946 : *op->resnull = outerslot->tts_isnull[attnum];
591 : :
592 : 25284946 : EEO_NEXT();
593 : : }
594 : :
595 : 33599905 : EEO_CASE(EEOP_SCAN_VAR)
596 : : {
597 : 33599905 : int attnum = op->d.var.attnum;
598 : :
599 : : /* See EEOP_INNER_VAR comments */
600 : :
601 [ + - - + ]: 33599905 : Assert(attnum >= 0 && attnum < scanslot->tts_nvalid);
602 : 33599905 : *op->resvalue = scanslot->tts_values[attnum];
603 : 33599905 : *op->resnull = scanslot->tts_isnull[attnum];
604 : :
605 : 33599905 : EEO_NEXT();
606 : : }
607 : :
608 : 3 : EEO_CASE(EEOP_INNER_SYSVAR)
609 : : {
1985 610 : 3 : ExecEvalSysVar(state, op, econtext, innerslot);
2588 611 : 3 : EEO_NEXT();
612 : : }
613 : :
614 : 6 : EEO_CASE(EEOP_OUTER_SYSVAR)
615 : : {
1985 616 : 6 : ExecEvalSysVar(state, op, econtext, outerslot);
2588 617 : 6 : EEO_NEXT();
618 : : }
619 : :
620 : 3455127 : EEO_CASE(EEOP_SCAN_SYSVAR)
621 : : {
1985 622 : 3455127 : ExecEvalSysVar(state, op, econtext, scanslot);
2588 623 : 3455121 : EEO_NEXT();
624 : : }
625 : :
626 : 19541 : EEO_CASE(EEOP_WHOLEROW)
627 : : {
628 : : /* too complex for an inline implementation */
629 : 19541 : ExecEvalWholeRowVar(state, op, econtext);
630 : :
631 : 19541 : EEO_NEXT();
632 : : }
633 : :
634 : 3746913 : EEO_CASE(EEOP_ASSIGN_INNER_VAR)
635 : : {
636 : 3746913 : int resultnum = op->d.assign_var.resultnum;
637 : 3746913 : int attnum = op->d.assign_var.attnum;
638 : :
639 : : /*
640 : : * We do not need CheckVarSlotCompatibility here; that was taken
641 : : * care of at compilation time. But see EEOP_INNER_VAR comments.
642 : : */
643 [ + - - + ]: 3746913 : Assert(attnum >= 0 && attnum < innerslot->tts_nvalid);
1070 tgl@sss.pgh.pa.us 644 [ + - - + ]: 3746913 : Assert(resultnum >= 0 && resultnum < resultslot->tts_tupleDescriptor->natts);
2588 andres@anarazel.de 645 : 3746913 : resultslot->tts_values[resultnum] = innerslot->tts_values[attnum];
646 : 3746913 : resultslot->tts_isnull[resultnum] = innerslot->tts_isnull[attnum];
647 : :
648 : 3746913 : EEO_NEXT();
649 : : }
650 : :
651 : 12324110 : EEO_CASE(EEOP_ASSIGN_OUTER_VAR)
652 : : {
653 : 12324110 : int resultnum = op->d.assign_var.resultnum;
654 : 12324110 : int attnum = op->d.assign_var.attnum;
655 : :
656 : : /*
657 : : * We do not need CheckVarSlotCompatibility here; that was taken
658 : : * care of at compilation time. But see EEOP_INNER_VAR comments.
659 : : */
660 [ + - - + ]: 12324110 : Assert(attnum >= 0 && attnum < outerslot->tts_nvalid);
1070 tgl@sss.pgh.pa.us 661 [ + - - + ]: 12324110 : Assert(resultnum >= 0 && resultnum < resultslot->tts_tupleDescriptor->natts);
2588 andres@anarazel.de 662 : 12324110 : resultslot->tts_values[resultnum] = outerslot->tts_values[attnum];
663 : 12324110 : resultslot->tts_isnull[resultnum] = outerslot->tts_isnull[attnum];
664 : :
665 : 12324110 : EEO_NEXT();
666 : : }
667 : :
668 : 30102812 : EEO_CASE(EEOP_ASSIGN_SCAN_VAR)
669 : : {
670 : 30102812 : int resultnum = op->d.assign_var.resultnum;
671 : 30102812 : int attnum = op->d.assign_var.attnum;
672 : :
673 : : /*
674 : : * We do not need CheckVarSlotCompatibility here; that was taken
675 : : * care of at compilation time. But see EEOP_INNER_VAR comments.
676 : : */
677 [ + - - + ]: 30102812 : Assert(attnum >= 0 && attnum < scanslot->tts_nvalid);
1070 tgl@sss.pgh.pa.us 678 [ + - - + ]: 30102812 : Assert(resultnum >= 0 && resultnum < resultslot->tts_tupleDescriptor->natts);
2588 andres@anarazel.de 679 : 30102812 : resultslot->tts_values[resultnum] = scanslot->tts_values[attnum];
680 : 30102812 : resultslot->tts_isnull[resultnum] = scanslot->tts_isnull[attnum];
681 : :
682 : 30102812 : EEO_NEXT();
683 : : }
684 : :
685 : 15311404 : EEO_CASE(EEOP_ASSIGN_TMP)
686 : : {
687 : 15311404 : int resultnum = op->d.assign_tmp.resultnum;
688 : :
1070 tgl@sss.pgh.pa.us 689 [ + - - + ]: 15311404 : Assert(resultnum >= 0 && resultnum < resultslot->tts_tupleDescriptor->natts);
2588 andres@anarazel.de 690 : 15311404 : resultslot->tts_values[resultnum] = state->resvalue;
691 : 15311404 : resultslot->tts_isnull[resultnum] = state->resnull;
692 : :
693 : 15311404 : EEO_NEXT();
694 : : }
695 : :
696 : 5381522 : EEO_CASE(EEOP_ASSIGN_TMP_MAKE_RO)
697 : : {
698 : 5381522 : int resultnum = op->d.assign_tmp.resultnum;
699 : :
1070 tgl@sss.pgh.pa.us 700 [ + - - + ]: 5381522 : Assert(resultnum >= 0 && resultnum < resultslot->tts_tupleDescriptor->natts);
2588 andres@anarazel.de 701 : 5381522 : resultslot->tts_isnull[resultnum] = state->resnull;
702 [ + + ]: 5381522 : if (!resultslot->tts_isnull[resultnum])
703 : 3826513 : resultslot->tts_values[resultnum] =
704 : 3826513 : MakeExpandedObjectReadOnlyInternal(state->resvalue);
705 : : else
706 : 1555009 : resultslot->tts_values[resultnum] = state->resvalue;
707 : :
708 : 5381522 : EEO_NEXT();
709 : : }
710 : :
711 : 11244909 : EEO_CASE(EEOP_CONST)
712 : : {
713 : 11244909 : *op->resnull = op->d.constval.isnull;
714 : 11244909 : *op->resvalue = op->d.constval.value;
715 : :
716 : 11244909 : EEO_NEXT();
717 : : }
718 : :
719 : : /*
720 : : * Function-call implementations. Arguments have previously been
721 : : * evaluated directly into fcinfo->args.
722 : : *
723 : : * As both STRICT checks and function-usage are noticeable performance
724 : : * wise, and function calls are a very hot-path (they also back
725 : : * operators!), it's worth having so many separate opcodes.
726 : : *
727 : : * Note: the reason for using a temporary variable "d", here and in
728 : : * other places, is that some compilers think "*op->resvalue = f();"
729 : : * requires them to evaluate op->resvalue into a register before
730 : : * calling f(), just in case f() is able to modify op->resvalue
731 : : * somehow. The extra line of code can save a useless register spill
732 : : * and reload across the function call.
733 : : */
734 : 926371 : EEO_CASE(EEOP_FUNCEXPR)
735 : : {
736 : 926371 : FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
737 : : Datum d;
738 : :
739 : 926371 : fcinfo->isnull = false;
2389 tgl@sss.pgh.pa.us 740 : 926371 : d = op->d.func.fn_addr(fcinfo);
741 : 921419 : *op->resvalue = d;
2588 andres@anarazel.de 742 : 921419 : *op->resnull = fcinfo->isnull;
743 : :
744 : 921419 : EEO_NEXT();
745 : : }
746 : :
747 : 49428946 : EEO_CASE(EEOP_FUNCEXPR_STRICT)
748 : : {
749 : 49428946 : FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
1905 750 : 49428946 : NullableDatum *args = fcinfo->args;
1529 751 : 49428946 : int nargs = op->d.func.nargs;
752 : : Datum d;
753 : :
754 : : /* strict function, so check for NULL args */
755 [ + + ]: 141047633 : for (int argno = 0; argno < nargs; argno++)
756 : : {
1905 757 [ + + ]: 92163905 : if (args[argno].isnull)
758 : : {
2588 759 : 545218 : *op->resnull = true;
760 : 545218 : goto strictfail;
761 : : }
762 : : }
763 : 48883728 : fcinfo->isnull = false;
2389 tgl@sss.pgh.pa.us 764 : 48883728 : d = op->d.func.fn_addr(fcinfo);
765 : 48880493 : *op->resvalue = d;
2588 andres@anarazel.de 766 : 48880493 : *op->resnull = fcinfo->isnull;
767 : :
768 : 49425711 : strictfail:
769 : 49425711 : EEO_NEXT();
770 : : }
771 : :
772 : 104 : EEO_CASE(EEOP_FUNCEXPR_FUSAGE)
773 : : {
774 : : /* not common enough to inline */
2217 775 : 104 : ExecEvalFuncExprFusage(state, op, econtext);
776 : :
2588 777 : 104 : EEO_NEXT();
778 : : }
779 : :
780 : 3 : EEO_CASE(EEOP_FUNCEXPR_STRICT_FUSAGE)
781 : : {
782 : : /* not common enough to inline */
2217 783 : 3 : ExecEvalFuncExprStrictFusage(state, op, econtext);
784 : :
2588 785 : 3 : EEO_NEXT();
786 : : }
787 : :
788 : : /*
789 : : * If any of its clauses is FALSE, an AND's result is FALSE regardless
790 : : * of the states of the rest of the clauses, so we can stop evaluating
791 : : * and return FALSE immediately. If none are FALSE and one or more is
792 : : * NULL, we return NULL; otherwise we return TRUE. This makes sense
793 : : * when you interpret NULL as "don't know": perhaps one of the "don't
794 : : * knows" would have been FALSE if we'd known its value. Only when
795 : : * all the inputs are known to be TRUE can we state confidently that
796 : : * the AND's result is TRUE.
797 : : */
798 : 511614 : EEO_CASE(EEOP_BOOL_AND_STEP_FIRST)
799 : : {
800 : 511614 : *op->d.boolexpr.anynull = false;
801 : :
802 : : /*
803 : : * EEOP_BOOL_AND_STEP_FIRST resets anynull, otherwise it's the
804 : : * same as EEOP_BOOL_AND_STEP - so fall through to that.
805 : : */
806 : :
807 : : /* FALL THROUGH */
808 : : }
809 : :
810 : 581237 : EEO_CASE(EEOP_BOOL_AND_STEP)
811 : : {
812 [ + + ]: 581237 : if (*op->resnull)
813 : : {
814 : 608 : *op->d.boolexpr.anynull = true;
815 : : }
816 [ + + ]: 580629 : else if (!DatumGetBool(*op->resvalue))
817 : : {
818 : : /* result is already set to FALSE, need not change it */
819 : : /* bail out early */
820 : 385266 : EEO_JUMP(op->d.boolexpr.jumpdone);
821 : : }
822 : :
823 : 195971 : EEO_NEXT();
824 : : }
825 : :
826 : 126348 : EEO_CASE(EEOP_BOOL_AND_STEP_LAST)
827 : : {
828 [ + + ]: 126348 : if (*op->resnull)
829 : : {
830 : : /* result is already set to NULL, need not change it */
831 : : }
832 [ + + ]: 125909 : else if (!DatumGetBool(*op->resvalue))
833 : : {
834 : : /* result is already set to FALSE, need not change it */
835 : :
836 : : /*
837 : : * No point jumping early to jumpdone - would be same target
838 : : * (as this is the last argument to the AND expression),
839 : : * except more expensive.
840 : : */
841 : : }
842 [ + + ]: 91568 : else if (*op->d.boolexpr.anynull)
843 : : {
844 : 186 : *op->resvalue = (Datum) 0;
845 : 186 : *op->resnull = true;
846 : : }
847 : : else
848 : : {
849 : : /* result is already set to TRUE, need not change it */
850 : : }
851 : :
852 : 126348 : EEO_NEXT();
853 : : }
854 : :
855 : : /*
856 : : * If any of its clauses is TRUE, an OR's result is TRUE regardless of
857 : : * the states of the rest of the clauses, so we can stop evaluating
858 : : * and return TRUE immediately. If none are TRUE and one or more is
859 : : * NULL, we return NULL; otherwise we return FALSE. This makes sense
860 : : * when you interpret NULL as "don't know": perhaps one of the "don't
861 : : * knows" would have been TRUE if we'd known its value. Only when all
862 : : * the inputs are known to be FALSE can we state confidently that the
863 : : * OR's result is FALSE.
864 : : */
865 : 1832480 : EEO_CASE(EEOP_BOOL_OR_STEP_FIRST)
866 : : {
867 : 1832480 : *op->d.boolexpr.anynull = false;
868 : :
869 : : /*
870 : : * EEOP_BOOL_OR_STEP_FIRST resets anynull, otherwise it's the same
871 : : * as EEOP_BOOL_OR_STEP - so fall through to that.
872 : : */
873 : :
874 : : /* FALL THROUGH */
875 : : }
876 : :
877 : 3548082 : EEO_CASE(EEOP_BOOL_OR_STEP)
878 : : {
879 [ + + ]: 3548082 : if (*op->resnull)
880 : : {
881 : 79517 : *op->d.boolexpr.anynull = true;
882 : : }
883 [ + + ]: 3468565 : else if (DatumGetBool(*op->resvalue))
884 : : {
885 : : /* result is already set to TRUE, need not change it */
886 : : /* bail out early */
887 : 242367 : EEO_JUMP(op->d.boolexpr.jumpdone);
888 : : }
889 : :
890 : 3305715 : EEO_NEXT();
891 : : }
892 : :
893 : 1590113 : EEO_CASE(EEOP_BOOL_OR_STEP_LAST)
894 : : {
895 [ + + ]: 1590113 : if (*op->resnull)
896 : : {
897 : : /* result is already set to NULL, need not change it */
898 : : }
899 [ + + ]: 1543009 : else if (DatumGetBool(*op->resvalue))
900 : : {
901 : : /* result is already set to TRUE, need not change it */
902 : :
903 : : /*
904 : : * No point jumping to jumpdone - would be same target (as
905 : : * this is the last argument to the AND expression), except
906 : : * more expensive.
907 : : */
908 : : }
909 [ + + ]: 1517812 : else if (*op->d.boolexpr.anynull)
910 : : {
911 : 3247 : *op->resvalue = (Datum) 0;
912 : 3247 : *op->resnull = true;
913 : : }
914 : : else
915 : : {
916 : : /* result is already set to FALSE, need not change it */
917 : : }
918 : :
919 : 1590113 : EEO_NEXT();
920 : : }
921 : :
922 : 1000145 : EEO_CASE(EEOP_BOOL_NOT_STEP)
923 : : {
924 : : /*
925 : : * Evaluation of 'not' is simple... if expr is false, then return
926 : : * 'true' and vice versa. It's safe to do this even on a
927 : : * nominally null value, so we ignore resnull; that means that
928 : : * NULL in produces NULL out, which is what we want.
929 : : */
930 : 1000145 : *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
931 : :
932 : 1000145 : EEO_NEXT();
933 : : }
934 : :
935 : 37602492 : EEO_CASE(EEOP_QUAL)
936 : : {
937 : : /* simplified version of BOOL_AND_STEP for use by ExecQual() */
938 : :
939 : : /* If argument (also result) is false or null ... */
940 [ + + ]: 37602492 : if (*op->resnull ||
941 [ + + ]: 37169022 : !DatumGetBool(*op->resvalue))
942 : : {
943 : : /* ... bail out early, returning FALSE */
944 : 15891433 : *op->resnull = false;
945 : 15891433 : *op->resvalue = BoolGetDatum(false);
946 : 15891433 : EEO_JUMP(op->d.qualexpr.jumpdone);
947 : : }
948 : :
949 : : /*
950 : : * Otherwise, leave the TRUE value in place, in case this is the
951 : : * last qual. Then, TRUE is the correct answer.
952 : : */
953 : :
954 : 21711059 : EEO_NEXT();
955 : : }
956 : :
957 : 159334 : EEO_CASE(EEOP_JUMP)
958 : : {
959 : : /* Unconditionally jump to target step */
960 : 159334 : EEO_JUMP(op->d.jump.jumpdone);
961 : : }
962 : :
963 : 362457 : EEO_CASE(EEOP_JUMP_IF_NULL)
964 : : {
965 : : /* Transfer control if current result is null */
966 [ + + ]: 362457 : if (*op->resnull)
967 : 1595 : EEO_JUMP(op->d.jump.jumpdone);
968 : :
969 : 360862 : EEO_NEXT();
970 : : }
971 : :
972 : 169386 : EEO_CASE(EEOP_JUMP_IF_NOT_NULL)
973 : : {
974 : : /* Transfer control if current result is non-null */
975 [ + + ]: 169386 : if (!*op->resnull)
976 : 114902 : EEO_JUMP(op->d.jump.jumpdone);
977 : :
978 : 54484 : EEO_NEXT();
979 : : }
980 : :
981 : 942210 : EEO_CASE(EEOP_JUMP_IF_NOT_TRUE)
982 : : {
983 : : /* Transfer control if current result is null or false */
984 [ + + + + ]: 942210 : if (*op->resnull || !DatumGetBool(*op->resvalue))
985 : 724519 : EEO_JUMP(op->d.jump.jumpdone);
986 : :
987 : 217691 : EEO_NEXT();
988 : : }
989 : :
990 : 536359 : EEO_CASE(EEOP_NULLTEST_ISNULL)
991 : : {
992 : 536359 : *op->resvalue = BoolGetDatum(*op->resnull);
993 : 536359 : *op->resnull = false;
994 : :
995 : 536359 : EEO_NEXT();
996 : : }
997 : :
998 : 1529812 : EEO_CASE(EEOP_NULLTEST_ISNOTNULL)
999 : : {
1000 : 1529812 : *op->resvalue = BoolGetDatum(!*op->resnull);
1001 : 1529812 : *op->resnull = false;
1002 : :
1003 : 1529812 : EEO_NEXT();
1004 : : }
1005 : :
1006 : 348 : EEO_CASE(EEOP_NULLTEST_ROWISNULL)
1007 : : {
1008 : : /* out of line implementation: too large */
1009 : 348 : ExecEvalRowNull(state, op, econtext);
1010 : :
1011 : 348 : EEO_NEXT();
1012 : : }
1013 : :
1014 : 277 : EEO_CASE(EEOP_NULLTEST_ROWISNOTNULL)
1015 : : {
1016 : : /* out of line implementation: too large */
1017 : 277 : ExecEvalRowNotNull(state, op, econtext);
1018 : :
1019 : 277 : EEO_NEXT();
1020 : : }
1021 : :
1022 : : /* BooleanTest implementations for all booltesttypes */
1023 : :
1024 : 39385 : EEO_CASE(EEOP_BOOLTEST_IS_TRUE)
1025 : : {
1026 [ + + ]: 39385 : if (*op->resnull)
1027 : : {
1028 : 38991 : *op->resvalue = BoolGetDatum(false);
2576 tgl@sss.pgh.pa.us 1029 : 38991 : *op->resnull = false;
1030 : : }
1031 : : /* else, input value is the correct output as well */
1032 : :
2588 andres@anarazel.de 1033 : 39385 : EEO_NEXT();
1034 : : }
1035 : :
1036 : 525 : EEO_CASE(EEOP_BOOLTEST_IS_NOT_TRUE)
1037 : : {
1038 [ + + ]: 525 : if (*op->resnull)
1039 : : {
1040 : 87 : *op->resvalue = BoolGetDatum(true);
2576 tgl@sss.pgh.pa.us 1041 : 87 : *op->resnull = false;
1042 : : }
1043 : : else
2588 andres@anarazel.de 1044 : 438 : *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
1045 : :
1046 : 525 : EEO_NEXT();
1047 : : }
1048 : :
1049 : 413 : EEO_CASE(EEOP_BOOLTEST_IS_FALSE)
1050 : : {
1051 [ + + ]: 413 : if (*op->resnull)
1052 : : {
1053 : 81 : *op->resvalue = BoolGetDatum(false);
2576 tgl@sss.pgh.pa.us 1054 : 81 : *op->resnull = false;
1055 : : }
1056 : : else
2588 andres@anarazel.de 1057 : 332 : *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
1058 : :
1059 : 413 : EEO_NEXT();
1060 : : }
1061 : :
1062 : 270 : EEO_CASE(EEOP_BOOLTEST_IS_NOT_FALSE)
1063 : : {
1064 [ + + ]: 270 : if (*op->resnull)
1065 : : {
1066 : 21 : *op->resvalue = BoolGetDatum(true);
2576 tgl@sss.pgh.pa.us 1067 : 21 : *op->resnull = false;
1068 : : }
1069 : : /* else, input value is the correct output as well */
1070 : :
2588 andres@anarazel.de 1071 : 270 : EEO_NEXT();
1072 : : }
1073 : :
1074 : 2369682 : EEO_CASE(EEOP_PARAM_EXEC)
1075 : : {
1076 : : /* out of line implementation: too large */
1077 : 2369682 : ExecEvalParamExec(state, op, econtext);
1078 : :
1079 : 2369674 : EEO_NEXT();
1080 : : }
1081 : :
1082 : 254378 : EEO_CASE(EEOP_PARAM_EXTERN)
1083 : : {
1084 : : /* out of line implementation: too large */
1085 : 254378 : ExecEvalParamExtern(state, op, econtext);
1086 : 254378 : EEO_NEXT();
1087 : : }
1088 : :
2306 tgl@sss.pgh.pa.us 1089 : 189229 : EEO_CASE(EEOP_PARAM_CALLBACK)
1090 : : {
1091 : : /* allow an extension module to supply a PARAM_EXTERN value */
1092 : 189229 : op->d.cparam.paramfunc(state, op, econtext);
1093 : 189226 : EEO_NEXT();
1094 : : }
1095 : :
2588 andres@anarazel.de 1096 : 25429 : EEO_CASE(EEOP_CASE_TESTVAL)
1097 : : {
1098 : : /*
1099 : : * Normally upper parts of the expression tree have setup the
1100 : : * values to be returned here, but some parts of the system
1101 : : * currently misuse {caseValue,domainValue}_{datum,isNull} to set
1102 : : * run-time data. So if no values have been set-up, use
1103 : : * ExprContext's. This isn't pretty, but also not *that* ugly,
1104 : : * and this is unlikely to be performance sensitive enough to
1105 : : * worry about an extra branch.
1106 : : */
1107 [ + + ]: 25429 : if (op->d.casetest.value)
1108 : : {
1109 : 22273 : *op->resvalue = *op->d.casetest.value;
1110 : 22273 : *op->resnull = *op->d.casetest.isnull;
1111 : : }
1112 : : else
1113 : : {
1114 : 3156 : *op->resvalue = econtext->caseValue_datum;
1115 : 3156 : *op->resnull = econtext->caseValue_isNull;
1116 : : }
1117 : :
1118 : 25429 : EEO_NEXT();
1119 : : }
1120 : :
1121 : 35933 : EEO_CASE(EEOP_DOMAIN_TESTVAL)
1122 : : {
1123 : : /*
1124 : : * See EEOP_CASE_TESTVAL comment.
1125 : : */
1126 [ + + ]: 35933 : if (op->d.casetest.value)
1127 : : {
1128 : 6211 : *op->resvalue = *op->d.casetest.value;
1129 : 6211 : *op->resnull = *op->d.casetest.isnull;
1130 : : }
1131 : : else
1132 : : {
1133 : 29722 : *op->resvalue = econtext->domainValue_datum;
1134 : 29722 : *op->resnull = econtext->domainValue_isNull;
1135 : : }
1136 : :
1137 : 35933 : EEO_NEXT();
1138 : : }
1139 : :
1140 : 2268 : EEO_CASE(EEOP_MAKE_READONLY)
1141 : : {
1142 : : /*
1143 : : * Force a varlena value that might be read multiple times to R/O
1144 : : */
1145 [ + + ]: 2268 : if (!*op->d.make_readonly.isnull)
1146 : 2233 : *op->resvalue =
1147 : 2233 : MakeExpandedObjectReadOnlyInternal(*op->d.make_readonly.value);
1148 : 2268 : *op->resnull = *op->d.make_readonly.isnull;
1149 : :
1150 : 2268 : EEO_NEXT();
1151 : : }
1152 : :
1153 : 3045966 : EEO_CASE(EEOP_IOCOERCE)
1154 : : {
1155 : : /*
1156 : : * Evaluate a CoerceViaIO node. This can be quite a hot path, so
1157 : : * inline as much work as possible. The source value is in our
1158 : : * result variable.
1159 : : *
1160 : : * Also look at ExecEvalCoerceViaIOSafe() if you change anything
1161 : : * here.
1162 : : */
1163 : : char *str;
1164 : :
1165 : : /* call output function (similar to OutputFunctionCall) */
1166 [ + + ]: 3045966 : if (*op->resnull)
1167 : : {
1168 : : /* output functions are not called on nulls */
1169 : 30650 : str = NULL;
1170 : : }
1171 : : else
1172 : : {
1173 : : FunctionCallInfo fcinfo_out;
1174 : :
1175 : 3015316 : fcinfo_out = op->d.iocoerce.fcinfo_data_out;
1905 1176 : 3015316 : fcinfo_out->args[0].value = *op->resvalue;
1177 : 3015316 : fcinfo_out->args[0].isnull = false;
1178 : :
2588 1179 : 3015316 : fcinfo_out->isnull = false;
1180 : 3015316 : str = DatumGetCString(FunctionCallInvoke(fcinfo_out));
1181 : :
1182 : : /* OutputFunctionCall assumes result isn't null */
1183 [ - + ]: 3015316 : Assert(!fcinfo_out->isnull);
1184 : : }
1185 : :
1186 : : /* call input function (similar to InputFunctionCall) */
1187 [ + + + + ]: 3045966 : if (!op->d.iocoerce.finfo_in->fn_strict || str != NULL)
1188 : : {
1189 : : FunctionCallInfo fcinfo_in;
1190 : : Datum d;
1191 : :
195 amitlan@postgresql.o 1192 : 3015371 : fcinfo_in = op->d.iocoerce.fcinfo_data_in;
1193 : 3015371 : fcinfo_in->args[0].value = PointerGetDatum(str);
1194 : 3015371 : fcinfo_in->args[0].isnull = *op->resnull;
1195 : : /* second and third arguments are already set up */
1196 : :
1197 : 3015371 : fcinfo_in->isnull = false;
1198 : 3015371 : d = FunctionCallInvoke(fcinfo_in);
1199 : 3015345 : *op->resvalue = d;
1200 : :
1201 : : /* Should get null result if and only if str is NULL */
1202 [ + + ]: 3015345 : if (str == NULL)
1203 : : {
2588 andres@anarazel.de 1204 [ - + ]: 51 : Assert(*op->resnull);
195 amitlan@postgresql.o 1205 [ - + ]: 51 : Assert(fcinfo_in->isnull);
1206 : : }
1207 : : else
1208 : : {
2588 andres@anarazel.de 1209 [ - + ]: 3015294 : Assert(!*op->resnull);
195 amitlan@postgresql.o 1210 [ - + ]: 3015294 : Assert(!fcinfo_in->isnull);
1211 : : }
1212 : : }
1213 : :
2588 andres@anarazel.de 1214 : 3045940 : EEO_NEXT();
1215 : : }
1216 : :
81 amitlan@postgresql.o 1217 :GNC 417 : EEO_CASE(EEOP_IOCOERCE_SAFE)
1218 : : {
1219 : 417 : ExecEvalCoerceViaIOSafe(state, op);
1220 : 417 : EEO_NEXT();
1221 : : }
1222 : :
2588 andres@anarazel.de 1223 :CBC 671412 : EEO_CASE(EEOP_DISTINCT)
1224 : : {
1225 : : /*
1226 : : * IS DISTINCT FROM must evaluate arguments (already done into
1227 : : * fcinfo->args) to determine whether they are NULL; if either is
1228 : : * NULL then the result is determined. If neither is NULL, then
1229 : : * proceed to evaluate the comparison function, which is just the
1230 : : * type's standard equality operator. We need not care whether
1231 : : * that function is strict. Because the handling of nulls is
1232 : : * different, we can't just reuse EEOP_FUNCEXPR.
1233 : : */
1234 : 671412 : FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
1235 : :
1236 : : /* check function arguments for NULLness */
1905 1237 [ + + + + ]: 671412 : if (fcinfo->args[0].isnull && fcinfo->args[1].isnull)
1238 : : {
1239 : : /* Both NULL? Then is not distinct... */
2588 1240 : 501650 : *op->resvalue = BoolGetDatum(false);
1241 : 501650 : *op->resnull = false;
1242 : : }
1905 1243 [ + + + + ]: 169762 : else if (fcinfo->args[0].isnull || fcinfo->args[1].isnull)
1244 : : {
1245 : : /* Only one is NULL? Then is distinct... */
2588 1246 : 145 : *op->resvalue = BoolGetDatum(true);
1247 : 145 : *op->resnull = false;
1248 : : }
1249 : : else
1250 : : {
1251 : : /* Neither null, so apply the equality function */
1252 : : Datum eqresult;
1253 : :
1254 : 169617 : fcinfo->isnull = false;
2411 peter_e@gmx.net 1255 : 169617 : eqresult = op->d.func.fn_addr(fcinfo);
1256 : : /* Must invert result of "="; safe to do even if null */
2588 andres@anarazel.de 1257 : 169617 : *op->resvalue = BoolGetDatum(!DatumGetBool(eqresult));
1258 : 169617 : *op->resnull = fcinfo->isnull;
1259 : : }
1260 : :
1261 : 671412 : EEO_NEXT();
1262 : : }
1263 : :
1264 : : /* see EEOP_DISTINCT for comments, this is just inverted */
2250 1265 : 6338395 : EEO_CASE(EEOP_NOT_DISTINCT)
1266 : : {
1267 : 6338395 : FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
1268 : :
1905 1269 [ + + + + ]: 6338395 : if (fcinfo->args[0].isnull && fcinfo->args[1].isnull)
1270 : : {
2250 1271 : 61243 : *op->resvalue = BoolGetDatum(true);
1272 : 61243 : *op->resnull = false;
1273 : : }
1905 1274 [ + + + + ]: 6277152 : else if (fcinfo->args[0].isnull || fcinfo->args[1].isnull)
1275 : : {
2250 1276 : 212 : *op->resvalue = BoolGetDatum(false);
1277 : 212 : *op->resnull = false;
1278 : : }
1279 : : else
1280 : : {
1281 : : Datum eqresult;
1282 : :
1283 : 6276940 : fcinfo->isnull = false;
1284 : 6276940 : eqresult = op->d.func.fn_addr(fcinfo);
1285 : 6276940 : *op->resvalue = eqresult;
1286 : 6276940 : *op->resnull = fcinfo->isnull;
1287 : : }
1288 : :
1289 : 6338395 : EEO_NEXT();
1290 : : }
1291 : :
2588 1292 : 3355 : EEO_CASE(EEOP_NULLIF)
1293 : : {
1294 : : /*
1295 : : * The arguments are already evaluated into fcinfo->args.
1296 : : */
1297 : 3355 : FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
1298 : :
1299 : : /* if either argument is NULL they can't be equal */
1905 1300 [ + + + + ]: 3355 : if (!fcinfo->args[0].isnull && !fcinfo->args[1].isnull)
1301 : : {
1302 : : Datum result;
1303 : :
2588 1304 : 3328 : fcinfo->isnull = false;
2411 peter_e@gmx.net 1305 : 3328 : result = op->d.func.fn_addr(fcinfo);
1306 : :
1307 : : /* if the arguments are equal return null */
2588 andres@anarazel.de 1308 [ + - + + ]: 3328 : if (!fcinfo->isnull && DatumGetBool(result))
1309 : : {
1310 : 68 : *op->resvalue = (Datum) 0;
1311 : 68 : *op->resnull = true;
1312 : :
1313 : 68 : EEO_NEXT();
1314 : : }
1315 : : }
1316 : :
1317 : : /* Arguments aren't equal, so return the first one */
1905 1318 : 3287 : *op->resvalue = fcinfo->args[0].value;
1319 : 3287 : *op->resnull = fcinfo->args[0].isnull;
1320 : :
2588 1321 : 3287 : EEO_NEXT();
1322 : : }
1323 : :
333 michael@paquier.xyz 1324 : 8809 : EEO_CASE(EEOP_SQLVALUEFUNCTION)
1325 : : {
1326 : : /*
1327 : : * Doesn't seem worthwhile to have an inline implementation
1328 : : * efficiency-wise.
1329 : : */
1330 : 8809 : ExecEvalSQLValueFunction(state, op);
1331 : :
1332 : 8809 : EEO_NEXT();
1333 : : }
1334 : :
2588 andres@anarazel.de 1335 : 1 : EEO_CASE(EEOP_CURRENTOFEXPR)
1336 : : {
1337 : : /* error invocation uses space, and shouldn't ever occur */
1338 : 1 : ExecEvalCurrentOfExpr(state, op);
1339 : :
2588 andres@anarazel.de 1340 :UBC 0 : EEO_NEXT();
1341 : : }
1342 : :
2565 peter_e@gmx.net 1343 :CBC 498 : EEO_CASE(EEOP_NEXTVALUEEXPR)
1344 : : {
1345 : : /*
1346 : : * Doesn't seem worthwhile to have an inline implementation
1347 : : * efficiency-wise.
1348 : : */
2466 tgl@sss.pgh.pa.us 1349 : 498 : ExecEvalNextValueExpr(state, op);
1350 : :
2565 peter_e@gmx.net 1351 : 498 : EEO_NEXT();
1352 : : }
1353 : :
2588 andres@anarazel.de 1354 : 369231 : EEO_CASE(EEOP_ARRAYEXPR)
1355 : : {
1356 : : /* too complex for an inline implementation */
1357 : 369231 : ExecEvalArrayExpr(state, op);
1358 : :
1359 : 369231 : EEO_NEXT();
1360 : : }
1361 : :
1362 : 33171 : EEO_CASE(EEOP_ARRAYCOERCE)
1363 : : {
1364 : : /* too complex for an inline implementation */
2388 tgl@sss.pgh.pa.us 1365 : 33171 : ExecEvalArrayCoerce(state, op, econtext);
1366 : :
2588 andres@anarazel.de 1367 : 33155 : EEO_NEXT();
1368 : : }
1369 : :
1370 : 13550 : EEO_CASE(EEOP_ROW)
1371 : : {
1372 : : /* too complex for an inline implementation */
1373 : 13550 : ExecEvalRow(state, op);
1374 : :
1375 : 13550 : EEO_NEXT();
1376 : : }
1377 : :
1378 : 104346 : EEO_CASE(EEOP_ROWCOMPARE_STEP)
1379 : : {
1380 : 104346 : FunctionCallInfo fcinfo = op->d.rowcompare_step.fcinfo_data;
1381 : : Datum d;
1382 : :
1383 : : /* force NULL result if strict fn and NULL input */
1384 [ + - ]: 104346 : if (op->d.rowcompare_step.finfo->fn_strict &&
1905 1385 [ + - + + ]: 104346 : (fcinfo->args[0].isnull || fcinfo->args[1].isnull))
1386 : : {
2588 1387 : 9 : *op->resnull = true;
1388 : 9 : EEO_JUMP(op->d.rowcompare_step.jumpnull);
1389 : : }
1390 : :
1391 : : /* Apply comparison function */
1392 : 104337 : fcinfo->isnull = false;
2389 tgl@sss.pgh.pa.us 1393 : 104337 : d = op->d.rowcompare_step.fn_addr(fcinfo);
1394 : 104337 : *op->resvalue = d;
1395 : :
1396 : : /* force NULL result if NULL function result */
2588 andres@anarazel.de 1397 [ - + ]: 104337 : if (fcinfo->isnull)
1398 : : {
2588 andres@anarazel.de 1399 :UBC 0 : *op->resnull = true;
1400 : 0 : EEO_JUMP(op->d.rowcompare_step.jumpnull);
1401 : : }
2588 andres@anarazel.de 1402 :CBC 104337 : *op->resnull = false;
1403 : :
1404 : : /* If unequal, no need to compare remaining columns */
1405 [ + + ]: 104337 : if (DatumGetInt32(*op->resvalue) != 0)
1406 : : {
1407 : 47256 : EEO_JUMP(op->d.rowcompare_step.jumpdone);
1408 : : }
1409 : :
1410 : 57081 : EEO_NEXT();
1411 : : }
1412 : :
1413 : 47256 : EEO_CASE(EEOP_ROWCOMPARE_FINAL)
1414 : : {
1415 : 47256 : int32 cmpresult = DatumGetInt32(*op->resvalue);
1416 : 47256 : RowCompareType rctype = op->d.rowcompare_final.rctype;
1417 : :
1418 : 47256 : *op->resnull = false;
1419 [ + + + + : 47256 : switch (rctype)
- ]
1420 : : {
1421 : : /* EQ and NE cases aren't allowed here */
1422 : 17202 : case ROWCOMPARE_LT:
1423 : 17202 : *op->resvalue = BoolGetDatum(cmpresult < 0);
1424 : 17202 : break;
1425 : 30000 : case ROWCOMPARE_LE:
1426 : 30000 : *op->resvalue = BoolGetDatum(cmpresult <= 0);
1427 : 30000 : break;
1428 : 3 : case ROWCOMPARE_GE:
1429 : 3 : *op->resvalue = BoolGetDatum(cmpresult >= 0);
1430 : 3 : break;
1431 : 51 : case ROWCOMPARE_GT:
1432 : 51 : *op->resvalue = BoolGetDatum(cmpresult > 0);
1433 : 51 : break;
2588 andres@anarazel.de 1434 :UBC 0 : default:
1435 : 0 : Assert(false);
1436 : : break;
1437 : : }
1438 : :
2588 andres@anarazel.de 1439 :CBC 47256 : EEO_NEXT();
1440 : : }
1441 : :
1442 : 11638 : EEO_CASE(EEOP_MINMAX)
1443 : : {
1444 : : /* too complex for an inline implementation */
1445 : 11638 : ExecEvalMinMax(state, op);
1446 : :
1447 : 11638 : EEO_NEXT();
1448 : : }
1449 : :
1450 : 56513 : EEO_CASE(EEOP_FIELDSELECT)
1451 : : {
1452 : : /* too complex for an inline implementation */
1453 : 56513 : ExecEvalFieldSelect(state, op, econtext);
1454 : :
1455 : 56513 : EEO_NEXT();
1456 : : }
1457 : :
1458 : 260 : EEO_CASE(EEOP_FIELDSTORE_DEFORM)
1459 : : {
1460 : : /* too complex for an inline implementation */
1461 : 260 : ExecEvalFieldStoreDeForm(state, op, econtext);
1462 : :
1463 : 260 : EEO_NEXT();
1464 : : }
1465 : :
1466 : 260 : EEO_CASE(EEOP_FIELDSTORE_FORM)
1467 : : {
1468 : : /* too complex for an inline implementation */
1469 : 260 : ExecEvalFieldStoreForm(state, op, econtext);
1470 : :
1471 : 260 : EEO_NEXT();
1472 : : }
1473 : :
1222 tgl@sss.pgh.pa.us 1474 : 356729 : EEO_CASE(EEOP_SBSREF_SUBSCRIPTS)
1475 : : {
1476 : : /* Precheck SubscriptingRef subscript(s) */
1477 [ + + ]: 356729 : if (op->d.sbsref_subscript.subscriptfunc(state, op, econtext))
1478 : : {
2588 andres@anarazel.de 1479 : 356702 : EEO_NEXT();
1480 : : }
1481 : : else
1482 : : {
1483 : : /* Subscript is null, short-circuit SubscriptingRef to NULL */
1899 alvherre@alvh.no-ip. 1484 : 15 : EEO_JUMP(op->d.sbsref_subscript.jumpdone);
1485 : : }
1486 : : }
1487 : :
1488 : 138 : EEO_CASE(EEOP_SBSREF_OLD)
1222 tgl@sss.pgh.pa.us 1489 : 945 : EEO_CASE(EEOP_SBSREF_ASSIGN)
1490 : 356838 : EEO_CASE(EEOP_SBSREF_FETCH)
1491 : : {
1492 : : /* Perform a SubscriptingRef fetch or assignment */
1493 : 356838 : op->d.sbsref.subscriptfunc(state, op, econtext);
1494 : :
2588 andres@anarazel.de 1495 : 356781 : EEO_NEXT();
1496 : : }
1497 : :
1498 : 6006 : EEO_CASE(EEOP_CONVERT_ROWTYPE)
1499 : : {
1500 : : /* too complex for an inline implementation */
1501 : 6006 : ExecEvalConvertRowtype(state, op, econtext);
1502 : :
1503 : 6006 : EEO_NEXT();
1504 : : }
1505 : :
1506 : 2018391 : EEO_CASE(EEOP_SCALARARRAYOP)
1507 : : {
1508 : : /* too complex for an inline implementation */
1509 : 2018391 : ExecEvalScalarArrayOp(state, op);
1510 : :
1511 : 2018391 : EEO_NEXT();
1512 : : }
1513 : :
1102 drowley@postgresql.o 1514 : 2305 : EEO_CASE(EEOP_HASHED_SCALARARRAYOP)
1515 : : {
1516 : : /* too complex for an inline implementation */
1517 : 2305 : ExecEvalHashedScalarArrayOp(state, op, econtext);
1518 : :
1519 : 2305 : EEO_NEXT();
1520 : : }
1521 : :
2588 andres@anarazel.de 1522 : 198 : EEO_CASE(EEOP_DOMAIN_NOTNULL)
1523 : : {
1524 : : /* too complex for an inline implementation */
1525 : 198 : ExecEvalConstraintNotNull(state, op);
1526 : :
1527 : 142 : EEO_NEXT();
1528 : : }
1529 : :
1530 : 5939 : EEO_CASE(EEOP_DOMAIN_CHECK)
1531 : : {
1532 : : /* too complex for an inline implementation */
1533 : 5939 : ExecEvalConstraintCheck(state, op);
1534 : :
1535 : 5728 : EEO_NEXT();
1536 : : }
1537 : :
1538 : 22041 : EEO_CASE(EEOP_XMLEXPR)
1539 : : {
1540 : : /* too complex for an inline implementation */
1541 : 22041 : ExecEvalXmlExpr(state, op);
1542 : :
1543 : 21990 : EEO_NEXT();
1544 : : }
1545 : :
382 alvherre@alvh.no-ip. 1546 : 291 : EEO_CASE(EEOP_JSON_CONSTRUCTOR)
1547 : : {
1548 : : /* too complex for an inline implementation */
1549 : 291 : ExecEvalJsonConstructor(state, op, econtext);
1550 : 256 : EEO_NEXT();
1551 : : }
1552 : :
380 1553 : 1370 : EEO_CASE(EEOP_IS_JSON)
1554 : : {
1555 : : /* too complex for an inline implementation */
1556 : 1370 : ExecEvalJsonIsPredicate(state, op);
1557 : :
1558 : 1370 : EEO_NEXT();
1559 : : }
1560 : :
24 amitlan@postgresql.o 1561 :GNC 2477 : EEO_CASE(EEOP_JSONEXPR_PATH)
1562 : : {
1563 : : /* too complex for an inline implementation */
1564 : 2477 : EEO_JUMP(ExecEvalJsonExprPath(state, op, econtext));
1565 : : }
1566 : :
1567 : 1050 : EEO_CASE(EEOP_JSONEXPR_COERCION)
1568 : : {
1569 : : /* too complex for an inline implementation */
1570 : 1050 : ExecEvalJsonCoercion(state, op, econtext);
1571 : :
1572 : 1008 : EEO_NEXT();
1573 : : }
1574 : :
1575 : 1113 : EEO_CASE(EEOP_JSONEXPR_COERCION_FINISH)
1576 : : {
1577 : : /* too complex for an inline implementation */
1578 : 1113 : ExecEvalJsonCoercionFinish(state, op);
1579 : :
1580 : 1113 : EEO_NEXT();
1581 : : }
1582 : :
2588 andres@anarazel.de 1583 :CBC 451037 : EEO_CASE(EEOP_AGGREF)
1584 : : {
1585 : : /*
1586 : : * Returns a Datum whose value is the precomputed aggregate value
1587 : : * found in the given expression context.
1588 : : */
1237 heikki.linnakangas@i 1589 : 451037 : int aggno = op->d.aggref.aggno;
1590 : :
2588 andres@anarazel.de 1591 [ - + ]: 451037 : Assert(econtext->ecxt_aggvalues != NULL);
1592 : :
1237 heikki.linnakangas@i 1593 : 451037 : *op->resvalue = econtext->ecxt_aggvalues[aggno];
1594 : 451037 : *op->resnull = econtext->ecxt_aggnulls[aggno];
1595 : :
2588 andres@anarazel.de 1596 : 451037 : EEO_NEXT();
1597 : : }
1598 : :
1599 : 844 : EEO_CASE(EEOP_GROUPING_FUNC)
1600 : : {
1601 : : /* too complex/uncommon for an inline implementation */
1602 : 844 : ExecEvalGroupingFunc(state, op);
1603 : :
1604 : 844 : EEO_NEXT();
1605 : : }
1606 : :
1607 : 503351 : EEO_CASE(EEOP_WINDOW_FUNC)
1608 : : {
1609 : : /*
1610 : : * Like Aggref, just return a precomputed value from the econtext.
1611 : : */
1612 : 503351 : WindowFuncExprState *wfunc = op->d.window_func.wfstate;
1613 : :
1614 [ - + ]: 503351 : Assert(econtext->ecxt_aggvalues != NULL);
1615 : :
1616 : 503351 : *op->resvalue = econtext->ecxt_aggvalues[wfunc->wfuncno];
1617 : 503351 : *op->resnull = econtext->ecxt_aggnulls[wfunc->wfuncno];
1618 : :
1619 : 503351 : EEO_NEXT();
1620 : : }
1621 : :
28 dean.a.rasheed@gmail 1622 :GNC 205 : EEO_CASE(EEOP_MERGE_SUPPORT_FUNC)
1623 : : {
1624 : : /* too complex/uncommon for an inline implementation */
1625 : 205 : ExecEvalMergeSupportFunc(state, op, econtext);
1626 : :
1627 : 205 : EEO_NEXT();
1628 : : }
1629 : :
2588 andres@anarazel.de 1630 :CBC 1413085 : EEO_CASE(EEOP_SUBPLAN)
1631 : : {
1632 : : /* too complex for an inline implementation */
1633 : 1413085 : ExecEvalSubPlan(state, op, econtext);
1634 : :
1635 : 1413082 : EEO_NEXT();
1636 : : }
1637 : :
1638 : : /* evaluate a strict aggregate deserialization function */
2287 1639 : 333 : EEO_CASE(EEOP_AGG_STRICT_DESERIALIZE)
1640 : : {
1641 : : /* Don't call a strict deserialization function with NULL input */
1905 1642 [ + + ]: 333 : if (op->d.agg_deserialize.fcinfo_data->args[0].isnull)
2287 1643 : 24 : EEO_JUMP(op->d.agg_deserialize.jumpnull);
1644 : :
1645 : : /* fallthrough */
1646 : : }
1647 : :
1648 : : /* evaluate aggregate deserialization function (non-strict portion) */
1649 : 309 : EEO_CASE(EEOP_AGG_DESERIALIZE)
1650 : : {
1651 : 309 : FunctionCallInfo fcinfo = op->d.agg_deserialize.fcinfo_data;
1529 1652 : 309 : AggState *aggstate = castNode(AggState, state->parent);
1653 : : MemoryContext oldContext;
1654 : :
1655 : : /*
1656 : : * We run the deserialization functions in per-input-tuple memory
1657 : : * context.
1658 : : */
2287 1659 : 309 : oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory);
1660 : 309 : fcinfo->isnull = false;
1661 : 309 : *op->resvalue = FunctionCallInvoke(fcinfo);
1662 : 309 : *op->resnull = fcinfo->isnull;
1663 : 309 : MemoryContextSwitchTo(oldContext);
1664 : :
1665 : 309 : EEO_NEXT();
1666 : : }
1667 : :
1668 : : /*
1669 : : * Check that a strict aggregate transition / combination function's
1670 : : * input is not NULL.
1671 : : */
1672 : :
1529 1673 : 2537572 : EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK_ARGS)
1674 : : {
1675 : 2537572 : NullableDatum *args = op->d.agg_strict_input_check.args;
2287 1676 : 2537572 : int nargs = op->d.agg_strict_input_check.nargs;
1677 : :
1529 1678 [ + + ]: 5116326 : for (int argno = 0; argno < nargs; argno++)
1679 : : {
1680 [ + + ]: 2657926 : if (args[argno].isnull)
2287 1681 : 79172 : EEO_JUMP(op->d.agg_strict_input_check.jumpnull);
1682 : : }
1683 : 2458400 : EEO_NEXT();
1684 : : }
1685 : :
1529 1686 : 188352 : EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK_NULLS)
1687 : : {
1688 : 188352 : bool *nulls = op->d.agg_strict_input_check.nulls;
1905 1689 : 188352 : int nargs = op->d.agg_strict_input_check.nargs;
1690 : :
1529 1691 [ + + ]: 354204 : for (int argno = 0; argno < nargs; argno++)
1692 : : {
1693 [ + + ]: 188352 : if (nulls[argno])
1905 1694 : 22500 : EEO_JUMP(op->d.agg_strict_input_check.jumpnull);
1695 : : }
1696 : 165852 : EEO_NEXT();
1697 : : }
1698 : :
1699 : : /*
1700 : : * Check for a NULL pointer to the per-group states.
1701 : : */
1702 : :
1502 jdavis@postgresql.or 1703 : 1306104 : EEO_CASE(EEOP_AGG_PLAIN_PERGROUP_NULLCHECK)
1704 : : {
1705 : 1306104 : AggState *aggstate = castNode(AggState, state->parent);
1431 tgl@sss.pgh.pa.us 1706 : 1306104 : AggStatePerGroup pergroup_allaggs =
331 1707 : 1306104 : aggstate->all_pergroups[op->d.agg_plain_pergroup_nullcheck.setoff];
1708 : :
1502 jdavis@postgresql.or 1709 [ + + ]: 1306104 : if (pergroup_allaggs == NULL)
1710 : 1022076 : EEO_JUMP(op->d.agg_plain_pergroup_nullcheck.jumpnull);
1711 : :
1712 : 284028 : EEO_NEXT();
1713 : : }
1714 : :
1715 : : /*
1716 : : * Different types of aggregate transition functions are implemented
1717 : : * as different types of steps, to avoid incurring unnecessary
1718 : : * overhead. There's a step type for each valid combination of having
1719 : : * a by value / by reference transition type, [not] needing to the
1720 : : * initialize the transition value for the first row in a group from
1721 : : * input, and [not] strict transition function.
1722 : : *
1723 : : * Could optimize further by splitting off by-reference for
1724 : : * fixed-length types, but currently that doesn't seem worth it.
1725 : : */
1726 : :
1511 andres@anarazel.de 1727 : 324428 : EEO_CASE(EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL)
1728 : : {
1529 1729 : 324428 : AggState *aggstate = castNode(AggState, state->parent);
1511 1730 : 324428 : AggStatePerTrans pertrans = op->d.agg_trans.pertrans;
1431 tgl@sss.pgh.pa.us 1731 : 324428 : AggStatePerGroup pergroup =
331 1732 : 324428 : &aggstate->all_pergroups[op->d.agg_trans.setoff][op->d.agg_trans.transno];
1733 : :
1511 andres@anarazel.de 1734 [ - + ]: 324428 : Assert(pertrans->transtypeByVal);
1735 : :
2287 1736 [ + + ]: 324428 : if (pergroup->noTransValue)
1737 : : {
1738 : : /* If transValue has not yet been initialized, do so now. */
1511 1739 : 4380 : ExecAggInitGroup(aggstate, pertrans, pergroup,
1740 : : op->d.agg_trans.aggcontext);
1741 : : /* copied trans value from input, done this round */
1742 : : }
1743 [ + - ]: 320048 : else if (likely(!pergroup->transValueIsNull))
1744 : : {
1745 : : /* invoke transition function, unless prevented by strictness */
1746 : 320048 : ExecAggPlainTransByVal(aggstate, pertrans, pergroup,
1747 : : op->d.agg_trans.aggcontext,
1748 : : op->d.agg_trans.setno);
1749 : : }
1750 : :
2287 1751 : 324428 : EEO_NEXT();
1752 : : }
1753 : :
1754 : : /* see comments above EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL */
1511 1755 : 9361408 : EEO_CASE(EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL)
1756 : : {
1529 1757 : 9361408 : AggState *aggstate = castNode(AggState, state->parent);
1511 1758 : 9361408 : AggStatePerTrans pertrans = op->d.agg_trans.pertrans;
1431 tgl@sss.pgh.pa.us 1759 : 9361408 : AggStatePerGroup pergroup =
331 1760 : 9361408 : &aggstate->all_pergroups[op->d.agg_trans.setoff][op->d.agg_trans.transno];
1761 : :
1511 andres@anarazel.de 1762 [ - + ]: 9361408 : Assert(pertrans->transtypeByVal);
1763 : :
1764 [ + + ]: 9361408 : if (likely(!pergroup->transValueIsNull))
1765 : 9331399 : ExecAggPlainTransByVal(aggstate, pertrans, pergroup,
1766 : : op->d.agg_trans.aggcontext,
1767 : : op->d.agg_trans.setno);
1768 : :
2287 1769 : 9361408 : EEO_NEXT();
1770 : : }
1771 : :
1772 : : /* see comments above EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL */
1773 : 5075241 : EEO_CASE(EEOP_AGG_PLAIN_TRANS_BYVAL)
1774 : : {
1529 1775 : 5075241 : AggState *aggstate = castNode(AggState, state->parent);
1511 1776 : 5075241 : AggStatePerTrans pertrans = op->d.agg_trans.pertrans;
1431 tgl@sss.pgh.pa.us 1777 : 5075241 : AggStatePerGroup pergroup =
331 1778 : 5075241 : &aggstate->all_pergroups[op->d.agg_trans.setoff][op->d.agg_trans.transno];
1779 : :
2287 andres@anarazel.de 1780 [ - + ]: 5075241 : Assert(pertrans->transtypeByVal);
1781 : :
1511 1782 : 5075241 : ExecAggPlainTransByVal(aggstate, pertrans, pergroup,
1783 : : op->d.agg_trans.aggcontext,
1784 : : op->d.agg_trans.setno);
1785 : :
2287 1786 : 5075211 : EEO_NEXT();
1787 : : }
1788 : :
1789 : : /* see comments above EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL */
1511 1790 : 193744 : EEO_CASE(EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF)
1791 : : {
1529 1792 : 193744 : AggState *aggstate = castNode(AggState, state->parent);
1511 1793 : 193744 : AggStatePerTrans pertrans = op->d.agg_trans.pertrans;
1431 tgl@sss.pgh.pa.us 1794 : 193744 : AggStatePerGroup pergroup =
331 1795 : 193744 : &aggstate->all_pergroups[op->d.agg_trans.setoff][op->d.agg_trans.transno];
1796 : :
2287 andres@anarazel.de 1797 [ - + ]: 193744 : Assert(!pertrans->transtypeByVal);
1798 : :
1511 1799 [ + + ]: 193744 : if (pergroup->noTransValue)
1800 : 25681 : ExecAggInitGroup(aggstate, pertrans, pergroup,
1801 : : op->d.agg_trans.aggcontext);
1802 [ + - ]: 168063 : else if (likely(!pergroup->transValueIsNull))
1803 : 168063 : ExecAggPlainTransByRef(aggstate, pertrans, pergroup,
1804 : : op->d.agg_trans.aggcontext,
1805 : : op->d.agg_trans.setno);
1806 : :
1807 : 193741 : EEO_NEXT();
1808 : : }
1809 : :
1810 : : /* see comments above EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL */
1811 : 1297221 : EEO_CASE(EEOP_AGG_PLAIN_TRANS_STRICT_BYREF)
1812 : : {
1813 : 1297221 : AggState *aggstate = castNode(AggState, state->parent);
1814 : 1297221 : AggStatePerTrans pertrans = op->d.agg_trans.pertrans;
1431 tgl@sss.pgh.pa.us 1815 : 1297221 : AggStatePerGroup pergroup =
331 1816 : 1297221 : &aggstate->all_pergroups[op->d.agg_trans.setoff][op->d.agg_trans.transno];
1817 : :
1511 andres@anarazel.de 1818 [ - + ]: 1297221 : Assert(!pertrans->transtypeByVal);
1819 : :
1820 [ + - ]: 1297221 : if (likely(!pergroup->transValueIsNull))
1821 : 1297221 : ExecAggPlainTransByRef(aggstate, pertrans, pergroup,
1822 : : op->d.agg_trans.aggcontext,
1823 : : op->d.agg_trans.setno);
1824 : 1297221 : EEO_NEXT();
1825 : : }
1826 : :
1827 : : /* see comments above EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL */
1828 : 11779 : EEO_CASE(EEOP_AGG_PLAIN_TRANS_BYREF)
1829 : : {
1830 : 11779 : AggState *aggstate = castNode(AggState, state->parent);
1831 : 11779 : AggStatePerTrans pertrans = op->d.agg_trans.pertrans;
1431 tgl@sss.pgh.pa.us 1832 : 11779 : AggStatePerGroup pergroup =
331 1833 : 11779 : &aggstate->all_pergroups[op->d.agg_trans.setoff][op->d.agg_trans.transno];
1834 : :
1511 andres@anarazel.de 1835 [ - + ]: 11779 : Assert(!pertrans->transtypeByVal);
1836 : :
1837 : 11779 : ExecAggPlainTransByRef(aggstate, pertrans, pergroup,
1838 : : op->d.agg_trans.aggcontext,
1839 : : op->d.agg_trans.setno);
1840 : :
2287 1841 : 11779 : EEO_NEXT();
1842 : : }
1843 : :
621 drowley@postgresql.o 1844 : 182892 : EEO_CASE(EEOP_AGG_PRESORTED_DISTINCT_SINGLE)
1845 : : {
1846 : 182892 : AggStatePerTrans pertrans = op->d.agg_presorted_distinctcheck.pertrans;
1847 : 182892 : AggState *aggstate = castNode(AggState, state->parent);
1848 : :
1849 [ + + ]: 182892 : if (ExecEvalPreOrderedDistinctSingle(aggstate, pertrans))
1850 : 50954 : EEO_NEXT();
1851 : : else
1852 : 131938 : EEO_JUMP(op->d.agg_presorted_distinctcheck.jumpdistinct);
1853 : : }
1854 : :
1855 : 360 : EEO_CASE(EEOP_AGG_PRESORTED_DISTINCT_MULTI)
1856 : : {
1857 : 360 : AggState *aggstate = castNode(AggState, state->parent);
1858 : 360 : AggStatePerTrans pertrans = op->d.agg_presorted_distinctcheck.pertrans;
1859 : :
1860 [ + + ]: 360 : if (ExecEvalPreOrderedDistinctMulti(aggstate, pertrans))
1861 : 156 : EEO_NEXT();
1862 : : else
1863 : 204 : EEO_JUMP(op->d.agg_presorted_distinctcheck.jumpdistinct);
1864 : : }
1865 : :
1866 : : /* process single-column ordered aggregate datum */
2287 andres@anarazel.de 1867 : 422192 : EEO_CASE(EEOP_AGG_ORDERED_TRANS_DATUM)
1868 : : {
1869 : : /* too complex for an inline implementation */
1870 : 422192 : ExecEvalAggOrderedTransDatum(state, op, econtext);
1871 : :
1872 : 422192 : EEO_NEXT();
1873 : : }
1874 : :
1875 : : /* process multi-column ordered aggregate tuple */
1876 : 90 : EEO_CASE(EEOP_AGG_ORDERED_TRANS_TUPLE)
1877 : : {
1878 : : /* too complex for an inline implementation */
1879 : 90 : ExecEvalAggOrderedTransTuple(state, op, econtext);
1880 : :
773 andrew@dunslane.net 1881 : 90 : EEO_NEXT();
1882 : : }
1883 : :
2588 andres@anarazel.de 1884 :UBC 0 : EEO_CASE(EEOP_LAST)
1885 : : {
1886 : : /* unreachable */
1887 : 0 : Assert(false);
1888 : : goto out;
1889 : : }
1890 : : }
1891 : :
2588 andres@anarazel.de 1892 :CBC 71317063 : out:
1893 : 71317063 : *isnull = state->resnull;
1894 : 71317063 : return state->resvalue;
1895 : : }
1896 : :
1897 : : /*
1898 : : * Expression evaluation callback that performs extra checks before executing
1899 : : * the expression. Declared extern so other methods of execution can use it
1900 : : * too.
1901 : : */
1902 : : Datum
2298 1903 : 876810 : ExecInterpExprStillValid(ExprState *state, ExprContext *econtext, bool *isNull)
1904 : : {
1905 : : /*
1906 : : * First time through, check whether attribute matches Var. Might not be
1907 : : * ok anymore, due to schema changes.
1908 : : */
1909 : 876810 : CheckExprStillValid(state, econtext);
1910 : :
1911 : : /* skip the check during further executions */
1912 : 876798 : state->evalfunc = (ExprStateEvalFunc) state->evalfunc_private;
1913 : :
1914 : : /* and actually execute */
1915 : 876798 : return state->evalfunc(state, econtext, isNull);
1916 : : }
1917 : :
1918 : : /*
1919 : : * Check that an expression is still valid in the face of potential schema
1920 : : * changes since the plan has been created.
1921 : : */
1922 : : void
1923 : 876810 : CheckExprStillValid(ExprState *state, ExprContext *econtext)
1924 : : {
1925 : : TupleTableSlot *innerslot;
1926 : : TupleTableSlot *outerslot;
1927 : : TupleTableSlot *scanslot;
1928 : :
1929 : 876810 : innerslot = econtext->ecxt_innertuple;
1930 : 876810 : outerslot = econtext->ecxt_outertuple;
1931 : 876810 : scanslot = econtext->ecxt_scantuple;
1932 : :
1529 1933 [ + + ]: 4789897 : for (int i = 0; i < state->steps_len; i++)
1934 : : {
2287 1935 : 3913099 : ExprEvalStep *op = &state->steps[i];
1936 : :
2298 1937 [ + + + + ]: 3913099 : switch (ExecEvalStepOp(state, op))
1938 : : {
1939 : 36935 : case EEOP_INNER_VAR:
1940 : : {
1941 : 36935 : int attnum = op->d.var.attnum;
1942 : :
1943 : 36935 : CheckVarSlotCompatibility(innerslot, attnum + 1, op->d.var.vartype);
1944 : 36935 : break;
1945 : : }
1946 : :
1947 : 96474 : case EEOP_OUTER_VAR:
1948 : : {
1949 : 96474 : int attnum = op->d.var.attnum;
1950 : :
1951 : 96474 : CheckVarSlotCompatibility(outerslot, attnum + 1, op->d.var.vartype);
1952 : 96474 : break;
1953 : : }
1954 : :
1955 : 146593 : case EEOP_SCAN_VAR:
1956 : : {
1957 : 146593 : int attnum = op->d.var.attnum;
1958 : :
1959 : 146593 : CheckVarSlotCompatibility(scanslot, attnum + 1, op->d.var.vartype);
1960 : 146581 : break;
1961 : : }
1962 : 3633097 : default:
1963 : 3633097 : break;
1964 : : }
1965 : : }
1966 : 876798 : }
1967 : :
1968 : : /*
1969 : : * Check whether a user attribute in a slot can be referenced by a Var
1970 : : * expression. This should succeed unless there have been schema changes
1971 : : * since the expression tree has been created.
1972 : : */
1973 : : static void
2588 1974 : 280002 : CheckVarSlotCompatibility(TupleTableSlot *slot, int attnum, Oid vartype)
1975 : : {
1976 : : /*
1977 : : * What we have to check for here is the possibility of an attribute
1978 : : * having been dropped or changed in type since the plan tree was created.
1979 : : * Ideally the plan will get invalidated and not re-used, but just in
1980 : : * case, we keep these defenses. Fortunately it's sufficient to check
1981 : : * once on the first time through.
1982 : : *
1983 : : * Note: ideally we'd check typmod as well as typid, but that seems
1984 : : * impractical at the moment: in many cases the tupdesc will have been
1985 : : * generated by ExecTypeFromTL(), and that can't guarantee to generate an
1986 : : * accurate typmod in all cases, because some expression node types don't
1987 : : * carry typmod. Fortunately, for precisely that reason, there should be
1988 : : * no places with a critical dependency on the typmod of a value.
1989 : : *
1990 : : * System attributes don't require checking since their types never
1991 : : * change.
1992 : : */
1993 [ + - ]: 280002 : if (attnum > 0)
1994 : : {
1995 : 280002 : TupleDesc slot_tupdesc = slot->tts_tupleDescriptor;
1996 : : Form_pg_attribute attr;
1997 : :
2489 tgl@sss.pgh.pa.us 1998 [ - + ]: 280002 : if (attnum > slot_tupdesc->natts) /* should never happen */
2588 andres@anarazel.de 1999 [ # # ]:UBC 0 : elog(ERROR, "attribute number %d exceeds number of columns %d",
2000 : : attnum, slot_tupdesc->natts);
2001 : :
2429 andres@anarazel.de 2002 :CBC 280002 : attr = TupleDescAttr(slot_tupdesc, attnum - 1);
2003 : :
2574 tgl@sss.pgh.pa.us 2004 [ + + ]: 280002 : if (attr->attisdropped)
2005 [ + - ]: 6 : ereport(ERROR,
2006 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
2007 : : errmsg("attribute %d of type %s has been dropped",
2008 : : attnum, format_type_be(slot_tupdesc->tdtypeid))));
2009 : :
2010 [ + + ]: 279996 : if (vartype != attr->atttypid)
2011 [ + - ]: 6 : ereport(ERROR,
2012 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
2013 : : errmsg("attribute %d of type %s has wrong type",
2014 : : attnum, format_type_be(slot_tupdesc->tdtypeid)),
2015 : : errdetail("Table has type %s, but query expects %s.",
2016 : : format_type_be(attr->atttypid),
2017 : : format_type_be(vartype))));
2018 : : }
2588 andres@anarazel.de 2019 : 279990 : }
2020 : :
2021 : : /*
2022 : : * Verify that the slot is compatible with a EEOP_*_FETCHSOME operation.
2023 : : */
2024 : : static void
1977 2025 : 78063697 : CheckOpSlotCompatibility(ExprEvalStep *op, TupleTableSlot *slot)
2026 : : {
2027 : : #ifdef USE_ASSERT_CHECKING
2028 : : /* there's nothing to check */
2029 [ + + ]: 78063697 : if (!op->d.fetch.fixed)
2030 : 5820098 : return;
2031 : :
2032 : : /*
2033 : : * Should probably fixed at some point, but for now it's easier to allow
2034 : : * buffer and heap tuples to be used interchangeably.
2035 : : */
1976 2036 [ + + ]: 72243599 : if (slot->tts_ops == &TTSOpsBufferHeapTuple &&
1977 2037 [ - + ]: 44716084 : op->d.fetch.kind == &TTSOpsHeapTuple)
1977 andres@anarazel.de 2038 :UBC 0 : return;
1977 andres@anarazel.de 2039 [ + + ]:CBC 72243599 : if (slot->tts_ops == &TTSOpsHeapTuple &&
1976 2040 [ - + ]: 15000 : op->d.fetch.kind == &TTSOpsBufferHeapTuple)
1977 andres@anarazel.de 2041 :UBC 0 : return;
2042 : :
2043 : : /*
2044 : : * At the moment we consider it OK if a virtual slot is used instead of a
2045 : : * specific type of slot, as a virtual slot never needs to be deformed.
2046 : : */
1977 andres@anarazel.de 2047 [ + + ]:CBC 72243599 : if (slot->tts_ops == &TTSOpsVirtual)
2048 : 1060432 : return;
2049 : :
2050 [ - + ]: 71183167 : Assert(op->d.fetch.kind == slot->tts_ops);
2051 : : #endif
2052 : : }
2053 : :
2054 : : /*
2055 : : * get_cached_rowtype: utility function to lookup a rowtype tupdesc
2056 : : *
2057 : : * type_id, typmod: identity of the rowtype
2058 : : * rowcache: space for caching identity info
2059 : : * (rowcache->cacheptr must be initialized to NULL)
2060 : : * changed: if not NULL, *changed is set to true on any update
2061 : : *
2062 : : * The returned TupleDesc is not guaranteed pinned; caller must pin it
2063 : : * to use it across any operation that might incur cache invalidation,
2064 : : * including for example detoasting of input tuples.
2065 : : * (The TupleDesc is always refcounted, so just use IncrTupleDescRefCount.)
2066 : : *
2067 : : * NOTE: because composite types can change contents, we must be prepared
2068 : : * to re-do this during any node execution; cannot call just once during
2069 : : * expression initialization.
2070 : : */
2071 : : static TupleDesc
2588 2072 : 69039 : get_cached_rowtype(Oid type_id, int32 typmod,
2073 : : ExprEvalRowtypeCache *rowcache,
2074 : : bool *changed)
2075 : : {
1097 tgl@sss.pgh.pa.us 2076 [ + + ]: 69039 : if (type_id != RECORDOID)
2077 : : {
2078 : : /*
2079 : : * It's a named composite type, so use the regular typcache. Do a
2080 : : * lookup first time through, or if the composite type changed. Note:
2081 : : * "tupdesc_id == 0" may look redundant, but it protects against the
2082 : : * admittedly-theoretical possibility that type_id was RECORDOID the
2083 : : * last time through, so that the cacheptr isn't TypeCacheEntry *.
2084 : : */
2085 : 21794 : TypeCacheEntry *typentry = (TypeCacheEntry *) rowcache->cacheptr;
2086 : :
2087 [ + + + - : 21794 : if (unlikely(typentry == NULL ||
- + + + +
+ ]
2088 : : rowcache->tupdesc_id == 0 ||
2089 : : typentry->tupDesc_identifier != rowcache->tupdesc_id))
2090 : : {
2091 : 3152 : typentry = lookup_type_cache(type_id, TYPECACHE_TUPDESC);
2092 [ - + ]: 3152 : if (typentry->tupDesc == NULL)
1097 tgl@sss.pgh.pa.us 2093 [ # # ]:UBC 0 : ereport(ERROR,
2094 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2095 : : errmsg("type %s is not composite",
2096 : : format_type_be(type_id))));
1097 tgl@sss.pgh.pa.us 2097 :CBC 3152 : rowcache->cacheptr = (void *) typentry;
2098 : 3152 : rowcache->tupdesc_id = typentry->tupDesc_identifier;
2099 [ + + ]: 3152 : if (changed)
2100 : 358 : *changed = true;
2101 : : }
2102 : 21794 : return typentry->tupDesc;
2103 : : }
2104 : : else
2105 : : {
2106 : : /*
2107 : : * A RECORD type, once registered, doesn't change for the life of the
2108 : : * backend. So we don't need a typcache entry as such, which is good
2109 : : * because there isn't one. It's possible that the caller is asking
2110 : : * about a different type than before, though.
2111 : : */
2112 : 47245 : TupleDesc tupDesc = (TupleDesc) rowcache->cacheptr;
2113 : :
2114 [ + + + - : 47245 : if (unlikely(tupDesc == NULL ||
+ - - + +
+ - + +
+ ]
2115 : : rowcache->tupdesc_id != 0 ||
2116 : : type_id != tupDesc->tdtypeid ||
2117 : : typmod != tupDesc->tdtypmod))
2118 : : {
2119 : 1024 : tupDesc = lookup_rowtype_tupdesc(type_id, typmod);
2120 : : /* Drop pin acquired by lookup_rowtype_tupdesc */
2121 [ + + ]: 1024 : ReleaseTupleDesc(tupDesc);
2122 : 1024 : rowcache->cacheptr = (void *) tupDesc;
2123 : 1024 : rowcache->tupdesc_id = 0; /* not a valid value for non-RECORD */
2124 [ - + ]: 1024 : if (changed)
1097 tgl@sss.pgh.pa.us 2125 :UBC 0 : *changed = true;
2126 : : }
1097 tgl@sss.pgh.pa.us 2127 :CBC 47245 : return tupDesc;
2128 : : }
2129 : : }
2130 : :
2131 : :
2132 : : /*
2133 : : * Fast-path functions, for very simple expressions
2134 : : */
2135 : :
2136 : : /* implementation of ExecJust(Inner|Outer|Scan)Var */
2137 : : static pg_attribute_always_inline Datum
1658 andres@anarazel.de 2138 : 8569800 : ExecJustVarImpl(ExprState *state, TupleTableSlot *slot, bool *isnull)
2139 : : {
2588 2140 : 8569800 : ExprEvalStep *op = &state->steps[1];
2141 : 8569800 : int attnum = op->d.var.attnum + 1;
2142 : :
1977 2143 : 8569800 : CheckOpSlotCompatibility(&state->steps[0], slot);
2144 : :
2145 : : /*
2146 : : * Since we use slot_getattr(), we don't need to implement the FETCHSOME
2147 : : * step explicitly, and we also needn't Assert that the attnum is in range
2148 : : * --- slot_getattr() will take care of any problems.
2149 : : */
2588 2150 : 8569800 : return slot_getattr(slot, attnum, isnull);
2151 : : }
2152 : :
2153 : : /* Simple reference to inner Var */
2154 : : static Datum
1658 2155 : 2368534 : ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull)
2156 : : {
2157 : 2368534 : return ExecJustVarImpl(state, econtext->ecxt_innertuple, isnull);
2158 : : }
2159 : :
2160 : : /* Simple reference to outer Var */
2161 : : static Datum
2162 : 6068970 : ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull)
2163 : : {
2164 : 6068970 : return ExecJustVarImpl(state, econtext->ecxt_outertuple, isnull);
2165 : : }
2166 : :
2167 : : /* Simple reference to scan Var */
2168 : : static Datum
2169 : 132296 : ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull)
2170 : : {
2171 : 132296 : return ExecJustVarImpl(state, econtext->ecxt_scantuple, isnull);
2172 : : }
2173 : :
2174 : : /* implementation of ExecJustAssign(Inner|Outer|Scan)Var */
2175 : : static pg_attribute_always_inline Datum
2176 : 7006889 : ExecJustAssignVarImpl(ExprState *state, TupleTableSlot *inslot, bool *isnull)
2177 : : {
2588 2178 : 7006889 : ExprEvalStep *op = &state->steps[1];
2179 : 7006889 : int attnum = op->d.assign_var.attnum + 1;
2180 : 7006889 : int resultnum = op->d.assign_var.resultnum;
2181 : 7006889 : TupleTableSlot *outslot = state->resultslot;
2182 : :
1977 2183 : 7006889 : CheckOpSlotCompatibility(&state->steps[0], inslot);
2184 : :
2185 : : /*
2186 : : * We do not need CheckVarSlotCompatibility here; that was taken care of
2187 : : * at compilation time.
2188 : : *
2189 : : * Since we use slot_getattr(), we don't need to implement the FETCHSOME
2190 : : * step explicitly, and we also needn't Assert that the attnum is in range
2191 : : * --- slot_getattr() will take care of any problems. Nonetheless, check
2192 : : * that resultnum is in range.
2193 : : */
1070 tgl@sss.pgh.pa.us 2194 [ + - - + ]: 7006889 : Assert(resultnum >= 0 && resultnum < outslot->tts_tupleDescriptor->natts);
2588 andres@anarazel.de 2195 : 14013778 : outslot->tts_values[resultnum] =
2196 : 7006889 : slot_getattr(inslot, attnum, &outslot->tts_isnull[resultnum]);
2197 : 7006889 : return 0;
2198 : : }
2199 : :
2200 : : /* Evaluate inner Var and assign to appropriate column of result tuple */
2201 : : static Datum
1658 2202 : 32892 : ExecJustAssignInnerVar(ExprState *state, ExprContext *econtext, bool *isnull)
2203 : : {
2204 : 32892 : return ExecJustAssignVarImpl(state, econtext->ecxt_innertuple, isnull);
2205 : : }
2206 : :
2207 : : /* Evaluate outer Var and assign to appropriate column of result tuple */
2208 : : static Datum
2588 2209 : 556442 : ExecJustAssignOuterVar(ExprState *state, ExprContext *econtext, bool *isnull)
2210 : : {
1658 2211 : 556442 : return ExecJustAssignVarImpl(state, econtext->ecxt_outertuple, isnull);
2212 : : }
2213 : :
2214 : : /* Evaluate scan Var and assign to appropriate column of result tuple */
2215 : : static Datum
2588 2216 : 6417555 : ExecJustAssignScanVar(ExprState *state, ExprContext *econtext, bool *isnull)
2217 : : {
1658 2218 : 6417555 : return ExecJustAssignVarImpl(state, econtext->ecxt_scantuple, isnull);
2219 : : }
2220 : :
2221 : : /* Evaluate CASE_TESTVAL and apply a strict function to it */
2222 : : static Datum
2388 tgl@sss.pgh.pa.us 2223 : 10573 : ExecJustApplyFuncToCase(ExprState *state, ExprContext *econtext, bool *isnull)
2224 : : {
2225 : 10573 : ExprEvalStep *op = &state->steps[0];
2226 : : FunctionCallInfo fcinfo;
2227 : : NullableDatum *args;
2228 : : int nargs;
2229 : : Datum d;
2230 : :
2231 : : /*
2232 : : * XXX with some redesign of the CaseTestExpr mechanism, maybe we could
2233 : : * get rid of this data shuffling?
2234 : : */
2235 : 10573 : *op->resvalue = *op->d.casetest.value;
2236 : 10573 : *op->resnull = *op->d.casetest.isnull;
2237 : :
2238 : 10573 : op++;
2239 : :
1529 andres@anarazel.de 2240 : 10573 : nargs = op->d.func.nargs;
2388 tgl@sss.pgh.pa.us 2241 : 10573 : fcinfo = op->d.func.fcinfo_data;
1905 andres@anarazel.de 2242 : 10573 : args = fcinfo->args;
2243 : :
2244 : : /* strict function, so check for NULL args */
1529 2245 [ + + ]: 21302 : for (int argno = 0; argno < nargs; argno++)
2246 : : {
1905 2247 [ + + ]: 10735 : if (args[argno].isnull)
2248 : : {
2388 tgl@sss.pgh.pa.us 2249 : 6 : *isnull = true;
2250 : 6 : return (Datum) 0;
2251 : : }
2252 : : }
2253 : 10567 : fcinfo->isnull = false;
2254 : 10567 : d = op->d.func.fn_addr(fcinfo);
2255 : 10558 : *isnull = fcinfo->isnull;
2256 : 10558 : return d;
2257 : : }
2258 : :
2259 : : /* Simple Const expression */
2260 : : static Datum
1658 andres@anarazel.de 2261 : 872893 : ExecJustConst(ExprState *state, ExprContext *econtext, bool *isnull)
2262 : : {
2263 : 872893 : ExprEvalStep *op = &state->steps[0];
2264 : :
2265 : 872893 : *isnull = op->d.constval.isnull;
2266 : 872893 : return op->d.constval.value;
2267 : : }
2268 : :
2269 : : /* implementation of ExecJust(Inner|Outer|Scan)VarVirt */
2270 : : static pg_attribute_always_inline Datum
2271 : 9993532 : ExecJustVarVirtImpl(ExprState *state, TupleTableSlot *slot, bool *isnull)
2272 : : {
2273 : 9993532 : ExprEvalStep *op = &state->steps[0];
2274 : 9993532 : int attnum = op->d.var.attnum;
2275 : :
2276 : : /*
2277 : : * As it is guaranteed that a virtual slot is used, there never is a need
2278 : : * to perform tuple deforming (nor would it be possible). Therefore
2279 : : * execExpr.c has not emitted an EEOP_*_FETCHSOME step. Verify, as much as
2280 : : * possible, that that determination was accurate.
2281 : : */
2282 [ - + ]: 9993532 : Assert(TTS_IS_VIRTUAL(slot));
2283 [ - + ]: 9993532 : Assert(TTS_FIXED(slot));
2284 [ + - - + ]: 9993532 : Assert(attnum >= 0 && attnum < slot->tts_nvalid);
2285 : :
2286 : 9993532 : *isnull = slot->tts_isnull[attnum];
2287 : :
2288 : 9993532 : return slot->tts_values[attnum];
2289 : : }
2290 : :
2291 : : /* Like ExecJustInnerVar, optimized for virtual slots */
2292 : : static Datum
2293 : 396778 : ExecJustInnerVarVirt(ExprState *state, ExprContext *econtext, bool *isnull)
2294 : : {
2295 : 396778 : return ExecJustVarVirtImpl(state, econtext->ecxt_innertuple, isnull);
2296 : : }
2297 : :
2298 : : /* Like ExecJustOuterVar, optimized for virtual slots */
2299 : : static Datum
2300 : 9596661 : ExecJustOuterVarVirt(ExprState *state, ExprContext *econtext, bool *isnull)
2301 : : {
2302 : 9596661 : return ExecJustVarVirtImpl(state, econtext->ecxt_outertuple, isnull);
2303 : : }
2304 : :
2305 : : /* Like ExecJustScanVar, optimized for virtual slots */
2306 : : static Datum
2307 : 93 : ExecJustScanVarVirt(ExprState *state, ExprContext *econtext, bool *isnull)
2308 : : {
2309 : 93 : return ExecJustVarVirtImpl(state, econtext->ecxt_scantuple, isnull);
2310 : : }
2311 : :
2312 : : /* implementation of ExecJustAssign(Inner|Outer|Scan)VarVirt */
2313 : : static pg_attribute_always_inline Datum
2314 : 806002 : ExecJustAssignVarVirtImpl(ExprState *state, TupleTableSlot *inslot, bool *isnull)
2315 : : {
2316 : 806002 : ExprEvalStep *op = &state->steps[0];
2317 : 806002 : int attnum = op->d.assign_var.attnum;
2318 : 806002 : int resultnum = op->d.assign_var.resultnum;
2319 : 806002 : TupleTableSlot *outslot = state->resultslot;
2320 : :
2321 : : /* see ExecJustVarVirtImpl for comments */
2322 : :
2323 [ - + ]: 806002 : Assert(TTS_IS_VIRTUAL(inslot));
2324 [ - + ]: 806002 : Assert(TTS_FIXED(inslot));
2325 [ + - - + ]: 806002 : Assert(attnum >= 0 && attnum < inslot->tts_nvalid);
1070 tgl@sss.pgh.pa.us 2326 [ + - - + ]: 806002 : Assert(resultnum >= 0 && resultnum < outslot->tts_tupleDescriptor->natts);
2327 : :
1658 andres@anarazel.de 2328 : 806002 : outslot->tts_values[resultnum] = inslot->tts_values[attnum];
2329 : 806002 : outslot->tts_isnull[resultnum] = inslot->tts_isnull[attnum];
2330 : :
2331 : 806002 : return 0;
2332 : : }
2333 : :
2334 : : /* Like ExecJustAssignInnerVar, optimized for virtual slots */
2335 : : static Datum
2336 : 60621 : ExecJustAssignInnerVarVirt(ExprState *state, ExprContext *econtext, bool *isnull)
2337 : : {
2338 : 60621 : return ExecJustAssignVarVirtImpl(state, econtext->ecxt_innertuple, isnull);
2339 : : }
2340 : :
2341 : : /* Like ExecJustAssignOuterVar, optimized for virtual slots */
2342 : : static Datum
2343 : 654051 : ExecJustAssignOuterVarVirt(ExprState *state, ExprContext *econtext, bool *isnull)
2344 : : {
2345 : 654051 : return ExecJustAssignVarVirtImpl(state, econtext->ecxt_outertuple, isnull);
2346 : : }
2347 : :
2348 : : /* Like ExecJustAssignScanVar, optimized for virtual slots */
2349 : : static Datum
2350 : 91330 : ExecJustAssignScanVarVirt(ExprState *state, ExprContext *econtext, bool *isnull)
2351 : : {
2352 : 91330 : return ExecJustAssignVarVirtImpl(state, econtext->ecxt_scantuple, isnull);
2353 : : }
2354 : :
2355 : : #if defined(EEO_USE_COMPUTED_GOTO)
2356 : : /*
2357 : : * Comparator used when building address->opcode lookup table for
2358 : : * ExecEvalStepOp() in the threaded dispatch case.
2359 : : */
2360 : : static int
2287 2361 : 25856723 : dispatch_compare_ptr(const void *a, const void *b)
2362 : : {
2298 2363 : 25856723 : const ExprEvalOpLookup *la = (const ExprEvalOpLookup *) a;
2364 : 25856723 : const ExprEvalOpLookup *lb = (const ExprEvalOpLookup *) b;
2365 : :
2366 [ + + ]: 25856723 : if (la->opcode < lb->opcode)
2367 : 16675303 : return -1;
2368 [ + + ]: 9181420 : else if (la->opcode > lb->opcode)
2369 : 5748329 : return 1;
2370 : 3433091 : return 0;
2371 : : }
2372 : : #endif
2373 : :
2374 : : /*
2375 : : * Do one-time initialization of interpretation machinery.
2376 : : */
2377 : : static void
2588 2378 : 1119584 : ExecInitInterpreter(void)
2379 : : {
2380 : : #if defined(EEO_USE_COMPUTED_GOTO)
2381 : : /* Set up externally-visible pointer to dispatch table */
2382 [ + + ]: 1119584 : if (dispatch_table == NULL)
2383 : : {
2384 : 9927 : dispatch_table = (const void **)
2385 : 9927 : DatumGetPointer(ExecInterpExpr(NULL, NULL, NULL));
2386 : :
2387 : : /* build reverse lookup table */
1529 2388 [ + + ]: 992700 : for (int i = 0; i < EEOP_LAST; i++)
2389 : : {
2298 2390 : 982773 : reverse_dispatch_table[i].opcode = dispatch_table[i];
2391 : 982773 : reverse_dispatch_table[i].op = (ExprEvalOp) i;
2392 : : }
2393 : :
2394 : : /* make it bsearch()able */
2395 : 9927 : qsort(reverse_dispatch_table,
2396 : : EEOP_LAST /* nmembers */ ,
2397 : : sizeof(ExprEvalOpLookup),
2398 : : dispatch_compare_ptr);
2399 : : }
2400 : : #endif
2588 2401 : 1119584 : }
2402 : :
2403 : : /*
2404 : : * Function to return the opcode of an expression step.
2405 : : *
2406 : : * When direct-threading is in use, ExprState->opcode isn't easily
2407 : : * decipherable. This function returns the appropriate enum member.
2408 : : */
2409 : : ExprEvalOp
2410 : 3913099 : ExecEvalStepOp(ExprState *state, ExprEvalStep *op)
2411 : : {
2412 : : #if defined(EEO_USE_COMPUTED_GOTO)
2413 [ + + ]: 3913099 : if (state->flags & EEO_FLAG_DIRECT_THREADED)
2414 : : {
2415 : : ExprEvalOpLookup key;
2416 : : ExprEvalOpLookup *res;
2417 : :
2287 2418 : 3433091 : key.opcode = (void *) op->opcode;
2298 2419 : 3433091 : res = bsearch(&key,
2420 : : reverse_dispatch_table,
2421 : : EEOP_LAST /* nmembers */ ,
2422 : : sizeof(ExprEvalOpLookup),
2423 : : dispatch_compare_ptr);
2287 2424 [ - + ]: 3433091 : Assert(res); /* unknown ops shouldn't get looked up */
2298 2425 : 3433091 : return res->op;
2426 : : }
2427 : : #endif
2588 2428 : 480008 : return (ExprEvalOp) op->opcode;
2429 : : }
2430 : :
2431 : :
2432 : : /*
2433 : : * Out-of-line helper functions for complex instructions.
2434 : : */
2435 : :
2436 : : /*
2437 : : * Evaluate EEOP_FUNCEXPR_FUSAGE
2438 : : */
2439 : : void
2217 2440 : 104 : ExecEvalFuncExprFusage(ExprState *state, ExprEvalStep *op,
2441 : : ExprContext *econtext)
2442 : : {
2443 : 104 : FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
2444 : : PgStat_FunctionCallUsage fcusage;
2445 : : Datum d;
2446 : :
2447 : 104 : pgstat_init_function_usage(fcinfo, &fcusage);
2448 : :
2449 : 104 : fcinfo->isnull = false;
2450 : 104 : d = op->d.func.fn_addr(fcinfo);
2451 : 104 : *op->resvalue = d;
2452 : 104 : *op->resnull = fcinfo->isnull;
2453 : :
2454 : 104 : pgstat_end_function_usage(&fcusage, true);
2455 : 104 : }
2456 : :
2457 : : /*
2458 : : * Evaluate EEOP_FUNCEXPR_STRICT_FUSAGE
2459 : : */
2460 : : void
2461 : 3 : ExecEvalFuncExprStrictFusage(ExprState *state, ExprEvalStep *op,
2462 : : ExprContext *econtext)
2463 : : {
2464 : :
2465 : 3 : FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
2466 : : PgStat_FunctionCallUsage fcusage;
1905 2467 : 3 : NullableDatum *args = fcinfo->args;
1529 2468 : 3 : int nargs = op->d.func.nargs;
2469 : : Datum d;
2470 : :
2471 : : /* strict function, so check for NULL args */
2472 [ + + ]: 9 : for (int argno = 0; argno < nargs; argno++)
2473 : : {
1905 2474 [ - + ]: 6 : if (args[argno].isnull)
2475 : : {
2217 andres@anarazel.de 2476 :UBC 0 : *op->resnull = true;
2477 : 0 : return;
2478 : : }
2479 : : }
2480 : :
2217 andres@anarazel.de 2481 :CBC 3 : pgstat_init_function_usage(fcinfo, &fcusage);
2482 : :
2483 : 3 : fcinfo->isnull = false;
2484 : 3 : d = op->d.func.fn_addr(fcinfo);
2485 : 3 : *op->resvalue = d;
2486 : 3 : *op->resnull = fcinfo->isnull;
2487 : :
2488 : 3 : pgstat_end_function_usage(&fcusage, true);
2489 : : }
2490 : :
2491 : : /*
2492 : : * Evaluate a PARAM_EXEC parameter.
2493 : : *
2494 : : * PARAM_EXEC params (internal executor parameters) are stored in the
2495 : : * ecxt_param_exec_vals array, and can be accessed by array index.
2496 : : */
2497 : : void
2588 2498 : 2369682 : ExecEvalParamExec(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
2499 : : {
2500 : : ParamExecData *prm;
2501 : :
2502 : 2369682 : prm = &(econtext->ecxt_param_exec_vals[op->d.param.paramid]);
2503 [ + + ]: 2369682 : if (unlikely(prm->execPlan != NULL))
2504 : : {
2505 : : /* Parameter not evaluated yet, so go do it */
2506 : 4704 : ExecSetParamPlan(prm->execPlan, econtext);
2507 : : /* ExecSetParamPlan should have processed this param... */
2508 [ - + ]: 4696 : Assert(prm->execPlan == NULL);
2509 : : }
2510 : 2369674 : *op->resvalue = prm->value;
2511 : 2369674 : *op->resnull = prm->isnull;
2512 : 2369674 : }
2513 : :
2514 : : /*
2515 : : * Evaluate a PARAM_EXTERN parameter.
2516 : : *
2517 : : * PARAM_EXTERN parameters must be sought in ecxt_param_list_info.
2518 : : */
2519 : : void
2520 : 254378 : ExecEvalParamExtern(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
2521 : : {
2522 : 254378 : ParamListInfo paramInfo = econtext->ecxt_param_list_info;
2523 : 254378 : int paramId = op->d.param.paramid;
2524 : :
2525 [ + - + - : 254378 : if (likely(paramInfo &&
+ - + - +
- ]
2526 : : paramId > 0 && paramId <= paramInfo->numParams))
2527 : : {
2528 : : ParamExternData *prm;
2529 : : ParamExternData prmdata;
2530 : :
2531 : : /* give hook a chance in case parameter is dynamic */
2306 tgl@sss.pgh.pa.us 2532 [ + + ]: 254378 : if (paramInfo->paramFetch != NULL)
2533 : 92 : prm = paramInfo->paramFetch(paramInfo, paramId, false, &prmdata);
2534 : : else
2535 : 254286 : prm = ¶mInfo->params[paramId - 1];
2536 : :
2588 andres@anarazel.de 2537 [ + - ]: 254378 : if (likely(OidIsValid(prm->ptype)))
2538 : : {
2539 : : /* safety check in case hook did something unexpected */
2540 [ - + ]: 254378 : if (unlikely(prm->ptype != op->d.param.paramtype))
2588 andres@anarazel.de 2541 [ # # ]:UBC 0 : ereport(ERROR,
2542 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
2543 : : errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
2544 : : paramId,
2545 : : format_type_be(prm->ptype),
2546 : : format_type_be(op->d.param.paramtype))));
2588 andres@anarazel.de 2547 :CBC 254378 : *op->resvalue = prm->value;
2548 : 254378 : *op->resnull = prm->isnull;
2549 : 254378 : return;
2550 : : }
2551 : : }
2552 : :
2588 andres@anarazel.de 2553 [ # # ]:UBC 0 : ereport(ERROR,
2554 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
2555 : : errmsg("no value found for parameter %d", paramId)));
2556 : : }
2557 : :
2558 : : /*
2559 : : * Evaluate a CoerceViaIO node in soft-error mode.
2560 : : *
2561 : : * The source value is in op's result variable.
2562 : : *
2563 : : * Note: This implements EEOP_IOCOERCE_SAFE. If you change anything here,
2564 : : * also look at the inline code for EEOP_IOCOERCE.
2565 : : */
2566 : : void
81 amitlan@postgresql.o 2567 :GNC 417 : ExecEvalCoerceViaIOSafe(ExprState *state, ExprEvalStep *op)
2568 : : {
2569 : : char *str;
2570 : :
2571 : : /* call output function (similar to OutputFunctionCall) */
2572 [ + + ]: 417 : if (*op->resnull)
2573 : : {
2574 : : /* output functions are not called on nulls */
2575 : 3 : str = NULL;
2576 : : }
2577 : : else
2578 : : {
2579 : : FunctionCallInfo fcinfo_out;
2580 : :
2581 : 414 : fcinfo_out = op->d.iocoerce.fcinfo_data_out;
2582 : 414 : fcinfo_out->args[0].value = *op->resvalue;
2583 : 414 : fcinfo_out->args[0].isnull = false;
2584 : :
2585 : 414 : fcinfo_out->isnull = false;
2586 : 414 : str = DatumGetCString(FunctionCallInvoke(fcinfo_out));
2587 : :
2588 : : /* OutputFunctionCall assumes result isn't null */
2589 [ - + ]: 414 : Assert(!fcinfo_out->isnull);
2590 : : }
2591 : :
2592 : : /* call input function (similar to InputFunctionCallSafe) */
2593 [ + - + + ]: 417 : if (!op->d.iocoerce.finfo_in->fn_strict || str != NULL)
2594 : : {
2595 : : FunctionCallInfo fcinfo_in;
2596 : :
2597 : 414 : fcinfo_in = op->d.iocoerce.fcinfo_data_in;
2598 : 414 : fcinfo_in->args[0].value = PointerGetDatum(str);
2599 : 414 : fcinfo_in->args[0].isnull = *op->resnull;
2600 : : /* second and third arguments are already set up */
2601 : :
2602 : : /* ErrorSaveContext must be present. */
2603 [ - + ]: 414 : Assert(IsA(fcinfo_in->context, ErrorSaveContext));
2604 : :
2605 : 414 : fcinfo_in->isnull = false;
2606 : 414 : *op->resvalue = FunctionCallInvoke(fcinfo_in);
2607 : :
2608 [ + - + - : 414 : if (SOFT_ERROR_OCCURRED(fcinfo_in->context))
+ + ]
2609 : : {
2610 : 12 : *op->resnull = true;
2611 : 12 : *op->resvalue = (Datum) 0;
2612 : 12 : return;
2613 : : }
2614 : :
2615 : : /* Should get null result if and only if str is NULL */
2616 [ - + ]: 402 : if (str == NULL)
81 amitlan@postgresql.o 2617 [ # # ]:UNC 0 : Assert(*op->resnull);
2618 : : else
81 amitlan@postgresql.o 2619 [ - + ]:GNC 402 : Assert(!*op->resnull);
2620 : : }
2621 : : }
2622 : :
2623 : : /*
2624 : : * Evaluate a SQLValueFunction expression.
2625 : : */
2626 : : void
333 michael@paquier.xyz 2627 :CBC 8809 : ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op)
2628 : : {
2629 : 8809 : LOCAL_FCINFO(fcinfo, 0);
2630 : 8809 : SQLValueFunction *svf = op->d.sqlvaluefunction.svf;
2631 : :
2632 : 8809 : *op->resnull = false;
2633 : :
2634 : : /*
2635 : : * Note: current_schema() can return NULL. current_user() etc currently
2636 : : * cannot, but might as well code those cases the same way for safety.
2637 : : */
2638 [ + + + + : 8809 : switch (svf->op)
+ + + + +
- ]
2639 : : {
2640 : 25 : case SVFOP_CURRENT_DATE:
2641 : 25 : *op->resvalue = DateADTGetDatum(GetSQLCurrentDate());
2642 : 25 : break;
2643 : 12 : case SVFOP_CURRENT_TIME:
2644 : : case SVFOP_CURRENT_TIME_N:
2645 : 12 : *op->resvalue = TimeTzADTPGetDatum(GetSQLCurrentTime(svf->typmod));
2646 : 12 : break;
2647 : 173 : case SVFOP_CURRENT_TIMESTAMP:
2648 : : case SVFOP_CURRENT_TIMESTAMP_N:
2649 : 173 : *op->resvalue = TimestampTzGetDatum(GetSQLCurrentTimestamp(svf->typmod));
2650 : 173 : break;
2651 : 12 : case SVFOP_LOCALTIME:
2652 : : case SVFOP_LOCALTIME_N:
2653 : 12 : *op->resvalue = TimeADTGetDatum(GetSQLLocalTime(svf->typmod));
2654 : 12 : break;
2655 : 33 : case SVFOP_LOCALTIMESTAMP:
2656 : : case SVFOP_LOCALTIMESTAMP_N:
2657 : 33 : *op->resvalue = TimestampGetDatum(GetSQLLocalTimestamp(svf->typmod));
2658 : 33 : break;
2659 : 8289 : case SVFOP_CURRENT_ROLE:
2660 : : case SVFOP_CURRENT_USER:
2661 : : case SVFOP_USER:
2662 : 8289 : InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
2663 : 8289 : *op->resvalue = current_user(fcinfo);
2664 : 8289 : *op->resnull = fcinfo->isnull;
2665 : 8289 : break;
2666 : 250 : case SVFOP_SESSION_USER:
2667 : 250 : InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
2668 : 250 : *op->resvalue = session_user(fcinfo);
2669 : 250 : *op->resnull = fcinfo->isnull;
2670 : 250 : break;
2671 : 6 : case SVFOP_CURRENT_CATALOG:
2672 : 6 : InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
2673 : 6 : *op->resvalue = current_database(fcinfo);
2674 : 6 : *op->resnull = fcinfo->isnull;
2675 : 6 : break;
2676 : 9 : case SVFOP_CURRENT_SCHEMA:
2677 : 9 : InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
2678 : 9 : *op->resvalue = current_schema(fcinfo);
2679 : 9 : *op->resnull = fcinfo->isnull;
2680 : 9 : break;
2681 : : }
2682 : 8809 : }
2683 : :
2684 : : /*
2685 : : * Raise error if a CURRENT OF expression is evaluated.
2686 : : *
2687 : : * The planner should convert CURRENT OF into a TidScan qualification, or some
2688 : : * other special handling in a ForeignScan node. So we have to be able to do
2689 : : * ExecInitExpr on a CurrentOfExpr, but we shouldn't ever actually execute it.
2690 : : * If we get here, we suppose we must be dealing with CURRENT OF on a foreign
2691 : : * table whose FDW doesn't handle it, and complain accordingly.
2692 : : */
2693 : : void
2588 andres@anarazel.de 2694 : 1 : ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op)
2695 : : {
2696 [ + - ]: 1 : ereport(ERROR,
2697 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2698 : : errmsg("WHERE CURRENT OF is not supported for this table type")));
2699 : : }
2700 : :
2701 : : /*
2702 : : * Evaluate NextValueExpr.
2703 : : */
2704 : : void
2466 tgl@sss.pgh.pa.us 2705 : 498 : ExecEvalNextValueExpr(ExprState *state, ExprEvalStep *op)
2706 : : {
2707 : 498 : int64 newval = nextval_internal(op->d.nextvalueexpr.seqid, false);
2708 : :
2709 [ + + + - ]: 498 : switch (op->d.nextvalueexpr.seqtypid)
2710 : : {
2711 : 15 : case INT2OID:
2712 : 15 : *op->resvalue = Int16GetDatum((int16) newval);
2713 : 15 : break;
2714 : 447 : case INT4OID:
2715 : 447 : *op->resvalue = Int32GetDatum((int32) newval);
2716 : 447 : break;
2717 : 36 : case INT8OID:
2718 : 36 : *op->resvalue = Int64GetDatum((int64) newval);
2719 : 36 : break;
2466 tgl@sss.pgh.pa.us 2720 :UBC 0 : default:
2721 [ # # ]: 0 : elog(ERROR, "unsupported sequence type %u",
2722 : : op->d.nextvalueexpr.seqtypid);
2723 : : }
2466 tgl@sss.pgh.pa.us 2724 :CBC 498 : *op->resnull = false;
2725 : 498 : }
2726 : :
2727 : : /*
2728 : : * Evaluate NullTest / IS NULL for rows.
2729 : : */
2730 : : void
2588 andres@anarazel.de 2731 : 348 : ExecEvalRowNull(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
2732 : : {
2733 : 348 : ExecEvalRowNullInt(state, op, econtext, true);
2734 : 348 : }
2735 : :
2736 : : /*
2737 : : * Evaluate NullTest / IS NOT NULL for rows.
2738 : : */
2739 : : void
2740 : 277 : ExecEvalRowNotNull(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
2741 : : {
2742 : 277 : ExecEvalRowNullInt(state, op, econtext, false);
2743 : 277 : }
2744 : :
2745 : : /* Common code for IS [NOT] NULL on a row value */
2746 : : static void
2747 : 625 : ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op,
2748 : : ExprContext *econtext, bool checkisnull)
2749 : : {
2750 : 625 : Datum value = *op->resvalue;
2751 : 625 : bool isnull = *op->resnull;
2752 : : HeapTupleHeader tuple;
2753 : : Oid tupType;
2754 : : int32 tupTypmod;
2755 : : TupleDesc tupDesc;
2756 : : HeapTupleData tmptup;
2757 : :
2758 : 625 : *op->resnull = false;
2759 : :
2760 : : /* NULL row variables are treated just as NULL scalar columns */
2761 [ + + ]: 625 : if (isnull)
2762 : : {
2763 : 78 : *op->resvalue = BoolGetDatum(checkisnull);
2764 : 371 : return;
2765 : : }
2766 : :
2767 : : /*
2768 : : * The SQL standard defines IS [NOT] NULL for a non-null rowtype argument
2769 : : * as:
2770 : : *
2771 : : * "R IS NULL" is true if every field is the null value.
2772 : : *
2773 : : * "R IS NOT NULL" is true if no field is the null value.
2774 : : *
2775 : : * This definition is (apparently intentionally) not recursive; so our
2776 : : * tests on the fields are primitive attisnull tests, not recursive checks
2777 : : * to see if they are all-nulls or no-nulls rowtypes.
2778 : : *
2779 : : * The standard does not consider the possibility of zero-field rows, but
2780 : : * here we consider them to vacuously satisfy both predicates.
2781 : : */
2782 : :
2783 : 547 : tuple = DatumGetHeapTupleHeader(value);
2784 : :
2785 : 547 : tupType = HeapTupleHeaderGetTypeId(tuple);
2786 : 547 : tupTypmod = HeapTupleHeaderGetTypMod(tuple);
2787 : :
2788 : : /* Lookup tupdesc if first time through or if type changes */
2789 : 547 : tupDesc = get_cached_rowtype(tupType, tupTypmod,
2790 : : &op->d.nulltest_row.rowcache, NULL);
2791 : :
2792 : : /*
2793 : : * heap_attisnull needs a HeapTuple not a bare HeapTupleHeader.
2794 : : */
2795 : 547 : tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
2796 : 547 : tmptup.t_data = tuple;
2797 : :
1529 2798 [ + + ]: 1318 : for (int att = 1; att <= tupDesc->natts; att++)
2799 : : {
2800 : : /* ignore dropped columns */
2429 2801 [ - + ]: 1064 : if (TupleDescAttr(tupDesc, att - 1)->attisdropped)
2588 andres@anarazel.de 2802 :UBC 0 : continue;
2209 andrew@dunslane.net 2803 [ + + ]:CBC 1064 : if (heap_attisnull(&tmptup, att, tupDesc))
2804 : : {
2805 : : /* null field disproves IS NOT NULL */
2588 andres@anarazel.de 2806 [ + + ]: 28 : if (!checkisnull)
2807 : : {
2808 : 16 : *op->resvalue = BoolGetDatum(false);
2809 : 16 : return;
2810 : : }
2811 : : }
2812 : : else
2813 : : {
2814 : : /* non-null field disproves IS NULL */
2815 [ + + ]: 1036 : if (checkisnull)
2816 : : {
2817 : 277 : *op->resvalue = BoolGetDatum(false);
2818 : 277 : return;
2819 : : }
2820 : : }
2821 : : }
2822 : :
2823 : 254 : *op->resvalue = BoolGetDatum(true);
2824 : : }
2825 : :
2826 : : /*
2827 : : * Evaluate an ARRAY[] expression.
2828 : : *
2829 : : * The individual array elements (or subarrays) have already been evaluated
2830 : : * into op->d.arrayexpr.elemvalues[]/elemnulls[].
2831 : : */
2832 : : void
2833 : 369231 : ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op)
2834 : : {
2835 : : ArrayType *result;
2836 : 369231 : Oid element_type = op->d.arrayexpr.elemtype;
2837 : 369231 : int nelems = op->d.arrayexpr.nelems;
2838 : 369231 : int ndims = 0;
2839 : : int dims[MAXDIM];
2840 : : int lbs[MAXDIM];
2841 : :
2842 : : /* Set non-null as default */
2843 : 369231 : *op->resnull = false;
2844 : :
2845 [ + + ]: 369231 : if (!op->d.arrayexpr.multidims)
2846 : : {
2847 : : /* Elements are presumably of scalar type */
2848 : 368992 : Datum *dvalues = op->d.arrayexpr.elemvalues;
2849 : 368992 : bool *dnulls = op->d.arrayexpr.elemnulls;
2850 : :
2851 : : /* setup for 1-D array of the given length */
2852 : 368992 : ndims = 1;
2853 : 368992 : dims[0] = nelems;
2854 : 368992 : lbs[0] = 1;
2855 : :
2856 : 368992 : result = construct_md_array(dvalues, dnulls, ndims, dims, lbs,
2857 : : element_type,
2858 : 368992 : op->d.arrayexpr.elemlength,
2859 : 368992 : op->d.arrayexpr.elembyval,
2860 : 368992 : op->d.arrayexpr.elemalign);
2861 : : }
2862 : : else
2863 : : {
2864 : : /* Must be nested array expressions */
2865 : 239 : int nbytes = 0;
2866 : : int nitems;
2867 : 239 : int outer_nelems = 0;
2868 : 239 : int elem_ndims = 0;
2869 : 239 : int *elem_dims = NULL;
2870 : 239 : int *elem_lbs = NULL;
2871 : 239 : bool firstone = true;
2872 : 239 : bool havenulls = false;
2873 : 239 : bool haveempty = false;
2874 : : char **subdata;
2875 : : bits8 **subbitmaps;
2876 : : int *subbytes;
2877 : : int *subnitems;
2878 : : int32 dataoffset;
2879 : : char *dat;
2880 : : int iitem;
2881 : :
2882 : 239 : subdata = (char **) palloc(nelems * sizeof(char *));
2883 : 239 : subbitmaps = (bits8 **) palloc(nelems * sizeof(bits8 *));
2884 : 239 : subbytes = (int *) palloc(nelems * sizeof(int));
2885 : 239 : subnitems = (int *) palloc(nelems * sizeof(int));
2886 : :
2887 : : /* loop through and get data area from each element */
1529 2888 [ + + ]: 667 : for (int elemoff = 0; elemoff < nelems; elemoff++)
2889 : : {
2890 : : Datum arraydatum;
2891 : : bool eisnull;
2892 : : ArrayType *array;
2893 : : int this_ndims;
2894 : :
2588 2895 : 428 : arraydatum = op->d.arrayexpr.elemvalues[elemoff];
2896 : 428 : eisnull = op->d.arrayexpr.elemnulls[elemoff];
2897 : :
2898 : : /* temporarily ignore null subarrays */
2899 [ - + ]: 428 : if (eisnull)
2900 : : {
2588 andres@anarazel.de 2901 :UBC 0 : haveempty = true;
2902 : 0 : continue;
2903 : : }
2904 : :
2588 andres@anarazel.de 2905 :CBC 428 : array = DatumGetArrayTypeP(arraydatum);
2906 : :
2907 : : /* run-time double-check on element type */
2908 [ - + ]: 428 : if (element_type != ARR_ELEMTYPE(array))
2588 andres@anarazel.de 2909 [ # # ]:UBC 0 : ereport(ERROR,
2910 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
2911 : : errmsg("cannot merge incompatible arrays"),
2912 : : errdetail("Array with element type %s cannot be "
2913 : : "included in ARRAY construct with element type %s.",
2914 : : format_type_be(ARR_ELEMTYPE(array)),
2915 : : format_type_be(element_type))));
2916 : :
2588 andres@anarazel.de 2917 :CBC 428 : this_ndims = ARR_NDIM(array);
2918 : : /* temporarily ignore zero-dimensional subarrays */
2919 [ - + ]: 428 : if (this_ndims <= 0)
2920 : : {
2588 andres@anarazel.de 2921 :UBC 0 : haveempty = true;
2922 : 0 : continue;
2923 : : }
2924 : :
2588 andres@anarazel.de 2925 [ + + ]:CBC 428 : if (firstone)
2926 : : {
2927 : : /* Get sub-array details from first member */
2928 : 239 : elem_ndims = this_ndims;
2929 : 239 : ndims = elem_ndims + 1;
2930 [ + - - + ]: 239 : if (ndims <= 0 || ndims > MAXDIM)
2588 andres@anarazel.de 2931 [ # # ]:UBC 0 : ereport(ERROR,
2932 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2933 : : errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
2934 : : ndims, MAXDIM)));
2935 : :
2588 andres@anarazel.de 2936 :CBC 239 : elem_dims = (int *) palloc(elem_ndims * sizeof(int));
2937 : 239 : memcpy(elem_dims, ARR_DIMS(array), elem_ndims * sizeof(int));
2938 : 239 : elem_lbs = (int *) palloc(elem_ndims * sizeof(int));
2939 : 239 : memcpy(elem_lbs, ARR_LBOUND(array), elem_ndims * sizeof(int));
2940 : :
2941 : 239 : firstone = false;
2942 : : }
2943 : : else
2944 : : {
2945 : : /* Check other sub-arrays are compatible */
2946 [ + - ]: 189 : if (elem_ndims != this_ndims ||
2947 [ + - ]: 189 : memcmp(elem_dims, ARR_DIMS(array),
2948 : 189 : elem_ndims * sizeof(int)) != 0 ||
2949 [ - + ]: 189 : memcmp(elem_lbs, ARR_LBOUND(array),
2950 : : elem_ndims * sizeof(int)) != 0)
2588 andres@anarazel.de 2951 [ # # ]:UBC 0 : ereport(ERROR,
2952 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2953 : : errmsg("multidimensional arrays must have array "
2954 : : "expressions with matching dimensions")));
2955 : : }
2956 : :
2588 andres@anarazel.de 2957 [ + + ]:CBC 428 : subdata[outer_nelems] = ARR_DATA_PTR(array);
2958 [ + + ]: 428 : subbitmaps[outer_nelems] = ARR_NULLBITMAP(array);
2959 [ + + ]: 428 : subbytes[outer_nelems] = ARR_SIZE(array) - ARR_DATA_OFFSET(array);
2960 : 428 : nbytes += subbytes[outer_nelems];
2961 : : /* check for overflow of total request */
385 tgl@sss.pgh.pa.us 2962 [ - + ]: 428 : if (!AllocSizeIsValid(nbytes))
385 tgl@sss.pgh.pa.us 2963 [ # # ]:UBC 0 : ereport(ERROR,
2964 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2965 : : errmsg("array size exceeds the maximum allowed (%d)",
2966 : : (int) MaxAllocSize)));
2588 andres@anarazel.de 2967 :CBC 428 : subnitems[outer_nelems] = ArrayGetNItems(this_ndims,
2968 : : ARR_DIMS(array));
2969 : 428 : havenulls |= ARR_HASNULL(array);
2970 : 428 : outer_nelems++;
2971 : : }
2972 : :
2973 : : /*
2974 : : * If all items were null or empty arrays, return an empty array;
2975 : : * otherwise, if some were and some weren't, raise error. (Note: we
2976 : : * must special-case this somehow to avoid trying to generate a 1-D
2977 : : * array formed from empty arrays. It's not ideal...)
2978 : : */
2979 [ - + ]: 239 : if (haveempty)
2980 : : {
2588 andres@anarazel.de 2981 [ # # ]:UBC 0 : if (ndims == 0) /* didn't find any nonempty array */
2982 : : {
2983 : 0 : *op->resvalue = PointerGetDatum(construct_empty_array(element_type));
2984 : 0 : return;
2985 : : }
2986 [ # # ]: 0 : ereport(ERROR,
2987 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2988 : : errmsg("multidimensional arrays must have array "
2989 : : "expressions with matching dimensions")));
2990 : : }
2991 : :
2992 : : /* setup for multi-D array */
2588 andres@anarazel.de 2993 :CBC 239 : dims[0] = outer_nelems;
2994 : 239 : lbs[0] = 1;
1529 2995 [ + + ]: 594 : for (int i = 1; i < ndims; i++)
2996 : : {
2588 2997 : 355 : dims[i] = elem_dims[i - 1];
2998 : 355 : lbs[i] = elem_lbs[i - 1];
2999 : : }
3000 : :
3001 : : /* check for subscript overflow */
385 tgl@sss.pgh.pa.us 3002 : 239 : nitems = ArrayGetNItems(ndims, dims);
1070 3003 : 239 : ArrayCheckBounds(ndims, dims, lbs);
3004 : :
2588 andres@anarazel.de 3005 [ + + ]: 239 : if (havenulls)
3006 : : {
3007 : 15 : dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
3008 : 15 : nbytes += dataoffset;
3009 : : }
3010 : : else
3011 : : {
3012 : 224 : dataoffset = 0; /* marker for no null bitmap */
3013 : 224 : nbytes += ARR_OVERHEAD_NONULLS(ndims);
3014 : : }
3015 : :
385 tgl@sss.pgh.pa.us 3016 : 239 : result = (ArrayType *) palloc0(nbytes);
2588 andres@anarazel.de 3017 : 239 : SET_VARSIZE(result, nbytes);
3018 : 239 : result->ndim = ndims;
3019 : 239 : result->dataoffset = dataoffset;
3020 : 239 : result->elemtype = element_type;
3021 : 239 : memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
3022 : 239 : memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
3023 : :
3024 [ + + ]: 239 : dat = ARR_DATA_PTR(result);
3025 : 239 : iitem = 0;
1529 3026 [ + + ]: 667 : for (int i = 0; i < outer_nelems; i++)
3027 : : {
2588 3028 : 428 : memcpy(dat, subdata[i], subbytes[i]);
3029 : 428 : dat += subbytes[i];
3030 [ + + ]: 428 : if (havenulls)
2588 andres@anarazel.de 3031 :UBC 0 : array_bitmap_copy(ARR_NULLBITMAP(result), iitem,
2588 andres@anarazel.de 3032 :CBC 30 : subbitmaps[i], 0,
3033 [ + - ]: 30 : subnitems[i]);
3034 : 428 : iitem += subnitems[i];
3035 : : }
3036 : : }
3037 : :
3038 : 369231 : *op->resvalue = PointerGetDatum(result);
3039 : : }
3040 : :
3041 : : /*
3042 : : * Evaluate an ArrayCoerceExpr expression.
3043 : : *
3044 : : * Source array is in step's result variable.
3045 : : */
3046 : : void
2388 tgl@sss.pgh.pa.us 3047 : 33171 : ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
3048 : : {
3049 : : Datum arraydatum;
3050 : :
3051 : : /* NULL array -> NULL result */
2588 andres@anarazel.de 3052 [ + + ]: 33171 : if (*op->resnull)
3053 : 116 : return;
3054 : :
3055 : 33055 : arraydatum = *op->resvalue;
3056 : :
3057 : : /*
3058 : : * If it's binary-compatible, modify the element type in the array header,
3059 : : * but otherwise leave the array as we received it.
3060 : : */
2388 tgl@sss.pgh.pa.us 3061 [ + + ]: 33055 : if (op->d.arraycoerce.elemexprstate == NULL)
3062 : : {
3063 : : /* Detoast input array if necessary, and copy in any case */
2588 andres@anarazel.de 3064 : 32742 : ArrayType *array = DatumGetArrayTypePCopy(arraydatum);
3065 : :
3066 : 32742 : ARR_ELEMTYPE(array) = op->d.arraycoerce.resultelemtype;
3067 : 32742 : *op->resvalue = PointerGetDatum(array);
3068 : 32742 : return;
3069 : : }
3070 : :
3071 : : /*
3072 : : * Use array_map to apply the sub-expression to each array element.
3073 : : */
2388 tgl@sss.pgh.pa.us 3074 : 297 : *op->resvalue = array_map(arraydatum,
3075 : 313 : op->d.arraycoerce.elemexprstate,
3076 : : econtext,
3077 : : op->d.arraycoerce.resultelemtype,
2588 andres@anarazel.de 3078 : 313 : op->d.arraycoerce.amstate);
3079 : : }
3080 : :
3081 : : /*
3082 : : * Evaluate a ROW() expression.
3083 : : *
3084 : : * The individual columns have already been evaluated into
3085 : : * op->d.row.elemvalues[]/elemnulls[].
3086 : : */
3087 : : void
3088 : 13550 : ExecEvalRow(ExprState *state, ExprEvalStep *op)
3089 : : {
3090 : : HeapTuple tuple;
3091 : :
3092 : : /* build tuple from evaluated field values */
3093 : 13550 : tuple = heap_form_tuple(op->d.row.tupdesc,
2588 andres@anarazel.de 3094 :GIC 13550 : op->d.row.elemvalues,
3095 : 13550 : op->d.row.elemnulls);
3096 : :
2588 andres@anarazel.de 3097 :CBC 13550 : *op->resvalue = HeapTupleGetDatum(tuple);
3098 : 13550 : *op->resnull = false;
3099 : 13550 : }
3100 : :
3101 : : /*
3102 : : * Evaluate GREATEST() or LEAST() expression (note this is *not* MIN()/MAX()).
3103 : : *
3104 : : * All of the to-be-compared expressions have already been evaluated into
3105 : : * op->d.minmax.values[]/nulls[].
3106 : : */
3107 : : void
3108 : 11638 : ExecEvalMinMax(ExprState *state, ExprEvalStep *op)
3109 : : {
3110 : 11638 : Datum *values = op->d.minmax.values;
3111 : 11638 : bool *nulls = op->d.minmax.nulls;
3112 : 11638 : FunctionCallInfo fcinfo = op->d.minmax.fcinfo_data;
3113 : 11638 : MinMaxOp operator = op->d.minmax.op;
3114 : :
3115 : : /* set at initialization */
1905 3116 [ - + ]: 11638 : Assert(fcinfo->args[0].isnull == false);
3117 [ - + ]: 11638 : Assert(fcinfo->args[1].isnull == false);
3118 : :
3119 : : /* default to null result */
2588 3120 : 11638 : *op->resnull = true;
3121 : :
1529 3122 [ + + ]: 35061 : for (int off = 0; off < op->d.minmax.nelems; off++)
3123 : : {
3124 : : /* ignore NULL inputs */
2588 3125 [ + + ]: 23423 : if (nulls[off])
3126 : 61 : continue;
3127 : :
3128 [ + + ]: 23362 : if (*op->resnull)
3129 : : {
3130 : : /* first nonnull input, adopt value */
3131 : 11638 : *op->resvalue = values[off];
3132 : 11638 : *op->resnull = false;
3133 : : }
3134 : : else
3135 : : {
3136 : : int cmpresult;
3137 : :
3138 : : /* apply comparison function */
1905 3139 : 11724 : fcinfo->args[0].value = *op->resvalue;
3140 : 11724 : fcinfo->args[1].value = values[off];
3141 : :
2588 3142 : 11724 : fcinfo->isnull = false;
3143 : 11724 : cmpresult = DatumGetInt32(FunctionCallInvoke(fcinfo));
3144 [ - + ]: 11724 : if (fcinfo->isnull) /* probably should not happen */
2588 andres@anarazel.de 3145 :UBC 0 : continue;
3146 : :
2588 andres@anarazel.de 3147 [ + + + + ]:CBC 11724 : if (cmpresult > 0 && operator == IS_LEAST)
3148 : 124 : *op->resvalue = values[off];
3149 [ + + + + ]: 11600 : else if (cmpresult < 0 && operator == IS_GREATEST)
3150 : 101 : *op->resvalue = values[off];
3151 : : }
3152 : : }
3153 : 11638 : }
3154 : :
3155 : : /*
3156 : : * Evaluate a FieldSelect node.
3157 : : *
3158 : : * Source record is in step's result variable.
3159 : : */
3160 : : void
3161 : 56513 : ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
3162 : : {
3163 : 56513 : AttrNumber fieldnum = op->d.fieldselect.fieldnum;
3164 : : Datum tupDatum;
3165 : : HeapTupleHeader tuple;
3166 : : Oid tupType;
3167 : : int32 tupTypmod;
3168 : : TupleDesc tupDesc;
3169 : : Form_pg_attribute attr;
3170 : : HeapTupleData tmptup;
3171 : :
3172 : : /* NULL record -> NULL result */
3173 [ + + ]: 56513 : if (*op->resnull)
3174 : 88 : return;
3175 : :
3176 : 56425 : tupDatum = *op->resvalue;
3177 : :
3178 : : /* We can special-case expanded records for speed */
2252 tgl@sss.pgh.pa.us 3179 [ + + + + ]: 56425 : if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(tupDatum)))
3180 : 331 : {
3181 : 331 : ExpandedRecordHeader *erh = (ExpandedRecordHeader *) DatumGetEOHP(tupDatum);
3182 : :
3183 [ - + ]: 331 : Assert(erh->er_magic == ER_MAGIC);
3184 : :
3185 : : /* Extract record's TupleDesc */
3186 : 331 : tupDesc = expanded_record_get_tupdesc(erh);
3187 : :
3188 : : /*
3189 : : * Find field's attr record. Note we don't support system columns
3190 : : * here: a datum tuple doesn't have valid values for most of the
3191 : : * interesting system columns anyway.
3192 : : */
3193 [ - + ]: 331 : if (fieldnum <= 0) /* should never happen */
2252 tgl@sss.pgh.pa.us 3194 [ # # ]:UBC 0 : elog(ERROR, "unsupported reference to system column %d in FieldSelect",
3195 : : fieldnum);
2252 tgl@sss.pgh.pa.us 3196 [ - + ]:CBC 331 : if (fieldnum > tupDesc->natts) /* should never happen */
2252 tgl@sss.pgh.pa.us 3197 [ # # ]:UBC 0 : elog(ERROR, "attribute number %d exceeds number of columns %d",
3198 : : fieldnum, tupDesc->natts);
2252 tgl@sss.pgh.pa.us 3199 :CBC 331 : attr = TupleDescAttr(tupDesc, fieldnum - 1);
3200 : :
3201 : : /* Check for dropped column, and force a NULL result if so */
3202 [ - + ]: 331 : if (attr->attisdropped)
3203 : : {
2252 tgl@sss.pgh.pa.us 3204 :UBC 0 : *op->resnull = true;
3205 : 0 : return;
3206 : : }
3207 : :
3208 : : /* Check for type mismatch --- possible after ALTER COLUMN TYPE? */
3209 : : /* As in CheckVarSlotCompatibility, we should but can't check typmod */
2252 tgl@sss.pgh.pa.us 3210 [ - + ]:CBC 331 : if (op->d.fieldselect.resulttype != attr->atttypid)
2252 tgl@sss.pgh.pa.us 3211 [ # # ]:UBC 0 : ereport(ERROR,
3212 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
3213 : : errmsg("attribute %d has wrong type", fieldnum),
3214 : : errdetail("Table has type %s, but query expects %s.",
3215 : : format_type_be(attr->atttypid),
3216 : : format_type_be(op->d.fieldselect.resulttype))));
3217 : :
3218 : : /* extract the field */
2252 tgl@sss.pgh.pa.us 3219 :CBC 331 : *op->resvalue = expanded_record_get_field(erh, fieldnum,
3220 : : op->resnull);
3221 : : }
3222 : : else
3223 : : {
3224 : : /* Get the composite datum and extract its type fields */
3225 : 56094 : tuple = DatumGetHeapTupleHeader(tupDatum);
3226 : :
3227 : 56094 : tupType = HeapTupleHeaderGetTypeId(tuple);
3228 : 56094 : tupTypmod = HeapTupleHeaderGetTypMod(tuple);
3229 : :
3230 : : /* Lookup tupdesc if first time through or if type changes */
3231 : 56094 : tupDesc = get_cached_rowtype(tupType, tupTypmod,
3232 : : &op->d.fieldselect.rowcache, NULL);
3233 : :
3234 : : /*
3235 : : * Find field's attr record. Note we don't support system columns
3236 : : * here: a datum tuple doesn't have valid values for most of the
3237 : : * interesting system columns anyway.
3238 : : */
3239 [ - + ]: 56094 : if (fieldnum <= 0) /* should never happen */
2252 tgl@sss.pgh.pa.us 3240 [ # # ]:UBC 0 : elog(ERROR, "unsupported reference to system column %d in FieldSelect",
3241 : : fieldnum);
2252 tgl@sss.pgh.pa.us 3242 [ - + ]:CBC 56094 : if (fieldnum > tupDesc->natts) /* should never happen */
2252 tgl@sss.pgh.pa.us 3243 [ # # ]:UBC 0 : elog(ERROR, "attribute number %d exceeds number of columns %d",
3244 : : fieldnum, tupDesc->natts);
2252 tgl@sss.pgh.pa.us 3245 :CBC 56094 : attr = TupleDescAttr(tupDesc, fieldnum - 1);
3246 : :
3247 : : /* Check for dropped column, and force a NULL result if so */
3248 [ - + ]: 56094 : if (attr->attisdropped)
3249 : : {
2252 tgl@sss.pgh.pa.us 3250 :UBC 0 : *op->resnull = true;
3251 : 0 : return;
3252 : : }
3253 : :
3254 : : /* Check for type mismatch --- possible after ALTER COLUMN TYPE? */
3255 : : /* As in CheckVarSlotCompatibility, we should but can't check typmod */
2252 tgl@sss.pgh.pa.us 3256 [ - + ]:CBC 56094 : if (op->d.fieldselect.resulttype != attr->atttypid)
2252 tgl@sss.pgh.pa.us 3257 [ # # ]:UBC 0 : ereport(ERROR,
3258 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
3259 : : errmsg("attribute %d has wrong type", fieldnum),
3260 : : errdetail("Table has type %s, but query expects %s.",
3261 : : format_type_be(attr->atttypid),
3262 : : format_type_be(op->d.fieldselect.resulttype))));
3263 : :
3264 : : /* heap_getattr needs a HeapTuple not a bare HeapTupleHeader */
2252 tgl@sss.pgh.pa.us 3265 :CBC 56094 : tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
3266 : 56094 : tmptup.t_data = tuple;
3267 : :
3268 : : /* extract the field */
3269 : 56094 : *op->resvalue = heap_getattr(&tmptup,
3270 : : fieldnum,
3271 : : tupDesc,
3272 : : op->resnull);
3273 : : }
3274 : : }
3275 : :
3276 : : /*
3277 : : * Deform source tuple, filling in the step's values/nulls arrays, before
3278 : : * evaluating individual new values as part of a FieldStore expression.
3279 : : * Subsequent steps will overwrite individual elements of the values/nulls
3280 : : * arrays with the new field values, and then FIELDSTORE_FORM will build the
3281 : : * new tuple value.
3282 : : *
3283 : : * Source record is in step's result variable.
3284 : : */
3285 : : void
2588 andres@anarazel.de 3286 : 260 : ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
3287 : : {
3288 [ + + ]: 260 : if (*op->resnull)
3289 : : {
3290 : : /* Convert null input tuple into an all-nulls row */
3291 : 128 : memset(op->d.fieldstore.nulls, true,
3292 : 128 : op->d.fieldstore.ncolumns * sizeof(bool));
3293 : : }
3294 : : else
3295 : : {
3296 : : /*
3297 : : * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader. We
3298 : : * set all the fields in the struct just in case.
3299 : : */
3300 : 132 : Datum tupDatum = *op->resvalue;
3301 : : HeapTupleHeader tuphdr;
3302 : : HeapTupleData tmptup;
3303 : : TupleDesc tupDesc;
3304 : :
3305 : 132 : tuphdr = DatumGetHeapTupleHeader(tupDatum);
3306 : 132 : tmptup.t_len = HeapTupleHeaderGetDatumLength(tuphdr);
3307 : 132 : ItemPointerSetInvalid(&(tmptup.t_self));
3308 : 132 : tmptup.t_tableOid = InvalidOid;
3309 : 132 : tmptup.t_data = tuphdr;
3310 : :
3311 : : /*
3312 : : * Lookup tupdesc if first time through or if type changes. Because
3313 : : * we don't pin the tupdesc, we must not do this lookup until after
3314 : : * doing DatumGetHeapTupleHeader: that could do database access while
3315 : : * detoasting the datum.
3316 : : */
290 tgl@sss.pgh.pa.us 3317 : 132 : tupDesc = get_cached_rowtype(op->d.fieldstore.fstore->resulttype, -1,
3318 : : op->d.fieldstore.rowcache, NULL);
3319 : :
3320 : : /* Check that current tupdesc doesn't have more fields than allocated */
3321 [ - + ]: 132 : if (unlikely(tupDesc->natts > op->d.fieldstore.ncolumns))
290 tgl@sss.pgh.pa.us 3322 [ # # ]:UBC 0 : elog(ERROR, "too many columns in composite type %u",
3323 : : op->d.fieldstore.fstore->resulttype);
3324 : :
2588 andres@anarazel.de 3325 :CBC 132 : heap_deform_tuple(&tmptup, tupDesc,
3326 : : op->d.fieldstore.values,
3327 : : op->d.fieldstore.nulls);
3328 : : }
3329 : 260 : }
3330 : :
3331 : : /*
3332 : : * Compute the new composite datum after each individual field value of a
3333 : : * FieldStore expression has been evaluated.
3334 : : */
3335 : : void
3336 : 260 : ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
3337 : : {
3338 : : TupleDesc tupDesc;
3339 : : HeapTuple tuple;
3340 : :
3341 : : /* Lookup tupdesc (should be valid already) */
1097 tgl@sss.pgh.pa.us 3342 : 260 : tupDesc = get_cached_rowtype(op->d.fieldstore.fstore->resulttype, -1,
3343 : : op->d.fieldstore.rowcache, NULL);
3344 : :
3345 : 260 : tuple = heap_form_tuple(tupDesc,
2588 andres@anarazel.de 3346 :GIC 260 : op->d.fieldstore.values,
3347 : 260 : op->d.fieldstore.nulls);
3348 : :
2588 andres@anarazel.de 3349 :CBC 260 : *op->resvalue = HeapTupleGetDatum(tuple);
3350 : 260 : *op->resnull = false;
3351 : 260 : }
3352 : :
3353 : : /*
3354 : : * Evaluate a rowtype coercion operation.
3355 : : * This may require rearranging field positions.
3356 : : *
3357 : : * Source record is in step's result variable.
3358 : : */
3359 : : void
3360 : 6006 : ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
3361 : : {
3362 : : HeapTuple result;
3363 : : Datum tupDatum;
3364 : : HeapTupleHeader tuple;
3365 : : HeapTupleData tmptup;
3366 : : TupleDesc indesc,
3367 : : outdesc;
1097 tgl@sss.pgh.pa.us 3368 : 6006 : bool changed = false;
3369 : :
3370 : : /* NULL in -> NULL out */
2588 andres@anarazel.de 3371 [ + + ]: 6006 : if (*op->resnull)
3372 : 3 : return;
3373 : :
3374 : 6003 : tupDatum = *op->resvalue;
3375 : 6003 : tuple = DatumGetHeapTupleHeader(tupDatum);
3376 : :
3377 : : /*
3378 : : * Lookup tupdescs if first time through or if type changes. We'd better
3379 : : * pin them since type conversion functions could do catalog lookups and
3380 : : * hence cause cache invalidation.
3381 : : */
1097 tgl@sss.pgh.pa.us 3382 : 6003 : indesc = get_cached_rowtype(op->d.convert_rowtype.inputtype, -1,
3383 : : op->d.convert_rowtype.incache,
3384 : : &changed);
3385 : 6003 : IncrTupleDescRefCount(indesc);
3386 : 6003 : outdesc = get_cached_rowtype(op->d.convert_rowtype.outputtype, -1,
3387 : : op->d.convert_rowtype.outcache,
3388 : : &changed);
3389 : 6003 : IncrTupleDescRefCount(outdesc);
3390 : :
3391 : : /*
3392 : : * We used to be able to assert that incoming tuples are marked with
3393 : : * exactly the rowtype of indesc. However, now that ExecEvalWholeRowVar
3394 : : * might change the tuples' marking to plain RECORD due to inserting
3395 : : * aliases, we can only make this weak test:
3396 : : */
2588 andres@anarazel.de 3397 [ - + - - ]: 6003 : Assert(HeapTupleHeaderGetTypeId(tuple) == indesc->tdtypeid ||
3398 : : HeapTupleHeaderGetTypeId(tuple) == RECORDOID);
3399 : :
3400 : : /* if first time through, or after change, initialize conversion map */
1097 tgl@sss.pgh.pa.us 3401 [ + + ]: 6003 : if (changed)
3402 : : {
3403 : : MemoryContext old_cxt;
3404 : :
3405 : : /* allocate map in long-lived memory context */
2588 andres@anarazel.de 3406 : 179 : old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
3407 : :
3408 : : /* prepare map from old to new attribute numbers */
1685 alvherre@alvh.no-ip. 3409 : 179 : op->d.convert_rowtype.map = convert_tuples_by_name(indesc, outdesc);
3410 : :
2588 andres@anarazel.de 3411 : 179 : MemoryContextSwitchTo(old_cxt);
3412 : : }
3413 : :
3414 : : /* Following steps need a HeapTuple not a bare HeapTupleHeader */
3415 : 6003 : tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
3416 : 6003 : tmptup.t_data = tuple;
3417 : :
2565 tgl@sss.pgh.pa.us 3418 [ + + ]: 6003 : if (op->d.convert_rowtype.map != NULL)
3419 : : {
3420 : : /* Full conversion with attribute rearrangement needed */
2021 andres@anarazel.de 3421 : 286 : result = execute_attr_map_tuple(&tmptup, op->d.convert_rowtype.map);
3422 : : /* Result already has appropriate composite-datum header fields */
2565 tgl@sss.pgh.pa.us 3423 : 286 : *op->resvalue = HeapTupleGetDatum(result);
3424 : : }
3425 : : else
3426 : : {
3427 : : /*
3428 : : * The tuple is physically compatible as-is, but we need to insert the
3429 : : * destination rowtype OID in its composite-datum header field, so we
3430 : : * have to copy it anyway. heap_copy_tuple_as_datum() is convenient
3431 : : * for this since it will both make the physical copy and insert the
3432 : : * correct composite header fields. Note that we aren't expecting to
3433 : : * have to flatten any toasted fields: the input was a composite
3434 : : * datum, so it shouldn't contain any. So heap_copy_tuple_as_datum()
3435 : : * is overkill here, but its check for external fields is cheap.
3436 : : */
3437 : 5717 : *op->resvalue = heap_copy_tuple_as_datum(&tmptup, outdesc);
3438 : : }
3439 : :
1097 3440 : 6003 : DecrTupleDescRefCount(indesc);
3441 : 6003 : DecrTupleDescRefCount(outdesc);
3442 : : }
3443 : :
3444 : : /*
3445 : : * Evaluate "scalar op ANY/ALL (array)".
3446 : : *
3447 : : * Source array is in our result area, scalar arg is already evaluated into
3448 : : * fcinfo->args[0].
3449 : : *
3450 : : * The operator always yields boolean, and we combine the results across all
3451 : : * array elements using OR and AND (for ANY and ALL respectively). Of course
3452 : : * we short-circuit as soon as the result is known.
3453 : : */
3454 : : void
2588 andres@anarazel.de 3455 : 2018391 : ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
3456 : : {
3457 : 2018391 : FunctionCallInfo fcinfo = op->d.scalararrayop.fcinfo_data;
3458 : 2018391 : bool useOr = op->d.scalararrayop.useOr;
3459 : 2018391 : bool strictfunc = op->d.scalararrayop.finfo->fn_strict;
3460 : : ArrayType *arr;
3461 : : int nitems;
3462 : : Datum result;
3463 : : bool resultnull;
3464 : : int16 typlen;
3465 : : bool typbyval;
3466 : : char typalign;
3467 : : char *s;
3468 : : bits8 *bitmap;
3469 : : int bitmask;
3470 : :
3471 : : /*
3472 : : * If the array is NULL then we return NULL --- it's not very meaningful
3473 : : * to do anything else, even if the operator isn't strict.
3474 : : */
3475 [ + + ]: 2018391 : if (*op->resnull)
3476 : 77460 : return;
3477 : :
3478 : : /* Else okay to fetch and detoast the array */
3479 : 1940931 : arr = DatumGetArrayTypeP(*op->resvalue);
3480 : :
3481 : : /*
3482 : : * If the array is empty, we return either FALSE or TRUE per the useOr
3483 : : * flag. This is correct even if the scalar is NULL; since we would
3484 : : * evaluate the operator zero times, it matters not whether it would want
3485 : : * to return NULL.
3486 : : */
3487 : 1940931 : nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
3488 [ + + ]: 1940931 : if (nitems <= 0)
3489 : : {
3490 : 5866 : *op->resvalue = BoolGetDatum(!useOr);
3491 : 5866 : *op->resnull = false;
3492 : 5866 : return;
3493 : : }
3494 : :
3495 : : /*
3496 : : * If the scalar is NULL, and the function is strict, return NULL; no
3497 : : * point in iterating the loop.
3498 : : */
1905 3499 [ + + + + ]: 1935065 : if (fcinfo->args[0].isnull && strictfunc)
3500 : : {
2588 3501 : 493 : *op->resnull = true;
3502 : 493 : return;
3503 : : }
3504 : :
3505 : : /*
3506 : : * We arrange to look up info about the element type only once per series
3507 : : * of calls, assuming the element type doesn't change underneath us.
3508 : : */
3509 [ + + ]: 1934572 : if (op->d.scalararrayop.element_type != ARR_ELEMTYPE(arr))
3510 : : {
3511 : 6961 : get_typlenbyvalalign(ARR_ELEMTYPE(arr),
3512 : : &op->d.scalararrayop.typlen,
3513 : : &op->d.scalararrayop.typbyval,
3514 : : &op->d.scalararrayop.typalign);
3515 : 6961 : op->d.scalararrayop.element_type = ARR_ELEMTYPE(arr);
3516 : : }
3517 : :
3518 : 1934572 : typlen = op->d.scalararrayop.typlen;
3519 : 1934572 : typbyval = op->d.scalararrayop.typbyval;
3520 : 1934572 : typalign = op->d.scalararrayop.typalign;
3521 : :
3522 : : /* Initialize result appropriately depending on useOr */
3523 : 1934572 : result = BoolGetDatum(!useOr);
3524 : 1934572 : resultnull = false;
3525 : :
3526 : : /* Loop over the array elements */
3527 [ + + ]: 1934572 : s = (char *) ARR_DATA_PTR(arr);
3528 [ + + ]: 1934572 : bitmap = ARR_NULLBITMAP(arr);
3529 : 1934572 : bitmask = 1;
3530 : :
1529 3531 [ + + ]: 5213452 : for (int i = 0; i < nitems; i++)
3532 : : {
3533 : : Datum elt;
3534 : : Datum thisresult;
3535 : :
3536 : : /* Get array element, checking for NULL */
2588 3537 [ + + + + ]: 4115343 : if (bitmap && (*bitmap & bitmask) == 0)
3538 : : {
1905 3539 : 107632 : fcinfo->args[1].value = (Datum) 0;
3540 : 107632 : fcinfo->args[1].isnull = true;
3541 : : }
3542 : : else
3543 : : {
2588 3544 : 4007711 : elt = fetch_att(s, typbyval, typlen);
3545 [ + + + - : 4007711 : s = att_addlength_pointer(s, typlen, s);
- + - - -
- - - - +
- - ]
3546 [ + + + + : 4007711 : s = (char *) att_align_nominal(s, typalign);
+ + - + ]
1905 3547 : 4007711 : fcinfo->args[1].value = elt;
3548 : 4007711 : fcinfo->args[1].isnull = false;
3549 : : }
3550 : :
3551 : : /* Call comparison function */
3552 [ + + + + ]: 4115343 : if (fcinfo->args[1].isnull && strictfunc)
3553 : : {
2588 3554 : 107620 : fcinfo->isnull = true;
3555 : 107620 : thisresult = (Datum) 0;
3556 : : }
3557 : : else
3558 : : {
3559 : 4007723 : fcinfo->isnull = false;
2411 peter_e@gmx.net 3560 : 4007723 : thisresult = op->d.scalararrayop.fn_addr(fcinfo);
3561 : : }
3562 : :
3563 : : /* Combine results per OR or AND semantics */
2588 andres@anarazel.de 3564 [ + + ]: 4115343 : if (fcinfo->isnull)
3565 : 107668 : resultnull = true;
3566 [ + + ]: 4007675 : else if (useOr)
3567 : : {
3568 [ + + ]: 3601178 : if (DatumGetBool(thisresult))
3569 : : {
3570 : 545401 : result = BoolGetDatum(true);
3571 : 545401 : resultnull = false;
3572 : 545401 : break; /* needn't look at any more elements */
3573 : : }
3574 : : }
3575 : : else
3576 : : {
3577 [ + + ]: 406497 : if (!DatumGetBool(thisresult))
3578 : : {
3579 : 291062 : result = BoolGetDatum(false);
3580 : 291062 : resultnull = false;
3581 : 291062 : break; /* needn't look at any more elements */
3582 : : }
3583 : : }
3584 : :
3585 : : /* advance bitmap pointer if any */
3586 [ + + ]: 3278880 : if (bitmap)
3587 : : {
3588 : 383994 : bitmask <<= 1;
3589 [ + + ]: 383994 : if (bitmask == 0x100)
3590 : : {
3591 : 388 : bitmap++;
3592 : 388 : bitmask = 1;
3593 : : }
3594 : : }
3595 : : }
3596 : :
3597 : 1934572 : *op->resvalue = result;
3598 : 1934572 : *op->resnull = resultnull;
3599 : : }
3600 : :
3601 : : /*
3602 : : * Hash function for scalar array hash op elements.
3603 : : *
3604 : : * We use the element type's default hash opclass, and the column collation
3605 : : * if the type is collation-sensitive.
3606 : : */
3607 : : static uint32
1102 drowley@postgresql.o 3608 : 3228 : saop_element_hash(struct saophash_hash *tb, Datum key)
3609 : : {
3610 : 3228 : ScalarArrayOpExprHashTable *elements_tab = (ScalarArrayOpExprHashTable *) tb->private_data;
648 3611 : 3228 : FunctionCallInfo fcinfo = &elements_tab->hash_fcinfo_data;
3612 : : Datum hash;
3613 : :
1102 3614 : 3228 : fcinfo->args[0].value = key;
3615 : 3228 : fcinfo->args[0].isnull = false;
3616 : :
648 3617 : 3228 : hash = elements_tab->hash_finfo.fn_addr(fcinfo);
3618 : :
1102 3619 : 3228 : return DatumGetUInt32(hash);
3620 : : }
3621 : :
3622 : : /*
3623 : : * Matching function for scalar array hash op elements, to be used in hashtable
3624 : : * lookups.
3625 : : */
3626 : : static bool
3627 : 2199 : saop_hash_element_match(struct saophash_hash *tb, Datum key1, Datum key2)
3628 : : {
3629 : : Datum result;
3630 : :
3631 : 2199 : ScalarArrayOpExprHashTable *elements_tab = (ScalarArrayOpExprHashTable *) tb->private_data;
3632 : 2199 : FunctionCallInfo fcinfo = elements_tab->op->d.hashedscalararrayop.fcinfo_data;
3633 : :
3634 : 2199 : fcinfo->args[0].value = key1;
3635 : 2199 : fcinfo->args[0].isnull = false;
3636 : 2199 : fcinfo->args[1].value = key2;
3637 : 2199 : fcinfo->args[1].isnull = false;
3638 : :
648 3639 : 2199 : result = elements_tab->op->d.hashedscalararrayop.finfo->fn_addr(fcinfo);
3640 : :
1102 3641 : 2199 : return DatumGetBool(result);
3642 : : }
3643 : :
3644 : : /*
3645 : : * Evaluate "scalar op ANY (const array)".
3646 : : *
3647 : : * Similar to ExecEvalScalarArrayOp, but optimized for faster repeat lookups
3648 : : * by building a hashtable on the first lookup. This hashtable will be reused
3649 : : * by subsequent lookups. Unlike ExecEvalScalarArrayOp, this version only
3650 : : * supports OR semantics.
3651 : : *
3652 : : * Source array is in our result area, scalar arg is already evaluated into
3653 : : * fcinfo->args[0].
3654 : : *
3655 : : * The operator always yields boolean.
3656 : : */
3657 : : void
3658 : 2305 : ExecEvalHashedScalarArrayOp(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
3659 : : {
3660 : 2305 : ScalarArrayOpExprHashTable *elements_tab = op->d.hashedscalararrayop.elements_tab;
3661 : 2305 : FunctionCallInfo fcinfo = op->d.hashedscalararrayop.fcinfo_data;
1012 3662 : 2305 : bool inclause = op->d.hashedscalararrayop.inclause;
1102 3663 : 2305 : bool strictfunc = op->d.hashedscalararrayop.finfo->fn_strict;
3664 : 2305 : Datum scalar = fcinfo->args[0].value;
3665 : 2305 : bool scalar_isnull = fcinfo->args[0].isnull;
3666 : : Datum result;
3667 : : bool resultnull;
3668 : : bool hashfound;
3669 : :
3670 : : /* We don't setup a hashed scalar array op if the array const is null. */
3671 [ - + ]: 2305 : Assert(!*op->resnull);
3672 : :
3673 : : /*
3674 : : * If the scalar is NULL, and the function is strict, return NULL; no
3675 : : * point in executing the search.
3676 : : */
3677 [ + + + + ]: 2305 : if (fcinfo->args[0].isnull && strictfunc)
3678 : : {
3679 : 34 : *op->resnull = true;
3680 : 34 : return;
3681 : : }
3682 : :
3683 : : /* Build the hash table on first evaluation */
3684 [ + + ]: 2271 : if (elements_tab == NULL)
3685 : : {
3686 : : ScalarArrayOpExpr *saop;
3687 : : int16 typlen;
3688 : : bool typbyval;
3689 : : char typalign;
3690 : : int nitems;
3691 : 76 : bool has_nulls = false;
3692 : : char *s;
3693 : : bits8 *bitmap;
3694 : : int bitmask;
3695 : : MemoryContext oldcontext;
3696 : : ArrayType *arr;
3697 : :
648 3698 : 76 : saop = op->d.hashedscalararrayop.saop;
3699 : :
1102 3700 : 76 : arr = DatumGetArrayTypeP(*op->resvalue);
3701 : 76 : nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
3702 : :
3703 : 76 : get_typlenbyvalalign(ARR_ELEMTYPE(arr),
3704 : : &typlen,
3705 : : &typbyval,
3706 : : &typalign);
3707 : :
3708 : 76 : oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
3709 : :
3710 : : elements_tab = (ScalarArrayOpExprHashTable *)
648 3711 : 76 : palloc0(offsetof(ScalarArrayOpExprHashTable, hash_fcinfo_data) +
3712 : : SizeForFunctionCallInfo(1));
1102 3713 : 76 : op->d.hashedscalararrayop.elements_tab = elements_tab;
3714 : 76 : elements_tab->op = op;
3715 : :
648 3716 : 76 : fmgr_info(saop->hashfuncid, &elements_tab->hash_finfo);
3717 : 76 : fmgr_info_set_expr((Node *) saop, &elements_tab->hash_finfo);
3718 : :
3719 : 76 : InitFunctionCallInfoData(elements_tab->hash_fcinfo_data,
3720 : : &elements_tab->hash_finfo,
3721 : : 1,
3722 : : saop->inputcollid,
3723 : : NULL,
3724 : : NULL);
3725 : :
3726 : : /*
3727 : : * Create the hash table sizing it according to the number of elements
3728 : : * in the array. This does assume that the array has no duplicates.
3729 : : * If the array happens to contain many duplicate values then it'll
3730 : : * just mean that we sized the table a bit on the large side.
3731 : : */
1102 3732 : 76 : elements_tab->hashtab = saophash_create(CurrentMemoryContext, nitems,
3733 : : elements_tab);
3734 : :
3735 : 76 : MemoryContextSwitchTo(oldcontext);
3736 : :
3737 [ + + ]: 76 : s = (char *) ARR_DATA_PTR(arr);
3738 [ + + ]: 76 : bitmap = ARR_NULLBITMAP(arr);
3739 : 76 : bitmask = 1;
3740 [ + + ]: 1120 : for (int i = 0; i < nitems; i++)
3741 : : {
3742 : : /* Get array element, checking for NULL. */
3743 [ + + + + ]: 1044 : if (bitmap && (*bitmap & bitmask) == 0)
3744 : : {
3745 : 87 : has_nulls = true;
3746 : : }
3747 : : else
3748 : : {
3749 : : Datum element;
3750 : :
3751 : 957 : element = fetch_att(s, typbyval, typlen);
3752 [ + + + - : 957 : s = att_addlength_pointer(s, typlen, s);
- + - - -
- - - - +
- - ]
3753 [ + + - + : 957 : s = (char *) att_align_nominal(s, typalign);
- - - - ]
3754 : :
3755 : 957 : saophash_insert(elements_tab->hashtab, element, &hashfound);
3756 : : }
3757 : :
3758 : : /* Advance bitmap pointer if any. */
3759 [ + + ]: 1044 : if (bitmap)
3760 : : {
3761 : 285 : bitmask <<= 1;
3762 [ + + ]: 285 : if (bitmask == 0x100)
3763 : : {
3764 : 27 : bitmap++;
3765 : 27 : bitmask = 1;
3766 : : }
3767 : : }
3768 : : }
3769 : :
3770 : : /*
3771 : : * Remember if we had any nulls so that we know if we need to execute
3772 : : * non-strict functions with a null lhs value if no match is found.
3773 : : */
3774 : 76 : op->d.hashedscalararrayop.has_nulls = has_nulls;
3775 : : }
3776 : :
3777 : : /* Check the hash to see if we have a match. */
3778 : 2271 : hashfound = NULL != saophash_lookup(elements_tab->hashtab, scalar);
3779 : :
3780 : : /* the result depends on if the clause is an IN or NOT IN clause */
1012 3781 [ + + ]: 2271 : if (inclause)
3782 : 427 : result = BoolGetDatum(hashfound); /* IN */
3783 : : else
3784 : 1844 : result = BoolGetDatum(!hashfound); /* NOT IN */
3785 : :
1102 3786 : 2271 : resultnull = false;
3787 : :
3788 : : /*
3789 : : * If we didn't find a match in the array, we still might need to handle
3790 : : * the possibility of null values. We didn't put any NULLs into the
3791 : : * hashtable, but instead marked if we found any when building the table
3792 : : * in has_nulls.
3793 : : */
1012 3794 [ + + + + ]: 2271 : if (!hashfound && op->d.hashedscalararrayop.has_nulls)
3795 : : {
1102 3796 [ + + ]: 21 : if (strictfunc)
3797 : : {
3798 : :
3799 : : /*
3800 : : * We have nulls in the array so a non-null lhs and no match must
3801 : : * yield NULL.
3802 : : */
3803 : 12 : result = (Datum) 0;
3804 : 12 : resultnull = true;
3805 : : }
3806 : : else
3807 : : {
3808 : : /*
3809 : : * Execute function will null rhs just once.
3810 : : *
3811 : : * The hash lookup path will have scribbled on the lhs argument so
3812 : : * we need to set it up also (even though we entered this function
3813 : : * with it already set).
3814 : : */
3815 : 9 : fcinfo->args[0].value = scalar;
3816 : 9 : fcinfo->args[0].isnull = scalar_isnull;
3817 : 9 : fcinfo->args[1].value = (Datum) 0;
3818 : 9 : fcinfo->args[1].isnull = true;
3819 : :
648 3820 : 9 : result = op->d.hashedscalararrayop.finfo->fn_addr(fcinfo);
1102 3821 : 9 : resultnull = fcinfo->isnull;
3822 : :
3823 : : /*
3824 : : * Reverse the result for NOT IN clauses since the above function
3825 : : * is the equality function and we need not-equals.
3826 : : */
1012 3827 [ + + ]: 9 : if (!inclause)
3828 : 6 : result = !result;
3829 : : }
3830 : : }
3831 : :
1102 3832 : 2271 : *op->resvalue = result;
3833 : 2271 : *op->resnull = resultnull;
3834 : : }
3835 : :
3836 : : /*
3837 : : * Evaluate a NOT NULL domain constraint.
3838 : : */
3839 : : void
2588 andres@anarazel.de 3840 : 198 : ExecEvalConstraintNotNull(ExprState *state, ExprEvalStep *op)
3841 : : {
3842 [ + + ]: 198 : if (*op->resnull)
81 amitlan@postgresql.o 3843 [ + + ]:GNC 59 : errsave((Node *) op->d.domaincheck.escontext,
3844 : : (errcode(ERRCODE_NOT_NULL_VIOLATION),
3845 : : errmsg("domain %s does not allow null values",
3846 : : format_type_be(op->d.domaincheck.resulttype)),
3847 : : errdatatype(op->d.domaincheck.resulttype)));
2588 andres@anarazel.de 3848 :CBC 142 : }
3849 : :
3850 : : /*
3851 : : * Evaluate a CHECK domain constraint.
3852 : : */
3853 : : void
3854 : 5939 : ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op)
3855 : : {
3856 [ + + ]: 5939 : if (!*op->d.domaincheck.checknull &&
3857 [ + + ]: 5417 : !DatumGetBool(*op->d.domaincheck.checkvalue))
81 amitlan@postgresql.o 3858 [ + + ]:GNC 217 : errsave((Node *) op->d.domaincheck.escontext,
3859 : : (errcode(ERRCODE_CHECK_VIOLATION),
3860 : : errmsg("value for domain %s violates check constraint \"%s\"",
3861 : : format_type_be(op->d.domaincheck.resulttype),
3862 : : op->d.domaincheck.constraintname),
3863 : : errdomainconstraint(op->d.domaincheck.resulttype,
3864 : : op->d.domaincheck.constraintname)));
2588 andres@anarazel.de 3865 :CBC 5728 : }
3866 : :
3867 : : /*
3868 : : * Evaluate the various forms of XmlExpr.
3869 : : *
3870 : : * Arguments have been evaluated into named_argvalue/named_argnull
3871 : : * and/or argvalue/argnull arrays.
3872 : : */
3873 : : void
3874 : 22041 : ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op)
3875 : : {
3876 : 22041 : XmlExpr *xexpr = op->d.xmlexpr.xexpr;
3877 : : Datum value;
3878 : :
3879 : 22041 : *op->resnull = true; /* until we get a result */
3880 : 22041 : *op->resvalue = (Datum) 0;
3881 : :
3882 [ + + + + : 22041 : switch (xexpr->op)
+ + + +
- ]
3883 : : {
3884 : 27 : case IS_XMLCONCAT:
3885 : : {
3886 : 27 : Datum *argvalue = op->d.xmlexpr.argvalue;
3887 : 27 : bool *argnull = op->d.xmlexpr.argnull;
3888 : 27 : List *values = NIL;
3889 : :
1529 3890 [ + + ]: 87 : for (int i = 0; i < list_length(xexpr->args); i++)
3891 : : {
2588 3892 [ + + ]: 60 : if (!argnull[i])
3893 : 45 : values = lappend(values, DatumGetPointer(argvalue[i]));
3894 : : }
3895 : :
3896 [ + + ]: 27 : if (values != NIL)
3897 : : {
3898 : 21 : *op->resvalue = PointerGetDatum(xmlconcat(values));
3899 : 21 : *op->resnull = false;
3900 : : }
3901 : : }
3902 : 27 : break;
3903 : :
3904 : 10851 : case IS_XMLFOREST:
3905 : : {
3906 : 10851 : Datum *argvalue = op->d.xmlexpr.named_argvalue;
3907 : 10851 : bool *argnull = op->d.xmlexpr.named_argnull;
3908 : : StringInfoData buf;
3909 : : ListCell *lc;
3910 : : ListCell *lc2;
3911 : : int i;
3912 : :
3913 : 10851 : initStringInfo(&buf);
3914 : :
3915 : 10851 : i = 0;
3916 [ + - + + : 75897 : forboth(lc, xexpr->named_args, lc2, xexpr->arg_names)
+ - + + +
+ + - +
+ ]
3917 : : {
3918 : 65046 : Expr *e = (Expr *) lfirst(lc);
3919 : 65046 : char *argname = strVal(lfirst(lc2));
3920 : :
3921 [ + + ]: 65046 : if (!argnull[i])
3922 : : {
3923 : 54191 : value = argvalue[i];
3924 : 54191 : appendStringInfo(&buf, "<%s>%s</%s>",
3925 : : argname,
3926 : : map_sql_value_to_xml_value(value,
3927 : : exprType((Node *) e), true),
3928 : : argname);
3929 : 54191 : *op->resnull = false;
3930 : : }
3931 : 65046 : i++;
3932 : : }
3933 : :
3934 [ + - ]: 10851 : if (!*op->resnull)
3935 : : {
3936 : : text *result;
3937 : :
3938 : 10851 : result = cstring_to_text_with_len(buf.data, buf.len);
3939 : 10851 : *op->resvalue = PointerGetDatum(result);
3940 : : }
3941 : :
3942 : 10851 : pfree(buf.data);
3943 : : }
3944 : 10851 : break;
3945 : :
3946 : 10929 : case IS_XMLELEMENT:
3947 : 10929 : *op->resvalue = PointerGetDatum(xmlelement(xexpr,
3948 : : op->d.xmlexpr.named_argvalue,
3949 : : op->d.xmlexpr.named_argnull,
3950 : : op->d.xmlexpr.argvalue,
3951 : : op->d.xmlexpr.argnull));
3952 : 10926 : *op->resnull = false;
3953 : 10926 : break;
3954 : :
3955 : 66 : case IS_XMLPARSE:
3956 : : {
3957 : 66 : Datum *argvalue = op->d.xmlexpr.argvalue;
3958 : 66 : bool *argnull = op->d.xmlexpr.argnull;
3959 : : text *data;
3960 : : bool preserve_whitespace;
3961 : :
3962 : : /* arguments are known to be text, bool */
3963 [ - + ]: 66 : Assert(list_length(xexpr->args) == 2);
3964 : :
3965 [ - + ]: 66 : if (argnull[0])
2588 andres@anarazel.de 3966 :UBC 0 : return;
2588 andres@anarazel.de 3967 :CBC 66 : value = argvalue[0];
3968 : 66 : data = DatumGetTextPP(value);
3969 : :
3970 [ - + ]: 66 : if (argnull[1]) /* probably can't happen */
2588 andres@anarazel.de 3971 :UBC 0 : return;
2588 andres@anarazel.de 3972 :CBC 66 : value = argvalue[1];
3973 : 66 : preserve_whitespace = DatumGetBool(value);
3974 : :
3975 : 66 : *op->resvalue = PointerGetDatum(xmlparse(data,
3976 : : xexpr->xmloption,
3977 : : preserve_whitespace));
3978 : 42 : *op->resnull = false;
3979 : : }
3980 : 42 : break;
3981 : :
3982 : 36 : case IS_XMLPI:
3983 : : {
3984 : : text *arg;
3985 : : bool isnull;
3986 : :
3987 : : /* optional argument is known to be text */
3988 [ - + ]: 36 : Assert(list_length(xexpr->args) <= 1);
3989 : :
3990 [ + + ]: 36 : if (xexpr->args)
3991 : : {
3992 : 21 : isnull = op->d.xmlexpr.argnull[0];
3993 [ + + ]: 21 : if (isnull)
3994 : 9 : arg = NULL;
3995 : : else
3996 : 12 : arg = DatumGetTextPP(op->d.xmlexpr.argvalue[0]);
3997 : : }
3998 : : else
3999 : : {
4000 : 15 : arg = NULL;
4001 : 15 : isnull = false;
4002 : : }
4003 : :
4004 : 36 : *op->resvalue = PointerGetDatum(xmlpi(xexpr->name,
4005 : : arg,
4006 : : isnull,
4007 : : op->resnull));
4008 : : }
4009 : 27 : break;
4010 : :
4011 : 30 : case IS_XMLROOT:
4012 : : {
4013 : 30 : Datum *argvalue = op->d.xmlexpr.argvalue;
4014 : 30 : bool *argnull = op->d.xmlexpr.argnull;
4015 : : xmltype *data;
4016 : : text *version;
4017 : : int standalone;
4018 : :
4019 : : /* arguments are known to be xml, text, int */
4020 [ - + ]: 30 : Assert(list_length(xexpr->args) == 3);
4021 : :
4022 [ - + ]: 30 : if (argnull[0])
2588 andres@anarazel.de 4023 :UBC 0 : return;
2588 andres@anarazel.de 4024 :CBC 30 : data = DatumGetXmlP(argvalue[0]);
4025 : :
4026 [ + + ]: 30 : if (argnull[1])
4027 : 18 : version = NULL;
4028 : : else
4029 : 12 : version = DatumGetTextPP(argvalue[1]);
4030 : :
4031 [ - + ]: 30 : Assert(!argnull[2]); /* always present */
4032 : 30 : standalone = DatumGetInt32(argvalue[2]);
4033 : :
4034 : 30 : *op->resvalue = PointerGetDatum(xmlroot(data,
4035 : : version,
4036 : : standalone));
4037 : 30 : *op->resnull = false;
4038 : : }
4039 : 30 : break;
4040 : :
4041 : 90 : case IS_XMLSERIALIZE:
4042 : : {
4043 : 90 : Datum *argvalue = op->d.xmlexpr.argvalue;
4044 : 90 : bool *argnull = op->d.xmlexpr.argnull;
4045 : :
4046 : : /* argument type is known to be xml */
4047 [ - + ]: 90 : Assert(list_length(xexpr->args) == 1);
4048 : :
4049 [ + + ]: 90 : if (argnull[0])
4050 : 6 : return;
4051 : 84 : value = argvalue[0];
4052 : :
396 tgl@sss.pgh.pa.us 4053 : 138 : *op->resvalue =
4054 : 84 : PointerGetDatum(xmltotext_with_options(DatumGetXmlP(value),
4055 : : xexpr->xmloption,
4056 : 84 : xexpr->indent));
2588 andres@anarazel.de 4057 : 69 : *op->resnull = false;
4058 : : }
4059 : 69 : break;
4060 : :
4061 : 12 : case IS_DOCUMENT:
4062 : : {
4063 : 12 : Datum *argvalue = op->d.xmlexpr.argvalue;
4064 : 12 : bool *argnull = op->d.xmlexpr.argnull;
4065 : :
4066 : : /* optional argument is known to be xml */
4067 [ - + ]: 12 : Assert(list_length(xexpr->args) == 1);
4068 : :
4069 [ - + ]: 12 : if (argnull[0])
2588 andres@anarazel.de 4070 :UBC 0 : return;
2588 andres@anarazel.de 4071 :CBC 12 : value = argvalue[0];
4072 : :
4073 : 24 : *op->resvalue =
4074 : 12 : BoolGetDatum(xml_is_document(DatumGetXmlP(value)));
4075 : 12 : *op->resnull = false;
4076 : : }
4077 : 12 : break;
4078 : :
2588 andres@anarazel.de 4079 :UBC 0 : default:
4080 [ # # ]: 0 : elog(ERROR, "unrecognized XML operation");
4081 : : break;
4082 : : }
4083 : : }
4084 : :
4085 : : /*
4086 : : * Evaluate a JSON constructor expression.
4087 : : */
4088 : : void
380 alvherre@alvh.no-ip. 4089 :CBC 291 : ExecEvalJsonConstructor(ExprState *state, ExprEvalStep *op,
4090 : : ExprContext *econtext)
4091 : : {
4092 : : Datum res;
4093 : 291 : JsonConstructorExprState *jcstate = op->d.json_constructor.jcstate;
4094 : 291 : JsonConstructorExpr *ctor = jcstate->constructor;
4095 : 291 : bool is_jsonb = ctor->returning->format->format_type == JS_FORMAT_JSONB;
4096 : 291 : bool isnull = false;
4097 : :
4098 [ + + ]: 291 : if (ctor->type == JSCTOR_JSON_ARRAY)
4099 : : res = (is_jsonb ?
4100 [ + + ]: 86 : jsonb_build_array_worker :
4101 : : json_build_array_worker) (jcstate->nargs,
380 alvherre@alvh.no-ip. 4102 :GIC 86 : jcstate->arg_values,
4103 : 86 : jcstate->arg_nulls,
4104 : 86 : jcstate->arg_types,
380 alvherre@alvh.no-ip. 4105 :CBC 86 : jcstate->constructor->absent_on_null);
4106 [ + + ]: 205 : else if (ctor->type == JSCTOR_JSON_OBJECT)
4107 : : res = (is_jsonb ?
4108 [ + + ]: 158 : jsonb_build_object_worker :
4109 : : json_build_object_worker) (jcstate->nargs,
380 alvherre@alvh.no-ip. 4110 :GIC 158 : jcstate->arg_values,
4111 : 158 : jcstate->arg_nulls,
4112 : 158 : jcstate->arg_types,
380 alvherre@alvh.no-ip. 4113 :CBC 158 : jcstate->constructor->absent_on_null,
4114 : 158 : jcstate->constructor->unique);
269 amitlan@postgresql.o 4115 [ + + ]:GNC 47 : else if (ctor->type == JSCTOR_JSON_SCALAR)
4116 : : {
4117 [ + + ]: 43 : if (jcstate->arg_nulls[0])
4118 : : {
4119 : 8 : res = (Datum) 0;
4120 : 8 : isnull = true;
4121 : : }
4122 : : else
4123 : : {
4124 : 35 : Datum value = jcstate->arg_values[0];
4125 : 35 : Oid outfuncid = jcstate->arg_type_cache[0].outfuncid;
4126 : 35 : JsonTypeCategory category = (JsonTypeCategory)
4127 : 35 : jcstate->arg_type_cache[0].category;
4128 : :
4129 [ - + ]: 35 : if (is_jsonb)
269 amitlan@postgresql.o 4130 :UNC 0 : res = datum_to_jsonb(value, category, outfuncid);
4131 : : else
269 amitlan@postgresql.o 4132 :GNC 35 : res = datum_to_json(value, category, outfuncid);
4133 : : }
4134 : : }
4135 [ + - ]: 4 : else if (ctor->type == JSCTOR_JSON_PARSE)
4136 : : {
4137 [ - + ]: 4 : if (jcstate->arg_nulls[0])
4138 : : {
269 amitlan@postgresql.o 4139 :UNC 0 : res = (Datum) 0;
4140 : 0 : isnull = true;
4141 : : }
4142 : : else
4143 : : {
269 amitlan@postgresql.o 4144 :GNC 4 : Datum value = jcstate->arg_values[0];
4145 : 4 : text *js = DatumGetTextP(value);
4146 : :
4147 [ - + ]: 4 : if (is_jsonb)
269 amitlan@postgresql.o 4148 :UNC 0 : res = jsonb_from_text(js, true);
4149 : : else
4150 : : {
269 amitlan@postgresql.o 4151 :GNC 4 : (void) json_validate(js, true, true);
269 amitlan@postgresql.o 4152 :UNC 0 : res = value;
4153 : : }
4154 : : }
4155 : : }
4156 : : else
380 alvherre@alvh.no-ip. 4157 [ # # ]:UBC 0 : elog(ERROR, "invalid JsonConstructorExpr type %d", ctor->type);
4158 : :
380 alvherre@alvh.no-ip. 4159 :CBC 256 : *op->resvalue = res;
4160 : 256 : *op->resnull = isnull;
4161 : 256 : }
4162 : :
4163 : : /*
4164 : : * Evaluate a IS JSON predicate.
4165 : : */
4166 : : void
4167 : 1370 : ExecEvalJsonIsPredicate(ExprState *state, ExprEvalStep *op)
4168 : : {
4169 : 1370 : JsonIsPredicate *pred = op->d.is_json.pred;
4170 : 1370 : Datum js = *op->resvalue;
4171 : : Oid exprtype;
4172 : : bool res;
4173 : :
4174 [ + + ]: 1370 : if (*op->resnull)
4175 : : {
4176 : 51 : *op->resvalue = BoolGetDatum(false);
4177 : 51 : return;
4178 : : }
4179 : :
4180 : 1319 : exprtype = exprType(pred->expr);
4181 : :
4182 [ + + + + ]: 1319 : if (exprtype == TEXTOID || exprtype == JSONOID)
4183 : 1055 : {
4184 : 1055 : text *json = DatumGetTextP(js);
4185 : :
4186 [ + + ]: 1055 : if (pred->item_type == JS_TYPE_ANY)
4187 : 719 : res = true;
4188 : : else
4189 : : {
4190 [ + + + + ]: 336 : switch (json_get_first_token(json, false))
4191 : : {
4192 : 147 : case JSON_TOKEN_OBJECT_START:
4193 : 147 : res = pred->item_type == JS_TYPE_OBJECT;
4194 : 147 : break;
4195 : 63 : case JSON_TOKEN_ARRAY_START:
4196 : 63 : res = pred->item_type == JS_TYPE_ARRAY;
4197 : 63 : break;
4198 : 108 : case JSON_TOKEN_STRING:
4199 : : case JSON_TOKEN_NUMBER:
4200 : : case JSON_TOKEN_TRUE:
4201 : : case JSON_TOKEN_FALSE:
4202 : : case JSON_TOKEN_NULL:
4203 : 108 : res = pred->item_type == JS_TYPE_SCALAR;
4204 : 108 : break;
4205 : 18 : default:
4206 : 18 : res = false;
4207 : 18 : break;
4208 : : }
4209 : : }
4210 : :
4211 : : /*
4212 : : * Do full parsing pass only for uniqueness check or for JSON text
4213 : : * validation.
4214 : : */
4215 [ + + + + : 1055 : if (res && (pred->unique_keys || exprtype == TEXTOID))
+ + ]
4216 : 660 : res = json_validate(json, pred->unique_keys, false);
4217 : : }
4218 [ + - ]: 264 : else if (exprtype == JSONBOID)
4219 : : {
4220 [ + + ]: 264 : if (pred->item_type == JS_TYPE_ANY)
4221 : 165 : res = true;
4222 : : else
4223 : : {
4224 : 99 : Jsonb *jb = DatumGetJsonbP(js);
4225 : :
4226 [ + + + - ]: 99 : switch (pred->item_type)
4227 : : {
4228 : 33 : case JS_TYPE_OBJECT:
4229 : 33 : res = JB_ROOT_IS_OBJECT(jb);
4230 : 33 : break;
4231 : 33 : case JS_TYPE_ARRAY:
4232 [ + + + + ]: 33 : res = JB_ROOT_IS_ARRAY(jb) && !JB_ROOT_IS_SCALAR(jb);
4233 : 33 : break;
4234 : 33 : case JS_TYPE_SCALAR:
4235 [ + + + + ]: 33 : res = JB_ROOT_IS_ARRAY(jb) && JB_ROOT_IS_SCALAR(jb);
4236 : 33 : break;
380 alvherre@alvh.no-ip. 4237 :UBC 0 : default:
4238 : 0 : res = false;
4239 : 0 : break;
4240 : : }
4241 : : }
4242 : :
4243 : : /* Key uniqueness check is redundant for jsonb */
4244 : : }
4245 : : else
4246 : 0 : res = false;
4247 : :
380 alvherre@alvh.no-ip. 4248 :CBC 1319 : *op->resvalue = BoolGetDatum(res);
4249 : : }
4250 : :
4251 : : /*
4252 : : * Evaluate a jsonpath against a document, both of which must have been
4253 : : * evaluated and their values saved in op->d.jsonexpr.jsestate.
4254 : : *
4255 : : * If an error occurs during JsonPath* evaluation or when coercing its result
4256 : : * to the RETURNING type, JsonExprState.error is set to true, provided the
4257 : : * ON ERROR behavior is not ERROR. Similarly, if JsonPath{Query|Value}() found
4258 : : * no matching items, JsonExprState.empty is set to true, provided the ON EMPTY
4259 : : * behavior is not ERROR. That is to signal to the subsequent steps that check
4260 : : * those flags to return the ON ERROR / ON EMPTY expression.
4261 : : *
4262 : : * Return value is the step address to be performed next. It will be one of
4263 : : * jump_error, jump_empty, jump_eval_coercion, or jump_end, all given in
4264 : : * op->d.jsonexpr.jsestate.
4265 : : */
4266 : : int
24 amitlan@postgresql.o 4267 :GNC 2477 : ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op,
4268 : : ExprContext *econtext)
4269 : : {
4270 : 2477 : JsonExprState *jsestate = op->d.jsonexpr.jsestate;
4271 : 2477 : JsonExpr *jsexpr = jsestate->jsexpr;
4272 : : Datum item;
4273 : : JsonPath *path;
4274 : 2477 : bool throw_error = jsexpr->on_error->btype == JSON_BEHAVIOR_ERROR;
4275 : 2477 : bool error = false,
4276 : 2477 : empty = false;
4277 : 2477 : int jump_eval_coercion = jsestate->jump_eval_coercion;
4278 : 2477 : char *val_string = NULL;
4279 : :
4280 : 2477 : item = jsestate->formatted_expr.value;
4281 : 2477 : path = DatumGetJsonPathP(jsestate->pathspec.value);
4282 : :
4283 : : /* Set error/empty to false. */
4284 : 2477 : memset(&jsestate->error, 0, sizeof(NullableDatum));
4285 : 2477 : memset(&jsestate->empty, 0, sizeof(NullableDatum));
4286 : :
4287 : : /*
4288 : : * Also reset ErrorSaveContext contents for the next row. Since we don't
4289 : : * set details_wanted, we don't need to also reset error_data, which would
4290 : : * be NULL anyway.
4291 : : */
4292 [ + - - + ]: 2477 : Assert(!jsestate->escontext.details_wanted &&
4293 : : jsestate->escontext.error_data == NULL);
4294 : 2477 : jsestate->escontext.error_occurred = false;
4295 : :
4296 [ + + + - ]: 2477 : switch (jsexpr->op)
4297 : : {
4298 : 255 : case JSON_EXISTS_OP:
4299 : : {
4300 : 255 : bool exists = JsonPathExists(item, path,
4301 [ + + ]: 255 : !throw_error ? &error : NULL,
4302 : : jsestate->args);
4303 : :
4304 [ + + ]: 252 : if (!error)
4305 : : {
4306 : 174 : *op->resvalue = BoolGetDatum(exists);
4307 : 174 : *op->resnull = false;
4308 : : }
4309 : : }
4310 : 252 : break;
4311 : :
4312 : 1185 : case JSON_QUERY_OP:
4313 : 1185 : *op->resvalue = JsonPathQuery(item, path, jsexpr->wrapper, &empty,
4314 [ + + ]: 1185 : !throw_error ? &error : NULL,
4315 : : jsestate->args);
4316 : :
4317 : 1182 : *op->resnull = (DatumGetPointer(*op->resvalue) == NULL);
4318 : :
4319 : : /* Handle OMIT QUOTES. */
4320 [ + + + + ]: 1182 : if (!*op->resnull && jsexpr->omit_quotes)
4321 : : {
4322 : 129 : val_string = JsonbUnquote(DatumGetJsonbP(*op->resvalue));
4323 : :
4324 : : /*
4325 : : * Pass the string as a text value to the cast expression if
4326 : : * one present. If not, use the input function call below to
4327 : : * do the coercion.
4328 : : */
4329 [ + + ]: 129 : if (jump_eval_coercion >= 0)
4330 : 108 : *op->resvalue =
4331 : 108 : DirectFunctionCall1(textin,
4332 : : PointerGetDatum(val_string));
4333 : : }
4334 : 1182 : break;
4335 : :
4336 : 1037 : case JSON_VALUE_OP:
4337 : : {
4338 : 1037 : JsonbValue *jbv = JsonPathValue(item, path, &empty,
4339 [ + + ]: 1037 : !throw_error ? &error : NULL,
4340 : : jsestate->args);
4341 : :
4342 [ + + ]: 1022 : if (jbv == NULL)
4343 : : {
4344 : : /* Will be coerced with coercion_expr, if any. */
4345 : 237 : *op->resvalue = (Datum) 0;
4346 : 237 : *op->resnull = true;
4347 : : }
4348 [ + - + - ]: 785 : else if (!error && !empty)
4349 : : {
4350 [ + + ]: 785 : if (jsexpr->returning->typid == JSONOID ||
4351 [ + + ]: 770 : jsexpr->returning->typid == JSONBOID)
4352 : : {
4353 : 27 : val_string = DatumGetCString(DirectFunctionCall1(jsonb_out,
4354 : : JsonbPGetDatum(JsonbValueToJsonb(jbv))));
4355 : : }
4356 : : else
4357 : : {
4358 : 758 : val_string = ExecGetJsonValueItemString(jbv, op->resnull);
4359 : :
4360 : : /*
4361 : : * Pass the string as a text value to the cast
4362 : : * expression if one present. If not, use the input
4363 : : * function call below to do the coercion.
4364 : : */
4365 : 758 : *op->resvalue = PointerGetDatum(val_string);
4366 [ + + ]: 758 : if (jump_eval_coercion >= 0)
4367 : 72 : *op->resvalue = DirectFunctionCall1(textin, *op->resvalue);
4368 : : }
4369 : : }
4370 : 1022 : break;
4371 : : }
4372 : :
4373 : : /* JSON_TABLE_OP can't happen here */
4374 : :
24 amitlan@postgresql.o 4375 :UNC 0 : default:
4376 [ # # ]: 0 : elog(ERROR, "unrecognized SQL/JSON expression op %d",
4377 : : (int) jsexpr->op);
4378 : : return false;
4379 : : }
4380 : :
4381 : : /*
4382 : : * Coerce the result value to the RETURNING type by calling its input
4383 : : * function.
4384 : : */
24 amitlan@postgresql.o 4385 [ + + + + ]:GNC 2456 : if (!*op->resnull && jsexpr->use_io_coercion)
4386 : : {
4387 : : FunctionCallInfo fcinfo;
4388 : :
4389 [ - + ]: 734 : Assert(jump_eval_coercion == -1);
4390 : 734 : fcinfo = jsestate->input_fcinfo;
4391 [ - + ]: 734 : Assert(fcinfo != NULL);
4392 [ - + ]: 734 : Assert(val_string != NULL);
4393 : 734 : fcinfo->args[0].value = PointerGetDatum(val_string);
4394 : 734 : fcinfo->args[0].isnull = *op->resnull;
4395 : :
4396 : : /*
4397 : : * Second and third arguments are already set up in
4398 : : * ExecInitJsonExpr().
4399 : : */
4400 : :
4401 : 734 : fcinfo->isnull = false;
4402 : 734 : *op->resvalue = FunctionCallInvoke(fcinfo);
4403 [ + - + - : 710 : if (SOFT_ERROR_OCCURRED(&jsestate->escontext))
+ + ]
4404 : 75 : error = true;
4405 : : }
4406 : :
4407 : : /* Handle ON EMPTY. */
4408 [ + + ]: 2432 : if (empty)
4409 : : {
4410 [ + + ]: 246 : if (jsexpr->on_empty)
4411 : : {
4412 [ + + ]: 111 : if (jsexpr->on_empty->btype == JSON_BEHAVIOR_ERROR)
4413 [ + - ]: 27 : ereport(ERROR,
4414 : : errcode(ERRCODE_NO_SQL_JSON_ITEM),
4415 : : errmsg("no SQL/JSON item"));
4416 : : else
4417 : 84 : jsestate->empty.value = BoolGetDatum(true);
4418 : :
4419 [ - + ]: 84 : Assert(jsestate->jump_empty >= 0);
4420 : 84 : return jsestate->jump_empty;
4421 : : }
4422 [ + + ]: 135 : else if (jsexpr->on_error->btype == JSON_BEHAVIOR_ERROR)
4423 [ + - ]: 12 : ereport(ERROR,
4424 : : errcode(ERRCODE_NO_SQL_JSON_ITEM),
4425 : : errmsg("no SQL/JSON item"));
4426 : : else
4427 : 123 : jsestate->error.value = BoolGetDatum(true);
4428 : :
4429 : 123 : *op->resvalue = (Datum) 0;
4430 : 123 : *op->resnull = true;
4431 : :
4432 [ + - - + ]: 123 : Assert(!throw_error && jsestate->jump_error >= 0);
4433 : 123 : return jsestate->jump_error;
4434 : : }
4435 : :
4436 : : /*
4437 : : * ON ERROR. Wouldn't get here if the behavior is ERROR, because they
4438 : : * would have already been thrown.
4439 : : */
4440 [ + + ]: 2186 : if (error)
4441 : : {
4442 [ + - - + ]: 240 : Assert(!throw_error && jsestate->jump_error >= 0);
4443 : 240 : *op->resvalue = (Datum) 0;
4444 : 240 : *op->resnull = true;
4445 : 240 : jsestate->error.value = BoolGetDatum(true);
4446 : 240 : return jsestate->jump_error;
4447 : : }
4448 : :
4449 [ + + ]: 1946 : return jump_eval_coercion >= 0 ? jump_eval_coercion : jsestate->jump_end;
4450 : : }
4451 : :
4452 : : /*
4453 : : * Convert the given JsonbValue to its C string representation
4454 : : *
4455 : : * *resnull is set if the JsonbValue is a jbvNull.
4456 : : */
4457 : : static char *
4458 : 758 : ExecGetJsonValueItemString(JsonbValue *item, bool *resnull)
4459 : : {
4460 : 758 : *resnull = false;
4461 : :
4462 : : /* get coercion state reference and datum of the corresponding SQL type */
4463 [ - + + + : 758 : switch (item->type)
+ - - ]
4464 : : {
24 amitlan@postgresql.o 4465 :UNC 0 : case jbvNull:
4466 : 0 : *resnull = true;
4467 : 0 : return NULL;
4468 : :
24 amitlan@postgresql.o 4469 :GNC 163 : case jbvString:
4470 : : {
4471 : 163 : char *str = palloc(item->val.string.len + 1);
4472 : :
4473 : 163 : memcpy(str, item->val.string.val, item->val.string.len);
4474 : 163 : str[item->val.string.len] = '\0';
4475 : 163 : return str;
4476 : : }
4477 : :
4478 : 532 : case jbvNumeric:
4479 : 532 : return DatumGetCString(DirectFunctionCall1(numeric_out,
4480 : : NumericGetDatum(item->val.numeric)));
4481 : :
4482 : 42 : case jbvBool:
4483 : 42 : return DatumGetCString(DirectFunctionCall1(boolout,
4484 : : BoolGetDatum(item->val.boolean)));
4485 : :
4486 : 21 : case jbvDatetime:
4487 [ + + + + : 21 : switch (item->val.datetime.typid)
+ - ]
4488 : : {
4489 : 3 : case DATEOID:
4490 : 3 : return DatumGetCString(DirectFunctionCall1(date_out,
4491 : : item->val.datetime.value));
4492 : 3 : case TIMEOID:
4493 : 3 : return DatumGetCString(DirectFunctionCall1(time_out,
4494 : : item->val.datetime.value));
4495 : 3 : case TIMETZOID:
4496 : 3 : return DatumGetCString(DirectFunctionCall1(timetz_out,
4497 : : item->val.datetime.value));
4498 : 3 : case TIMESTAMPOID:
4499 : 3 : return DatumGetCString(DirectFunctionCall1(timestamp_out,
4500 : : item->val.datetime.value));
4501 : 9 : case TIMESTAMPTZOID:
4502 : 9 : return DatumGetCString(DirectFunctionCall1(timestamptz_out,
4503 : : item->val.datetime.value));
24 amitlan@postgresql.o 4504 :UNC 0 : default:
4505 [ # # ]: 0 : elog(ERROR, "unexpected jsonb datetime type oid %u",
4506 : : item->val.datetime.typid);
4507 : : }
4508 : : break;
4509 : :
4510 : 0 : case jbvArray:
4511 : : case jbvObject:
4512 : : case jbvBinary:
4513 : 0 : return DatumGetCString(DirectFunctionCall1(jsonb_out,
4514 : : JsonbPGetDatum(JsonbValueToJsonb(item))));
4515 : :
4516 : 0 : default:
4517 [ # # ]: 0 : elog(ERROR, "unexpected jsonb value type %d", item->type);
4518 : : }
4519 : :
4520 : : Assert(false);
4521 : : *resnull = true;
4522 : : return NULL;
4523 : : }
4524 : :
4525 : : /*
4526 : : * Coerce a jsonb value produced by ExecEvalJsonExprPath() or an ON ERROR /
4527 : : * ON EMPTY behavior expression to the target type.
4528 : : *
4529 : : * Any soft errors that occur here will be checked by
4530 : : * EEOP_JSONEXPR_COERCION_FINISH that will run after this.
4531 : : */
4532 : : void
24 amitlan@postgresql.o 4533 :GNC 1050 : ExecEvalJsonCoercion(ExprState *state, ExprEvalStep *op,
4534 : : ExprContext *econtext)
4535 : : {
4536 : 1050 : ErrorSaveContext *escontext = op->d.jsonexpr_coercion.escontext;
4537 : :
4538 : 1050 : *op->resvalue = json_populate_type(*op->resvalue, JSONBOID,
4539 : : op->d.jsonexpr_coercion.targettype,
4540 : : op->d.jsonexpr_coercion.targettypmod,
4541 : : &op->d.jsonexpr_coercion.json_populate_type_cache,
4542 : : econtext->ecxt_per_query_memory,
4543 : : op->resnull, (Node *) escontext);
4544 : 1008 : }
4545 : :
4546 : : /*
4547 : : * Checks if an error occurred either when evaluating JsonExpr.coercion_expr or
4548 : : * in ExecEvalJsonCoercion(). If so, this sets JsonExprState.error to trigger
4549 : : * the ON ERROR handling steps.
4550 : : */
4551 : : void
4552 : 1113 : ExecEvalJsonCoercionFinish(ExprState *state, ExprEvalStep *op)
4553 : : {
4554 : 1113 : JsonExprState *jsestate = op->d.jsonexpr.jsestate;
4555 : :
4556 [ + - + - : 1113 : if (SOFT_ERROR_OCCURRED(&jsestate->escontext))
+ + ]
4557 : : {
4558 : 156 : *op->resvalue = (Datum) 0;
4559 : 156 : *op->resnull = true;
4560 : 156 : jsestate->error.value = BoolGetDatum(true);
4561 : : }
4562 : 1113 : }
4563 : :
4564 : : /*
4565 : : * ExecEvalGroupingFunc
4566 : : *
4567 : : * Computes a bitmask with a bit for each (unevaluated) argument expression
4568 : : * (rightmost arg is least significant bit).
4569 : : *
4570 : : * A bit is set if the corresponding expression is NOT part of the set of
4571 : : * grouping expressions in the current grouping set.
4572 : : */
4573 : : void
2588 andres@anarazel.de 4574 :CBC 844 : ExecEvalGroupingFunc(ExprState *state, ExprEvalStep *op)
4575 : : {
1529 4576 : 844 : AggState *aggstate = castNode(AggState, state->parent);
2588 4577 : 844 : int result = 0;
1529 4578 : 844 : Bitmapset *grouped_cols = aggstate->grouped_cols;
4579 : : ListCell *lc;
4580 : :
2588 4581 [ + + + + : 2089 : foreach(lc, op->d.grouping_func.clauses)
+ + ]
4582 : : {
4583 : 1245 : int attnum = lfirst_int(lc);
4584 : :
4585 : 1245 : result <<= 1;
4586 : :
4587 [ + + ]: 1245 : if (!bms_is_member(attnum, grouped_cols))
4588 : 486 : result |= 1;
4589 : : }
4590 : :
4591 : 844 : *op->resvalue = Int32GetDatum(result);
4592 : 844 : *op->resnull = false;
4593 : 844 : }
4594 : :
4595 : : /*
4596 : : * ExecEvalMergeSupportFunc
4597 : : *
4598 : : * Returns information about the current MERGE action for its RETURNING list.
4599 : : */
4600 : : void
28 dean.a.rasheed@gmail 4601 :GNC 205 : ExecEvalMergeSupportFunc(ExprState *state, ExprEvalStep *op,
4602 : : ExprContext *econtext)
4603 : : {
4604 : 205 : ModifyTableState *mtstate = castNode(ModifyTableState, state->parent);
4605 : 205 : MergeActionState *relaction = mtstate->mt_merge_action;
4606 : :
4607 [ - + ]: 205 : if (!relaction)
28 dean.a.rasheed@gmail 4608 [ # # ]:UNC 0 : elog(ERROR, "no merge action in progress");
4609 : :
4610 : : /* Return the MERGE action ("INSERT", "UPDATE", or "DELETE") */
28 dean.a.rasheed@gmail 4611 [ + + + - :GNC 205 : switch (relaction->mas_action->commandType)
- ]
4612 : : {
4613 : 66 : case CMD_INSERT:
4614 : 66 : *op->resvalue = PointerGetDatum(cstring_to_text_with_len("INSERT", 6));
4615 : 66 : *op->resnull = false;
4616 : 66 : break;
4617 : 85 : case CMD_UPDATE:
4618 : 85 : *op->resvalue = PointerGetDatum(cstring_to_text_with_len("UPDATE", 6));
4619 : 85 : *op->resnull = false;
4620 : 85 : break;
4621 : 54 : case CMD_DELETE:
4622 : 54 : *op->resvalue = PointerGetDatum(cstring_to_text_with_len("DELETE", 6));
4623 : 54 : *op->resnull = false;
4624 : 54 : break;
28 dean.a.rasheed@gmail 4625 :UNC 0 : case CMD_NOTHING:
4626 [ # # ]: 0 : elog(ERROR, "unexpected merge action: DO NOTHING");
4627 : : break;
4628 : 0 : default:
4629 [ # # ]: 0 : elog(ERROR, "unrecognized commandType: %d",
4630 : : (int) relaction->mas_action->commandType);
4631 : : }
28 dean.a.rasheed@gmail 4632 :GNC 205 : }
4633 : :
4634 : : /*
4635 : : * Hand off evaluation of a subplan to nodeSubplan.c
4636 : : */
4637 : : void
2588 andres@anarazel.de 4638 :CBC 1413085 : ExecEvalSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
4639 : : {
4640 : 1413085 : SubPlanState *sstate = op->d.subplan.sstate;
4641 : :
4642 : : /* could potentially be nested, so make sure there's enough stack */
4643 : 1413085 : check_stack_depth();
4644 : :
4645 : 1413085 : *op->resvalue = ExecSubPlan(sstate, econtext, op->resnull);
4646 : 1413082 : }
4647 : :
4648 : : /*
4649 : : * Evaluate a wholerow Var expression.
4650 : : *
4651 : : * Returns a Datum whose value is the value of a whole-row range variable
4652 : : * with respect to given expression context.
4653 : : */
4654 : : void
4655 : 19541 : ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
4656 : : {
4657 : 19541 : Var *variable = op->d.wholerow.var;
4658 : : TupleTableSlot *slot;
4659 : : TupleDesc output_tupdesc;
4660 : : MemoryContext oldcontext;
4661 : : HeapTupleHeader dtuple;
4662 : : HeapTuple tuple;
4663 : :
4664 : : /* This was checked by ExecInitExpr */
4665 [ - + ]: 19541 : Assert(variable->varattno == InvalidAttrNumber);
4666 : :
4667 : : /* Get the input slot we want */
4668 [ + + + ]: 19541 : switch (variable->varno)
4669 : : {
4670 : 45 : case INNER_VAR:
4671 : : /* get the tuple from the inner node */
4672 : 45 : slot = econtext->ecxt_innertuple;
4673 : 45 : break;
4674 : :
4675 : 9 : case OUTER_VAR:
4676 : : /* get the tuple from the outer node */
4677 : 9 : slot = econtext->ecxt_outertuple;
4678 : 9 : break;
4679 : :
4680 : : /* INDEX_VAR is handled by default case */
4681 : :
4682 : 19487 : default:
4683 : : /* get the tuple from the relation being scanned */
4684 : 19487 : slot = econtext->ecxt_scantuple;
4685 : 19487 : break;
4686 : : }
4687 : :
4688 : : /* Apply the junkfilter if any */
4689 [ + + ]: 19541 : if (op->d.wholerow.junkFilter != NULL)
4690 : 30 : slot = ExecFilterJunk(op->d.wholerow.junkFilter, slot);
4691 : :
4692 : : /*
4693 : : * If first time through, obtain tuple descriptor and check compatibility.
4694 : : *
4695 : : * XXX: It'd be great if this could be moved to the expression
4696 : : * initialization phase, but due to using slots that's currently not
4697 : : * feasible.
4698 : : */
4699 [ + + ]: 19541 : if (op->d.wholerow.first)
4700 : : {
4701 : : /* optimistically assume we don't need slow path */
4702 : 1278 : op->d.wholerow.slow = false;
4703 : :
4704 : : /*
4705 : : * If the Var identifies a named composite type, we must check that
4706 : : * the actual tuple type is compatible with it.
4707 : : */
4708 [ + + ]: 1278 : if (variable->vartype != RECORDOID)
4709 : : {
4710 : : TupleDesc var_tupdesc;
4711 : : TupleDesc slot_tupdesc;
4712 : :
4713 : : /*
4714 : : * We really only care about numbers of attributes and data types.
4715 : : * Also, we can ignore type mismatch on columns that are dropped
4716 : : * in the destination type, so long as (1) the physical storage
4717 : : * matches or (2) the actual column value is NULL. Case (1) is
4718 : : * helpful in some cases involving out-of-date cached plans, while
4719 : : * case (2) is expected behavior in situations such as an INSERT
4720 : : * into a table with dropped columns (the planner typically
4721 : : * generates an INT4 NULL regardless of the dropped column type).
4722 : : * If we find a dropped column and cannot verify that case (1)
4723 : : * holds, we have to use the slow path to check (2) for each row.
4724 : : *
4725 : : * If vartype is a domain over composite, just look through that
4726 : : * to the base composite type.
4727 : : */
674 tgl@sss.pgh.pa.us 4728 : 746 : var_tupdesc = lookup_rowtype_tupdesc_domain(variable->vartype,
4729 : : -1, false);
4730 : :
2588 andres@anarazel.de 4731 : 746 : slot_tupdesc = slot->tts_tupleDescriptor;
4732 : :
4733 [ - + ]: 746 : if (var_tupdesc->natts != slot_tupdesc->natts)
2588 andres@anarazel.de 4734 [ # # ]:UBC 0 : ereport(ERROR,
4735 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
4736 : : errmsg("table row type and query-specified row type do not match"),
4737 : : errdetail_plural("Table row contains %d attribute, but query expects %d.",
4738 : : "Table row contains %d attributes, but query expects %d.",
4739 : : slot_tupdesc->natts,
4740 : : slot_tupdesc->natts,
4741 : : var_tupdesc->natts)));
4742 : :
1529 andres@anarazel.de 4743 [ + + ]:CBC 2966 : for (int i = 0; i < var_tupdesc->natts; i++)
4744 : : {
2429 4745 : 2220 : Form_pg_attribute vattr = TupleDescAttr(var_tupdesc, i);
4746 : 2220 : Form_pg_attribute sattr = TupleDescAttr(slot_tupdesc, i);
4747 : :
2588 4748 [ + - ]: 2220 : if (vattr->atttypid == sattr->atttypid)
4749 : 2220 : continue; /* no worries */
2588 andres@anarazel.de 4750 [ # # ]:UBC 0 : if (!vattr->attisdropped)
4751 [ # # ]: 0 : ereport(ERROR,
4752 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
4753 : : errmsg("table row type and query-specified row type do not match"),
4754 : : errdetail("Table has type %s at ordinal position %d, but query expects %s.",
4755 : : format_type_be(sattr->atttypid),
4756 : : i + 1,
4757 : : format_type_be(vattr->atttypid))));
4758 : :
4759 [ # # ]: 0 : if (vattr->attlen != sattr->attlen ||
4760 [ # # ]: 0 : vattr->attalign != sattr->attalign)
4761 : 0 : op->d.wholerow.slow = true; /* need to check for nulls */
4762 : : }
4763 : :
4764 : : /*
4765 : : * Use the variable's declared rowtype as the descriptor for the
4766 : : * output values. In particular, we *must* absorb any
4767 : : * attisdropped markings.
4768 : : */
2588 andres@anarazel.de 4769 :CBC 746 : oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
4770 : 746 : output_tupdesc = CreateTupleDescCopy(var_tupdesc);
4771 : 746 : MemoryContextSwitchTo(oldcontext);
4772 : :
4773 [ + - ]: 746 : ReleaseTupleDesc(var_tupdesc);
4774 : : }
4775 : : else
4776 : : {
4777 : : /*
4778 : : * In the RECORD case, we use the input slot's rowtype as the
4779 : : * descriptor for the output values, modulo possibly assigning new
4780 : : * column names below.
4781 : : */
4782 : 532 : oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
4783 : 532 : output_tupdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
4784 : 532 : MemoryContextSwitchTo(oldcontext);
4785 : :
4786 : : /*
4787 : : * It's possible that the input slot is a relation scan slot and
4788 : : * so is marked with that relation's rowtype. But we're supposed
4789 : : * to be returning RECORD, so reset to that.
4790 : : */
759 tgl@sss.pgh.pa.us 4791 : 532 : output_tupdesc->tdtypeid = RECORDOID;
4792 : 532 : output_tupdesc->tdtypmod = -1;
4793 : :
4794 : : /*
4795 : : * We already got the correct physical datatype info above, but
4796 : : * now we should try to find the source RTE and adopt its column
4797 : : * aliases, since it's unlikely that the input slot has the
4798 : : * desired names.
4799 : : *
4800 : : * If we can't locate the RTE, assume the column names we've got
4801 : : * are OK. (As of this writing, the only cases where we can't
4802 : : * locate the RTE are in execution of trigger WHEN clauses, and
4803 : : * then the Var will have the trigger's relation's rowtype, so its
4804 : : * names are fine.) Also, if the creator of the RTE didn't bother
4805 : : * to fill in an eref field, assume our column names are OK. (This
4806 : : * happens in COPY, and perhaps other places.)
4807 : : */
4808 [ + - ]: 532 : if (econtext->ecxt_estate &&
4809 [ + - ]: 532 : variable->varno <= econtext->ecxt_estate->es_range_table_size)
4810 : : {
4811 : 532 : RangeTblEntry *rte = exec_rt_fetch(variable->varno,
4812 : 532 : econtext->ecxt_estate);
4813 : :
4814 [ + - ]: 532 : if (rte->eref)
4815 : 532 : ExecTypeSetColNames(output_tupdesc, rte->eref->colnames);
4816 : : }
4817 : : }
4818 : :
4819 : : /* Bless the tupdesc if needed, and save it in the execution state */
2588 andres@anarazel.de 4820 : 1278 : op->d.wholerow.tupdesc = BlessTupleDesc(output_tupdesc);
4821 : :
4822 : 1278 : op->d.wholerow.first = false;
4823 : : }
4824 : :
4825 : : /*
4826 : : * Make sure all columns of the slot are accessible in the slot's
4827 : : * Datum/isnull arrays.
4828 : : */
4829 : 19541 : slot_getallattrs(slot);
4830 : :
4831 [ - + ]: 19541 : if (op->d.wholerow.slow)
4832 : : {
4833 : : /* Check to see if any dropped attributes are non-null */
2588 andres@anarazel.de 4834 :UBC 0 : TupleDesc tupleDesc = slot->tts_tupleDescriptor;
4835 : 0 : TupleDesc var_tupdesc = op->d.wholerow.tupdesc;
4836 : :
4837 [ # # ]: 0 : Assert(var_tupdesc->natts == tupleDesc->natts);
4838 : :
1529 4839 [ # # ]: 0 : for (int i = 0; i < var_tupdesc->natts; i++)
4840 : : {
2429 4841 : 0 : Form_pg_attribute vattr = TupleDescAttr(var_tupdesc, i);
4842 : 0 : Form_pg_attribute sattr = TupleDescAttr(tupleDesc, i);
4843 : :
2588 4844 [ # # ]: 0 : if (!vattr->attisdropped)
4845 : 0 : continue; /* already checked non-dropped cols */
4846 [ # # ]: 0 : if (slot->tts_isnull[i])
4847 : 0 : continue; /* null is always okay */
4848 [ # # ]: 0 : if (vattr->attlen != sattr->attlen ||
4849 [ # # ]: 0 : vattr->attalign != sattr->attalign)
4850 [ # # ]: 0 : ereport(ERROR,
4851 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
4852 : : errmsg("table row type and query-specified row type do not match"),
4853 : : errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.",
4854 : : i + 1)));
4855 : : }
4856 : : }
4857 : :
4858 : : /*
4859 : : * Build a composite datum, making sure any toasted fields get detoasted.
4860 : : *
4861 : : * (Note: it is critical that we not change the slot's state here.)
4862 : : */
2576 tgl@sss.pgh.pa.us 4863 :CBC 19541 : tuple = toast_build_flattened_tuple(slot->tts_tupleDescriptor,
4864 : : slot->tts_values,
4865 : : slot->tts_isnull);
4866 : 19541 : dtuple = tuple->t_data;
4867 : :
4868 : : /*
4869 : : * Label the datum with the composite type info we identified before.
4870 : : *
4871 : : * (Note: we could skip doing this by passing op->d.wholerow.tupdesc to
4872 : : * the tuple build step; but that seems a tad risky so let's not.)
4873 : : */
2588 andres@anarazel.de 4874 : 19541 : HeapTupleHeaderSetTypeId(dtuple, op->d.wholerow.tupdesc->tdtypeid);
4875 : 19541 : HeapTupleHeaderSetTypMod(dtuple, op->d.wholerow.tupdesc->tdtypmod);
4876 : :
4877 : 19541 : *op->resvalue = PointerGetDatum(dtuple);
2576 tgl@sss.pgh.pa.us 4878 : 19541 : *op->resnull = false;
2588 andres@anarazel.de 4879 : 19541 : }
4880 : :
4881 : : void
1985 4882 : 3455136 : ExecEvalSysVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext,
4883 : : TupleTableSlot *slot)
4884 : : {
4885 : : Datum d;
4886 : :
4887 : : /* slot_getsysattr has sufficient defenses against bad attnums */
1976 4888 : 3455136 : d = slot_getsysattr(slot,
4889 : : op->d.var.attnum,
4890 : : op->resnull);
4891 : 3455130 : *op->resvalue = d;
4892 : : /* this ought to be unreachable, but it's cheap enough to check */
4893 [ - + ]: 3455130 : if (unlikely(*op->resnull))
1985 andres@anarazel.de 4894 [ # # ]:UBC 0 : elog(ERROR, "failed to fetch attribute from slot");
1985 andres@anarazel.de 4895 :CBC 3455130 : }
4896 : :
4897 : : /*
4898 : : * Transition value has not been initialized. This is the first non-NULL input
4899 : : * value for a group. We use it as the initial value for transValue.
4900 : : */
4901 : : void
1511 4902 : 30061 : ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroup,
4903 : : ExprContext *aggcontext)
4904 : : {
1905 4905 : 30061 : FunctionCallInfo fcinfo = pertrans->transfn_fcinfo;
4906 : : MemoryContext oldContext;
4907 : :
4908 : : /*
4909 : : * We must copy the datum into aggcontext if it is pass-by-ref. We do not
4910 : : * need to pfree the old transValue, since it's NULL. (We already checked
4911 : : * that the agg's input type is binary-compatible with its transtype, so
4912 : : * straight copy here is OK.)
4913 : : */
1511 4914 : 30061 : oldContext = MemoryContextSwitchTo(aggcontext->ecxt_per_tuple_memory);
1905 4915 : 60122 : pergroup->transValue = datumCopy(fcinfo->args[1].value,
2287 4916 : 30061 : pertrans->transtypeByVal,
4917 : 30061 : pertrans->transtypeLen);
4918 : 30061 : pergroup->transValueIsNull = false;
4919 : 30061 : pergroup->noTransValue = false;
4920 : 30061 : MemoryContextSwitchTo(oldContext);
4921 : 30061 : }
4922 : :
4923 : : /*
4924 : : * Ensure that the new transition value is stored in the aggcontext,
4925 : : * rather than the per-tuple context. This should be invoked only when
4926 : : * we know (a) the transition data type is pass-by-reference, and (b)
4927 : : * the newValue is distinct from the oldValue.
4928 : : *
4929 : : * NB: This can change the current memory context.
4930 : : *
4931 : : * We copy the presented newValue into the aggcontext, except when the datum
4932 : : * points to a R/W expanded object that is already a child of the aggcontext,
4933 : : * in which case we need not copy. We then delete the oldValue, if not null.
4934 : : *
4935 : : * If the presented datum points to a R/W expanded object that is a child of
4936 : : * some other context, ideally we would just reparent it under the aggcontext.
4937 : : * Unfortunately, that doesn't work easily, and it wouldn't help anyway for
4938 : : * aggregate-aware transfns. We expect that a transfn that deals in expanded
4939 : : * objects and is aware of the memory management conventions for aggregate
4940 : : * transition values will (1) on first call, return a R/W expanded object that
4941 : : * is already in the right context, allowing us to do nothing here, and (2) on
4942 : : * subsequent calls, modify and return that same object, so that control
4943 : : * doesn't even reach here. However, if we have a generic transfn that
4944 : : * returns a new R/W expanded object (probably in the per-tuple context),
4945 : : * reparenting that result would cause problems. We'd pass that R/W object to
4946 : : * the next invocation of the transfn, and then it would be at liberty to
4947 : : * change or delete that object, and if it deletes it then our own attempt to
4948 : : * delete the now-old transvalue afterwards would be a double free. We avoid
4949 : : * this problem by forcing the stored transvalue to always be a flat
4950 : : * non-expanded object unless the transfn is visibly doing aggregate-aware
4951 : : * memory management. This is somewhat inefficient, but the best answer to
4952 : : * that is to write a smarter transfn.
4953 : : */
4954 : : Datum
356 tgl@sss.pgh.pa.us 4955 : 30904 : ExecAggCopyTransValue(AggState *aggstate, AggStatePerTrans pertrans,
4956 : : Datum newValue, bool newValueIsNull,
4957 : : Datum oldValue, bool oldValueIsNull)
4958 : : {
1546 andres@anarazel.de 4959 [ - + ]: 30904 : Assert(newValue != oldValue);
4960 : :
2287 4961 [ + - ]: 30904 : if (!newValueIsNull)
4962 : : {
4963 : 30904 : MemoryContextSwitchTo(aggstate->curaggcontext->ecxt_per_tuple_memory);
4964 [ + + + - ]: 30988 : if (DatumIsReadWriteExpandedObject(newValue,
4965 : : false,
4966 [ + + ]: 30901 : pertrans->transtypeLen) &&
4967 [ + + ]: 84 : MemoryContextGetParent(DatumGetEOHP(newValue)->eoh_context) == CurrentMemoryContext)
4968 : : /* do nothing */ ;
4969 : : else
4970 : 30901 : newValue = datumCopy(newValue,
4971 : 30901 : pertrans->transtypeByVal,
4972 : 30901 : pertrans->transtypeLen);
4973 : : }
4974 : : else
4975 : : {
4976 : : /*
4977 : : * Ensure that AggStatePerGroup->transValue ends up being 0, so
4978 : : * callers can safely compare newValue/oldValue without having to
4979 : : * check their respective nullness.
4980 : : */
1546 andres@anarazel.de 4981 :UBC 0 : newValue = (Datum) 0;
4982 : : }
4983 : :
2287 andres@anarazel.de 4984 [ + + ]:CBC 30904 : if (!oldValueIsNull)
4985 : : {
4986 [ + + - + : 30847 : if (DatumIsReadWriteExpandedObject(oldValue,
- - ]
4987 : : false,
4988 : : pertrans->transtypeLen))
2287 andres@anarazel.de 4989 :UBC 0 : DeleteExpandedObject(oldValue);
4990 : : else
2287 andres@anarazel.de 4991 :CBC 30847 : pfree(DatumGetPointer(oldValue));
4992 : : }
4993 : :
4994 : 30904 : return newValue;
4995 : : }
4996 : :
4997 : : /*
4998 : : * ExecEvalPreOrderedDistinctSingle
4999 : : * Returns true when the aggregate transition value Datum is distinct
5000 : : * from the previous input Datum and returns false when the input Datum
5001 : : * matches the previous input Datum.
5002 : : */
5003 : : bool
621 drowley@postgresql.o 5004 : 182892 : ExecEvalPreOrderedDistinctSingle(AggState *aggstate, AggStatePerTrans pertrans)
5005 : : {
5006 : 182892 : Datum value = pertrans->transfn_fcinfo->args[1].value;
5007 : 182892 : bool isnull = pertrans->transfn_fcinfo->args[1].isnull;
5008 : :
5009 [ + + ]: 182892 : if (!pertrans->haslast ||
5010 [ + + ]: 173766 : pertrans->lastisnull != isnull ||
426 5011 [ + + + + ]: 173751 : (!isnull && !DatumGetBool(FunctionCall2Coll(&pertrans->equalfnOne,
5012 : : pertrans->aggCollation,
5013 : : pertrans->lastdatum, value))))
5014 : : {
5015 [ + + + + ]: 50954 : if (pertrans->haslast && !pertrans->inputtypeByVal &&
5016 [ + - ]: 12973 : !pertrans->lastisnull)
621 5017 : 12973 : pfree(DatumGetPointer(pertrans->lastdatum));
5018 : :
5019 : 50954 : pertrans->haslast = true;
5020 [ + + ]: 50954 : if (!isnull)
5021 : : {
5022 : : MemoryContext oldContext;
5023 : :
5024 : 50936 : oldContext = MemoryContextSwitchTo(aggstate->curaggcontext->ecxt_per_tuple_memory);
5025 : :
5026 : 101872 : pertrans->lastdatum = datumCopy(value, pertrans->inputtypeByVal,
5027 : 50936 : pertrans->inputtypeLen);
5028 : :
5029 : 50936 : MemoryContextSwitchTo(oldContext);
5030 : : }
5031 : : else
5032 : 18 : pertrans->lastdatum = (Datum) 0;
5033 : 50954 : pertrans->lastisnull = isnull;
5034 : 50954 : return true;
5035 : : }
5036 : :
5037 : 131938 : return false;
5038 : : }
5039 : :
5040 : : /*
5041 : : * ExecEvalPreOrderedDistinctMulti
5042 : : * Returns true when the aggregate input is distinct from the previous
5043 : : * input and returns false when the input matches the previous input, or
5044 : : * when there was no previous input.
5045 : : */
5046 : : bool
5047 : 360 : ExecEvalPreOrderedDistinctMulti(AggState *aggstate, AggStatePerTrans pertrans)
5048 : : {
5049 : 360 : ExprContext *tmpcontext = aggstate->tmpcontext;
101 5050 : 360 : bool isdistinct = false; /* for now */
5051 : : TupleTableSlot *save_outer;
5052 : : TupleTableSlot *save_inner;
5053 : :
621 5054 [ + + ]: 1410 : for (int i = 0; i < pertrans->numTransInputs; i++)
5055 : : {
5056 : 1050 : pertrans->sortslot->tts_values[i] = pertrans->transfn_fcinfo->args[i + 1].value;
5057 : 1050 : pertrans->sortslot->tts_isnull[i] = pertrans->transfn_fcinfo->args[i + 1].isnull;
5058 : : }
5059 : :
5060 : 360 : ExecClearTuple(pertrans->sortslot);
5061 : 360 : pertrans->sortslot->tts_nvalid = pertrans->numInputs;
5062 : 360 : ExecStoreVirtualTuple(pertrans->sortslot);
5063 : :
5064 : : /* save the previous slots before we overwrite them */
101 5065 : 360 : save_outer = tmpcontext->ecxt_outertuple;
5066 : 360 : save_inner = tmpcontext->ecxt_innertuple;
5067 : :
621 5068 : 360 : tmpcontext->ecxt_outertuple = pertrans->sortslot;
5069 : 360 : tmpcontext->ecxt_innertuple = pertrans->uniqslot;
5070 : :
5071 [ + + ]: 360 : if (!pertrans->haslast ||
5072 [ + + ]: 312 : !ExecQual(pertrans->equalfnMulti, tmpcontext))
5073 : : {
5074 [ + + ]: 156 : if (pertrans->haslast)
5075 : 108 : ExecClearTuple(pertrans->uniqslot);
5076 : :
5077 : 156 : pertrans->haslast = true;
5078 : 156 : ExecCopySlot(pertrans->uniqslot, pertrans->sortslot);
5079 : :
101 5080 : 156 : isdistinct = true;
5081 : : }
5082 : :
5083 : : /* restore the original slots */
5084 : 360 : tmpcontext->ecxt_outertuple = save_outer;
5085 : 360 : tmpcontext->ecxt_innertuple = save_inner;
5086 : :
5087 : 360 : return isdistinct;
5088 : : }
5089 : :
5090 : : /*
5091 : : * Invoke ordered transition function, with a datum argument.
5092 : : */
5093 : : void
2287 andres@anarazel.de 5094 : 422192 : ExecEvalAggOrderedTransDatum(ExprState *state, ExprEvalStep *op,
5095 : : ExprContext *econtext)
5096 : : {
5097 : 422192 : AggStatePerTrans pertrans = op->d.agg_trans.pertrans;
5098 : 422192 : int setno = op->d.agg_trans.setno;
5099 : :
5100 : 422192 : tuplesort_putdatum(pertrans->sortstates[setno],
5101 : 422192 : *op->resvalue, *op->resnull);
5102 : 422192 : }
5103 : :
5104 : : /*
5105 : : * Invoke ordered transition function, with a tuple argument.
5106 : : */
5107 : : void
5108 : 90 : ExecEvalAggOrderedTransTuple(ExprState *state, ExprEvalStep *op,
5109 : : ExprContext *econtext)
5110 : : {
5111 : 90 : AggStatePerTrans pertrans = op->d.agg_trans.pertrans;
5112 : 90 : int setno = op->d.agg_trans.setno;
5113 : :
5114 : 90 : ExecClearTuple(pertrans->sortslot);
5115 : 90 : pertrans->sortslot->tts_nvalid = pertrans->numInputs;
5116 : 90 : ExecStoreVirtualTuple(pertrans->sortslot);
5117 : 90 : tuplesort_puttupleslot(pertrans->sortstates[setno], pertrans->sortslot);
5118 : 90 : }
5119 : :
5120 : : /* implementation of transition function invocation for byval types */
5121 : : static pg_attribute_always_inline void
1511 5122 : 14726688 : ExecAggPlainTransByVal(AggState *aggstate, AggStatePerTrans pertrans,
5123 : : AggStatePerGroup pergroup,
5124 : : ExprContext *aggcontext, int setno)
5125 : : {
5126 : 14726688 : FunctionCallInfo fcinfo = pertrans->transfn_fcinfo;
5127 : : MemoryContext oldContext;
5128 : : Datum newVal;
5129 : :
5130 : : /* cf. select_current_set() */
5131 : 14726688 : aggstate->curaggcontext = aggcontext;
5132 : 14726688 : aggstate->current_set = setno;
5133 : :
5134 : : /* set up aggstate->curpertrans for AggGetAggref() */
5135 : 14726688 : aggstate->curpertrans = pertrans;
5136 : :
5137 : : /* invoke transition function in per-tuple context */
5138 : 14726688 : oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory);
5139 : :
5140 : 14726688 : fcinfo->args[0].value = pergroup->transValue;
5141 : 14726688 : fcinfo->args[0].isnull = pergroup->transValueIsNull;
5142 : 14726688 : fcinfo->isnull = false; /* just in case transfn doesn't set it */
5143 : :
5144 : 14726688 : newVal = FunctionCallInvoke(fcinfo);
5145 : :
5146 : 14726658 : pergroup->transValue = newVal;
5147 : 14726658 : pergroup->transValueIsNull = fcinfo->isnull;
5148 : :
5149 : 14726658 : MemoryContextSwitchTo(oldContext);
5150 : 14726658 : }
5151 : :
5152 : : /* implementation of transition function invocation for byref types */
5153 : : static pg_attribute_always_inline void
5154 : 1477063 : ExecAggPlainTransByRef(AggState *aggstate, AggStatePerTrans pertrans,
5155 : : AggStatePerGroup pergroup,
5156 : : ExprContext *aggcontext, int setno)
5157 : : {
5158 : 1477063 : FunctionCallInfo fcinfo = pertrans->transfn_fcinfo;
5159 : : MemoryContext oldContext;
5160 : : Datum newVal;
5161 : :
5162 : : /* cf. select_current_set() */
5163 : 1477063 : aggstate->curaggcontext = aggcontext;
5164 : 1477063 : aggstate->current_set = setno;
5165 : :
5166 : : /* set up aggstate->curpertrans for AggGetAggref() */
5167 : 1477063 : aggstate->curpertrans = pertrans;
5168 : :
5169 : : /* invoke transition function in per-tuple context */
5170 : 1477063 : oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory);
5171 : :
5172 : 1477063 : fcinfo->args[0].value = pergroup->transValue;
5173 : 1477063 : fcinfo->args[0].isnull = pergroup->transValueIsNull;
5174 : 1477063 : fcinfo->isnull = false; /* just in case transfn doesn't set it */
5175 : :
5176 : 1477063 : newVal = FunctionCallInvoke(fcinfo);
5177 : :
5178 : : /*
5179 : : * For pass-by-ref datatype, must copy the new value into aggcontext and
5180 : : * free the prior transValue. But if transfn returned a pointer to its
5181 : : * first input, we don't need to do anything.
5182 : : *
5183 : : * It's safe to compare newVal with pergroup->transValue without regard
5184 : : * for either being NULL, because ExecAggCopyTransValue takes care to set
5185 : : * transValue to 0 when NULL. Otherwise we could end up accidentally not
5186 : : * reparenting, when the transValue has the same numerical value as
5187 : : * newValue, despite being NULL. This is a somewhat hot path, making it
5188 : : * undesirable to instead solve this with another branch for the common
5189 : : * case of the transition function returning its (modified) input
5190 : : * argument.
5191 : : */
5192 [ + + ]: 1477060 : if (DatumGetPointer(newVal) != DatumGetPointer(pergroup->transValue))
356 tgl@sss.pgh.pa.us 5193 : 30904 : newVal = ExecAggCopyTransValue(aggstate, pertrans,
5194 : 30904 : newVal, fcinfo->isnull,
5195 : : pergroup->transValue,
5196 : 30904 : pergroup->transValueIsNull);
5197 : :
1511 andres@anarazel.de 5198 : 1477060 : pergroup->transValue = newVal;
5199 : 1477060 : pergroup->transValueIsNull = fcinfo->isnull;
5200 : :
5201 : 1477060 : MemoryContextSwitchTo(oldContext);
5202 : 1477060 : }
|