From 697204cd7f68751be0132323fcf54943431909f4 Mon Sep 17 00:00:00 2001 From: shihao zhong Date: Tue, 27 Oct 2026 22:07:20 -0400 Subject: [PATCH 3/3] Let JIT deforming trust not-null constraints only when the slot does. --- src/backend/executor/execExpr.c | 24 ++++++++++++++++++++++++ src/backend/jit/llvm/llvmjit_deform.c | 20 +++++++++++++++----- src/backend/jit/llvm/llvmjit_expr.c | 3 ++- src/include/executor/execExpr.h | 6 ++++++ src/include/jit/llvmjit.h | 3 ++- 5 files changed, 49 insertions(+), 7 deletions(-) diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index 9a7424273c0..2738d6a180a 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -3059,6 +3059,7 @@ ExecComputeSlotInfo(ExprState *state, ExprEvalStep *op) TupleDesc desc = NULL; const TupleTableSlotOps *tts_ops = NULL; bool isfixed = false; + bool notnull_trusted = false; ExprEvalOp opcode = op->opcode; Assert(opcode == EEOP_INNER_FETCHSOME || @@ -3127,7 +3128,28 @@ ExecComputeSlotInfo(ExprState *state, ExprEvalStep *op) tts_ops = parent->scanops; if (parent->scanopsset) + { isfixed = parent->scanopsfixed; + + /* + * scanopsset is only set by ScanState nodes, whose scan slot is + * ss_ScanTupleSlot. Only that slot records whether its tuples + * are known to obey the descriptor's not-null constraints, which + * is not the case when a snapshot older than a constraint is in + * use. SubqueryScan scans another node's slot instead, but its + * own slot does not carry the flag, so this stays conservative. + */ + if (opcode == EEOP_SCAN_FETCHSOME) + { + TupleTableSlot *slot = ((ScanState *) parent)->ss_ScanTupleSlot; + + if (slot != NULL && TTS_OBEYS_NOT_NULL_CONSTRAINTS(slot)) + { + Assert(slot->tts_tupleDescriptor == desc); + notnull_trusted = true; + } + } + } } if (isfixed && desc != NULL && tts_ops != NULL) @@ -3135,12 +3157,14 @@ ExecComputeSlotInfo(ExprState *state, ExprEvalStep *op) op->d.fetch.fixed = true; op->d.fetch.kind = tts_ops; op->d.fetch.known_desc = desc; + op->d.fetch.notnull_trusted = notnull_trusted; } else { op->d.fetch.fixed = false; op->d.fetch.kind = NULL; op->d.fetch.known_desc = NULL; + op->d.fetch.notnull_trusted = false; } /* if the slot is known to always virtual we never need to deform */ diff --git a/src/backend/jit/llvm/llvmjit_deform.c b/src/backend/jit/llvm/llvmjit_deform.c index b28316e5b0d..320a8c402e4 100644 --- a/src/backend/jit/llvm/llvmjit_deform.c +++ b/src/backend/jit/llvm/llvmjit_deform.c @@ -29,10 +29,16 @@ /* * Create a function that deforms a tuple of type desc up to natts columns. + * + * Only if notnull_trusted is set may the tuples in the slot be assumed to + * obey desc's valid not-null constraints. A snapshot older than a constraint + * can still see nulls in that column, and then the null bitmap must be + * consulted for every column. */ LLVMValueRef slot_compile_deform(LLVMJitContext *context, TupleDesc desc, - const TupleTableSlotOps *ops, int natts) + const TupleTableSlotOps *ops, int natts, + bool notnull_trusted) { char *funcname; @@ -111,6 +117,8 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc, { CompactAttribute *catt = TupleDescCompactAttr(desc, attnum); Form_pg_attribute attr = TupleDescAttr(desc, attnum); + bool attnotnull = notnull_trusted && + catt->attnullability == ATTNULLABLE_VALID; /* * If the column is declared NOT NULL then it must be present in every @@ -126,7 +134,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc, * combination of attisdropped && attnotnull combination shouldn't * exist. */ - if (catt->attnullability == ATTNULLABLE_VALID && + if (attnotnull && !catt->atthasmissing && !catt->attisdropped && attr->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL) @@ -398,6 +406,8 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc, { CompactAttribute *att = TupleDescCompactAttr(desc, attnum); Form_pg_attribute attr = TupleDescAttr(desc, attnum); + bool attnotnull = notnull_trusted && + att->attnullability == ATTNULLABLE_VALID; LLVMValueRef v_incby; int alignto = att->attalignby; @@ -445,7 +455,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc, * present in the tuple, virtual generated columns are always stored * as NULL, so we must always perform NULL checks for these. */ - if (att->attnullability != ATTNULLABLE_VALID || + if (!attnotnull || attr->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL) { LLVMBasicBlockRef b_ifnotnull; @@ -612,7 +622,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc, known_alignment = -1; attguaranteedalign = false; } - else if (att->attnullability == ATTNULLABLE_VALID && + else if (attnotnull && attguaranteedalign && known_alignment >= 0) { /* @@ -623,7 +633,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc, Assert(att->attlen > 0); known_alignment += att->attlen; } - else if (att->attnullability == ATTNULLABLE_VALID && + else if (attnotnull && attr->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL && (att->attlen % alignto) == 0) { diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c index fc80d3e55fe..93637c1d80c 100644 --- a/src/backend/jit/llvm/llvmjit_expr.c +++ b/src/backend/jit/llvm/llvmjit_expr.c @@ -406,7 +406,8 @@ llvm_compile_expr(ExprState *state) l_jit_deform = slot_compile_deform(context, desc, tts_ops, - op->d.fetch.last_var); + op->d.fetch.last_var, + op->d.fetch.notnull_trusted); INSTR_TIME_SET_CURRENT(deform_endtime); INSTR_TIME_ACCUM_DIFF(context->base.instr.deform_counter, deform_endtime, deform_starttime); diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h index c61b3d624d5..4fdfbc77022 100644 --- a/src/include/executor/execExpr.h +++ b/src/include/executor/execExpr.h @@ -329,6 +329,12 @@ typedef struct ExprEvalStep TupleDesc known_desc; /* type of slot, can only be relied upon if fixed is set */ const TupleTableSlotOps *kind; + + /* + * can tuples in the slot be assumed to obey known_desc's valid + * not-null constraints? Only set if fixed is set. + */ + bool notnull_trusted; } fetch; /* for EEOP_INNER/OUTER/SCAN/OLD/NEW_[SYS]VAR */ diff --git a/src/include/jit/llvmjit.h b/src/include/jit/llvmjit.h index a647dd65ba2..2edbde86b03 100644 --- a/src/include/jit/llvmjit.h +++ b/src/include/jit/llvmjit.h @@ -131,7 +131,8 @@ extern void llvm_inline(LLVMModuleRef mod); extern bool llvm_compile_expr(struct ExprState *state); struct TupleTableSlotOps; extern LLVMValueRef slot_compile_deform(struct LLVMJitContext *context, TupleDesc desc, - const struct TupleTableSlotOps *ops, int natts); + const struct TupleTableSlotOps *ops, int natts, + bool notnull_trusted); /* **************************************************************************** -- 2.37.1 (Apple Git-137.1)