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