public inbox for [email protected]
help / color / mirror / Atom feedFrom: Thomas Munro <[email protected]>
To: Tom Lane <[email protected]>
Cc: PostgreSQL Hackers <[email protected]>
Subject: Re: A stack allocation API
Date: Thu, 12 Mar 2026 14:52:16 +1300
Message-ID: <CA+hUKG+Shdfa_QcVWXrHyhWGDJcPsw2aXWRQxnaUpyUa2fMffA@mail.gmail.com> (raw)
In-Reply-To: <[email protected]>
References: <CA+hUKG+ixUUYOGRcuZpkk5pmJZaUQv6VCPAjdTZXFP5vA8jxcA@mail.gmail.com>
<[email protected]>
On Sat, Feb 28, 2026 at 4:35 AM Tom Lane <[email protected]> wrote:
> > I also wondered if we might have a reasonable case for using alloca(),
> > where available. It's pretty much the thing we are emulating, but
> > keeps the stack nice and compact without big holes to step over for
> > the following call to strcoll_l() or whatever it might be.
>
> +1 for investigating alloca(). The one disadvantage I can see
> to making this coding pattern more common is that it'll result in
> increased stack usage, which is not great now and will become
> considerably less great in our hypothetical multithreaded future.
> If we can fix it so the typical stack consumption is a good deal
> less than 1KB, that worry would be alleviated.
Yeah. I thought about all that quite a bit, and the dangers of
longjmp(), and various portability arcana, and came up with a v2.
Safety:
* DECLARE_STACK_BUFFER() is intended for general use, and gives you
only 128 bytes with the array implementation, enough for lots of
values/nulls arrays and such, and there is also
DECLARE_STACK_BUFFER_LARGE() for the pre-existing locales code that
already uses 1024-byte arrays. To be really paranoid, I suppose we
could instead always use palloc() if you don't have alloca() support,
except when you ask for _LARGE, but I suspect 128 might be OK: it's in
the territory of plain-old-stack-frame-with-a-few-variables. But I
don't think it matters terribly much either way, as no modern system
would really use array mode.
* If using using alloca(), as far as I can see we don't have to be so
cautious *if* we additionally impose a total stack size limit. Here I
chose halfway to check_stack_depth()'s trigger point, but much less
than that would also be fine I think. You can see what that compiles
to at the end. Thoughts?
* I came up with a way to implement
static_assert(!pg_in_lexical_scope(PG_{TRY,CATCH,FINALLY})).
setlongjmp()ing around inside the same stack frame obviously breaks
this stuff fundamentally. I think it might be possible that some very
restricted patterns could made to work: if you allocated before (but
not inside) PG_TRY, either using volatile variables (or perhaps if
PG_TRY contained a compiler barrier?) then it freeing might be OK in
PG_CATCH, for example if plpy_exec.c moves its allocations up. I'm
not too sure about that, though, and for now all stack buffer
operations are banned in all three scopes. Better implementation
techniques for the resulting caveman meta-programming exercise are
welcome... it took me quite some time to make it out of the valley of
shadow warnings.
Portability:
* There are no general purpose systems left that have stacks that grow
upwards AFAICS, right? So I propose that we define PG_STACK_DIRECTION
to -1 in pg_config_manual.h, but continue to write code that should
work correctly if you set it to +1. That simplifies the macrology and
de-branchifies the code. It doesn't make sense to tolerate
non-existent computers by running zillions of runtime tests around the
world every day.
* I guess I should probably actually test it on PA-RISC (the last
stack-grows-up system?) under Qemu or something. The way I approached
this was to write expressions in terms of PG_STACK_DIRECTION, instead
of chunks of #ifdef'd code that would never be seen by a living
compiler and thus inevitably bit-rot. We could still assert that
reality matches expectations in a few spots, and then worry about
configure-time, #ifdef or template file control if a new platform ever
appears with an upward stack.
* PostgreSQL already wouldn't work with "split stack" code (it would
bogusly error out of check_stack_depth() when it meets a far away
stack segment), or other exotic systems like spaghetti stacks. I
don't think this is much of an additional jump in our assumptions
about how C is actually implemented, it's just assuming that there are
no systems that grow down one day and up the next. (A number of CPU
architectures could technically do that, but their ABI firmly nails
down which way it has to be, and the answer is down).
* For estimating whether there is enough free space to call alloca(),
it prefers to use builtins to read the stack pointer (GCC, Clang 22+),
but failing that it wastes a register that initially holds a
conservative estimate (address of a local variable, itself, which is
surely deeper-or-equal to the real stack pointer), and later holds the
result of the last alloca() call, which is 100% accurate on
downward-stack systems, and potentially only out by a small amount of
alignment fuzz on non-existent upward-stack systems because I assumed
that new sp = alloca(size) + size. (It would be more accurate if I
did TYPALIGN(X, size), but I don't know what its X is, I only know
that it's >= alignof(max_align_t), so I'd have to go and read the
relevant ABI manual for this hypothetical upward-stack system to find
out, but since it doesn't exist, all thoughts about a perfect
estimation disappear in a puff of logic.)
* When judging if a pointer came from the stack, it additionally needs
an least-deep bound, but if there is no builtin for the current stack
frame address then it uses check_stack_depth.c's stack_base_ptr. Both
are reliable, all we need is a pointer to anywhere less deep in the
stack than anything we've alloca()'d. The builtin might technically
give you something from a function you're inlined into, but that's OK
too. In practice probably only MSVC uses stack_base_ptr.
* It only uses literal "alloca()" for MSVC, which turns into a builtin
and thus has a property that I'm relying on. For Unixen it uses a
GCC/Clang builtin directly, because otherwise I'd have to worry about
that property too. Builtins are safe in a function call argument
because the compiler implements and understands them, but the original
alloca() from old Unixen somehow moved the stack pointer without the
compiler's knowledge, which used to trash the stack if it had already
started pushing other stuff onto the stack for the function call, at
least in some circumstances. I assume no such things exist now, but
you'd still have to figure out which header to include, and that
varies on niche systems (<alloca.h>, <stddef.h>, ...?).
* For builtin detection, I ran into CC vs CXX vs CLANG problems, so I
gave up and invented pg_has_builtin(). I don't propose it be used
except where needed though since it doesn't exist on GCC < 10 (it's an
idea from Clang). This means that old build farm compilers would use
the array version, which I think is probably desirable for coverage.
Current distros are shipping GCC 11+, probably mostly 14 or so, so I
think it's OK to exclude 10 from a minor optimisation project.
Potential sites that can use this:
I have attached a bunch of easy candidate changes, almost all
mechanical changes for small temporary arrays of values/nulls,
scankeys, etc or temporary C-strings where no refactoring was needed
and the no-escape property was obvious to the eye. In the case of the
planner, I worked a bit harder to find a couple of things that have
pass-down-but-never-capture semantics, based on hunches... FWIW I do
see a small planner speed-up in the join-order-optimisation test David
mentioned the other day in simple tests, though I haven't studied it
seriously enough to report on yet. I'm actually not too sure yet how
to evaluate this stuff at a higher level, ie the effects of tiny
micro-optimisations scattered all over the tree, and only in the
arbitrary places that didn't need refactoring. So for now I'm
reporting only on a toy C-stringification microbenchmark: see end.
Ideas for further development:
* I wanted this to be useful for SIMD, so I made sure that
stack_buffer_alloc_object(T) would handle strict alignment
automatically based on alignof(T). palloc_object(T) et al should get
the same treatment, as I wrote in another thread about the funky __128
situation (and I have basic patches, for another day).
* I see how to implement _realloc(): if ptr == sp, then you have the
most recent allocation, so if you (somehow) know the original size you
can allocate the difference and memmove(). (This condition would
never be met for stack-upwards, but that's OK). In all other cases
you'd probably fall straight back to palloc() I think. I don't know
if there'd be much call for it though.
* Presumably this could benefit from a sprinkle of VALGRIND and sanitizer clues.
* It would be nice to figure out how to make at least one compiler
complain about very obvious escapes, as they do if you return a
pointer to a normal stack variable, but I guess the main problem isn't
the obvious ones.
* Many uses of temporary C strings wouldn't be needed if we accepted
known-length strings in more APIs; it may be unavoidable for libc
functions, but our own reader etc should probably ideally be able to
cope... I have a separate project investigating a centralised string
iterator so that we can make all of our string processing behave the
same way and will think about that...
* There are quite a few places that form or copy temporary
non-escaping index tuples, tuples, tupledescs, values/nulls extracted
from arrays etc that could probably live on the stack quite happily if
we split a few functions into "how much do you need?" and "in place"
pairs. That's been done in a few ad hoc spots for parallel query eg
tuple descriptors.
* Independently of this project, but really the same sort of thing, I
have long wanted to look into all the places that form a tuple, copy
it and then free it (hashing, sorting, materialising) when they should
form into caller-supplied destination, be it the stack, a hash table,
a tuple queue or whatever else...
* Something like "defer" for magic automatic pfree() on scope exit if
we can figure out how to do it without waiting for the year 202y + 10,
as mentioned in reply to Andres...
* Even without that, I suppose GCC cleanup callbacks could still be
used to scribble on the memory that goes out of scope. I guess it
must be pretty hard to miss bugs anyway since the stack is already
scribbled to smithereens.
For a very simple micro-benchmark, I tried a trivial C-string
conversion wrapper:
for (int i = 0; i < 10000000; ++i)
my_strlen("hello world", 11);
Given this definition of a completely useless function that exercises
C-string construction:
size_t
my_strlen(const char *data, size_t size)
{
char *cstr;
size_t result;
DECLARE_STACK_BUFFER();
cstr = stack_buffer_strdup_with_len(data, size);
result = strlen(cstr);
stack_buffer_free(cstr);
return result;
}
With STACK_BUFFER_USE_PALLOC, which just expands those macros to
palloc()/pfree(), it runs in ~216ms here, and with
STACK_BUFFER_USE_ALLOCA or STACK_BUFFER_USE_ARRAY it runs in ~155ms,
so that's a 1.39x speedup.
The generated code looks like:
0x0000000000979323 <+19>: mov 0x442a06(%rip),%rdx #
0xdbbd30 <stack_soft_limit_ptr>
0x000000000097932a <+26>: lea -0x400(%rsp),%rax <--
sp - STACK_BUFFER_DEFAULT
0x0000000000979332 <+34>: cmp %rdx,%rax
0x0000000000979335 <+37>: cmovb %rdx,%rax <--
take the nearer address
%rax now holds the threshold stack address against which all
allocation attempts will be compared. Uninteresting computation of
-(size + 1) for NUL byte:
0x0000000000979339 <+41>: mov %rsi,%rdx
0x000000000097933c <+44>: not %rdx
Then allocation attempt:
0x000000000097933f <+47>: add %rsp,%rdx
0x0000000000979342 <+50>: cmp %rdx,%rax <--
would it fit?
0x0000000000979345 <+53>: jae 0x979380 <my_strlen+112> <--
nope, jump to slow path
0x0000000000979347 <+55>: lea 0x10(%rsi),%rax
0x000000000097934b <+59>: mov %rsi,%rdx
0x000000000097934e <+62>: mov %rdi,%rsi
0x0000000000979351 <+65>: and $0xfffffffffffffff0,%rax <--
alloca() aligns
0x0000000000979355 <+69>: sub %rax,%rsp <--
alloca() allocates
Then it's then a straight line run through the uninteresting bits to
return the result, with stack_buffer_free() entirely elided:
0x0000000000979358 <+72>: mov %rsp,%rdi
0x000000000097935b <+75>: call 0x4950d0 <memcpy@plt>
0x0000000000979360 <+80>: movb $0x0,(%rsp,%rbx,1)
0x0000000000979364 <+84>: mov %rsp,%rdi
0x0000000000979367 <+87>: call 0x494890 <strlen@plt>
0x000000000097936c <+92>: lea -0x18(%rbp),%rsp
0x0000000000979370 <+96>: pop %rbx
0x0000000000979371 <+97>: pop %r12
0x0000000000979373 <+99>: pop %r13
0x0000000000979375 <+101>: pop %rbp
0x0000000000979376 <+102>: ret
If it wouldn't fit, the out-of-line implementation looks like what you
get with STACK_BUFFER_USE_PALLOC:
0x0000000000979377 <+103>: nopw 0x0(%rax,%rax,1)
0x0000000000979380 <+112>: lea 0x1(%rsi),%rdi
0x0000000000979384 <+116>: call 0xa252f0 <palloc>
0x0000000000979389 <+121>: mov %rbx,%rdx
0x000000000097938c <+124>: mov %r12,%rsi
0x000000000097938f <+127>: mov %rax,%r13
0x0000000000979392 <+130>: mov %rax,%rdi
0x0000000000979395 <+133>: call 0x4950d0 <memcpy@plt>
0x000000000097939a <+138>: movb $0x0,0x0(%r13,%rbx,1)
0x00000000009793a0 <+144>: mov %r13,%rdi
0x00000000009793a3 <+147>: call 0x494890 <strlen@plt>
0x00000000009793a8 <+152>: cmp %rsp,%r13
0x00000000009793ab <+155>: jb 0x9793b2 <my_strlen+162>
0x00000000009793ad <+157>: cmp %r13,%rbp
0x00000000009793b0 <+160>: jae 0x97936c <my_strlen+92>
0x00000000009793b2 <+162>: mov %r13,%rdi
0x00000000009793b5 <+165>: mov %rax,-0x28(%rbp)
0x00000000009793b9 <+169>: call 0xa255c0 <pfree>
0x00000000009793be <+174>: mov -0x28(%rbp),%rax
0x00000000009793c2 <+178>: jmp 0x97936c <my_strlen+92>
In more complicated functions GCC's costing only moves the actual
palloc() and pfree() calls out-of-line and jumps back to the
straight-line code, while here it decided it was cheaper to duplicate
the memcpy(), strlen() calls. Either way the straight-line code
assumes the stack will be used, which is hopefully correct most of the
time.
It became a lot more aggressive about that sort of thing when I added
the "stack_buffer_maybe_pfree" flag which seemed to flip GCC's
costing; before that it would often leave the
if-it's-not-between-%rbp-and-%rsp-then-call-pfree code in the main
code. I'm not 100% sure about all that, but it *looks* like an
effective optimisation.
I've attached some data on allocation sizes from the regression tests
(so not hitting all the places changed here), captured with
STACK_BUFFER_USE_PALLOC_LOG. Obviously not representative of real
usage, but that technique can be used to check real allocation sizes
and stack depths for any workload.
. o O ( I originally called it stack_buffer because I started with
"let's standardise the array trick", so it was a buffer. Perhaps a
better name for all this stuff would be pg_stack_alloc() or such,
since it doesn't really have a buffer anymore except in a fallback
mode... )
loc | func | count | size_avg | size_max | depth_avg | depth_max
--------------------------+-------------------------------------------+--------+----------+----------+-----------+-----------
allpaths.c:1071 | set_append_rel_size | 3421 | 65 | 144 | 6272 | 10192
analyzejoins.c:2369 | remove_self_joins_recurse | 7933 | 19 | 64 | 6032 | 9520
appendinfo.c:618 | adjust_appendrel_attrs_multilevel | 7812 | 11 | 24 | 6718 | 10080
appendinfo.c:695 | adjust_child_relids_multilevel | 191 | 9 | 24 | 6021 | 8320
arrayfuncs.c:1118 | array_out | 4270 | 50 | 80000 | 6111 | 10112
arrayfuncs.c:1119 | array_out | 4270 | 6 | 10000 | 6127 | 10128
arrayfuncs.c:3286 | array_map | 50 | 21 | 88 | 6029 | 8784
arrayfuncs.c:3287 | array_map | 50 | 3 | 11 | 6045 | 8800
arrayfuncs.c:6492 | array_replace_internal | 12 | 31 | 40 | 5843 | 6304
arrayfuncs.c:6493 | array_replace_internal | 12 | 4 | 5 | 5859 | 6320
brin_tuple.c:125 | brin_form_tuple | 1026 | 224 | 512 | 6882 | 8272
brin_tuple.c:126 | brin_form_tuple | 1026 | 28 | 64 | 6898 | 8288
brin_tuple.c:127 | brin_form_tuple | 1026 | 4 | 8 | 6882 | 8272
brin_tuple.c:130 | brin_form_tuple | 1026 | 224 | 512 | 6898 | 8288
clauses.c:4784 | fetch_function_defaults | 1014 | 289 | 1039 | 5507 | 8896
clauses.c:5990 | make_SAOP_expr | 569 | 17 | 48 | 8196 | 10544
clauses.c:5991 | make_SAOP_expr | 569 | 2 | 6 | 8212 | 10560
datum.c:502 | datumSerialize | 1 | 32 | 32 | 5648 | 5648
equivclass.c:1387 | generate_base_implied_equalities_no_const | 7654 | 43 | 200 | 5955 | 11456
execPartition.c:2541 | InitExecPartitionPruneContexts | 8 | 35 | 76 | 5852 | 5984
execTuples.c:2336 | BuildTupleFromCStrings | 5062 | 87 | 136 | 5632 | 6560
execTuples.c:2337 | BuildTupleFromCStrings | 5062 | 11 | 17 | 5648 | 6576
fmgr.c:235 | fmgr_info_cxt_security | 196 | 13 | 24 | 6577 | 8624
fmgr.c:385 | fmgr_info_C_lang | 333 | 18 | 33 | 6066 | 9264
fmgr.c:389 | fmgr_info_C_lang | 333 | 23 | 61 | 6066 | 9264
genam.c:441 | systable_beginscan | 882614 | 119 | 288 | 6769 | 67600
genam.c:696 | systable_beginscan_ordered | 1819 | 79 | 144 | 12617 | 61792
ginscan.c:429 | ginNewScanKey | 5 | 426 | 456 | 7930 | 8944
hashfunc.c:302 | hashtext | 445 | 4 | 6 | 5167 | 5648
hashfunc.c:359 | hashtextextended | 4 | 6 | 6 | 6072 | 6176
heapam.c:8773 | bottomup_sort_and_shrink | 218 | 3884 | 7992 | 13476 | 15456
heapam.c:8846 | bottomup_sort_and_shrink | 218 | 5179 | 10656 | 13476 | 15456
heapam_handler.c:723 | heapam_relation_copy_for_cluster | 54 | 24 | 272 | 5949 | 6544
heapam_handler.c:724 | heapam_relation_copy_for_cluster | 54 | 3 | 34 | 5965 | 6560
heaptuple.c:1236 | heap_modify_tuple | 5399 | 230 | 272 | 6361 | 9392
heaptuple.c:1237 | heap_modify_tuple | 5399 | 29 | 34 | 6377 | 9408
heaptuple.c:1300 | heap_modify_tuple_by_cols | 18 | 201 | 272 | 5351 | 5920
heaptuple.c:1301 | heap_modify_tuple_by_cols | 18 | 25 | 34 | 5367 | 5936
indxpath.c:1297 | group_similar_or_args | 818 | 61 | 312 | 7129 | 10592
indxpath.c:1861 | choose_bitmap_and | 8761 | 19 | 40 | 7412 | 10688
joinrels.c:1781 | try_partitionwise_join | 1255 | 17 | 48 | 6588 | 8960
joinrels.c:1864 | build_child_join_sjinfo | 529 | 11 | 40 | 6424 | 7264
joinrels.c:1866 | build_child_join_sjinfo | 529 | 9 | 24 | 6440 | 7280
jsonfuncs.c:1689 | jsonb_set_element | 41 | 2 | 6 | 5394 | 5424
jsonfuncs.c:2965 | populate_array | 272 | 5 | 12 | 5742 | 6256
jsonfuncs.c:3161 | populate_scalar | 2 | 15 | 16 | 6480 | 6480
jsonfuncs.c:3174 | populate_scalar | 62 | 5 | 20 | 5700 | 6096
jsonfuncs.c:3193 | populate_scalar | 268 | 8 | 24 | 6317 | 6960
jsonfuncs.c:3195 | populate_scalar | 1 | 5 | 5 | 5776 | 5776
jsonfuncs.c:3564 | populate_record | 729 | 59 | 136 | 5791 | 6784
jsonfuncs.c:3565 | populate_record | 729 | 7 | 17 | 5807 | 6800
like_support.c:1005 | like_fixed_prefix | 363 | 10 | 25 | 6853 | 8592
like_support.c:1013 | like_fixed_prefix | 2 | 3 | 3 | 6800 | 7184
like_support.c:1018 | like_fixed_prefix | 365 | 10 | 25 | 6853 | 8592
like_support.c:1105 | like_fixed_prefix_ci | 36 | 20 | 28 | 7276 | 8208
like_support.c:1108 | like_fixed_prefix_ci | 36 | 20 | 28 | 7276 | 8208
like_support.c:1136 | like_fixed_prefix_ci | 36 | 3 | 9 | 7276 | 8208
like_support.c:1150 | like_fixed_prefix_ci | 17 | 15 | 25 | 6777 | 7440
like_support.c:1200 | regex_fixed_prefix | 76 | 9 | 54 | 7006 | 7872
like_support.c:1222 | regex_fixed_prefix | 107 | 13 | 58 | 6793 | 8672
like_support.c:1653 | make_greater_string | 235 | 7 | 26 | 7305 | 8704
like_support.c:1689 | make_greater_string | 6 | 13 | 20 | 6549 | 7136
nbtpreprocesskeys.c:1574 | _bt_unmark_keys | 1 | 4 | 4 | 8048 | 8048
nbtpreprocesskeys.c:1659 | _bt_unmark_keys | 1 | 72 | 72 | 8048 | 8048
nbtpreprocesskeys.c:1660 | _bt_unmark_keys | 1 | 216 | 216 | 8064 | 8064
nbtpreprocesskeys.c:1665 | _bt_unmark_keys | 1 | 48 | 48 | 8048 | 8048
nbtpreprocesskeys.c:1666 | _bt_unmark_keys | 1 | 144 | 144 | 8064 | 8064
nodeAgg.c:4356 | build_pertrans_for_aggref | 95 | 5 | 12 | 5727 | 7072
nodeAgg.c:4391 | GetAggInitVal | 2838 | 3 | 18 | 5996 | 18992
nodeHashjoin.c:845 | ExecInitHashJoin | 4167 | 4 | 12 | 5505 | 10688
nodeHashjoin.c:846 | ExecInitHashJoin | 4167 | 4 | 12 | 5521 | 10704
nodeHashjoin.c:847 | ExecInitHashJoin | 4167 | 1 | 3 | 5521 | 10704
nodeMemoize.c:1012 | ExecInitMemoize | 212 | 4 | 8 | 5938 | 8816
nodeWindowAgg.c:3170 | GetAggInitVal | 117 | 5 | 8 | 5540 | 8736
numeric.c:6787 | set_var_from_str | 15126 | 11 | 96 | 6258 | 12304
numeric.c:9058 | div_var | 907 | 132 | 22528 | 6588 | 8512
parse_clause.c:739 | transformRangeTableFunc | 22 | 33 | 64 | 5036 | 6128
parse_func.c:1771 | func_get_detail | 995 | 291 | 1039 | 5809 | 9616
parse_type.c:375 | typenameTypeMod | 947 | 9 | 24 | 5993 | 17360
partbounds.c:2397 | fix_merged_indexes | 8 | 22 | 24 | 6896 | 7280
partbounds.c:2459 | generate_matching_part_pairs | 122 | 11 | 24 | 6970 | 7776
partbounds.c:2460 | generate_matching_part_pairs | 122 | 11 | 24 | 6986 | 7792
partbounds.c:366 | create_hash_bounds | 106 | 26 | 96 | 6323 | 7920
partbounds.c:487 | create_list_bounds | 930 | 51 | 400 | 6461 | 10208
partbounds.c:5130 | calculate_partition_bound_for_merge | 26 | 18 | 24 | 6480 | 6480
partbounds.c:5771 | check_partitions_for_split | 46 | 24 | 40 | 6544 | 6544
partbounds.c:5801 | check_partitions_for_split | 38 | 21 | 32 | 6544 | 6544
partbounds.c:5816 | check_partitions_for_split | 38 | 21 | 32 | 6544 | 6544
partbounds.c:705 | create_range_bounds | 1310 | 34 | 144 | 6621 | 10032
partbounds.c:745 | create_range_bounds | 1310 | 32 | 128 | 6621 | 10032
partprune.c:252 | make_partition_pruneinfo | 1025 | 23 | 128 | 6413 | 9728
partprune.c:475 | make_partitionedrel_pruneinfo | 816 | 23 | 128 | 6568 | 9728
partprune.c:523 | make_partitionedrel_pruneinfo | 10 | 8 | 8 | 5894 | 6224
pathkeys.c:1682 | select_outer_pathkeys_for_merge | 55425 | 9 | 32 | 6908 | 12384
pathkeys.c:1683 | select_outer_pathkeys_for_merge | 55425 | 4 | 16 | 6924 | 12400
pg_locale_icu.c:761 | strnxfrm_icu | 958 | 5 | 8 | 5266 | 6288
pg_locale_libc.c:1255 | char2wchar | 163964 | 3 | 58 | 8296 | 16080
pg_locale_libc.c:510 | strlower_libc_mb | 5530 | 31 | 232 | 7797 | 15984
pg_locale_libc.c:521 | strlower_libc_mb | 5530 | 28 | 229 | 7797 | 15984
pg_locale_libc.c:617 | strtitle_libc_mb | 1 | 40 | 40 | 5696 | 5696
pg_locale_libc.c:634 | strtitle_libc_mb | 1 | 37 | 37 | 5696 | 5696
pg_locale_libc.c:712 | strupper_libc_mb | 158433 | 11 | 52 | 8214 | 8560
pg_locale_libc.c:723 | strupper_libc_mb | 158433 | 8 | 49 | 8214 | 8560
pg_locale_libc.c:906 | strncoll_libc | 311574 | 16 | 3201 | 7437 | 23920
pg_locale_libc.c:909 | strncoll_libc | 311574 | 17 | 3201 | 7437 | 23920
plancat.c:1808 | get_relation_statistics | 138 | 956 | 1720 | 8532 | 8624
planner.c:3059 | extract_rollup_sets | 151 | 33 | 80 | 5655 | 7360
planner.c:3060 | extract_rollup_sets | 151 | 33 | 80 | 5671 | 7376
planner.c:3061 | extract_rollup_sets | 151 | 33 | 80 | 5671 | 7376
planner.c:3062 | extract_rollup_sets | 151 | 8 | 20 | 5671 | 7376
planner.c:3147 | extract_rollup_sets | 151 | 13 | 32 | 5655 | 7360
planner.c:6043 | select_active_windows | 426 | 18 | 80 | 5531 | 9472
planner.c:8134 | apply_scanjoin_target_to_paths | 1572 | 10 | 56 | 6283 | 9904
planner.c:8252 | create_partitionwise_grouping_paths | 136 | 13 | 24 | 6131 | 6880
prepagg.c:532 | GetAggInitVal | 2007 | 3 | 18 | 6301 | 14400
prepunion.c:1640 | generate_append_tlist | 714 | 11 | 24 | 5822 | 19488
rowtypes.c:1161 | record_eq | 560 | 16 | 40 | 5386 | 6080
rowtypes.c:1162 | record_eq | 560 | 2 | 5 | 5402 | 6096
rowtypes.c:1164 | record_eq | 560 | 16 | 40 | 5402 | 6096
rowtypes.c:1165 | record_eq | 560 | 2 | 5 | 5402 | 6096
rowtypes.c:1443 | record_image_cmp | 103 | 31 | 48 | 6511 | 6800
rowtypes.c:1444 | record_image_cmp | 103 | 4 | 6 | 6527 | 6816
rowtypes.c:1446 | record_image_cmp | 103 | 31 | 48 | 6527 | 6816
rowtypes.c:1447 | record_image_cmp | 103 | 4 | 6 | 6527 | 6816
rowtypes.c:146 | record_in | 66 | 19 | 40 | 5989 | 6944
rowtypes.c:147 | record_in | 66 | 2 | 5 | 6005 | 6960
rowtypes.c:1691 | record_image_eq | 33 | 26 | 48 | 6577 | 6976
rowtypes.c:1692 | record_image_eq | 33 | 3 | 6 | 6593 | 6992
rowtypes.c:1694 | record_image_eq | 33 | 26 | 48 | 6593 | 6992
rowtypes.c:1695 | record_image_eq | 33 | 3 | 6 | 6593 | 6992
rowtypes.c:1885 | hash_record | 150 | 16 | 24 | 5328 | 6800
rowtypes.c:1886 | hash_record | 150 | 2 | 3 | 5344 | 6816
rowtypes.c:2008 | hash_record_extended | 5 | 16 | 16 | 6467 | 6832
rowtypes.c:2009 | hash_record_extended | 5 | 2 | 2 | 6483 | 6848
rowtypes.c:391 | record_out | 2754 | 20 | 48 | 5681 | 10224
rowtypes.c:392 | record_out | 2754 | 2 | 6 | 5697 | 10240
rowtypes.c:915 | record_cmp | 655 | 25 | 160 | 6493 | 7888
rowtypes.c:916 | record_cmp | 655 | 3 | 20 | 6509 | 7904
rowtypes.c:918 | record_cmp | 655 | 25 | 160 | 6509 | 7904
rowtypes.c:919 | record_cmp | 655 | 3 | 20 | 6509 | 7904
ruleutils.c:1364 | pg_get_indexdef_worker | 60 | 385 | 576 | 5793 | 10896
ruleutils.c:1560 | pg_get_indexdef_worker | 42 | 434 | 637 | 5574 | 7632
ruleutils.c:1705 | pg_get_statisticsobj_worker | 14 | 756 | 1312 | 5479 | 7136
ruleutils.c:1880 | pg_get_statisticsobjdef_expressions | 24 | 667 | 770 | 5693 | 5888
ruleutils.c:1996 | pg_get_partkeydef_worker | 11 | 493 | 1032 | 5735 | 5984
ruleutils.c:2732 | pg_get_expr_worker | 650 | 478 | 3139 | 5424 | 7056
ruleutils.c:3347 | print_function_arguments | 2 | 219 | 298 | 5248 | 5248
ruleutils.c:3549 | pg_get_function_arg_default | 5 | 244 | 307 | 5120 | 5120
setrefs.c:1182 | set_plan_refs | 212 | 74 | 112 | 4770 | 5712
setrefs.c:1235 | set_plan_refs | 273 | 60 | 144 | 4996 | 7808
setrefs.c:1415 | set_indexonlyscan_references | 1829 | 35 | 80 | 5889 | 13424
setrefs.c:2055 | set_hash_references | 4075 | 51 | 272 | 5773 | 11056
setrefs.c:2433 | set_join_references | 14074 | 189 | 784 | 5294 | 10848
setrefs.c:2434 | set_join_references | 14074 | 178 | 560 | 5294 | 10848
setrefs.c:2584 | set_upper_references | 9720 | 42 | 560 | 5947 | 14656
setrefs.c:3521 | set_returning_clause_references | 442 | 63 | 272 | 4958 | 8272
setrefs.c:3603 | set_windowagg_runcondition_references | 457 | 70 | 352 | 5069 | 8224
tsvector_op.c:2555 | ts_process_call | 1144 | 3 | 4 | 5216 | 5216
xact.c:5611 | SerializeTransactionState | 159 | 9 | 104 | 6605 | 16912
xact.c:670 | AssignTransactionId | 33 | 29 | 208 | 6240 | 11136
(160 rows)
Attachments:
[text/plain] regression-stack-buffer-alloc.txt (19.8K, 2-regression-stack-buffer-alloc.txt)
download | inline:
loc | func | count | size_avg | size_max | depth_avg | depth_max
--------------------------+-------------------------------------------+--------+----------+----------+-----------+-----------
allpaths.c:1071 | set_append_rel_size | 3421 | 65 | 144 | 6272 | 10192
analyzejoins.c:2369 | remove_self_joins_recurse | 7933 | 19 | 64 | 6032 | 9520
appendinfo.c:618 | adjust_appendrel_attrs_multilevel | 7812 | 11 | 24 | 6718 | 10080
appendinfo.c:695 | adjust_child_relids_multilevel | 191 | 9 | 24 | 6021 | 8320
arrayfuncs.c:1118 | array_out | 4270 | 50 | 80000 | 6111 | 10112
arrayfuncs.c:1119 | array_out | 4270 | 6 | 10000 | 6127 | 10128
arrayfuncs.c:3286 | array_map | 50 | 21 | 88 | 6029 | 8784
arrayfuncs.c:3287 | array_map | 50 | 3 | 11 | 6045 | 8800
arrayfuncs.c:6492 | array_replace_internal | 12 | 31 | 40 | 5843 | 6304
arrayfuncs.c:6493 | array_replace_internal | 12 | 4 | 5 | 5859 | 6320
brin_tuple.c:125 | brin_form_tuple | 1026 | 224 | 512 | 6882 | 8272
brin_tuple.c:126 | brin_form_tuple | 1026 | 28 | 64 | 6898 | 8288
brin_tuple.c:127 | brin_form_tuple | 1026 | 4 | 8 | 6882 | 8272
brin_tuple.c:130 | brin_form_tuple | 1026 | 224 | 512 | 6898 | 8288
clauses.c:4784 | fetch_function_defaults | 1014 | 289 | 1039 | 5507 | 8896
clauses.c:5990 | make_SAOP_expr | 569 | 17 | 48 | 8196 | 10544
clauses.c:5991 | make_SAOP_expr | 569 | 2 | 6 | 8212 | 10560
datum.c:502 | datumSerialize | 1 | 32 | 32 | 5648 | 5648
equivclass.c:1387 | generate_base_implied_equalities_no_const | 7654 | 43 | 200 | 5955 | 11456
execPartition.c:2541 | InitExecPartitionPruneContexts | 8 | 35 | 76 | 5852 | 5984
execTuples.c:2336 | BuildTupleFromCStrings | 5062 | 87 | 136 | 5632 | 6560
execTuples.c:2337 | BuildTupleFromCStrings | 5062 | 11 | 17 | 5648 | 6576
fmgr.c:235 | fmgr_info_cxt_security | 196 | 13 | 24 | 6577 | 8624
fmgr.c:385 | fmgr_info_C_lang | 333 | 18 | 33 | 6066 | 9264
fmgr.c:389 | fmgr_info_C_lang | 333 | 23 | 61 | 6066 | 9264
genam.c:441 | systable_beginscan | 882614 | 119 | 288 | 6769 | 67600
genam.c:696 | systable_beginscan_ordered | 1819 | 79 | 144 | 12617 | 61792
ginscan.c:429 | ginNewScanKey | 5 | 426 | 456 | 7930 | 8944
hashfunc.c:302 | hashtext | 445 | 4 | 6 | 5167 | 5648
hashfunc.c:359 | hashtextextended | 4 | 6 | 6 | 6072 | 6176
heapam.c:8773 | bottomup_sort_and_shrink | 218 | 3884 | 7992 | 13476 | 15456
heapam.c:8846 | bottomup_sort_and_shrink | 218 | 5179 | 10656 | 13476 | 15456
heapam_handler.c:723 | heapam_relation_copy_for_cluster | 54 | 24 | 272 | 5949 | 6544
heapam_handler.c:724 | heapam_relation_copy_for_cluster | 54 | 3 | 34 | 5965 | 6560
heaptuple.c:1236 | heap_modify_tuple | 5399 | 230 | 272 | 6361 | 9392
heaptuple.c:1237 | heap_modify_tuple | 5399 | 29 | 34 | 6377 | 9408
heaptuple.c:1300 | heap_modify_tuple_by_cols | 18 | 201 | 272 | 5351 | 5920
heaptuple.c:1301 | heap_modify_tuple_by_cols | 18 | 25 | 34 | 5367 | 5936
indxpath.c:1297 | group_similar_or_args | 818 | 61 | 312 | 7129 | 10592
indxpath.c:1861 | choose_bitmap_and | 8761 | 19 | 40 | 7412 | 10688
joinrels.c:1781 | try_partitionwise_join | 1255 | 17 | 48 | 6588 | 8960
joinrels.c:1864 | build_child_join_sjinfo | 529 | 11 | 40 | 6424 | 7264
joinrels.c:1866 | build_child_join_sjinfo | 529 | 9 | 24 | 6440 | 7280
jsonfuncs.c:1689 | jsonb_set_element | 41 | 2 | 6 | 5394 | 5424
jsonfuncs.c:2965 | populate_array | 272 | 5 | 12 | 5742 | 6256
jsonfuncs.c:3161 | populate_scalar | 2 | 15 | 16 | 6480 | 6480
jsonfuncs.c:3174 | populate_scalar | 62 | 5 | 20 | 5700 | 6096
jsonfuncs.c:3193 | populate_scalar | 268 | 8 | 24 | 6317 | 6960
jsonfuncs.c:3195 | populate_scalar | 1 | 5 | 5 | 5776 | 5776
jsonfuncs.c:3564 | populate_record | 729 | 59 | 136 | 5791 | 6784
jsonfuncs.c:3565 | populate_record | 729 | 7 | 17 | 5807 | 6800
like_support.c:1005 | like_fixed_prefix | 363 | 10 | 25 | 6853 | 8592
like_support.c:1013 | like_fixed_prefix | 2 | 3 | 3 | 6800 | 7184
like_support.c:1018 | like_fixed_prefix | 365 | 10 | 25 | 6853 | 8592
like_support.c:1105 | like_fixed_prefix_ci | 36 | 20 | 28 | 7276 | 8208
like_support.c:1108 | like_fixed_prefix_ci | 36 | 20 | 28 | 7276 | 8208
like_support.c:1136 | like_fixed_prefix_ci | 36 | 3 | 9 | 7276 | 8208
like_support.c:1150 | like_fixed_prefix_ci | 17 | 15 | 25 | 6777 | 7440
like_support.c:1200 | regex_fixed_prefix | 76 | 9 | 54 | 7006 | 7872
like_support.c:1222 | regex_fixed_prefix | 107 | 13 | 58 | 6793 | 8672
like_support.c:1653 | make_greater_string | 235 | 7 | 26 | 7305 | 8704
like_support.c:1689 | make_greater_string | 6 | 13 | 20 | 6549 | 7136
nbtpreprocesskeys.c:1574 | _bt_unmark_keys | 1 | 4 | 4 | 8048 | 8048
nbtpreprocesskeys.c:1659 | _bt_unmark_keys | 1 | 72 | 72 | 8048 | 8048
nbtpreprocesskeys.c:1660 | _bt_unmark_keys | 1 | 216 | 216 | 8064 | 8064
nbtpreprocesskeys.c:1665 | _bt_unmark_keys | 1 | 48 | 48 | 8048 | 8048
nbtpreprocesskeys.c:1666 | _bt_unmark_keys | 1 | 144 | 144 | 8064 | 8064
nodeAgg.c:4356 | build_pertrans_for_aggref | 95 | 5 | 12 | 5727 | 7072
nodeAgg.c:4391 | GetAggInitVal | 2838 | 3 | 18 | 5996 | 18992
nodeHashjoin.c:845 | ExecInitHashJoin | 4167 | 4 | 12 | 5505 | 10688
nodeHashjoin.c:846 | ExecInitHashJoin | 4167 | 4 | 12 | 5521 | 10704
nodeHashjoin.c:847 | ExecInitHashJoin | 4167 | 1 | 3 | 5521 | 10704
nodeMemoize.c:1012 | ExecInitMemoize | 212 | 4 | 8 | 5938 | 8816
nodeWindowAgg.c:3170 | GetAggInitVal | 117 | 5 | 8 | 5540 | 8736
numeric.c:6787 | set_var_from_str | 15126 | 11 | 96 | 6258 | 12304
numeric.c:9058 | div_var | 907 | 132 | 22528 | 6588 | 8512
parse_clause.c:739 | transformRangeTableFunc | 22 | 33 | 64 | 5036 | 6128
parse_func.c:1771 | func_get_detail | 995 | 291 | 1039 | 5809 | 9616
parse_type.c:375 | typenameTypeMod | 947 | 9 | 24 | 5993 | 17360
partbounds.c:2397 | fix_merged_indexes | 8 | 22 | 24 | 6896 | 7280
partbounds.c:2459 | generate_matching_part_pairs | 122 | 11 | 24 | 6970 | 7776
partbounds.c:2460 | generate_matching_part_pairs | 122 | 11 | 24 | 6986 | 7792
partbounds.c:366 | create_hash_bounds | 106 | 26 | 96 | 6323 | 7920
partbounds.c:487 | create_list_bounds | 930 | 51 | 400 | 6461 | 10208
partbounds.c:5130 | calculate_partition_bound_for_merge | 26 | 18 | 24 | 6480 | 6480
partbounds.c:5771 | check_partitions_for_split | 46 | 24 | 40 | 6544 | 6544
partbounds.c:5801 | check_partitions_for_split | 38 | 21 | 32 | 6544 | 6544
partbounds.c:5816 | check_partitions_for_split | 38 | 21 | 32 | 6544 | 6544
partbounds.c:705 | create_range_bounds | 1310 | 34 | 144 | 6621 | 10032
partbounds.c:745 | create_range_bounds | 1310 | 32 | 128 | 6621 | 10032
partprune.c:252 | make_partition_pruneinfo | 1025 | 23 | 128 | 6413 | 9728
partprune.c:475 | make_partitionedrel_pruneinfo | 816 | 23 | 128 | 6568 | 9728
partprune.c:523 | make_partitionedrel_pruneinfo | 10 | 8 | 8 | 5894 | 6224
pathkeys.c:1682 | select_outer_pathkeys_for_merge | 55425 | 9 | 32 | 6908 | 12384
pathkeys.c:1683 | select_outer_pathkeys_for_merge | 55425 | 4 | 16 | 6924 | 12400
pg_locale_icu.c:761 | strnxfrm_icu | 958 | 5 | 8 | 5266 | 6288
pg_locale_libc.c:1255 | char2wchar | 163964 | 3 | 58 | 8296 | 16080
pg_locale_libc.c:510 | strlower_libc_mb | 5530 | 31 | 232 | 7797 | 15984
pg_locale_libc.c:521 | strlower_libc_mb | 5530 | 28 | 229 | 7797 | 15984
pg_locale_libc.c:617 | strtitle_libc_mb | 1 | 40 | 40 | 5696 | 5696
pg_locale_libc.c:634 | strtitle_libc_mb | 1 | 37 | 37 | 5696 | 5696
pg_locale_libc.c:712 | strupper_libc_mb | 158433 | 11 | 52 | 8214 | 8560
pg_locale_libc.c:723 | strupper_libc_mb | 158433 | 8 | 49 | 8214 | 8560
pg_locale_libc.c:906 | strncoll_libc | 311574 | 16 | 3201 | 7437 | 23920
pg_locale_libc.c:909 | strncoll_libc | 311574 | 17 | 3201 | 7437 | 23920
plancat.c:1808 | get_relation_statistics | 138 | 956 | 1720 | 8532 | 8624
planner.c:3059 | extract_rollup_sets | 151 | 33 | 80 | 5655 | 7360
planner.c:3060 | extract_rollup_sets | 151 | 33 | 80 | 5671 | 7376
planner.c:3061 | extract_rollup_sets | 151 | 33 | 80 | 5671 | 7376
planner.c:3062 | extract_rollup_sets | 151 | 8 | 20 | 5671 | 7376
planner.c:3147 | extract_rollup_sets | 151 | 13 | 32 | 5655 | 7360
planner.c:6043 | select_active_windows | 426 | 18 | 80 | 5531 | 9472
planner.c:8134 | apply_scanjoin_target_to_paths | 1572 | 10 | 56 | 6283 | 9904
planner.c:8252 | create_partitionwise_grouping_paths | 136 | 13 | 24 | 6131 | 6880
prepagg.c:532 | GetAggInitVal | 2007 | 3 | 18 | 6301 | 14400
prepunion.c:1640 | generate_append_tlist | 714 | 11 | 24 | 5822 | 19488
rowtypes.c:1161 | record_eq | 560 | 16 | 40 | 5386 | 6080
rowtypes.c:1162 | record_eq | 560 | 2 | 5 | 5402 | 6096
rowtypes.c:1164 | record_eq | 560 | 16 | 40 | 5402 | 6096
rowtypes.c:1165 | record_eq | 560 | 2 | 5 | 5402 | 6096
rowtypes.c:1443 | record_image_cmp | 103 | 31 | 48 | 6511 | 6800
rowtypes.c:1444 | record_image_cmp | 103 | 4 | 6 | 6527 | 6816
rowtypes.c:1446 | record_image_cmp | 103 | 31 | 48 | 6527 | 6816
rowtypes.c:1447 | record_image_cmp | 103 | 4 | 6 | 6527 | 6816
rowtypes.c:146 | record_in | 66 | 19 | 40 | 5989 | 6944
rowtypes.c:147 | record_in | 66 | 2 | 5 | 6005 | 6960
rowtypes.c:1691 | record_image_eq | 33 | 26 | 48 | 6577 | 6976
rowtypes.c:1692 | record_image_eq | 33 | 3 | 6 | 6593 | 6992
rowtypes.c:1694 | record_image_eq | 33 | 26 | 48 | 6593 | 6992
rowtypes.c:1695 | record_image_eq | 33 | 3 | 6 | 6593 | 6992
rowtypes.c:1885 | hash_record | 150 | 16 | 24 | 5328 | 6800
rowtypes.c:1886 | hash_record | 150 | 2 | 3 | 5344 | 6816
rowtypes.c:2008 | hash_record_extended | 5 | 16 | 16 | 6467 | 6832
rowtypes.c:2009 | hash_record_extended | 5 | 2 | 2 | 6483 | 6848
rowtypes.c:391 | record_out | 2754 | 20 | 48 | 5681 | 10224
rowtypes.c:392 | record_out | 2754 | 2 | 6 | 5697 | 10240
rowtypes.c:915 | record_cmp | 655 | 25 | 160 | 6493 | 7888
rowtypes.c:916 | record_cmp | 655 | 3 | 20 | 6509 | 7904
rowtypes.c:918 | record_cmp | 655 | 25 | 160 | 6509 | 7904
rowtypes.c:919 | record_cmp | 655 | 3 | 20 | 6509 | 7904
ruleutils.c:1364 | pg_get_indexdef_worker | 60 | 385 | 576 | 5793 | 10896
ruleutils.c:1560 | pg_get_indexdef_worker | 42 | 434 | 637 | 5574 | 7632
ruleutils.c:1705 | pg_get_statisticsobj_worker | 14 | 756 | 1312 | 5479 | 7136
ruleutils.c:1880 | pg_get_statisticsobjdef_expressions | 24 | 667 | 770 | 5693 | 5888
ruleutils.c:1996 | pg_get_partkeydef_worker | 11 | 493 | 1032 | 5735 | 5984
ruleutils.c:2732 | pg_get_expr_worker | 650 | 478 | 3139 | 5424 | 7056
ruleutils.c:3347 | print_function_arguments | 2 | 219 | 298 | 5248 | 5248
ruleutils.c:3549 | pg_get_function_arg_default | 5 | 244 | 307 | 5120 | 5120
setrefs.c:1182 | set_plan_refs | 212 | 74 | 112 | 4770 | 5712
setrefs.c:1235 | set_plan_refs | 273 | 60 | 144 | 4996 | 7808
setrefs.c:1415 | set_indexonlyscan_references | 1829 | 35 | 80 | 5889 | 13424
setrefs.c:2055 | set_hash_references | 4075 | 51 | 272 | 5773 | 11056
setrefs.c:2433 | set_join_references | 14074 | 189 | 784 | 5294 | 10848
setrefs.c:2434 | set_join_references | 14074 | 178 | 560 | 5294 | 10848
setrefs.c:2584 | set_upper_references | 9720 | 42 | 560 | 5947 | 14656
setrefs.c:3521 | set_returning_clause_references | 442 | 63 | 272 | 4958 | 8272
setrefs.c:3603 | set_windowagg_runcondition_references | 457 | 70 | 352 | 5069 | 8224
tsvector_op.c:2555 | ts_process_call | 1144 | 3 | 4 | 5216 | 5216
xact.c:5611 | SerializeTransactionState | 159 | 9 | 104 | 6605 | 16912
xact.c:670 | AssignTransactionId | 33 | 29 | 208 | 6240 | 11136
(160 rows)
[text/x-patch] v2-0001-Provide-PG_STACK_DIRECTION-configuration-macro.patch (2.4K, 3-v2-0001-Provide-PG_STACK_DIRECTION-configuration-macro.patch)
download | inline diff:
From 7573e40d57e15abca8a29f9907d29f2d4a97c119 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Wed, 4 Mar 2026 18:12:54 +1300
Subject: [PATCH v2 01/19] Provide PG_STACK_DIRECTION configuration macro.
General purpose systems capable of running PostgreSQL with stacks that
grow upwards rather down downwards don't seem to be found in the wild
these days.
Define a pg_config_manual.h variable that can be set to -1 or 1, to
remove runtime branching from stack-measuring code. We could always add
feature detection if a new system shows up that wants the other setting,
but runtime doesn't make sense for a fixed properly of a system.
Though the existing user of that fact is very simple and doesn't save
much effort at all, a proposed patch would also like to know this at
compile time.
---
src/backend/utils/misc/stack_depth.c | 10 ++++++++--
src/include/pg_config_manual.h | 8 ++++++++
2 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/src/backend/utils/misc/stack_depth.c b/src/backend/utils/misc/stack_depth.c
index 61a07cf824e..3cd1b6f2336 100644
--- a/src/backend/utils/misc/stack_depth.c
+++ b/src/backend/utils/misc/stack_depth.c
@@ -118,9 +118,15 @@ stack_is_too_deep(void)
/*
* Take abs value, since stacks grow up on some machines, down on others
+ * (historical). This formulation amounts to a no-op on modern systems.
*/
- if (stack_depth < 0)
- stack_depth = -stack_depth;
+ stack_depth *= -(PG_STACK_DIRECTION);
+
+ /*
+ * If this assertion fails, either PG_STACK_DIRECTION is wrong or this
+ * system doesn't have a traditional stack.
+ */
+ Assert(stack_depth >= 0);
/*
* Trouble?
diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
index 521b49b8888..75035b9e796 100644
--- a/src/include/pg_config_manual.h
+++ b/src/include/pg_config_manual.h
@@ -222,6 +222,14 @@
*/
#define PG_IO_ALIGN_SIZE 4096
+/*
+ * Historical systems had stacks that grew upwards in address space, but none
+ * of them remain. Our stack depth-checking code no longer wastes cycles
+ * considering that possibility at runtime. It assumes (but also asserts)
+ * that the stack grows in the direction defined here: -1 or +1.
+ */
+#define PG_STACK_DIRECTION -1
+
/*
*------------------------------------------------------------------------
* The following symbols are for enabling debugging code, not for
--
2.53.0
[text/x-patch] v2-0002-Provide-pg_has_builtin-macro.patch (1.5K, 4-v2-0002-Provide-pg_has_builtin-macro.patch)
download | inline diff:
From 2de6675b66388341ba9d71bcaa47c47e87f667b9 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Wed, 11 Mar 2026 13:26:22 +1300
Subject: [PATCH v2 02/19] Provide pg_has_builtin() macro.
The traditional approach of configure/meson problems that define
HAVE__BUILTIN_FOO doesn't work for results that vary across the CC, CXX
and CLANG compilers. In Clang since early version, and in GCC 10+, you
can test with __has_builtin(__builtin_foo) instead. This wrapper can be
tested in preprocessor conditions using pg_has_builtin(__builtin_foo).
This approach has a bootstrapping problem, since __has_builtin() itself
was adopted only ~6 years ago by GCC, so it will always evaluate to
false for older GCC versions. Traditional configure probes therefore
remain useful for well established builtins where the three selected
compilers are unlikely to differ. This macro is useful for builtins
that were more recently invented, or adopted late by one or the other of
GCC and Clang.
---
src/include/c.h | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/src/include/c.h b/src/include/c.h
index 5b678283469..5c74da5004e 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -116,6 +116,16 @@ extern "C++"
#define inline
#endif
+/*
+ * Clang and GCC 10+ can test for a builtin's presence. This can be useful
+ * for builtins that might be present for CC but not CXX or CLANG.
+ */
+#ifdef __has_builtin
+#define pg_has_builtin(x) __has_builtin(x)
+#else
+#define pg_has_builtin(x) 0
+#endif
+
/*
* Attribute macros
*
--
2.53.0
[text/x-patch] v2-0003-Refactor-check_stack_depth-mechanism.patch (7.1K, 5-v2-0003-Refactor-check_stack_depth-mechanism.patch)
download | inline diff:
From 3a1db8f04f661ae2cb93b2fecf6b02277fe01fd3 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Fri, 6 Mar 2026 20:53:23 +1300
Subject: [PATCH v2 03/19] Refactor check_stack_depth() mechanism.
* The check is now inline.
* The stack is now assumed to grow in PG_STACK_DIRECTION.
* A new "soft" limit is exposed, for a later patch to use.
---
src/backend/utils/misc/stack_depth.c | 113 +++++++++------------------
src/include/miscadmin.h | 53 ++++++++++++-
2 files changed, 90 insertions(+), 76 deletions(-)
diff --git a/src/backend/utils/misc/stack_depth.c b/src/backend/utils/misc/stack_depth.c
index 3cd1b6f2336..aea589e1a27 100644
--- a/src/backend/utils/misc/stack_depth.c
+++ b/src/backend/utils/misc/stack_depth.c
@@ -25,15 +25,28 @@
/* GUC variable for maximum stack depth (measured in kilobytes) */
int max_stack_depth = 100;
-/* max_stack_depth converted to bytes for speed of checking */
-static ssize_t max_stack_depth_bytes = 100 * (ssize_t) 1024;
-
/*
- * Stack base pointer -- initialized by set_stack_base(), which
- * should be called from main().
+ * Thresholds -- initialized by set_stack_base(). These have external linkage
+ * so they can be used by inlined code.
*/
-static char *stack_base_ptr = NULL;
+const void *stack_base_ptr = NULL;
+const void *stack_soft_limit_ptr = NULL;
+const void *stack_hard_limit_ptr = NULL;
+
+
+static void
+compute_limit_ptrs(int kb)
+{
+ ssize_t bytes = kb * (ssize_t) 1024;
+
+ /* Advertise a soft limit halfway through the allowed size. */
+ stack_soft_limit_ptr = (const char *) stack_base_ptr +
+ (bytes / 2) * PG_STACK_DIRECTION;
+ /* This is the size at which check_stack_depth() will fail. */
+ stack_hard_limit_ptr = (const char *) stack_base_ptr +
+ bytes * PG_STACK_DIRECTION;
+}
/*
* set_stack_base: set up reference point for stack depth checking
@@ -48,7 +61,7 @@ set_stack_base(void)
#endif
pg_stack_base_t old;
- old = stack_base_ptr;
+ old = (pg_stack_base_t) stack_base_ptr;
/*
* Set up reference point for stack depth checking. On recent gcc we use
@@ -61,6 +74,8 @@ set_stack_base(void)
stack_base_ptr = &stack_base;
#endif
+ compute_limit_ptrs(max_stack_depth);
+
return old;
}
@@ -76,73 +91,11 @@ set_stack_base(void)
void
restore_stack_base(pg_stack_base_t base)
{
- stack_base_ptr = base;
-}
-
-
-/*
- * check_stack_depth/stack_is_too_deep: check for excessively deep recursion
- *
- * This should be called someplace in any recursive routine that might possibly
- * recurse deep enough to overflow the stack. Most Unixen treat stack
- * overflow as an unrecoverable SIGSEGV, so we want to error out ourselves
- * before hitting the hardware limit.
- *
- * check_stack_depth() just throws an error summarily. stack_is_too_deep()
- * can be used by code that wants to handle the error condition itself.
- */
-void
-check_stack_depth(void)
-{
- if (stack_is_too_deep())
- {
- ereport(ERROR,
- (errcode(ERRCODE_STATEMENT_TOO_COMPLEX),
- errmsg("stack depth limit exceeded"),
- errhint("Increase the configuration parameter \"max_stack_depth\" (currently %dkB), "
- "after ensuring the platform's stack depth limit is adequate.",
- max_stack_depth)));
- }
-}
-
-bool
-stack_is_too_deep(void)
-{
- char stack_top_loc;
- ssize_t stack_depth;
-
- /*
- * Compute distance from reference point to my local variables
- */
- stack_depth = (ssize_t) (stack_base_ptr - &stack_top_loc);
+ stack_base_ptr = (const void *) base;
- /*
- * Take abs value, since stacks grow up on some machines, down on others
- * (historical). This formulation amounts to a no-op on modern systems.
- */
- stack_depth *= -(PG_STACK_DIRECTION);
-
- /*
- * If this assertion fails, either PG_STACK_DIRECTION is wrong or this
- * system doesn't have a traditional stack.
- */
- Assert(stack_depth >= 0);
-
- /*
- * Trouble?
- *
- * The test on stack_base_ptr prevents us from erroring out if called
- * before that's been set. Logically it should be done first, but putting
- * it last avoids wasting cycles during normal cases.
- */
- if (stack_depth > max_stack_depth_bytes &&
- stack_base_ptr != NULL)
- return true;
-
- return false;
+ compute_limit_ptrs(max_stack_depth);
}
-
/* GUC check hook for max_stack_depth */
bool
check_max_stack_depth(int *newval, void **extra, GucSource source)
@@ -160,13 +113,25 @@ check_max_stack_depth(int *newval, void **extra, GucSource source)
return true;
}
+/*
+ * Out-of-line part of check_stack_depth().
+ */
+void
+report_stack_is_too_deep(void)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_STATEMENT_TOO_COMPLEX),
+ errmsg("stack depth limit exceeded"),
+ errhint("Increase the configuration parameter \"max_stack_depth\" (currently %dkB), "
+ "after ensuring the platform's stack depth limit is adequate.",
+ max_stack_depth)));
+}
+
/* GUC assign hook for max_stack_depth */
void
assign_max_stack_depth(int newval, void *extra)
{
- ssize_t newval_bytes = newval * (ssize_t) 1024;
-
- max_stack_depth_bytes = newval_bytes;
+ compute_limit_ptrs(newval);
}
/*
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index f16f35659b9..c618804e71f 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -293,15 +293,64 @@ extern PGDLLIMPORT bool VacuumCostActive;
extern PGDLLIMPORT int max_stack_depth;
+extern PGDLLIMPORT const void *stack_base_ptr;
+extern PGDLLIMPORT const void *stack_soft_limit_ptr;
+extern PGDLLIMPORT const void *stack_hard_limit_ptr;
+
/* Required daylight between max_stack_depth and the kernel limit, in bytes */
#define STACK_DEPTH_SLOP (512 * 1024)
+/* Check if stack pointer p1 is deeper than p2. */
+static inline bool
+stack_ptr_deeper_p(const void *p1, const void *p2)
+{
+ const char *c1 = (const char *) p1;
+ const char *c2 = (const char *) p2;
+
+ if (PG_STACK_DIRECTION < 0)
+ return c1 < c2;
+ else
+ return c1 > c2;
+}
+
+/* Check if the stack has exceeded the configured hard limit. */
+static inline bool
+stack_is_too_deep(void)
+{
+#if pg_has_builtin(__builtin_stack_address)
+ const char *sp = (const char *) __builtin_stack_address();
+#else
+ char c;
+ const char *sp = &c;
+#endif
+
+ return stack_ptr_deeper_p(sp, stack_hard_limit_ptr);
+}
+
+extern void report_stack_is_too_deep(void);
+
+/*
+ * check_stack_depth/stack_is_too_deep: check for excessively deep recursion
+ *
+ * This should be called someplace in any recursive routine that might possibly
+ * recurse deep enough to overflow the stack. Most Unixen treat stack
+ * overflow as an unrecoverable SIGSEGV, so we want to error out ourselves
+ * before hitting the hardware limit.
+ *
+ * check_stack_depth() just throws an error summarily. stack_is_too_deep()
+ * can be used by code that wants to handle the error condition itself.
+ */
+static inline void
+check_stack_depth(void)
+{
+ if (unlikely(stack_is_too_deep()))
+ report_stack_is_too_deep();
+}
+
typedef char *pg_stack_base_t;
extern pg_stack_base_t set_stack_base(void);
extern void restore_stack_base(pg_stack_base_t base);
-extern void check_stack_depth(void);
-extern bool stack_is_too_deep(void);
extern ssize_t get_stack_depth_rlimit(void);
/* in tcop/utility.c */
--
2.53.0
[text/x-patch] v2-0004-Provide-way-for-macros-to-detect-PG_TRY-scope.patch (10.7K, 6-v2-0004-Provide-way-for-macros-to-detect-PG_TRY-scope.patch)
download | inline diff:
From e09e2b0f12df3eaa55f37db55d0d00d12a92bf8a Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Wed, 4 Mar 2026 17:30:46 +1300
Subject: [PATCH v2 04/19] Provide way for macros to detect PG_TRY scope.
For code that can't work safely around setjmp()/longjmp() and thus in a
PG_TRY/CATCH/FINALLY block, provide a macro
"pg_in_lexical_scope(PG_TRY)" that can be tested at compile time.
---
configure | 101 +++++++++++++++++++++++++++++++++++++
configure.ac | 13 +++++
meson.build | 9 ++++
src/include/c.h | 21 ++++++++
src/include/pg_config.h.in | 6 +++
src/include/utils/elog.h | 36 +++++++++++++
6 files changed, 186 insertions(+)
diff --git a/configure b/configure
index 42621ecd051..5b3a9975e9e 100755
--- a/configure
+++ b/configure
@@ -5992,6 +5992,107 @@ if test x"$pgac_cv_prog_CXX_cxxflags__Wshadow_compatible_local" = x"yes"; then
fi
+ # We also want to make the usage of the above options available to macros.
+ NOT_THE_CFLAGS=""
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${CC} supports -Wshadow=compatible-local, for NOT_THE_CFLAGS" >&5
+$as_echo_n "checking whether ${CC} supports -Wshadow=compatible-local, for NOT_THE_CFLAGS... " >&6; }
+if ${pgac_cv_prog_CC_cflags__Wshadow_compatible_local+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ pgac_save_CFLAGS=$CFLAGS
+pgac_save_CC=$CC
+CC=${CC}
+CFLAGS="${NOT_THE_CFLAGS} -Wshadow=compatible-local"
+ac_save_c_werror_flag=$ac_c_werror_flag
+ac_c_werror_flag=yes
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ pgac_cv_prog_CC_cflags__Wshadow_compatible_local=yes
+else
+ pgac_cv_prog_CC_cflags__Wshadow_compatible_local=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_c_werror_flag=$ac_save_c_werror_flag
+CFLAGS="$pgac_save_CFLAGS"
+CC="$pgac_save_CC"
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_prog_CC_cflags__Wshadow_compatible_local" >&5
+$as_echo "$pgac_cv_prog_CC_cflags__Wshadow_compatible_local" >&6; }
+if test x"$pgac_cv_prog_CC_cflags__Wshadow_compatible_local" = x"yes"; then
+ NOT_THE_CFLAGS="${NOT_THE_CFLAGS} -Wshadow=compatible-local"
+fi
+
+ if test -n "$NOT_THE_CFLAGS"; then
+
+$as_echo "#define WARNING_CC_SHADOW_COMPATIBLE_LOCAL 1" >>confdefs.h
+
+ fi
+ NOT_THE_CFLAGS=""
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${CXX} supports -Wshadow=compatible-local, for NOT_THE_CFLAGS" >&5
+$as_echo_n "checking whether ${CXX} supports -Wshadow=compatible-local, for NOT_THE_CFLAGS... " >&6; }
+if ${pgac_cv_prog_CXX_cxxflags__Wshadow_compatible_local+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ pgac_save_CXXFLAGS=$CXXFLAGS
+pgac_save_CXX=$CXX
+CXX=${CXX}
+CXXFLAGS="${NOT_THE_CFLAGS} -Wshadow=compatible-local"
+ac_save_cxx_werror_flag=$ac_cxx_werror_flag
+ac_cxx_werror_flag=yes
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+ pgac_cv_prog_CXX_cxxflags__Wshadow_compatible_local=yes
+else
+ pgac_cv_prog_CXX_cxxflags__Wshadow_compatible_local=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+CXXFLAGS="$pgac_save_CXXFLAGS"
+CXX="$pgac_save_CXX"
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_prog_CXX_cxxflags__Wshadow_compatible_local" >&5
+$as_echo "$pgac_cv_prog_CXX_cxxflags__Wshadow_compatible_local" >&6; }
+if test x"$pgac_cv_prog_CXX_cxxflags__Wshadow_compatible_local" = x"yes"; then
+ NOT_THE_CFLAGS="${NOT_THE_CFLAGS} -Wshadow=compatible-local"
+fi
+
+ if test -n "$NOT_THE_CFLAGS"; then
+
+$as_echo "#define WARNING_CXX_SHADOW_COMPATIBLE_LOCAL 1" >>confdefs.h
+
+ fi
# This was included in -Wall/-Wformat in older GCC versions
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${CC} supports -Wformat-security, for CFLAGS" >&5
diff --git a/configure.ac b/configure.ac
index 61ec895d23c..de02fdcf058 100644
--- a/configure.ac
+++ b/configure.ac
@@ -577,6 +577,19 @@ if test "$GCC" = yes -a "$ICC" = no; then
PGAC_PROG_CXX_CFLAGS_OPT([-Wcast-function-type])
PGAC_PROG_CC_CFLAGS_OPT([-Wshadow=compatible-local])
PGAC_PROG_CXX_CFLAGS_OPT([-Wshadow=compatible-local])
+ # We also want to make the usage of the above options available to macros.
+ NOT_THE_CFLAGS=""
+ PGAC_PROG_VARCC_VARFLAGS_OPT(CC, NOT_THE_CFLAGS, [-Wshadow=compatible-local])
+ if test -n "$NOT_THE_CFLAGS"; then
+ AC_DEFINE([WARNING_CC_SHADOW_COMPATIBLE_LOCAL], 1,
+ [Define to 1 if your C compiler understands -Wshadow=compatible-local])
+ fi
+ NOT_THE_CFLAGS=""
+ PGAC_PROG_VARCXX_VARFLAGS_OPT(CXX, NOT_THE_CFLAGS, [-Wshadow=compatible-local])
+ if test -n "$NOT_THE_CFLAGS"; then
+ AC_DEFINE([WARNING_CXX_SHADOW_COMPATIBLE_LOCAL], 1,
+ [Define to 1 if your C++ compiler understands -Wshadow=compatible-local])
+ fi
# This was included in -Wall/-Wformat in older GCC versions
PGAC_PROG_CC_CFLAGS_OPT([-Wformat-security])
PGAC_PROG_CXX_CFLAGS_OPT([-Wformat-security])
diff --git a/meson.build b/meson.build
index 2df54409ca6..bbcc4197ccb 100644
--- a/meson.build
+++ b/meson.build
@@ -2206,6 +2206,15 @@ if have_cxx
cxxflags_warn += cxx.get_supported_arguments(common_warning_flags)
endif
+# Advertise whether -Wshadow=compatible-local is active, so that it can be
+# disabled in cases where that is expected.
+if cc.has_argument('-Wshadow=compatible-local')
+ cdata.set('WARNING_CC_SHADOW_COMPATIBLE_LOCAL', 1)
+endif
+if have_cxx and cxx.has_argument('-Wshadow=compatible-local')
+ cdata.set('WARNING_CXX_SHADOW_COMPATIBLE_LOCAL', 1)
+endif
+
# To require fallthrough attribute annotations, use
# -Wimplicit-fallthrough=5 with gcc and -Wimplicit-fallthrough with
# clang. The latter is also accepted on gcc but does not enforce
diff --git a/src/include/c.h b/src/include/c.h
index 5c74da5004e..5a524365ccc 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -559,6 +559,27 @@ typedef void (*pg_funcptr_t) (void);
#define HAVE_PRAGMA_GCC_SYSTEM_HEADER 1
#endif
+/*
+ * Sometimes it is useful to be able to disable GCC's shadow warnings for a
+ * specific declaration.
+ *
+ * -Wdeclaration-after-statement is also temporarily suppressed, because the
+ * pragma itself is treated as a statement while the purpose of these macros
+ * is to wrap a declaration.
+ */
+#if !defined(__clang__) && \
+ ((defined(__cplusplus) && defined(WARNING_CXX_SHADOW_COMPATIBLE_LOCAL)) || \
+ (!defined(__cplusplus) && defined(WARNING_CC_SHADOW_COMPATIBLE_LOCAL)))
+#define pg_begin_ignore_shadow_warning() \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wshadow=compatible-local\""); \
+ _Pragma("GCC diagnostic ignored \"-Wdeclaration-after-statement\"");
+#define pg_end_ignore_shadow_warning() \
+ _Pragma("GCC diagnostic pop")
+#else
+#define pg_begin_ignore_shadow_warning()
+#define pg_end_ignore_shadow_warning()
+#endif
/* ----------------------------------------------------------------
* Section 2: bool, true, false
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index cb0f53fade4..c67534cd97b 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -751,6 +751,12 @@
/* Define to 1 to build with ZSTD support. (--with-zstd) */
#undef USE_ZSTD
+/* Define to 1 if your C compiler understands -Wshadow=compatible-local */
+#undef WARNING_CC_SHADOW_COMPATIBLE_LOCAL
+
+/* Define to 1 if your C++ compiler understands -Wshadow=compatible-local */
+#undef WARNING_CXX_SHADOW_COMPATIBLE_LOCAL
+
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
significant byte first (like Motorola and SPARC, unlike Intel). */
#if defined AC_APPLE_UNIVERSAL_BUILD
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index a12b379e09a..30afcfbf453 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -316,6 +316,39 @@ typedef struct ErrorContextCallback
extern PGDLLIMPORT ErrorContextCallback *error_context_stack;
+/* Support for testing if a macro is inside a PG_TRY/... block. */
+typedef char pg_lexical_scope_false_type;
+typedef int pg_lexical_scope_true_type;
+
+/* Declare a tag as a variable in global scope. It has no storage. */
+#define pg_declare_lexical_scope_tag(name) \
+ extern pg_lexical_scope_false_type \
+ lexical_scope_tag_##name pg_attribute_unused()
+
+/*
+ * Declare a tag as a local typedef that hides the name of the variable,
+ * within the current lexical scope.
+ *
+ * On its own, "shadowing" a variable name with a type name doesn't trigger
+ * GCC's -Wshadow=compatible-local warning, but we want to be able to handle
+ * nested PG_TRY blocks using the same tag name. Disable the warning locally,
+ * if it's enabled.
+ */
+#define pg_set_lexical_scope_tag(name) \
+ pg_begin_ignore_shadow_warning() \
+ typedef pg_lexical_scope_true_type \
+ lexical_scope_tag_##name pg_attribute_unused(); \
+ pg_end_ignore_shadow_warning()
+
+/* Test whether we are inside a lexical scope that has "set" the tag. */
+#define pg_in_lexical_scope_p(name) \
+ (sizeof(lexical_scope_tag_##name) == sizeof(pg_lexical_scope_true_type))
+
+pg_declare_lexical_scope_tag(PG_TRY);
+pg_declare_lexical_scope_tag(PG_CATCH);
+pg_declare_lexical_scope_tag(PG_FINALLY);
+
+
/*----------
* API for catching ereport(ERROR) exits. Use these macros like so:
*
@@ -391,12 +424,14 @@ extern PGDLLIMPORT ErrorContextCallback *error_context_stack;
bool _do_rethrow##__VA_ARGS__ = false; \
if (sigsetjmp(_local_sigjmp_buf##__VA_ARGS__, 0) == 0) \
{ \
+ pg_set_lexical_scope_tag(PG_TRY); \
PG_exception_stack = &_local_sigjmp_buf##__VA_ARGS__
#define PG_CATCH(...) \
} \
else \
{ \
+ pg_set_lexical_scope_tag(PG_CATCH); \
PG_exception_stack = _save_exception_stack##__VA_ARGS__; \
error_context_stack = _save_context_stack##__VA_ARGS__
@@ -405,6 +440,7 @@ extern PGDLLIMPORT ErrorContextCallback *error_context_stack;
else \
_do_rethrow##__VA_ARGS__ = true; \
{ \
+ pg_set_lexical_scope_tag(PG_FINALLY); \
PG_exception_stack = _save_exception_stack##__VA_ARGS__; \
error_context_stack = _save_context_stack##__VA_ARGS__
--
2.53.0
[text/x-patch] v2-0005-Provide-stack-buffer-API.patch (18.7K, 7-v2-0005-Provide-stack-buffer-API.patch)
download | inline diff:
From f127592cd1b095d03a0e38f539948ee68283d5b1 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Fri, 27 Feb 2026 22:21:36 +1300
Subject: [PATCH v2 05/19] Provide stack buffer API.
Several places use a fixed-size array on the stack for non-escaping
memory of dynamic size, with fallback to palloc()/pfree(). Create a
standard API for that, and implement it with alloca() instead if
possible, while applying our traditional cap on stack growth.
Three implementations are provided:
* STACK_BUFFER_USE_ALLOCA: use alloca() or builtins
* STACK_BUFFER_USE_ARRAY: the traditioanl approach
* STACK_BUFFER_USE_PALLOC: development/testing
* STACK_BUFFER_USE_PALLOC_LOG: development/testing
Later patches will adopt the API in various parts of the tree to speed
up common operations.
Reviewed-by:
Discussion:
---
src/include/c.h | 10 +
src/include/utils/stack_buffer.h | 461 +++++++++++++++++++++++++++++++
2 files changed, 471 insertions(+)
create mode 100644 src/include/utils/stack_buffer.h
diff --git a/src/include/c.h b/src/include/c.h
index 5a524365ccc..c1074371c40 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -923,6 +923,16 @@ typedef NameData *Name;
/* we don't currently need wider versions of the other ALIGN macros */
#define MAXALIGN64(LEN) TYPEALIGN64(MAXIMUM_ALIGNOF, (LEN))
+/*
+ * MAXIMUM_ALIGNOF is respected by palloc() and similar. The C standard
+ * defines max_align_t as a type to be used for similar purposes, but Visual
+ * Studio forgot to define it in <stddef.h>. Supply the definition Clang uses
+ * on that platform, for use in contexts that refer to the standard library or
+ * compiler's behavior rather than PostgreSQL's.
+ */
+#ifdef _MSC_VER
+typedef double max_align_t;
+#endif
/* ----------------------------------------------------------------
* Section 6: assertions
diff --git a/src/include/utils/stack_buffer.h b/src/include/utils/stack_buffer.h
new file mode 100644
index 00000000000..e4057a452e6
--- /dev/null
+++ b/src/include/utils/stack_buffer.h
@@ -0,0 +1,461 @@
+/*-------------------------------------------------------------------------
+ *
+ * stack_buffer.h
+ * Allocator for objects that don't escape the current lexical scope.
+ *
+ * A palloc()-like interface to alloca(), for allocating memory efficiently on
+ * the stack. Raw alloca() is usually considered dangerous because of its
+ * inherent stack overflow risk, but this interface imposes limits on stack
+ * size and falls back to regular palloc() when they would be exceeded.
+ *
+ * Memory should still be freed explicitly with stack_buffer_free(). It is a
+ * no-op in the common case that pfree() doesn't need to be called.
+ *
+ * XXX It might be possible to use something like "defer" or equivalent
+ * compiler extensions to clean up palloc()'d memory automatically, in future
+ * work, and then stack_buffer_free() would not be necessary.
+ *
+ * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/stack_buffer.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef STACK_BUFFER_H
+#define STACK_BUFFER_H
+
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "miscadmin.h"
+
+#include <limits.h>
+#include <unistd.h>
+
+
+/* #define STACK_BUFFER_USE_PALLOC_LOG "/tmp/stack_buffer.csv" */
+
+/* Choose which implementation to use, if not defined manually. */
+#if !defined(STACK_BUFFER_USE_ARRAY) && \
+ !defined(STACK_BUFFER_USE_ALLOC) && \
+ !defined(STACK_BUFFER_USE_PALLOC) && \
+ !defined(STACK_BUFFER_USE_PALLOC_LOG)
+#if pg_has_builtin(__builtin_alloca_with_align)
+/* Use GCC/Clang builtin with explicit alignment argument. */
+#define STACK_BUFFER_USE_ALLOCA
+#elif defined(_MSC_VER)
+/*
+ * MSVC's alloca() maps to a builtin that works in function arguments. We can
+ * adjust for stricter-than-default alignment ourselves.
+ */
+#include <malloc.h> */
+#define STACK_BUFFER_USE_ALLOCA
+#else
+/* Traditional approach. */
+#define STACK_BUFFER_USE_ARRAY
+#endif
+#endif
+
+/*
+ * A default size of 128 bytes should be enough for many arrays of datums and
+ * null flags, without posing a danger to recursive code.
+ *
+ * If using alloca(), we don't need to be so cautious: unused capacity doesn't
+ * consume stack space, and a secondary limit is computed based on the
+ * remaining stack space.
+ */
+#define STACK_BUFFER_DEFAULT \
+ (stack_buffer_total_size_limited ? STACK_BUFFER_LARGE : 128)
+
+/*
+ * This tiny size is intended for cases that risk deep recursion, but can
+ * still often benefit from avoiding a palloc() call.
+ */
+#define STACK_BUFFER_TINY \
+ (stack_buffer_total_size_limited ? STACK_BUFFER_LARGE : 16)
+
+/*
+ * This larger size is intended only for non-recursive uses including
+ * conversions to C string format before calling standard library routines.
+ * Avoid allowing more than one buffer of this size to be active on the stack
+ * at a time.
+ */
+#define STACK_BUFFER_LARGE 1024
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Public stack buffer API.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/* Declare a stack buffer of default size. */
+#define DECLARE_STACK_BUFFER() \
+ DECLARE_STACK_BUFFER_SIZE(STACK_BUFFER_DEFAULT)
+
+/* As above, using the standard "tiny" size (see notes above). */
+#define DECLARE_STACK_BUFFER_TINY() \
+ DECLARE_STACK_BUFFER_SIZE(STACK_BUFFER_TINY)
+
+/* As above, using the standard "large" size (see notes above). */
+#define DECLARE_STACK_BUFFER_LARGE() \
+ DECLARE_STACK_BUFFER_SIZE(STACK_BUFFER_LARGE)
+
+/* As above, but with a caller-specified limit on stack usage. */
+#define DECLARE_STACK_BUFFER_SIZE(size) \
+ size_t stack_buffer_let_size; /* temp, avoids double eval */ \
+ bool stack_buffer_maybe_pfree = false; \
+ DECLARE_STACK_BUFFER_IMPL(size)
+
+/* Allocate memory, optionally with explicit alignment. */
+#define stack_buffer_alloc(size) \
+ stack_buffer_alloc_aligned((size), MAXIMUM_ALIGNOF)
+#define stack_buffer_alloc_aligned(size, align) \
+ (stack_buffer_sanity_checks(), \
+ stack_buffer_let_size = (size), \
+ stack_buffer_let_size = Max(stack_buffer_let_size, 1), \
+ stack_buffer_alloc_aligned_impl(stack_buffer_let_size, (align)))
+
+/* As above, but also zero the memory. */
+#define stack_buffer_alloc0(size) \
+ stack_buffer_alloc0_aligned((size), MAXIMUM_ALIGNOF)
+#define stack_buffer_alloc0_aligned(size, align) \
+ (stack_buffer_sanity_checks(), \
+ stack_buffer_let_size = (size), \
+ stack_buffer_let_size = Max(stack_buffer_let_size, 1), \
+ memset(stack_buffer_alloc_aligned_impl(stack_buffer_let_size, \
+ (align)), \
+ 0, \
+ stack_buffer_let_size))
+
+/* As above, but for a given type T. */
+#define stack_buffer_alloc_object(T) \
+ stack_buffer_alloc_array(T, 1)
+#define stack_buffer_alloc_array(T, n) \
+ ((T *) stack_buffer_alloc_aligned((n) * sizeof(T), alignof(T)))
+#define stack_buffer_alloc0_object(T) \
+ stack_buffer_alloc0_array(T, 1)
+#define stack_buffer_alloc0_array(T, n) \
+ ((T *) stack_buffer_alloc0_aligned((n) * sizeof(T), alignof(T)))
+
+/* Copy a string. */
+#define stack_buffer_strdup(cstr) \
+ stack_buffer_strdup_with_len((cstr), strlen(cstr))
+#define stack_buffer_strndup(cstr, n) \
+ stack_buffer_strdup_with_len((cstr), strnlen((cstr), (n)))
+#define stack_buffer_strdup_with_len(data, size) \
+ (stack_buffer_sanity_checks(), \
+ stack_buffer_let_size = (size), \
+ stack_buffer_strdup_with_len_impl(stack_buffer_alloc_aligned_impl(stack_buffer_let_size + 1, \
+ alignof(char)), \
+ (data), \
+ stack_buffer_let_size))
+#define stack_buffer_text_to_cstring(text) \
+ stack_buffer_strdup_with_len(VARDATA_ANY(text), VARSIZE_ANY_EXHDR(text))
+#define stack_buffer_text_datum_to_cstring(datum) \
+ stack_buffer_text_to_cstring((text *) DatumGetPointer(datum))
+
+/*
+ * Free memory allocated with the above interfaces. We don't expect to
+ * receive pointers allocated by palloc() directly and not via this API. That
+ * would break the stack_buffer_maybe_pfree optimization, and might limit
+ * future implementation techniques.
+ */
+#define stack_buffer_free(ptr) \
+ do \
+ { \
+ Assert(stack_buffer_stack_p(ptr) || stack_buffer_maybe_pfree); \
+ if (unlikely(stack_buffer_maybe_pfree) && \
+ !stack_buffer_stack_p(ptr)) \
+ pfree(ptr); \
+ } \
+ while (0)
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Private helper code common to all implementations.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/* Cannot work across setjmp()/longjmp() due to stack cloberring. */
+#define stack_buffer_sanity_checks() \
+ (StaticAssertExpr(!pg_in_lexical_scope_p(PG_TRY), \
+ "stack buffer API not allowed in PG_TRY"), \
+ StaticAssertExpr(!pg_in_lexical_scope_p(PG_CATCH), \
+ "stack buffer API not allowed in PG_CATCH"), \
+ StaticAssertExpr(!pg_in_lexical_scope_p(PG_FINALLY), \
+ "stack buffer API not allowed in PG_FINALLY"))
+
+/* Post-allocation part of stack_buffer_strdup_with_len(). */
+static inline char *
+stack_buffer_strdup_with_len_impl(char *dst, const char *data, size_t size)
+{
+ memcpy(dst, data, size);
+ dst[size] = 0;
+ return dst;
+}
+
+/*
+ * Allocate with palloc() or palloc_aligned(). We waste a register
+ * remembering if we've ever had to do this, to generate better straight-line
+ * code for the case where we don't have to free anything.
+ */
+#define stack_buffer_palloc_aligned(size, align) \
+ ((stack_buffer_maybe_pfree = true), \
+ ((align) > MAXIMUM_ALIGNOF ? \
+ palloc_aligned((size), (align), 0) : \
+ palloc(size))) /* can't ask for smaller alignment */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Low-level implementations below this point supply the following macros,
+ * which should not be used directly:
+ *
+ * 1. stack_buffer_total_size_limited (true/false)
+ * 2. DECLARE_STACK_BUFFER_IMPL(size)
+ * 3. stack_buffer_alloc_aligned(size, align)
+ * 4. stack_buffer_stack_p(ptr)
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*-------------------------------------------------------------------------
+ *
+ * Toy implementations for debugging.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/* Just use palloc. */
+#ifdef STACK_BUFFER_USE_PALLOC
+#define stack_buffer_total_size_limited false
+#define DECLARE_STACK_BUFFER_IMPL(size)
+#define stack_buffer_alloc_aligned_impl(size, align) \
+ stack_buffer_palloc_aligned((size), (align))
+#define stack_buffer_stack_p(ptr) false
+#endif
+
+/*
+ * Same, but log "location,function,size,depth" entries to a path specified
+ * by the STACK_BUFFER_USE_PALLOC_LOG macro.
+ */
+#ifdef STACK_BUFFER_USE_PALLOC_LOG
+#define stack_buffer_total_size_limited false
+#define DECLARE_STACK_BUFFER_IMPL(size) \
+ FILE *stack_buffer_log \
+ __attribute__((cleanup(stack_buffer_close_log))) = \
+ fopen(STACK_BUFFER_USE_PALLOC_LOG, "a+")
+#define stack_buffer_alloc_aligned_impl(size, align) \
+ (fprintf(stack_buffer_log, \
+ "%s:%d,%s,%zu,%zu\n", \
+ __FILE__, \
+ __LINE__, \
+ __func__, \
+ (size_t) (size), \
+ ((const char *) stack_base_ptr - \
+ (const char *) __builtin_stack_address())), \
+ stack_buffer_palloc_aligned((size), (align)))
+#define stack_buffer_stack_p(ptr) false
+static inline void
+stack_buffer_close_log(FILE **f)
+{
+ fclose(*f);
+}
+#endif
+
+/*-------------------------------------------------------------------------
+ *
+ * Simple array-based implementation.
+ *
+ * This is entirely standard C requiring no compiler extensions, but it leaves
+ * a big hole in the stack when you call another function and has no ability
+ * to respect the total stack size limit so we have to be much more cautious
+ * about sizing when we use it.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifdef STACK_BUFFER_USE_ARRAY
+
+#define stack_buffer_total_size_limited false
+
+#define DECLARE_STACK_BUFFER_IMPL(size) \
+ char stack_buffer_array[(size)]; \
+ char *stack_buffer_sp = stack_buffer_array + (size)
+
+#define stack_buffer_alloc_aligned_impl(size, align) \
+ stack_buffer_alloc_aligned_from_array(&stack_buffer_array[0], \
+ &stack_buffer_sp, \
+ (size), \
+ (align))
+
+#define stack_buffer_stack_p(ptr) \
+ ((char *) (ptr) >= &stack_buffer_array[0] && \
+ (char *) (ptr) < &stack_buffer_array[sizeof(stack_buffer_array)])
+
+static inline void *
+stack_buffer_alloc_aligned_from_array(const char *array,
+ char **sp,
+ size_t size,
+ size_t align)
+{
+ if (likely(size >= (uintptr_t) *sp)) /* avoids overflow with huge size */
+ {
+ char *result = *sp - size;
+
+ if (align > 1)
+ result = (char *) TYPEALIGN_DOWN(align, result);
+
+ if (likely(result >= array))
+ {
+ *sp = result;
+ return result;
+ }
+ }
+
+ return stack_buffer_palloc_aligned(size, align);
+}
+
+#endif
+
+/*-------------------------------------------------------------------------
+ *
+ * alloca()-based implementation.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifdef STACK_BUFFER_USE_ALLOCA
+
+#define stack_buffer_total_size_limited true
+
+#define DECLARE_STACK_BUFFER_IMPL(size) \
+ stack_buffer_declare_impl \
+ const char *stack_buffer_limit = \
+ stack_buffer_least_deep(stack_soft_limit_ptr, \
+ stack_buffer_sp + \
+ (size) * PG_STACK_DIRECTION)
+
+#define stack_buffer_alloc_aligned_impl(size, align) \
+ (likely(stack_buffer_alloca_would_fit_p((size), (align))) ? \
+ stack_buffer_alloca_aligned((size), (align)) : \
+ stack_buffer_palloc_aligned((size), (align)))
+
+#define stack_buffer_stack_p(ptr) \
+ (!stack_ptr_deeper_p((ptr), stack_buffer_sp) && \
+ !stack_ptr_deeper_p(stack_buffer_base, (ptr)))
+
+
+/* Portability details. */
+
+/*
+ * Builtins for interacting with the stack registers, if available. For all
+ * but __builtin_frame_address, which is older and unlikely to differ across
+ * the selected C, C++ and bitcode compilers, use pg_has_builtin().
+ */
+#if HAVE__BUILTIN_FRAME_ADDRESS
+#define stack_buffer_base ((const char *) __builtin_frame_address(0))
+#endif
+#if pg_has_builtin(__builtin_stack_address)
+#define stack_buffer_sp ((const char *) __builtin_stack_address())
+#endif
+#if pg_has_builtin(__builtin_alloca_with_align)
+#define stack_buffer_alloca_aligned_impl(size, align) \
+ __builtin_alloca_with_align((size), (align) * CHAR_BIT)
+#endif
+
+#if !defined(stack_buffer_alloca_aligned_impl)
+/* Use traditional alloca(), with adjustments for strict alignment. */
+#define stack_buffer_alloca_aligned_impl(size, align) \
+ ((align) <= stack_buffer_alignof_alloca ? \
+ alloca(size) : \
+ ((void *) TYPEALIGN((align), alloca((size) + (align) - 1))))
+#endif
+
+/*
+ * We assume alloca() and similar have alignment at least as strict as C
+ * requires, for estimation purposes.
+ *
+ * XXX In real systems it's usually higher (eg 16 bytes rather than 8). We
+ * could estimate more accurately if we had that information.
+ */
+#define stack_buffer_alignof_alloca alignof(max_align_t)
+
+/* Do we need variables to track the bounds of the stack buffer? */
+#if defined(stack_buffer_sp) && defined(stack_buffer_base)
+/* Case 1: nothing to do, we have "base" and "sp" builtins. */
+#define stack_buffer_declare_impl
+#define stack_buffer_alloca_aligned stack_buffer_alloca_aligned_impl
+#else
+/* Cases 2 and 3: we need to track one or both of them ourselves... */
+#if defined(stack_buffer_base)
+/*
+ * Case 2: we have a "base" builtin, but not "sp", so we need a variable. A
+ * pointer to itself is a good initial value. The real stack pointer is
+ * probably less deep, as the compiler probably didn't allocate space for some
+ * variables. That's OK: we err on the cautious side of the truth, and after
+ * the first allocation we'll have an accurate value.
+ */
+#define stack_buffer_declare_impl \
+ char *stack_buffer_sp = (char *) &stack_buffer_sp;
+#else
+/*
+ * Case 3: we have neither "base" nor "sp". As case 2, but we'll also use
+ * stack_depth.c's base pointer which is certainly less deep than anything in
+ * this stack frame.
+ *
+ * XXX If we used the address of one of our own stack variables as base, we'd
+ * need add some kind of slop factor since the compiler might not actually
+ * allocate space to it if is never spilled, and then alloca() might return an
+ * address on the wrong side of it, which would break stack_buffer_stack_p().
+ *
+ * XXX Another way to get an initial value for both variables would be to make
+ * a dummy alloca(1) call, wasting (probably) 16 bytes of stack. It is, after
+ * all, a builtin that gives you the stack pointer, it just happens to consume
+ * space while doing so...
+ */
+#define stack_buffer_declare_impl \
+ char *stack_buffer_sp = (char *) &stack_buffer_sp; \
+ char *stack_buffer_base = (char *) stack_base_ptr;
+#endif
+/* Allocation wrapper for cases 2 and 3 that updates stack_buffer_sp. */
+#define stack_buffer_alloca_aligned(size, align) \
+ ((stack_buffer_sp = stack_buffer_alloca_aligned_impl((size), \
+ (align))), \
+ (PG_STACK_DIRECTION < 0 ? stack_buffer_sp : ((stack_buffer_sp += \
+ (size))) - (size)))
+#endif
+
+/* Like Min(), but in the direction of stack growth. */
+#define stack_buffer_least_deep(p1, p2) \
+ (stack_ptr_deeper_p((p1), (p2)) ? (p2) : (p1))
+
+/*
+ * Predict new end of stack after hypothetical allocation. This might
+ * undershoot by a few bytes due stack_buffer_alignof_alloca being too low,
+ * and because we don't bother to do TYPEALIGN(stack_buffer_alignof_alloca,
+ * size), but that doesn't matter much for our purposes.
+ */
+static inline const char *
+stack_buffer_guess_new_sp(const char *sp, size_t size, size_t align)
+{
+ if (align <= stack_buffer_alignof_alloca)
+ return sp + size * PG_STACK_DIRECTION;
+
+ if (PG_STACK_DIRECTION < 0)
+ return (const char *) TYPEALIGN_DOWN(align, sp - size);
+ else
+ return (const char *) TYPEALIGN(align, sp) + size;
+}
+
+#define stack_buffer_alloca_would_fit_p(size, align) \
+ (stack_ptr_deeper_p(stack_buffer_limit, \
+ stack_buffer_guess_new_sp(stack_buffer_sp, \
+ (size), \
+ (align))))
+
+#endif
+
+#endif
--
2.53.0
[text/x-patch] v2-0006-Use-stack-buffer-in-locale-code.patch (14.6K, 8-v2-0006-Use-stack-buffer-in-locale-code.patch)
download | inline diff:
From b7d7ae501e7416d29c867fc8f10e7e447af084dd Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Thu, 5 Mar 2026 21:38:46 +1300
Subject: [PATCH v2 06/19] Use stack buffer in locale code.
Use stack buffers for temporary C strings, wchar_t strings, pg_wchar
strings with the new API. Some cases were already using open-coded
arrays, while others nearby were not but are obvious candidates, so
let's change them too.
These cases used TEXTBUFLEN (1KB), and they now inherit that same amount
of available space by using:
DECLARE_STACK_BUFFER_LARGE();
Discussion:
---
src/backend/utils/adt/pg_locale.c | 6 -
src/backend/utils/adt/pg_locale_icu.c | 59 +++-------
src/backend/utils/adt/pg_locale_libc.c | 150 +++++++++----------------
3 files changed, 71 insertions(+), 144 deletions(-)
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index 6c5c1019e1e..3a6190892a7 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -60,12 +60,6 @@
#define PGLOCALE_SUPPORT_ERROR(provider) \
elog(ERROR, "unsupported collprovider for %s: %c", __func__, provider)
-/*
- * This should be large enough that most strings will fit, but small enough
- * that we feel comfortable putting it on the stack
- */
-#define TEXTBUFLEN 1024
-
#define MAX_L10N_DATA 80
/* pg_locale_builtin.c */
diff --git a/src/backend/utils/adt/pg_locale_icu.c b/src/backend/utils/adt/pg_locale_icu.c
index 352b4c3885f..e64aff82738 100644
--- a/src/backend/utils/adt/pg_locale_icu.c
+++ b/src/backend/utils/adt/pg_locale_icu.c
@@ -39,16 +39,9 @@
#include "utils/formatting.h"
#include "utils/memutils.h"
#include "utils/pg_locale.h"
+#include "utils/stack_buffer.h"
#include "utils/syscache.h"
-/*
- * Size of stack buffer to use for string transformations, used to avoid heap
- * allocations in typical cases. This should be large enough that most strings
- * will fit, but small enough that we feel comfortable putting it on the
- * stack.
- */
-#define TEXTBUFLEN 1024
-
extern pg_locale_t create_pg_locale_icu(Oid collid, MemoryContext context);
#ifdef USE_ICU
@@ -755,23 +748,17 @@ size_t
strnxfrm_icu(char *dest, size_t destsize, const char *src, ssize_t srclen,
pg_locale_t locale)
{
- char sbuf[TEXTBUFLEN];
- char *buf = sbuf;
UChar *uchar;
int32_t ulen;
- size_t uchar_bsize;
Size result_bsize;
+ DECLARE_STACK_BUFFER_LARGE();
+
init_icu_converter();
ulen = uchar_length(icu_converter, src, srclen);
- uchar_bsize = (ulen + 1) * sizeof(UChar);
-
- if (uchar_bsize > TEXTBUFLEN)
- buf = palloc(uchar_bsize);
-
- uchar = (UChar *) buf;
+ uchar = stack_buffer_alloc_array(UChar, ulen + 1);
ulen = uchar_convert(icu_converter, uchar, ulen + 1, src, srclen);
@@ -786,8 +773,7 @@ strnxfrm_icu(char *dest, size_t destsize, const char *src, ssize_t srclen,
Assert(result_bsize > 0);
result_bsize--;
- if (buf != sbuf)
- pfree(buf);
+ stack_buffer_free(uchar);
/* if dest is defined, it should be nul-terminated */
Assert(result_bsize >= destsize || dest[result_bsize] == '\0');
@@ -1020,16 +1006,14 @@ static int
strncoll_icu(const char *arg1, ssize_t len1,
const char *arg2, ssize_t len2, pg_locale_t locale)
{
- char sbuf[TEXTBUFLEN];
- char *buf = sbuf;
int32_t ulen1;
int32_t ulen2;
- size_t bufsize1;
- size_t bufsize2;
UChar *uchar1,
*uchar2;
int result;
+ DECLARE_STACK_BUFFER_LARGE();
+
/* if encoding is UTF8, use more efficient strncoll_icu_utf8 */
#ifdef HAVE_UCOL_STRCOLLUTF8
Assert(GetDatabaseEncoding() != PG_UTF8);
@@ -1040,14 +1024,8 @@ strncoll_icu(const char *arg1, ssize_t len1,
ulen1 = uchar_length(icu_converter, arg1, len1);
ulen2 = uchar_length(icu_converter, arg2, len2);
- bufsize1 = (ulen1 + 1) * sizeof(UChar);
- bufsize2 = (ulen2 + 1) * sizeof(UChar);
-
- if (bufsize1 + bufsize2 > TEXTBUFLEN)
- buf = palloc(bufsize1 + bufsize2);
-
- uchar1 = (UChar *) buf;
- uchar2 = (UChar *) (buf + bufsize1);
+ uchar1 = stack_buffer_alloc_array(UChar, ulen1 + 1);
+ uchar2 = stack_buffer_alloc_array(UChar, ulen2 + 1);
ulen1 = uchar_convert(icu_converter, uchar1, ulen1 + 1, arg1, len1);
ulen2 = uchar_convert(icu_converter, uchar2, ulen2 + 1, arg2, len2);
@@ -1056,8 +1034,8 @@ strncoll_icu(const char *arg1, ssize_t len1,
uchar1, ulen1,
uchar2, ulen2);
- if (buf != sbuf)
- pfree(buf);
+ stack_buffer_free(uchar1);
+ stack_buffer_free(uchar2);
return result;
}
@@ -1068,16 +1046,15 @@ strnxfrm_prefix_icu(char *dest, size_t destsize,
const char *src, ssize_t srclen,
pg_locale_t locale)
{
- char sbuf[TEXTBUFLEN];
- char *buf = sbuf;
UCharIterator iter;
uint32_t state[2];
UErrorCode status;
int32_t ulen = -1;
UChar *uchar = NULL;
- size_t uchar_bsize;
Size result_bsize;
+ DECLARE_STACK_BUFFER_LARGE();
+
/* if encoding is UTF8, use more efficient strnxfrm_prefix_icu_utf8 */
Assert(GetDatabaseEncoding() != PG_UTF8);
@@ -1085,12 +1062,7 @@ strnxfrm_prefix_icu(char *dest, size_t destsize,
ulen = uchar_length(icu_converter, src, srclen);
- uchar_bsize = (ulen + 1) * sizeof(UChar);
-
- if (uchar_bsize > TEXTBUFLEN)
- buf = palloc(uchar_bsize);
-
- uchar = (UChar *) buf;
+ uchar = stack_buffer_alloc_array(UChar, ulen + 1);
ulen = uchar_convert(icu_converter, uchar, ulen + 1, src, srclen);
@@ -1108,8 +1080,7 @@ strnxfrm_prefix_icu(char *dest, size_t destsize,
(errmsg("sort key generation failed: %s",
u_errorName(status))));
- if (buf != sbuf)
- pfree(buf);
+ stack_buffer_free(uchar);
return result_bsize;
}
diff --git a/src/backend/utils/adt/pg_locale_libc.c b/src/backend/utils/adt/pg_locale_libc.c
index 78f6ea161a0..6f51c32814a 100644
--- a/src/backend/utils/adt/pg_locale_libc.c
+++ b/src/backend/utils/adt/pg_locale_libc.c
@@ -23,6 +23,7 @@
#include "utils/formatting.h"
#include "utils/memutils.h"
#include "utils/pg_locale.h"
+#include "utils/stack_buffer.h"
#include "utils/syscache.h"
#ifdef __GLIBC__
@@ -72,14 +73,6 @@
* NB: the coding here assumes pg_wchar is an unsigned type.
*/
-/*
- * Size of stack buffer to use for string transformations, used to avoid heap
- * allocations in typical cases. This should be large enough that most strings
- * will fit, but small enough that we feel comfortable putting it on the
- * stack.
- */
-#define TEXTBUFLEN 1024
-
extern pg_locale_t create_pg_locale_libc(Oid collid, MemoryContext context);
static int strncoll_libc(const char *arg1, ssize_t len1,
@@ -502,6 +495,8 @@ strlower_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
size_t curr_char;
size_t max_size;
+ DECLARE_STACK_BUFFER_LARGE();
+
if (srclen < 0)
srclen = strlen(src);
@@ -512,7 +507,7 @@ strlower_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
errmsg("out of memory")));
/* Output workspace cannot have more codes than input bytes */
- workspace = palloc_array(wchar_t, srclen + 1);
+ workspace = stack_buffer_alloc_array(wchar_t, srclen + 1);
char2wchar(workspace, srclen + 1, src, srclen, loc);
@@ -523,7 +518,7 @@ strlower_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
* Make result large enough; case change might change number of bytes
*/
max_size = curr_char * pg_database_encoding_max_length();
- result = palloc(max_size + 1);
+ result = stack_buffer_alloc(max_size + 1);
result_size = wchar2char(result, workspace, max_size + 1, loc);
@@ -533,8 +528,8 @@ strlower_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
dest[result_size] = '\0';
}
- pfree(workspace);
- pfree(result);
+ stack_buffer_free(workspace);
+ stack_buffer_free(result);
return result_size;
}
@@ -607,6 +602,8 @@ strtitle_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
size_t curr_char;
size_t max_size;
+ DECLARE_STACK_BUFFER_LARGE();
+
if (srclen < 0)
srclen = strlen(src);
@@ -617,7 +614,7 @@ strtitle_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
errmsg("out of memory")));
/* Output workspace cannot have more codes than input bytes */
- workspace = palloc_array(wchar_t, srclen + 1);
+ workspace = stack_buffer_alloc_array(wchar_t, srclen + 1);
char2wchar(workspace, srclen + 1, src, srclen, loc);
@@ -634,7 +631,7 @@ strtitle_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
* Make result large enough; case change might change number of bytes
*/
max_size = curr_char * pg_database_encoding_max_length();
- result = palloc(max_size + 1);
+ result = stack_buffer_alloc(max_size + 1);
result_size = wchar2char(result, workspace, max_size + 1, loc);
@@ -644,8 +641,8 @@ strtitle_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
dest[result_size] = '\0';
}
- pfree(workspace);
- pfree(result);
+ stack_buffer_free(workspace);
+ stack_buffer_free(result);
return result_size;
}
@@ -700,6 +697,8 @@ strupper_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
size_t curr_char;
size_t max_size;
+ DECLARE_STACK_BUFFER_LARGE();
+
if (srclen < 0)
srclen = strlen(src);
@@ -710,7 +709,7 @@ strupper_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
errmsg("out of memory")));
/* Output workspace cannot have more codes than input bytes */
- workspace = palloc_array(wchar_t, srclen + 1);
+ workspace = stack_buffer_alloc_array(wchar_t, srclen + 1);
char2wchar(workspace, srclen + 1, src, srclen, loc);
@@ -721,7 +720,7 @@ strupper_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
* Make result large enough; case change might change number of bytes
*/
max_size = curr_char * pg_database_encoding_max_length();
- result = palloc(max_size + 1);
+ result = stack_buffer_alloc(max_size + 1);
result_size = wchar2char(result, workspace, max_size + 1, loc);
@@ -731,8 +730,8 @@ strupper_libc_mb(char *dest, size_t destsize, const char *src, ssize_t srclen,
dest[result_size] = '\0';
}
- pfree(workspace);
- pfree(result);
+ stack_buffer_free(workspace);
+ stack_buffer_free(result);
return result_size;
}
@@ -896,48 +895,25 @@ int
strncoll_libc(const char *arg1, ssize_t len1, const char *arg2, ssize_t len2,
pg_locale_t locale)
{
- char sbuf[TEXTBUFLEN];
- char *buf = sbuf;
- size_t bufsize1 = (len1 == -1) ? 0 : len1 + 1;
- size_t bufsize2 = (len2 == -1) ? 0 : len2 + 1;
- const char *arg1n;
- const char *arg2n;
+ char *cstr1 = NULL;
+ char *cstr2 = NULL;
int result;
- if (bufsize1 + bufsize2 > TEXTBUFLEN)
- buf = palloc(bufsize1 + bufsize2);
+ DECLARE_STACK_BUFFER_LARGE();
/* nul-terminate arguments if necessary */
- if (len1 == -1)
- {
- arg1n = arg1;
- }
- else
- {
- char *buf1 = buf;
+ if (len1 != -1)
+ arg1 = cstr1 = stack_buffer_strdup_with_len(arg1, len1);
- memcpy(buf1, arg1, len1);
- buf1[len1] = '\0';
- arg1n = buf1;
- }
+ if (len2 != -1)
+ arg2 = cstr2 = stack_buffer_strdup_with_len(arg2, len2);
- if (len2 == -1)
- {
- arg2n = arg2;
- }
- else
- {
- char *buf2 = buf + bufsize1;
-
- memcpy(buf2, arg2, len2);
- buf2[len2] = '\0';
- arg2n = buf2;
- }
+ result = strcoll_l(arg1, arg2, locale->lt);
- result = strcoll_l(arg1n, arg2n, locale->lt);
-
- if (buf != sbuf)
- pfree(buf);
+ if (cstr1)
+ stack_buffer_free(cstr1);
+ if (cstr2)
+ stack_buffer_free(cstr2);
return result;
}
@@ -953,25 +929,17 @@ size_t
strnxfrm_libc(char *dest, size_t destsize, const char *src, ssize_t srclen,
pg_locale_t locale)
{
- char sbuf[TEXTBUFLEN];
- char *buf = sbuf;
- size_t bufsize = srclen + 1;
+ char *cstr;
size_t result;
+ DECLARE_STACK_BUFFER_LARGE();
+
if (srclen == -1)
return strxfrm_l(dest, src, destsize, locale->lt);
- if (bufsize > TEXTBUFLEN)
- buf = palloc(bufsize);
-
- /* nul-terminate argument */
- memcpy(buf, src, srclen);
- buf[srclen] = '\0';
-
- result = strxfrm_l(dest, buf, destsize, locale->lt);
-
- if (buf != sbuf)
- pfree(buf);
+ cstr = stack_buffer_strdup_with_len(src, srclen);
+ result = strxfrm_l(dest, cstr, destsize, locale->lt);
+ stack_buffer_free(cstr);
/* if dest is defined, it should be nul-terminated */
Assert(result >= destsize || dest[result] == '\0');
@@ -1057,15 +1025,13 @@ static int
strncoll_libc_win32_utf8(const char *arg1, ssize_t len1, const char *arg2,
ssize_t len2, pg_locale_t locale)
{
- char sbuf[TEXTBUFLEN];
- char *buf = sbuf;
- char *a1p,
- *a2p;
- int a1len;
- int a2len;
+ wchar_t *w1p;
+ wchar_t *w2p;
int r;
int result;
+ DECLARE_STACK_BUFFER_LARGE();
+
Assert(GetDatabaseEncoding() == PG_UTF8);
if (len1 == -1)
@@ -1073,50 +1039,42 @@ strncoll_libc_win32_utf8(const char *arg1, ssize_t len1, const char *arg2,
if (len2 == -1)
len2 = strlen(arg2);
- a1len = len1 * 2 + 2;
- a2len = len2 * 2 + 2;
-
- if (a1len + a2len > TEXTBUFLEN)
- buf = palloc(a1len + a2len);
-
- a1p = buf;
- a2p = buf + a1len;
+ w1p = stack_buffer_alloc_array(wchar_t, len1 + 1);
+ w2p = stack_buffer_alloc_array(wchar_t, len2 + 1);
/* API does not work for zero-length input */
if (len1 == 0)
r = 0;
else
{
- r = MultiByteToWideChar(CP_UTF8, 0, arg1, len1,
- (LPWSTR) a1p, a1len / 2);
+ r = MultiByteToWideChar(CP_UTF8, 0, arg1, len1, w1p, len1);
if (!r)
ereport(ERROR,
(errmsg("could not convert string to UTF-16: error code %lu",
GetLastError())));
}
- ((LPWSTR) a1p)[r] = 0;
+ w1p[r] = 0;
if (len2 == 0)
r = 0;
else
{
- r = MultiByteToWideChar(CP_UTF8, 0, arg2, len2,
- (LPWSTR) a2p, a2len / 2);
+ r = MultiByteToWideChar(CP_UTF8, 0, arg2, len2, w2p, len2);
if (!r)
ereport(ERROR,
(errmsg("could not convert string to UTF-16: error code %lu",
GetLastError())));
}
- ((LPWSTR) a2p)[r] = 0;
+ w2p[r] = 0;
errno = 0;
- result = wcscoll_l((LPWSTR) a1p, (LPWSTR) a2p, locale->lt);
+ result = wcscoll_l(w1p, w2p, locale->lt);
if (result == 2147483647) /* _NLSCMPERROR; missing from mingw headers */
ereport(ERROR,
(errmsg("could not compare Unicode strings: %m")));
- if (buf != sbuf)
- pfree(buf);
+ stack_buffer_free(w1p);
+ stack_buffer_free(w2p);
return result;
}
@@ -1289,8 +1247,12 @@ char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen,
else
#endif /* WIN32 */
{
+ char *str;
+
+ DECLARE_STACK_BUFFER_LARGE();
+
/* mbstowcs requires ending '\0' */
- char *str = pnstrdup(from, fromlen);
+ str = stack_buffer_strdup_with_len(from, fromlen);
if (loc == (locale_t) 0)
{
@@ -1303,7 +1265,7 @@ char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen,
result = mbstowcs_l(to, str, tolen, loc);
}
- pfree(str);
+ stack_buffer_free(str);
}
if (result == -1)
--
2.53.0
[text/x-patch] v2-0007-Use-stack-buffer-in-some-executor-code.patch (9.7K, 9-v2-0007-Use-stack-buffer-in-some-executor-code.patch)
download | inline diff:
From a67697f00b6ea05984544211c8a2d9cfa2fa1cb5 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Mon, 2 Mar 2026 03:55:56 +1300
Subject: [PATCH v2 07/19] Use stack buffer in some executor code.
Mechanical changes to the allocation of memory used for:
* string copies
* datums, nulls, Oids, keys and similar values linked to the
number of attributes
These are all non-escaping values that are not used in recursive
processes.
---
src/backend/executor/execPartition.c | 7 +++++--
src/backend/executor/execTuples.c | 11 +++++++----
src/backend/executor/nodeAgg.c | 13 +++++++++----
src/backend/executor/nodeHashjoin.c | 14 ++++++++------
src/backend/executor/nodeMemoize.c | 7 +++++--
src/backend/executor/nodeWindowAgg.c | 7 +++++--
src/backend/executor/spi.c | 11 +++++++----
7 files changed, 46 insertions(+), 24 deletions(-)
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index bab294f5e91..6cc6d6ea510 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -33,6 +33,7 @@
#include "utils/partcache.h"
#include "utils/rls.h"
#include "utils/ruleutils.h"
+#include "utils/stack_buffer.h"
/*-----------------------
@@ -2518,6 +2519,8 @@ InitExecPartitionPruneContexts(PartitionPruneState *prunestate,
int newidx;
bool fix_subplan_map = false;
+ DECLARE_STACK_BUFFER();
+
Assert(prunestate->do_exec_prune);
Assert(parent_plan != NULL);
estate = parent_plan->state;
@@ -2535,7 +2538,7 @@ InitExecPartitionPruneContexts(PartitionPruneState *prunestate,
* indexes to new ones. For convenience of initialization, we use
* 1-based indexes in this array and leave pruned items as 0.
*/
- new_subplan_indexes = palloc0_array(int, n_total_subplans);
+ new_subplan_indexes = stack_buffer_alloc0_array(int, n_total_subplans);
newidx = 1;
i = -1;
while ((i = bms_next_member(initially_valid_subplans, i)) >= 0)
@@ -2645,7 +2648,7 @@ InitExecPartitionPruneContexts(PartitionPruneState *prunestate,
bms_free(prunestate->other_subplans);
prunestate->other_subplans = new_other_subplans;
- pfree(new_subplan_indexes);
+ stack_buffer_free(new_subplan_indexes);
}
}
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index b768eae9e53..d105123e3e1 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -68,6 +68,7 @@
#include "utils/builtins.h"
#include "utils/expandeddatum.h"
#include "utils/lsyscache.h"
+#include "utils/stack_buffer.h"
#include "utils/typcache.h"
static TupleDesc ExecTypeFromTLInternal(List *targetList,
@@ -2330,8 +2331,10 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
int i;
HeapTuple tuple;
- dvalues = (Datum *) palloc(natts * sizeof(Datum));
- nulls = (bool *) palloc(natts * sizeof(bool));
+ DECLARE_STACK_BUFFER();
+
+ dvalues = stack_buffer_alloc_array(Datum, natts);
+ nulls = stack_buffer_alloc_array(bool, natts);
/*
* Call the "in" function for each non-dropped attribute, even for nulls,
@@ -2368,8 +2371,8 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
* Release locally palloc'd space. XXX would probably be good to pfree
* values of pass-by-reference datums, as well.
*/
- pfree(dvalues);
- pfree(nulls);
+ stack_buffer_free(dvalues);
+ stack_buffer_free(nulls);
return tuple;
}
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 7d487a165fa..ffc198f526d 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -273,6 +273,7 @@
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/memutils_memorychunk.h"
+#include "utils/stack_buffer.h"
#include "utils/syscache.h"
#include "utils/tuplesort.h"
@@ -4347,10 +4348,12 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
{
Oid *ops;
+ DECLARE_STACK_BUFFER();
+
Assert(numArguments > 0);
Assert(list_length(aggref->aggdistinct) == numDistinctCols);
- ops = palloc(numDistinctCols * sizeof(Oid));
+ ops = stack_buffer_alloc_array(Oid, numDistinctCols);
i = 0;
foreach(lc, aggref->aggdistinct)
@@ -4367,7 +4370,7 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
ops,
pertrans->sortCollations,
&aggstate->ss.ps);
- pfree(ops);
+ stack_buffer_free(ops);
}
pertrans->sortstates = palloc0_array(Tuplesortstate *, numGroupingSets);
@@ -4382,11 +4385,13 @@ GetAggInitVal(Datum textInitVal, Oid transtype)
char *strInitVal;
Datum initVal;
+ DECLARE_STACK_BUFFER();
+
getTypeInputInfo(transtype, &typinput, &typioparam);
- strInitVal = TextDatumGetCString(textInitVal);
+ strInitVal = stack_buffer_text_datum_to_cstring(textInitVal);
initVal = OidInputFunctionCall(typinput, strInitVal,
typioparam, -1);
- pfree(strInitVal);
+ stack_buffer_free(strInitVal);
return initVal;
}
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 5aa8a09b265..fd7a47d8b85 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -171,6 +171,7 @@
#include "miscadmin.h"
#include "utils/lsyscache.h"
#include "utils/sharedtuplestore.h"
+#include "utils/stack_buffer.h"
#include "utils/wait_event.h"
@@ -826,6 +827,7 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
ListCell *lc;
int nkeys;
+ DECLARE_STACK_BUFFER();
hjstate->hj_HashTupleSlot = slot;
@@ -840,9 +842,9 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
*/
nkeys = list_length(node->hashoperators);
- outer_hashfuncid = palloc_array(Oid, nkeys);
- inner_hashfuncid = palloc_array(Oid, nkeys);
- hash_strict = palloc_array(bool, nkeys);
+ outer_hashfuncid = stack_buffer_alloc_array(Oid, nkeys);
+ inner_hashfuncid = stack_buffer_alloc_array(Oid, nkeys);
+ hash_strict = stack_buffer_alloc_array(bool, nkeys);
/*
* Determine the hash function for each side of the join for the given
@@ -905,9 +907,9 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
}
/* no need to keep these */
- pfree(outer_hashfuncid);
- pfree(inner_hashfuncid);
- pfree(hash_strict);
+ stack_buffer_free(outer_hashfuncid);
+ stack_buffer_free(inner_hashfuncid);
+ stack_buffer_free(hash_strict);
}
/*
diff --git a/src/backend/executor/nodeMemoize.c b/src/backend/executor/nodeMemoize.c
index edf52efd4c3..09a5e674dd7 100644
--- a/src/backend/executor/nodeMemoize.c
+++ b/src/backend/executor/nodeMemoize.c
@@ -74,6 +74,7 @@
#include "miscadmin.h"
#include "utils/datum.h"
#include "utils/lsyscache.h"
+#include "utils/stack_buffer.h"
/* States of the ExecMemoize state machine */
#define MEMO_CACHE_LOOKUP 1 /* Attempt to perform a cache lookup */
@@ -958,6 +959,8 @@ ExecInitMemoize(Memoize *node, EState *estate, int eflags)
int nkeys;
Oid *eqfuncoids;
+ DECLARE_STACK_BUFFER();
+
/* check for unsupported flags */
Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
@@ -1006,7 +1009,7 @@ ExecInitMemoize(Memoize *node, EState *estate, int eflags)
* data */
mstate->hashfunctions = (FmgrInfo *) palloc(nkeys * sizeof(FmgrInfo));
- eqfuncoids = palloc(nkeys * sizeof(Oid));
+ eqfuncoids = stack_buffer_alloc_array(Oid, nkeys);
for (i = 0; i < nkeys; i++)
{
@@ -1033,7 +1036,7 @@ ExecInitMemoize(Memoize *node, EState *estate, int eflags)
node->param_exprs,
(PlanState *) mstate);
- pfree(eqfuncoids);
+ stack_buffer_free(eqfuncoids);
mstate->mem_used = 0;
/* Limit the total memory consumed by the cache to this */
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index d9b64b0f465..ed3744f5493 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -52,6 +52,7 @@
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/regproc.h"
+#include "utils/stack_buffer.h"
#include "utils/syscache.h"
#include "windowapi.h"
@@ -3163,11 +3164,13 @@ GetAggInitVal(Datum textInitVal, Oid transtype)
char *strInitVal;
Datum initVal;
+ DECLARE_STACK_BUFFER();
+
getTypeInputInfo(transtype, &typinput, &typioparam);
- strInitVal = TextDatumGetCString(textInitVal);
+ strInitVal = stack_buffer_text_datum_to_cstring(textInitVal);
initVal = OidInputFunctionCall(typinput, strInitVal,
typioparam, -1);
- pfree(strInitVal);
+ stack_buffer_free(strInitVal);
return initVal;
}
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 3019a3b2b97..dbbb710f877 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -31,6 +31,7 @@
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
+#include "utils/stack_buffer.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
@@ -1113,6 +1114,8 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
bool *n;
int i;
+ DECLARE_STACK_BUFFER();
+
if (rel == NULL || tuple == NULL || natts < 0 || attnum == NULL || Values == NULL)
{
SPI_result = SPI_ERROR_ARGUMENT;
@@ -1130,8 +1133,8 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
SPI_result = 0;
numberOfAttributes = rel->rd_att->natts;
- v = palloc_array(Datum, numberOfAttributes);
- n = palloc_array(bool, numberOfAttributes);
+ v = stack_buffer_alloc_array(Datum, numberOfAttributes);
+ n = stack_buffer_alloc_array(bool, numberOfAttributes);
/* fetch old values and nulls */
heap_deform_tuple(tuple, rel->rd_att, v, n);
@@ -1163,8 +1166,8 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
SPI_result = SPI_ERROR_NOATTRIBUTE;
}
- pfree(v);
- pfree(n);
+ stack_buffer_free(v);
+ stack_buffer_free(n);
MemoryContextSwitchTo(oldcxt);
--
2.53.0
[text/x-patch] v2-0008-Use-stack-buffer-in-some-adt-code.patch (36.7K, 10-v2-0008-Use-stack-buffer-in-some-adt-code.patch)
download | inline diff:
From 89a7bc6e554fce3d35c01eccdd6a1a618e6608d0 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Mon, 2 Mar 2026 04:08:31 +1300
Subject: [PATCH v2 08/19] Use stack buffer in some adt code.
---
src/backend/utils/adt/arrayfuncs.c | 39 ++++----
src/backend/utils/adt/datum.c | 7 +-
src/backend/utils/adt/jsonfuncs.c | 52 ++++++++---
src/backend/utils/adt/like_support.c | 59 +++++++-----
src/backend/utils/adt/numeric.c | 15 ++-
src/backend/utils/adt/rowtypes.c | 133 ++++++++++++++++-----------
src/backend/utils/adt/ruleutils.c | 47 ++++++----
src/backend/utils/adt/tsvector_op.c | 10 +-
8 files changed, 223 insertions(+), 139 deletions(-)
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 734e5fea45e..c2706be9276 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -35,6 +35,7 @@
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/selfuncs.h"
+#include "utils/stack_buffer.h"
#include "utils/typcache.h"
@@ -1051,6 +1052,8 @@ array_out(PG_FUNCTION_ARGS)
array_iter iter;
ArrayMetaState *my_extra;
+ DECLARE_STACK_BUFFER();
+
/*
* We arrange to look up info about element type, including its output
* conversion proc, only once per series of calls, assuming the element
@@ -1112,8 +1115,8 @@ array_out(PG_FUNCTION_ARGS)
* any overhead such as escaping backslashes), and detect whether each
* item needs double quotes.
*/
- values = (char **) palloc(nitems * sizeof(char *));
- needquotes = (bool *) palloc(nitems * sizeof(bool));
+ values = stack_buffer_alloc_array(char *, nitems);
+ needquotes = stack_buffer_alloc_array(bool, nitems);
overall_length = 0;
array_iter_setup(&iter, v, typlen, typbyval, typalign);
@@ -1257,8 +1260,8 @@ array_out(PG_FUNCTION_ARGS)
/* Assert that we calculated the string length accurately */
Assert(overall_length == (p - retval + 1));
- pfree(values);
- pfree(needquotes);
+ stack_buffer_free(values);
+ stack_buffer_free(needquotes);
PG_RETURN_CSTRING(retval);
}
@@ -3232,6 +3235,8 @@ array_map(Datum arrayd,
Datum *transform_source = exprstate->innermost_caseval;
bool *transform_source_isnull = exprstate->innermost_casenull;
+ DECLARE_STACK_BUFFER();
+
inpType = AARR_ELEMTYPE(v);
ndim = AARR_NDIM(v);
dim = AARR_DIMS(v);
@@ -3278,8 +3283,8 @@ array_map(Datum arrayd,
typalignby = typalign_to_alignby(typalign);
/* Allocate temporary arrays for new values */
- values = (Datum *) palloc(nitems * sizeof(Datum));
- nulls = (bool *) palloc(nitems * sizeof(bool));
+ values = stack_buffer_alloc_array(Datum, nitems);
+ nulls = stack_buffer_alloc_array(bool, nitems);
/* Loop over source data */
array_iter_setup(&iter, v, inp_typlen, inp_typbyval, inp_typalign);
@@ -3340,8 +3345,8 @@ array_map(Datum arrayd,
/*
* Note: do not risk trying to pfree the results of the called expression
*/
- pfree(values);
- pfree(nulls);
+ stack_buffer_free(values);
+ stack_buffer_free(nulls);
return PointerGetDatum(result);
}
@@ -6424,6 +6429,8 @@ array_replace_internal(ArrayType *array,
bool changed = false;
TypeCacheEntry *typentry;
+ DECLARE_STACK_BUFFER();
+
element_type = ARR_ELEMTYPE(array);
ndim = ARR_NDIM(array);
dim = ARR_DIMS(array);
@@ -6482,8 +6489,8 @@ array_replace_internal(ArrayType *array,
collation, NULL, NULL);
/* Allocate temporary arrays for new values */
- values = (Datum *) palloc(nitems * sizeof(Datum));
- nulls = (bool *) palloc(nitems * sizeof(bool));
+ values = stack_buffer_alloc_array(Datum, nitems);
+ nulls = stack_buffer_alloc_array(bool, nitems);
/* Loop over source data */
arraydataptr = ARR_DATA_PTR(array);
@@ -6599,16 +6606,16 @@ array_replace_internal(ArrayType *array,
*/
if (!changed)
{
- pfree(values);
- pfree(nulls);
+ stack_buffer_free(values);
+ stack_buffer_free(nulls);
return array;
}
/* If all elements were removed return an empty array */
if (nresult == 0)
{
- pfree(values);
- pfree(nulls);
+ stack_buffer_free(values);
+ stack_buffer_free(nulls);
return construct_empty_array(element_type);
}
@@ -6643,8 +6650,8 @@ array_replace_internal(ArrayType *array,
typlen, typbyval, typalign,
false);
- pfree(values);
- pfree(nulls);
+ stack_buffer_free(values);
+ stack_buffer_free(nulls);
return result;
}
diff --git a/src/backend/utils/adt/datum.c b/src/backend/utils/adt/datum.c
index 8832785540f..8b04ba8b9d7 100644
--- a/src/backend/utils/adt/datum.c
+++ b/src/backend/utils/adt/datum.c
@@ -48,6 +48,7 @@
#include "utils/datum.h"
#include "utils/expandeddatum.h"
#include "utils/fmgrprotos.h"
+#include "utils/stack_buffer.h"
/*-------------------------------------------------------------------------
@@ -464,6 +465,8 @@ datumSerialize(Datum value, bool isnull, bool typByVal, int typLen,
ExpandedObjectHeader *eoh = NULL;
int header;
+ DECLARE_STACK_BUFFER();
+
/* Write header word. */
if (isnull)
header = -2;
@@ -496,13 +499,13 @@ datumSerialize(Datum value, bool isnull, bool typByVal, int typLen,
* EOH_flatten_into expects the target address to be maxaligned,
* so we can't store directly to *start_address.
*/
- tmp = (char *) palloc(header);
+ tmp = (char *) stack_buffer_alloc(header);
EOH_flatten_into(eoh, tmp, header);
memcpy(*start_address, tmp, header);
*start_address += header;
/* be tidy. */
- pfree(tmp);
+ stack_buffer_free(tmp);
}
else
{
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index d5b64d7fca5..f0bed51a274 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -37,6 +37,7 @@
#include "utils/jsonfuncs.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
+#include "utils/stack_buffer.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
@@ -1681,7 +1682,11 @@ jsonb_set_element(Jsonb *jb, const Datum *path, int path_len,
{
JsonbInState state = {0};
JsonbIterator *it;
- bool *path_nulls = palloc0_array(bool, path_len);
+ bool *path_nulls;
+
+ DECLARE_STACK_BUFFER();
+
+ path_nulls = stack_buffer_alloc0_array(bool, path_len);
if (newval->type == jbvArray && newval->val.array.rawScalar)
*newval = newval->val.array.elems[0];
@@ -1692,7 +1697,7 @@ jsonb_set_element(Jsonb *jb, const Datum *path, int path_len,
JB_PATH_CREATE | JB_PATH_FILL_GAPS |
JB_PATH_CONSISTENT_POSITION);
- pfree(path_nulls);
+ stack_buffer_free(path_nulls);
PG_RETURN_JSONB_P(JsonbValueToJsonb(state.result));
}
@@ -2921,6 +2926,8 @@ populate_array(ArrayIOData *aio,
int *lbs;
int i;
+ DECLARE_STACK_BUFFER();
+
ctx.aio = aio;
ctx.mcxt = mcxt;
ctx.acxt = CurrentMemoryContext;
@@ -2955,7 +2962,7 @@ populate_array(ArrayIOData *aio,
Assert(ctx.ndims > 0);
- lbs = palloc_array(int, ctx.ndims);
+ lbs = stack_buffer_alloc_array(int, ctx.ndims);
for (i = 0; i < ctx.ndims; i++)
lbs[i] = 1;
@@ -2965,7 +2972,7 @@ populate_array(ArrayIOData *aio,
pfree(ctx.dims);
pfree(ctx.sizes);
- pfree(lbs);
+ stack_buffer_free(lbs);
*isnull = false;
return result;
@@ -3125,6 +3132,9 @@ populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv,
Datum res;
char *str = NULL;
const char *json = NULL;
+ bool str_stack = false;
+
+ DECLARE_STACK_BUFFER();
if (jsv->is_json)
{
@@ -3149,9 +3159,8 @@ populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv,
else if (len >= 0)
{
/* create a NUL-terminated version */
- str = palloc(len + 1);
- memcpy(str, json, len);
- str[len] = '\0';
+ str = stack_buffer_strndup(json, len);
+ str_stack = true;
}
else
{
@@ -3164,7 +3173,10 @@ populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv,
JsonbValue *jbv = jsv->val.jsonb;
if (jbv->type == jbvString && omit_quotes)
- str = pnstrdup(jbv->val.string.val, jbv->val.string.len);
+ {
+ str = stack_buffer_strndup(jbv->val.string.val, jbv->val.string.len);
+ str_stack = true;
+ }
else if (typid == JSONBOID)
{
Jsonb *jsonb = JsonbValueToJsonb(jbv); /* directly use jsonb */
@@ -3183,9 +3195,15 @@ populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv,
str = JsonbToCString(NULL, &jsonb->root, VARSIZE(jsonb));
}
else if (jbv->type == jbvString) /* quotes are stripped */
- str = pnstrdup(jbv->val.string.val, jbv->val.string.len);
+ {
+ str = stack_buffer_strndup(jbv->val.string.val, jbv->val.string.len);
+ str_stack = true;
+ }
else if (jbv->type == jbvBool)
- str = pstrdup(jbv->val.boolean ? "true" : "false");
+ {
+ str = stack_buffer_strdup(jbv->val.boolean ? "true" : "false");
+ str_stack = true;
+ }
else if (jbv->type == jbvNumeric)
str = DatumGetCString(DirectFunctionCall1(numeric_out,
PointerGetDatum(jbv->val.numeric)));
@@ -3204,7 +3222,9 @@ populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv,
}
/* free temporary buffer */
- if (str != json)
+ if (str_stack)
+ stack_buffer_free(str);
+ else if (str != json)
pfree(str);
return res;
@@ -3528,6 +3548,8 @@ populate_record(TupleDesc tupdesc,
int ncolumns = tupdesc->natts;
int i;
+ DECLARE_STACK_BUFFER();
+
/*
* if the input json is empty, we can only skip the rest if we were passed
* in a non-null record, since otherwise there may be issues with domain
@@ -3552,8 +3574,8 @@ populate_record(TupleDesc tupdesc,
record->ncolumns = ncolumns;
}
- values = (Datum *) palloc(ncolumns * sizeof(Datum));
- nulls = (bool *) palloc(ncolumns * sizeof(bool));
+ values = stack_buffer_alloc_array(Datum, ncolumns);
+ nulls = stack_buffer_alloc_array(bool, ncolumns);
if (defaultval)
{
@@ -3618,8 +3640,8 @@ populate_record(TupleDesc tupdesc,
res = heap_form_tuple(tupdesc, values, nulls);
- pfree(values);
- pfree(nulls);
+ stack_buffer_free(values);
+ stack_buffer_free(nulls);
return res->t_data;
}
diff --git a/src/backend/utils/adt/like_support.c b/src/backend/utils/adt/like_support.c
index 01cd6b10730..119ed842bf1 100644
--- a/src/backend/utils/adt/like_support.c
+++ b/src/backend/utils/adt/like_support.c
@@ -52,6 +52,7 @@
#include "utils/lsyscache.h"
#include "utils/pg_locale.h"
#include "utils/selfuncs.h"
+#include "utils/stack_buffer.h"
#include "utils/varlena.h"
@@ -994,12 +995,14 @@ like_fixed_prefix(Const *patt_const, Const **prefix_const,
int pos,
match_pos;
+ DECLARE_STACK_BUFFER();
+
/* the right-hand const is type text or bytea */
Assert(typeid == BYTEAOID || typeid == TEXTOID);
if (typeid != BYTEAOID)
{
- patt = TextDatumGetCString(patt_const->constvalue);
+ patt = stack_buffer_text_datum_to_cstring(patt_const->constvalue);
pattlen = strlen(patt);
}
else
@@ -1007,12 +1010,12 @@ like_fixed_prefix(Const *patt_const, Const **prefix_const,
bytea *bstr = DatumGetByteaPP(patt_const->constvalue);
pattlen = VARSIZE_ANY_EXHDR(bstr);
- patt = (char *) palloc(pattlen);
+ patt = (char *) stack_buffer_alloc(pattlen);
memcpy(patt, VARDATA_ANY(bstr), pattlen);
Assert(bstr == DatumGetPointer(patt_const->constvalue));
}
- match = palloc(pattlen + 1);
+ match = stack_buffer_alloc(pattlen + 1);
match_pos = 0;
for (pos = 0; pos < pattlen; pos++)
{
@@ -1042,8 +1045,8 @@ like_fixed_prefix(Const *patt_const, Const **prefix_const,
if (rest_selec != NULL)
*rest_selec = like_selectivity(&patt[pos], pattlen - pos, false);
- pfree(patt);
- pfree(match);
+ stack_buffer_free(patt);
+ stack_buffer_free(match);
/* in LIKE, an empty pattern is an exact match! */
if (pos == pattlen)
@@ -1075,6 +1078,8 @@ like_fixed_prefix_ci(Const *patt_const, Oid collation, Const **prefix_const,
int match_mblen;
pg_locale_t locale = 0;
+ DECLARE_STACK_BUFFER();
+
/* the right-hand const is type text or bytea */
Assert(typeid == BYTEAOID || typeid == TEXTOID);
@@ -1097,10 +1102,10 @@ like_fixed_prefix_ci(Const *patt_const, Oid collation, Const **prefix_const,
locale = pg_newlocale_from_collation(collation);
- wpatt = palloc((nbytes + 1) * sizeof(pg_wchar));
+ wpatt = stack_buffer_alloc_array(pg_wchar, nbytes + 1);
wpattlen = pg_mb2wchar_with_len(VARDATA_ANY(val), wpatt, nbytes);
- wmatch = palloc((nbytes + 1) * sizeof(pg_wchar));
+ wmatch = stack_buffer_alloc_array(pg_wchar, nbytes + 1);
for (wpos = 0; wpos < wpattlen; wpos++)
{
/* % and _ are wildcard characters in LIKE */
@@ -1128,13 +1133,13 @@ like_fixed_prefix_ci(Const *patt_const, Oid collation, Const **prefix_const,
wmatch[wmatch_pos] = '\0';
- match = palloc(pg_database_encoding_max_length() * wmatch_pos + 1);
+ match = stack_buffer_alloc(pg_database_encoding_max_length() * wmatch_pos + 1);
match_mblen = pg_wchar2mb_with_len(wmatch, match, wmatch_pos);
match[match_mblen] = '\0';
- pfree(wmatch);
+ stack_buffer_free(wmatch);
*prefix_const = string_to_const(match, TEXTOID);
- pfree(match);
+ stack_buffer_free(match);
if (rest_selec != NULL)
{
@@ -1142,14 +1147,14 @@ like_fixed_prefix_ci(Const *patt_const, Oid collation, Const **prefix_const,
char *rest;
int rest_mblen;
- rest = palloc(pg_database_encoding_max_length() * wrestlen + 1);
+ rest = stack_buffer_alloc(pg_database_encoding_max_length() * wrestlen + 1);
rest_mblen = pg_wchar2mb_with_len(&wpatt[wmatch_pos], rest, wrestlen);
*rest_selec = like_selectivity(rest, rest_mblen, true);
- pfree(rest);
+ stack_buffer_free(rest);
}
- pfree(wpatt);
+ stack_buffer_free(wpatt);
/* in LIKE, an empty pattern is an exact match! */
if (wpos == wpattlen)
@@ -1169,6 +1174,8 @@ regex_fixed_prefix(Const *patt_const, bool case_insensitive, Oid collation,
char *prefix;
bool exact;
+ DECLARE_STACK_BUFFER();
+
/*
* Should be unnecessary, there are no bytea regex operators defined. As
* such, it should be noted that the rest of this function has *not* been
@@ -1190,12 +1197,12 @@ regex_fixed_prefix(Const *patt_const, bool case_insensitive, Oid collation,
if (rest_selec != NULL)
{
- char *patt = TextDatumGetCString(patt_const->constvalue);
+ char *patt = stack_buffer_text_datum_to_cstring(patt_const->constvalue);
*rest_selec = regex_selectivity(patt, strlen(patt),
case_insensitive,
0);
- pfree(patt);
+ stack_buffer_free(patt);
}
return Pattern_Prefix_None;
@@ -1212,12 +1219,12 @@ regex_fixed_prefix(Const *patt_const, bool case_insensitive, Oid collation,
}
else
{
- char *patt = TextDatumGetCString(patt_const->constvalue);
+ char *patt = stack_buffer_text_datum_to_cstring(patt_const->constvalue);
*rest_selec = regex_selectivity(patt, strlen(patt),
case_insensitive,
strlen(prefix));
- pfree(patt);
+ stack_buffer_free(patt);
}
}
@@ -1619,6 +1626,8 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc, Oid collation)
char *cmptxt = NULL;
mbcharacter_incrementer charinc;
+ DECLARE_STACK_BUFFER();
+
/*
* Get a modifiable copy of the prefix string in C-string format, and set
* up the string we will compare to as a Datum. In C locale this can just
@@ -1630,7 +1639,7 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc, Oid collation)
bytea *bstr = DatumGetByteaPP(str_const->constvalue);
len = VARSIZE_ANY_EXHDR(bstr);
- workstr = (char *) palloc(len);
+ workstr = (char *) stack_buffer_alloc(len);
memcpy(workstr, VARDATA_ANY(bstr), len);
Assert(bstr == DatumGetPointer(str_const->constvalue));
cmpstr = str_const->constvalue;
@@ -1641,7 +1650,7 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc, Oid collation)
workstr = DatumGetCString(DirectFunctionCall1(nameout,
str_const->constvalue));
else
- workstr = TextDatumGetCString(str_const->constvalue);
+ workstr = stack_buffer_text_datum_to_cstring(str_const->constvalue);
len = strlen(workstr);
if (len == 0 || pg_newlocale_from_collation(collation)->collate_is_c)
cmpstr = str_const->constvalue;
@@ -1669,7 +1678,7 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc, Oid collation)
/* And build the string to compare to */
if (datatype == NAMEOID)
{
- cmptxt = palloc(len + 2);
+ cmptxt = stack_buffer_alloc(len + 2);
memcpy(cmptxt, workstr, len);
cmptxt[len] = suffixchar;
cmptxt[len + 1] = '\0';
@@ -1677,7 +1686,7 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc, Oid collation)
}
else
{
- cmptxt = palloc(VARHDRSZ + len + 1);
+ cmptxt = stack_buffer_alloc(VARHDRSZ + len + 1);
SET_VARSIZE(cmptxt, VARHDRSZ + len + 1);
memcpy(VARDATA(cmptxt), workstr, len);
*(VARDATA(cmptxt) + len) = suffixchar;
@@ -1729,8 +1738,8 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc, Oid collation)
{
/* Successfully made a string larger than cmpstr */
if (cmptxt)
- pfree(cmptxt);
- pfree(workstr);
+ stack_buffer_free(cmptxt);
+ stack_buffer_free(workstr);
return workstr_const;
}
@@ -1749,8 +1758,8 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc, Oid collation)
/* Failed... */
if (cmptxt)
- pfree(cmptxt);
- pfree(workstr);
+ stack_buffer_free(cmptxt);
+ stack_buffer_free(workstr);
return NULL;
}
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index d25b8ad505d..771ea2ba6b3 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -43,6 +43,7 @@
#include "utils/numeric.h"
#include "utils/pg_lsn.h"
#include "utils/sortsupport.h"
+#include "utils/stack_buffer.h"
/* ----------
* Uncomment the following to enable compilation of dump_numeric()
@@ -6755,6 +6756,8 @@ set_var_from_str(const char *str, const char *cp,
int offset;
NumericDigit *digits;
+ DECLARE_STACK_BUFFER();
+
/*
* We first parse the string to extract decimal digits and determine the
* correct decimal weight. Then convert to NBASE representation.
@@ -6781,7 +6784,7 @@ set_var_from_str(const char *str, const char *cp,
if (!isdigit((unsigned char) *cp))
goto invalid_syntax;
- decdigits = (unsigned char *) palloc(strlen(cp) + DEC_DIGITS * 2);
+ decdigits = (unsigned char *) stack_buffer_alloc(strlen(cp) + DEC_DIGITS * 2);
/* leading padding for digit alignment later */
memset(decdigits, 0, DEC_DIGITS);
@@ -6915,7 +6918,7 @@ set_var_from_str(const char *str, const char *cp,
i += DEC_DIGITS;
}
- pfree(decdigits);
+ stack_buffer_free(decdigits);
/* Strip any leading/trailing zeroes, and normalize weight if zero */
strip_var(dest);
@@ -8901,6 +8904,8 @@ div_var(const NumericVar *var1, const NumericVar *var2, NumericVar *result,
NumericDigit *res_digits;
int i;
+ DECLARE_STACK_BUFFER();
+
/*
* First of all division by zero check; we must not be handed an
* unnormalized divisor.
@@ -9050,8 +9055,8 @@ div_var(const NumericVar *var1, const NumericVar *var2, NumericVar *result,
* zero and not counted in div_ndigitpairs, so that the main loop below
* can safely read and write the (qi+1)'th digit in the approximate case.
*/
- dividend = (int64 *) palloc((div_ndigitpairs + 1) * sizeof(int64) +
- var2ndigitpairs * sizeof(int32));
+ dividend = (int64 *) stack_buffer_alloc((div_ndigitpairs + 1) * sizeof(int64) +
+ var2ndigitpairs * sizeof(int32));
divisor = (int32 *) (dividend + div_ndigitpairs + 1);
/* load var1 into dividend[0 .. var1ndigitpairs-1], zeroing the rest */
@@ -9389,7 +9394,7 @@ div_var(const NumericVar *var1, const NumericVar *var2, NumericVar *result,
}
Assert(carry == 0);
- pfree(dividend);
+ stack_buffer_free(dividend);
/*
* Finally, round or truncate the result to the requested precision.
diff --git a/src/backend/utils/adt/rowtypes.c b/src/backend/utils/adt/rowtypes.c
index e4eb7111ee7..bcceb998da7 100644
--- a/src/backend/utils/adt/rowtypes.c
+++ b/src/backend/utils/adt/rowtypes.c
@@ -25,6 +25,7 @@
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/lsyscache.h"
+#include "utils/stack_buffer.h"
#include "utils/typcache.h"
@@ -89,6 +90,8 @@ record_in(PG_FUNCTION_ARGS)
bool *nulls;
StringInfoData buf;
+ DECLARE_STACK_BUFFER();
+
check_stack_depth(); /* recurses for record-type columns */
/*
@@ -140,8 +143,8 @@ record_in(PG_FUNCTION_ARGS)
my_extra->ncolumns = ncolumns;
}
- values = palloc_array(Datum, ncolumns);
- nulls = palloc_array(bool, ncolumns);
+ values = stack_buffer_alloc_array(Datum, ncolumns);
+ nulls = stack_buffer_alloc_array(bool, ncolumns);
/*
* Scan the string. We use "buf" to accumulate the de-quoted data for
@@ -310,8 +313,8 @@ record_in(PG_FUNCTION_ARGS)
heap_freetuple(tuple);
pfree(buf.data);
- pfree(values);
- pfree(nulls);
+ stack_buffer_free(values);
+ stack_buffer_free(nulls);
ReleaseTupleDesc(tupdesc);
PG_RETURN_HEAPTUPLEHEADER(result);
@@ -341,6 +344,8 @@ record_out(PG_FUNCTION_ARGS)
bool *nulls;
StringInfoData buf;
+ DECLARE_STACK_BUFFER();
+
check_stack_depth(); /* recurses for record-type columns */
/* Extract type info from the tuple itself */
@@ -383,8 +388,8 @@ record_out(PG_FUNCTION_ARGS)
my_extra->ncolumns = ncolumns;
}
- values = palloc_array(Datum, ncolumns);
- nulls = palloc_array(bool, ncolumns);
+ values = stack_buffer_alloc_array(Datum, ncolumns);
+ nulls = stack_buffer_alloc_array(bool, ncolumns);
/* Break down the tuple into fields */
heap_deform_tuple(&tuple, tupdesc, values, nulls);
@@ -466,8 +471,8 @@ record_out(PG_FUNCTION_ARGS)
appendStringInfoChar(&buf, ')');
- pfree(values);
- pfree(nulls);
+ stack_buffer_free(values);
+ stack_buffer_free(nulls);
ReleaseTupleDesc(tupdesc);
PG_RETURN_CSTRING(buf.data);
@@ -493,6 +498,8 @@ record_recv(PG_FUNCTION_ARGS)
Datum *values;
bool *nulls;
+ DECLARE_STACK_BUFFER();
+
check_stack_depth(); /* recurses for record-type columns */
/*
@@ -539,8 +546,8 @@ record_recv(PG_FUNCTION_ARGS)
my_extra->ncolumns = ncolumns;
}
- values = palloc_array(Datum, ncolumns);
- nulls = palloc_array(bool, ncolumns);
+ values = stack_buffer_alloc_array(Datum, ncolumns);
+ nulls = stack_buffer_alloc_array(bool, ncolumns);
/* Fetch number of columns user thinks it has */
usercols = pq_getmsgint(buf, 4);
@@ -673,8 +680,8 @@ record_recv(PG_FUNCTION_ARGS)
memcpy(result, tuple->t_data, tuple->t_len);
heap_freetuple(tuple);
- pfree(values);
- pfree(nulls);
+ stack_buffer_free(values);
+ stack_buffer_free(nulls);
ReleaseTupleDesc(tupdesc);
PG_RETURN_HEAPTUPLEHEADER(result);
@@ -699,6 +706,8 @@ record_send(PG_FUNCTION_ARGS)
bool *nulls;
StringInfoData buf;
+ DECLARE_STACK_BUFFER();
+
check_stack_depth(); /* recurses for record-type columns */
/* Extract type info from the tuple itself */
@@ -741,8 +750,8 @@ record_send(PG_FUNCTION_ARGS)
my_extra->ncolumns = ncolumns;
}
- values = palloc_array(Datum, ncolumns);
- nulls = palloc_array(bool, ncolumns);
+ values = stack_buffer_alloc_array(Datum, ncolumns);
+ nulls = stack_buffer_alloc_array(bool, ncolumns);
/* Break down the tuple into fields */
heap_deform_tuple(&tuple, tupdesc, values, nulls);
@@ -800,8 +809,8 @@ record_send(PG_FUNCTION_ARGS)
VARSIZE(outputbytes) - VARHDRSZ);
}
- pfree(values);
- pfree(nulls);
+ stack_buffer_free(values);
+ stack_buffer_free(nulls);
ReleaseTupleDesc(tupdesc);
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
@@ -845,6 +854,8 @@ record_cmp(FunctionCallInfo fcinfo)
int i2;
int j;
+ DECLARE_STACK_BUFFER();
+
check_stack_depth(); /* recurses for record-type columns */
/* Extract type info from the tuples */
@@ -901,11 +912,11 @@ record_cmp(FunctionCallInfo fcinfo)
}
/* Break down the tuples into fields */
- values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
- nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
+ values1 = stack_buffer_alloc_array(Datum, ncolumns1);
+ nulls1 = stack_buffer_alloc_array(bool, ncolumns1);
heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
- values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
- nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
+ values2 = stack_buffer_alloc_array(Datum, ncolumns2);
+ nulls2 = stack_buffer_alloc_array(bool, ncolumns2);
heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
/*
@@ -1040,10 +1051,10 @@ record_cmp(FunctionCallInfo fcinfo)
errmsg("cannot compare record types with different numbers of columns")));
}
- pfree(values1);
- pfree(nulls1);
- pfree(values2);
- pfree(nulls2);
+ stack_buffer_free(values1);
+ stack_buffer_free(nulls1);
+ stack_buffer_free(values2);
+ stack_buffer_free(nulls2);
ReleaseTupleDesc(tupdesc1);
ReleaseTupleDesc(tupdesc2);
@@ -1089,6 +1100,8 @@ record_eq(PG_FUNCTION_ARGS)
int i2;
int j;
+ DECLARE_STACK_BUFFER();
+
check_stack_depth(); /* recurses for record-type columns */
/* Extract type info from the tuples */
@@ -1145,11 +1158,11 @@ record_eq(PG_FUNCTION_ARGS)
}
/* Break down the tuples into fields */
- values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
- nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
+ values1 = stack_buffer_alloc_array(Datum, ncolumns1);
+ nulls1 = stack_buffer_alloc_array(bool, ncolumns1);
heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
- values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
- nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
+ values2 = stack_buffer_alloc_array(Datum, ncolumns2);
+ nulls2 = stack_buffer_alloc_array(bool, ncolumns2);
heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
/*
@@ -1265,10 +1278,10 @@ record_eq(PG_FUNCTION_ARGS)
errmsg("cannot compare record types with different numbers of columns")));
}
- pfree(values1);
- pfree(nulls1);
- pfree(values2);
- pfree(nulls2);
+ stack_buffer_free(values1);
+ stack_buffer_free(nulls1);
+ stack_buffer_free(values2);
+ stack_buffer_free(nulls2);
ReleaseTupleDesc(tupdesc1);
ReleaseTupleDesc(tupdesc2);
@@ -1371,6 +1384,8 @@ record_image_cmp(FunctionCallInfo fcinfo)
int i2;
int j;
+ DECLARE_STACK_BUFFER();
+
/* Extract type info from the tuples */
tupType1 = HeapTupleHeaderGetTypeId(record1);
tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
@@ -1425,11 +1440,11 @@ record_image_cmp(FunctionCallInfo fcinfo)
}
/* Break down the tuples into fields */
- values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
- nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
+ values1 = stack_buffer_alloc_array(Datum, ncolumns1);
+ nulls1 = stack_buffer_alloc_array(bool, ncolumns1);
heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
- values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
- nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
+ values2 = stack_buffer_alloc_array(Datum, ncolumns2);
+ nulls2 = stack_buffer_alloc_array(bool, ncolumns2);
heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
/*
@@ -1568,10 +1583,10 @@ record_image_cmp(FunctionCallInfo fcinfo)
errmsg("cannot compare record types with different numbers of columns")));
}
- pfree(values1);
- pfree(nulls1);
- pfree(values2);
- pfree(nulls2);
+ stack_buffer_free(values1);
+ stack_buffer_free(nulls1);
+ stack_buffer_free(values2);
+ stack_buffer_free(nulls2);
ReleaseTupleDesc(tupdesc1);
ReleaseTupleDesc(tupdesc2);
@@ -1617,6 +1632,8 @@ record_image_eq(PG_FUNCTION_ARGS)
int i2;
int j;
+ DECLARE_STACK_BUFFER();
+
/* Extract type info from the tuples */
tupType1 = HeapTupleHeaderGetTypeId(record1);
tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
@@ -1671,11 +1688,11 @@ record_image_eq(PG_FUNCTION_ARGS)
}
/* Break down the tuples into fields */
- values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
- nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
+ values1 = stack_buffer_alloc_array(Datum, ncolumns1);
+ nulls1 = stack_buffer_alloc_array(bool, ncolumns1);
heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
- values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
- nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
+ values2 = stack_buffer_alloc_array(Datum, ncolumns2);
+ nulls2 = stack_buffer_alloc_array(bool, ncolumns2);
heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
/*
@@ -1753,10 +1770,10 @@ record_image_eq(PG_FUNCTION_ARGS)
errmsg("cannot compare record types with different numbers of columns")));
}
- pfree(values1);
- pfree(nulls1);
- pfree(values2);
- pfree(nulls2);
+ stack_buffer_free(values1);
+ stack_buffer_free(nulls1);
+ stack_buffer_free(values2);
+ stack_buffer_free(nulls2);
ReleaseTupleDesc(tupdesc1);
ReleaseTupleDesc(tupdesc2);
@@ -1822,6 +1839,8 @@ hash_record(PG_FUNCTION_ARGS)
Datum *values;
bool *nulls;
+ DECLARE_STACK_BUFFER();
+
check_stack_depth(); /* recurses for record-type columns */
/* Extract type info from tuple */
@@ -1863,8 +1882,8 @@ hash_record(PG_FUNCTION_ARGS)
}
/* Break down the tuple into fields */
- values = palloc_array(Datum, ncolumns);
- nulls = palloc_array(bool, ncolumns);
+ values = stack_buffer_alloc_array(Datum, ncolumns);
+ nulls = stack_buffer_alloc_array(bool, ncolumns);
heap_deform_tuple(&tuple, tupdesc, values, nulls);
for (int i = 0; i < ncolumns; i++)
@@ -1918,8 +1937,8 @@ hash_record(PG_FUNCTION_ARGS)
result = (result << 5) - result + element_hash;
}
- pfree(values);
- pfree(nulls);
+ stack_buffer_free(values);
+ stack_buffer_free(nulls);
ReleaseTupleDesc(tupdesc);
/* Avoid leaking memory when handed toasted input. */
@@ -1943,6 +1962,8 @@ hash_record_extended(PG_FUNCTION_ARGS)
Datum *values;
bool *nulls;
+ DECLARE_STACK_BUFFER();
+
check_stack_depth(); /* recurses for record-type columns */
/* Extract type info from tuple */
@@ -1984,8 +2005,8 @@ hash_record_extended(PG_FUNCTION_ARGS)
}
/* Break down the tuple into fields */
- values = palloc_array(Datum, ncolumns);
- nulls = palloc_array(bool, ncolumns);
+ values = stack_buffer_alloc_array(Datum, ncolumns);
+ nulls = stack_buffer_alloc_array(bool, ncolumns);
heap_deform_tuple(&tuple, tupdesc, values, nulls);
for (int i = 0; i < ncolumns; i++)
@@ -2041,8 +2062,8 @@ hash_record_extended(PG_FUNCTION_ARGS)
result = (result << 5) - result + element_hash;
}
- pfree(values);
- pfree(nulls);
+ stack_buffer_free(values);
+ stack_buffer_free(nulls);
ReleaseTupleDesc(tupdesc);
/* Avoid leaking memory when handed toasted input. */
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 6298a37f88e..8e87d37058d 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -67,6 +67,7 @@
#include "utils/rel.h"
#include "utils/ruleutils.h"
#include "utils/snapmgr.h"
+#include "utils/stack_buffer.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
#include "utils/varlena.h"
@@ -1298,6 +1299,8 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
char *str;
char *sep;
+ DECLARE_STACK_BUFFER();
+
/*
* Fetch the pg_index tuple by the Oid of the index
*/
@@ -1358,9 +1361,9 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
exprsDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
Anum_pg_index_indexprs);
- exprsString = TextDatumGetCString(exprsDatum);
+ exprsString = stack_buffer_text_datum_to_cstring(exprsDatum);
indexprs = (List *) stringToNode(exprsString);
- pfree(exprsString);
+ stack_buffer_free(exprsString);
}
else
indexprs = NIL;
@@ -1554,9 +1557,9 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
/* Convert text string to node tree */
predDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
Anum_pg_index_indpred);
- predString = TextDatumGetCString(predDatum);
+ predString = stack_buffer_text_datum_to_cstring(predDatum);
node = (Node *) stringToNode(predString);
- pfree(predString);
+ stack_buffer_free(predString);
/* Deparse */
str = deparse_expression_pretty(node, context, false, false,
@@ -1671,6 +1674,8 @@ pg_get_statisticsobj_worker(Oid statextid, bool columns_only, bool missing_ok)
bool has_exprs;
int ncolumns;
+ DECLARE_STACK_BUFFER();
+
statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
if (!HeapTupleIsValid(statexttup))
@@ -1697,9 +1702,9 @@ pg_get_statisticsobj_worker(Oid statextid, bool columns_only, bool missing_ok)
exprsDatum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
Anum_pg_statistic_ext_stxexprs);
- exprsString = TextDatumGetCString(exprsDatum);
+ exprsString = stack_buffer_text_datum_to_cstring(exprsDatum);
exprs = (List *) stringToNode(exprsString);
- pfree(exprsString);
+ stack_buffer_free(exprsString);
}
else
exprs = NIL;
@@ -1848,6 +1853,8 @@ pg_get_statisticsobjdef_expressions(PG_FUNCTION_ARGS)
char *tmp;
ArrayBuildState *astate = NULL;
+ DECLARE_STACK_BUFFER();
+
statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
if (!HeapTupleIsValid(statexttup))
@@ -1870,9 +1877,9 @@ pg_get_statisticsobjdef_expressions(PG_FUNCTION_ARGS)
*/
datum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
Anum_pg_statistic_ext_stxexprs);
- tmp = TextDatumGetCString(datum);
+ tmp = stack_buffer_text_datum_to_cstring(datum);
exprs = (List *) stringToNode(tmp);
- pfree(tmp);
+ stack_buffer_free(tmp);
context = deparse_context_for(get_relation_name(statextrec->stxrelid),
statextrec->stxrelid);
@@ -1950,6 +1957,8 @@ pg_get_partkeydef_worker(Oid relid, int prettyFlags,
char *str;
char *sep;
+ DECLARE_STACK_BUFFER();
+
tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(relid));
if (!HeapTupleIsValid(tuple))
{
@@ -1984,14 +1993,14 @@ pg_get_partkeydef_worker(Oid relid, int prettyFlags,
exprsDatum = SysCacheGetAttrNotNull(PARTRELID, tuple,
Anum_pg_partitioned_table_partexprs);
- exprsString = TextDatumGetCString(exprsDatum);
+ exprsString = stack_buffer_text_datum_to_cstring(exprsDatum);
partexprs = (List *) stringToNode(exprsString);
if (!IsA(partexprs, List))
elog(ERROR, "unexpected node type found in partexprs: %d",
(int) nodeTag(partexprs));
- pfree(exprsString);
+ stack_buffer_free(exprsString);
}
else
partexprs = NIL;
@@ -2717,13 +2726,15 @@ pg_get_expr_worker(text *expr, Oid relid, int prettyFlags)
Relation rel = NULL;
char *str;
+ DECLARE_STACK_BUFFER();
+
/* Convert input pg_node_tree (really TEXT) object to C string */
- exprstr = text_to_cstring(expr);
+ exprstr = stack_buffer_text_to_cstring(expr);
/* Convert expression to node tree */
node = (Node *) stringToNode(exprstr);
- pfree(exprstr);
+ stack_buffer_free(exprstr);
/*
* Throw error if the input is a querytree rather than an expression tree.
@@ -3315,6 +3326,8 @@ print_function_arguments(StringInfo buf, HeapTuple proctup,
ListCell *nextargdefault = NULL;
int i;
+ DECLARE_STACK_BUFFER();
+
numargs = get_func_arg_info(proctup,
&argtypes, &argnames, &argmodes);
@@ -3331,9 +3344,9 @@ print_function_arguments(StringInfo buf, HeapTuple proctup,
{
char *str;
- str = TextDatumGetCString(proargdefaults);
+ str = stack_buffer_text_datum_to_cstring(proargdefaults);
argdefaults = castNode(List, stringToNode(str));
- pfree(str);
+ stack_buffer_free(str);
nextargdefault = list_head(argdefaults);
/* nlackdefaults counts only *input* arguments lacking defaults */
nlackdefaults = proc->pronargs - list_length(argdefaults);
@@ -3506,6 +3519,8 @@ pg_get_function_arg_default(PG_FUNCTION_ARGS)
bool isnull;
int nth_default;
+ DECLARE_STACK_BUFFER();
+
proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(proctup))
PG_RETURN_NULL();
@@ -3531,9 +3546,9 @@ pg_get_function_arg_default(PG_FUNCTION_ARGS)
PG_RETURN_NULL();
}
- str = TextDatumGetCString(proargdefaults);
+ str = stack_buffer_text_datum_to_cstring(proargdefaults);
argdefaults = castNode(List, stringToNode(str));
- pfree(str);
+ stack_buffer_free(str);
proc = (Form_pg_proc) GETSTRUCT(proctup);
diff --git a/src/backend/utils/adt/tsvector_op.c b/src/backend/utils/adt/tsvector_op.c
index 71c7c7d3b3c..86c29f730ba 100644
--- a/src/backend/utils/adt/tsvector_op.c
+++ b/src/backend/utils/adt/tsvector_op.c
@@ -31,6 +31,7 @@
#include "utils/builtins.h"
#include "utils/regproc.h"
#include "utils/rel.h"
+#include "utils/stack_buffer.h"
typedef struct
@@ -2537,6 +2538,8 @@ ts_process_call(FuncCallContext *funcctx)
TSVectorStat *st;
StatEntry *entry;
+ DECLARE_STACK_BUFFER();
+
st = (TSVectorStat *) funcctx->user_fctx;
entry = walkStatEntryTree(st);
@@ -2549,9 +2552,8 @@ ts_process_call(FuncCallContext *funcctx)
char nentry[16];
HeapTuple tuple;
- values[0] = palloc(entry->lenlexeme + 1);
- memcpy(values[0], entry->lexeme, entry->lenlexeme);
- (values[0])[entry->lenlexeme] = '\0';
+ values[0] = stack_buffer_strdup_with_len(entry->lexeme,
+ entry->lenlexeme);
sprintf(ndoc, "%d", entry->ndoc);
values[1] = ndoc;
sprintf(nentry, "%d", entry->nentry);
@@ -2560,7 +2562,7 @@ ts_process_call(FuncCallContext *funcctx)
tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
result = HeapTupleGetDatum(tuple);
- pfree(values[0]);
+ stack_buffer_free(values[0]);
/* mark entry as already visited */
entry->ndoc = 0;
--
2.53.0
[text/x-patch] v2-0009-Use-stack-buffer-in-some-access-method-code.patch (17.1K, 11-v2-0009-Use-stack-buffer-in-some-access-method-code.patch)
download | inline diff:
From 943060f91d3edcb1c94837a09dfb5566bf2daded Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Mon, 2 Mar 2026 04:15:05 +1300
Subject: [PATCH v2 09/19] Use stack buffer in some access method code.
Mechanical changes including:
* temporary strings
* temporary datum, null, key lists etc
* WAL construction
---
src/backend/access/brin/brin_tuple.c | 19 +++++++++------
src/backend/access/common/heaptuple.c | 21 ++++++++++-------
src/backend/access/gin/ginscan.c | 7 ++++--
src/backend/access/hash/hashfunc.c | 12 ++++++----
src/backend/access/heap/heapam.c | 12 ++++++----
src/backend/access/heap/heapam_handler.c | 11 +++++----
src/backend/access/index/genam.c | 23 ++++++++++++-------
src/backend/access/nbtree/nbtpreprocesskeys.c | 23 +++++++++++--------
src/backend/access/nbtree/nbtxlog.c | 9 +++++---
9 files changed, 87 insertions(+), 50 deletions(-)
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index 69c233c62eb..13ff1d3f5d6 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -40,6 +40,7 @@
#include "access/tupmacs.h"
#include "utils/datum.h"
#include "utils/memutils.h"
+#include "utils/stack_buffer.h"
/*
@@ -117,14 +118,16 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
int nuntoasted = 0;
#endif
+ DECLARE_STACK_BUFFER();
+
Assert(brdesc->bd_totalstored > 0);
- values = palloc_array(Datum, brdesc->bd_totalstored);
- nulls = palloc0_array(bool, brdesc->bd_totalstored);
- phony_nullbitmap = palloc_array(bits8, BITMAPLEN(brdesc->bd_totalstored));
+ values = stack_buffer_alloc_array(Datum, brdesc->bd_totalstored);
+ nulls = stack_buffer_alloc0_array(bool, brdesc->bd_totalstored);
+ phony_nullbitmap = stack_buffer_alloc_array(bits8, BITMAPLEN(brdesc->bd_totalstored));
#ifdef TOAST_INDEX_HACK
- untoasted_values = palloc_array(Datum, brdesc->bd_totalstored);
+ untoasted_values = stack_buffer_alloc_array(Datum, brdesc->bd_totalstored);
#endif
/*
@@ -307,13 +310,15 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
phony_nullbitmap);
/* done with these */
- pfree(values);
- pfree(nulls);
- pfree(phony_nullbitmap);
+ stack_buffer_free(values);
+ stack_buffer_free(nulls);
+ stack_buffer_free(phony_nullbitmap);
#ifdef TOAST_INDEX_HACK
for (i = 0; i < nuntoasted; i++)
pfree(DatumGetPointer(untoasted_values[i]));
+
+ stack_buffer_free(untoasted_values);
#endif
/*
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 11bec20e82e..12f820056ba 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -65,6 +65,7 @@
#include "utils/expandeddatum.h"
#include "utils/hsearch.h"
#include "utils/memutils.h"
+#include "utils/stack_buffer.h"
/*
@@ -1219,6 +1220,8 @@ heap_modify_tuple(HeapTuple tuple,
bool *isnull;
HeapTuple newTuple;
+ DECLARE_STACK_BUFFER();
+
/*
* allocate and fill values and isnull arrays from either the tuple or the
* repl information, as appropriate.
@@ -1230,8 +1233,8 @@ heap_modify_tuple(HeapTuple tuple,
* O(N^2) if there are many non-replaced columns, so it seems better to
* err on the side of linear cost.
*/
- values = palloc_array(Datum, numberOfAttributes);
- isnull = palloc_array(bool, numberOfAttributes);
+ values = stack_buffer_alloc_array(Datum, numberOfAttributes);
+ isnull = stack_buffer_alloc_array(bool, numberOfAttributes);
heap_deform_tuple(tuple, tupleDesc, values, isnull);
@@ -1249,8 +1252,8 @@ heap_modify_tuple(HeapTuple tuple,
*/
newTuple = heap_form_tuple(tupleDesc, values, isnull);
- pfree(values);
- pfree(isnull);
+ stack_buffer_free(values);
+ stack_buffer_free(isnull);
/*
* copy the identification info of the old tuple: t_ctid, t_self
@@ -1288,12 +1291,14 @@ heap_modify_tuple_by_cols(HeapTuple tuple,
HeapTuple newTuple;
int i;
+ DECLARE_STACK_BUFFER();
+
/*
* allocate and fill values and isnull arrays from the tuple, then replace
* selected columns from the input arrays.
*/
- values = palloc_array(Datum, numberOfAttributes);
- isnull = palloc_array(bool, numberOfAttributes);
+ values = stack_buffer_alloc_array(Datum, numberOfAttributes);
+ isnull = stack_buffer_alloc_array(bool, numberOfAttributes);
heap_deform_tuple(tuple, tupleDesc, values, isnull);
@@ -1312,8 +1317,8 @@ heap_modify_tuple_by_cols(HeapTuple tuple,
*/
newTuple = heap_form_tuple(tupleDesc, values, isnull);
- pfree(values);
- pfree(isnull);
+ stack_buffer_free(values);
+ stack_buffer_free(isnull);
/*
* copy the identification info of the old tuple: t_ctid, t_self
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c
index fb929761ab7..6d3d2737080 100644
--- a/src/backend/access/gin/ginscan.c
+++ b/src/backend/access/gin/ginscan.c
@@ -20,6 +20,7 @@
#include "pgstat.h"
#include "utils/memutils.h"
#include "utils/rel.h"
+#include "utils/stack_buffer.h"
IndexScanDesc
@@ -420,10 +421,12 @@ ginNewScanKey(IndexScanDesc scan)
int iNormalKey;
int iExcludeOnly;
+ DECLARE_STACK_BUFFER();
+
/* We'd better have made at least one normal key */
Assert(numExcludeOnly < so->nkeys);
/* Make a temporary array to hold the re-ordered scan keys */
- tmpkeys = (GinScanKey) palloc(so->nkeys * sizeof(GinScanKeyData));
+ tmpkeys = stack_buffer_alloc_array(GinScanKeyData, so->nkeys);
/* Re-order the keys ... */
iNormalKey = 0;
iExcludeOnly = so->nkeys - numExcludeOnly;
@@ -446,7 +449,7 @@ ginNewScanKey(IndexScanDesc scan)
Assert(iExcludeOnly == so->nkeys);
/* ... and copy them back to so->keys[] */
memcpy(so->keys, tmpkeys, so->nkeys * sizeof(GinScanKeyData));
- pfree(tmpkeys);
+ stack_buffer_free(tmpkeys);
}
/*
diff --git a/src/backend/access/hash/hashfunc.c b/src/backend/access/hash/hashfunc.c
index 575342a21b6..cda9ce28780 100644
--- a/src/backend/access/hash/hashfunc.c
+++ b/src/backend/access/hash/hashfunc.c
@@ -31,6 +31,7 @@
#include "utils/float.h"
#include "utils/fmgrprotos.h"
#include "utils/pg_locale.h"
+#include "utils/stack_buffer.h"
#include "varatt.h"
/*
@@ -295,9 +296,10 @@ hashtext(PG_FUNCTION_ARGS)
const char *keydata = VARDATA_ANY(key);
size_t keylen = VARSIZE_ANY_EXHDR(key);
+ DECLARE_STACK_BUFFER();
bsize = pg_strnxfrm(NULL, 0, keydata, keylen, mylocale);
- buf = palloc(bsize + 1);
+ buf = stack_buffer_alloc(bsize + 1);
rsize = pg_strnxfrm(buf, bsize + 1, keydata, keylen, mylocale);
@@ -312,7 +314,7 @@ hashtext(PG_FUNCTION_ARGS)
*/
result = hash_any((uint8_t *) buf, bsize + 1);
- pfree(buf);
+ stack_buffer_free(buf);
}
/* Avoid leaking memory for toasted inputs */
@@ -351,8 +353,10 @@ hashtextextended(PG_FUNCTION_ARGS)
const char *keydata = VARDATA_ANY(key);
size_t keylen = VARSIZE_ANY_EXHDR(key);
+ DECLARE_STACK_BUFFER();
+
bsize = pg_strnxfrm(NULL, 0, keydata, keylen, mylocale);
- buf = palloc(bsize + 1);
+ buf = stack_buffer_alloc(bsize + 1);
rsize = pg_strnxfrm(buf, bsize + 1, keydata, keylen, mylocale);
@@ -368,7 +372,7 @@ hashtextextended(PG_FUNCTION_ARGS)
result = hash_any_extended((uint8_t *) buf, bsize + 1,
PG_GETARG_INT64(1));
- pfree(buf);
+ stack_buffer_free(buf);
}
PG_FREE_IF_COPY(key, 0);
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 8f1c11a9350..13171df183c 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -53,6 +53,7 @@
#include "utils/injection_point.h"
#include "utils/inval.h"
#include "utils/spccache.h"
+#include "utils/stack_buffer.h"
#include "utils/syscache.h"
@@ -8768,11 +8769,13 @@ bottomup_sort_and_shrink(TM_IndexDeleteOp *delstate)
int ncopied = 0;
int nblocksfavorable = 0;
+ DECLARE_STACK_BUFFER();
+
Assert(delstate->bottomup);
Assert(delstate->ndeltids > 0);
/* Calculate per-heap-block count of TIDs */
- blockgroups = palloc_array(IndexDeleteCounts, delstate->ndeltids);
+ blockgroups = stack_buffer_alloc_array(IndexDeleteCounts, delstate->ndeltids);
for (int i = 0; i < delstate->ndeltids; i++)
{
TM_IndexDelete *ideltid = &delstate->deltids[i];
@@ -8845,7 +8848,8 @@ bottomup_sort_and_shrink(TM_IndexDeleteOp *delstate)
/* Sort groups and rearrange caller's deltids array */
qsort(blockgroups, nblockgroups, sizeof(IndexDeleteCounts),
bottomup_sort_and_shrink_cmp);
- reordereddeltids = palloc(delstate->ndeltids * sizeof(TM_IndexDelete));
+ reordereddeltids = stack_buffer_alloc_array(TM_IndexDelete,
+ delstate->ndeltids);
nblockgroups = Min(BOTTOMUP_MAX_NBLOCKS, nblockgroups);
/* Determine number of favorable blocks at the start of final deltids */
@@ -8867,8 +8871,8 @@ bottomup_sort_and_shrink(TM_IndexDeleteOp *delstate)
sizeof(TM_IndexDelete) * ncopied);
delstate->ndeltids = ncopied;
- pfree(reordereddeltids);
- pfree(blockgroups);
+ stack_buffer_free(reordereddeltids);
+ stack_buffer_free(blockgroups);
return nblocksfavorable;
}
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index 5137d2510ea..cfb06ab82ac 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -45,6 +45,7 @@
#include "storage/smgr.h"
#include "utils/builtins.h"
#include "utils/rel.h"
+#include "utils/stack_buffer.h"
static void reform_and_rewrite_tuple(HeapTuple tuple,
Relation OldHeap, Relation NewHeap,
@@ -706,6 +707,8 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap,
BufferHeapTupleTableSlot *hslot;
BlockNumber prev_cblock = InvalidBlockNumber;
+ DECLARE_STACK_BUFFER();
+
/* Remember if it's a system catalog */
is_system_catalog = IsSystemRelation(OldHeap);
@@ -717,8 +720,8 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap,
/* Preallocate values/isnull arrays */
natts = newTupDesc->natts;
- values = palloc_array(Datum, natts);
- isnull = palloc_array(bool, natts);
+ values = stack_buffer_alloc_array(Datum, natts);
+ isnull = stack_buffer_alloc_array(bool, natts);
/* Initialize the rewrite operation */
rwstate = begin_heap_rewrite(OldHeap, NewHeap, OldestXmin, *xid_cutoff,
@@ -1002,8 +1005,8 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap,
end_heap_rewrite(rwstate);
/* Clean up */
- pfree(values);
- pfree(isnull);
+ stack_buffer_free(values);
+ stack_buffer_free(isnull);
}
/*
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 5e89b86a62c..30df0b8819b 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -36,6 +36,7 @@
#include "utils/rls.h"
#include "utils/ruleutils.h"
#include "utils/snapmgr.h"
+#include "utils/stack_buffer.h"
/* ----------------------------------------------------------------
@@ -303,6 +304,8 @@ index_compute_xid_horizon_for_tuples(Relation irel,
Page ipage = BufferGetPage(ibuf);
IndexTuple itup;
+ DECLARE_STACK_BUFFER();
+
Assert(nitems > 0);
delstate.irel = irel;
@@ -310,8 +313,8 @@ index_compute_xid_horizon_for_tuples(Relation irel,
delstate.bottomup = false;
delstate.bottomupfreespace = 0;
delstate.ndeltids = 0;
- delstate.deltids = palloc_array(TM_IndexDelete, nitems);
- delstate.status = palloc_array(TM_IndexStatus, nitems);
+ delstate.deltids = stack_buffer_alloc_array(TM_IndexDelete, nitems);
+ delstate.status = stack_buffer_alloc_array(TM_IndexStatus, nitems);
/* identify what the index tuples about to be deleted point to */
for (int i = 0; i < nitems; i++)
@@ -340,8 +343,8 @@ index_compute_xid_horizon_for_tuples(Relation irel,
/* assert tableam agrees that all items are deletable */
Assert(delstate.ndeltids == nitems);
- pfree(delstate.deltids);
- pfree(delstate.status);
+ stack_buffer_free(delstate.deltids);
+ stack_buffer_free(delstate.status);
return snapshotConflictHorizon;
}
@@ -433,7 +436,9 @@ systable_beginscan(Relation heapRelation,
int i;
ScanKey idxkey;
- idxkey = palloc_array(ScanKeyData, nkeys);
+ DECLARE_STACK_BUFFER();
+
+ idxkey = stack_buffer_alloc_array(ScanKeyData, nkeys);
/* Convert attribute numbers to be index column numbers. */
for (i = 0; i < nkeys; i++)
@@ -459,7 +464,7 @@ systable_beginscan(Relation heapRelation,
index_rescan(sysscan->iscan, idxkey, nkeys, NULL, 0);
sysscan->scan = NULL;
- pfree(idxkey);
+ stack_buffer_free(idxkey);
}
else
{
@@ -656,6 +661,8 @@ systable_beginscan_ordered(Relation heapRelation,
int i;
ScanKey idxkey;
+ DECLARE_STACK_BUFFER();
+
/* REINDEX can probably be a hard error here ... */
if (ReindexIsProcessingIndex(RelationGetRelid(indexRelation)))
ereport(ERROR,
@@ -686,7 +693,7 @@ systable_beginscan_ordered(Relation heapRelation,
sysscan->snapshot = NULL;
}
- idxkey = palloc_array(ScanKeyData, nkeys);
+ idxkey = stack_buffer_alloc_array(ScanKeyData, nkeys);
/* Convert attribute numbers to be index column numbers. */
for (i = 0; i < nkeys; i++)
@@ -720,7 +727,7 @@ systable_beginscan_ordered(Relation heapRelation,
index_rescan(sysscan->iscan, idxkey, nkeys, NULL, 0);
sysscan->scan = NULL;
- pfree(idxkey);
+ stack_buffer_free(idxkey);
return sysscan;
}
diff --git a/src/backend/access/nbtree/nbtpreprocesskeys.c b/src/backend/access/nbtree/nbtpreprocesskeys.c
index 39c0a5d610f..b62e6892345 100644
--- a/src/backend/access/nbtree/nbtpreprocesskeys.c
+++ b/src/backend/access/nbtree/nbtpreprocesskeys.c
@@ -23,6 +23,7 @@
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
+#include "utils/stack_buffer.h"
typedef struct BTScanKeyPreproc
{
@@ -1557,6 +1558,8 @@ _bt_unmark_keys(IndexScanDesc scan, int *keyDataMap)
haveReqForward,
haveReqBackward;
+ DECLARE_STACK_BUFFER();
+
/*
* Do an initial pass over so->keyData[] that determines which keys to
* keep as required. We expect so->keyData[] to still be in attribute
@@ -1568,7 +1571,7 @@ _bt_unmark_keys(IndexScanDesc scan, int *keyDataMap)
* Any requiredness markings that we might leave on later keys/attributes
* are predicated on there being required = keys on all prior columns.
*/
- unmarkikey = palloc0(so->numberOfKeys * sizeof(bool));
+ unmarkikey = stack_buffer_alloc0_array(bool, so->numberOfKeys);
nunmark = 0;
/* Set things up for first key's attribute */
@@ -1653,14 +1656,14 @@ _bt_unmark_keys(IndexScanDesc scan, int *keyDataMap)
* Next, allocate temp arrays: one for required keys that'll remain
* required, the other for all remaining keys
*/
- unmarkKeys = palloc(nunmark * sizeof(ScanKeyData));
- keepKeys = palloc((so->numberOfKeys - nunmark) * sizeof(ScanKeyData));
+ unmarkKeys = stack_buffer_alloc_array(ScanKeyData, nunmark);
+ keepKeys = stack_buffer_alloc_array(ScanKeyData, so->numberOfKeys - nunmark);
nunmarked = 0;
nkept = 0;
if (so->numArrayKeys)
{
- unmarkOrderProcs = palloc(nunmark * sizeof(FmgrInfo));
- keepOrderProcs = palloc((so->numberOfKeys - nunmark) * sizeof(FmgrInfo));
+ unmarkOrderProcs = stack_buffer_alloc_array(FmgrInfo, nunmark);
+ keepOrderProcs = stack_buffer_alloc_array(FmgrInfo, so->numberOfKeys - nunmark);
}
/*
@@ -1751,9 +1754,9 @@ _bt_unmark_keys(IndexScanDesc scan, int *keyDataMap)
memcpy(so->keyData + nkept, unmarkKeys, sizeof(ScanKeyData) * nunmarked);
/* Done with temp arrays */
- pfree(unmarkikey);
- pfree(keepKeys);
- pfree(unmarkKeys);
+ stack_buffer_free(unmarkikey);
+ stack_buffer_free(keepKeys);
+ stack_buffer_free(unmarkKeys);
/*
* Now copy so->orderProcs[] temp entries needed by scans with = array
@@ -1781,8 +1784,8 @@ _bt_unmark_keys(IndexScanDesc scan, int *keyDataMap)
_bt_reorder_array_cmp);
/* Done with temp arrays */
- pfree(unmarkOrderProcs);
- pfree(keepOrderProcs);
+ stack_buffer_free(unmarkOrderProcs);
+ stack_buffer_free(keepOrderProcs);
}
}
diff --git a/src/backend/access/nbtree/nbtxlog.c b/src/backend/access/nbtree/nbtxlog.c
index dff7d286fc8..14bd0fd2fd3 100644
--- a/src/backend/access/nbtree/nbtxlog.c
+++ b/src/backend/access/nbtree/nbtxlog.c
@@ -21,6 +21,7 @@
#include "access/xlogutils.h"
#include "storage/standby.h"
#include "utils/memutils.h"
+#include "utils/stack_buffer.h"
static MemoryContext opCtx; /* working memory for operations */
@@ -551,13 +552,15 @@ btree_xlog_updates(Page page, OffsetNumber *updatedoffsets,
ItemId itemid;
Size itemsz;
+ DECLARE_STACK_BUFFER();
+
for (int i = 0; i < nupdated; i++)
{
itemid = PageGetItemId(page, updatedoffsets[i]);
origtuple = (IndexTuple) PageGetItem(page, itemid);
- vacposting = palloc(offsetof(BTVacuumPostingData, deletetids) +
- updates->ndeletedtids * sizeof(uint16));
+ vacposting = stack_buffer_alloc(offsetof(BTVacuumPostingData, deletetids) +
+ updates->ndeletedtids * sizeof(uint16));
vacposting->updatedoffset = updatedoffsets[i];
vacposting->itup = origtuple;
vacposting->ndeletedtids = updates->ndeletedtids;
@@ -573,7 +576,7 @@ btree_xlog_updates(Page page, OffsetNumber *updatedoffsets,
elog(PANIC, "failed to update partially dead item");
pfree(vacposting->itup);
- pfree(vacposting);
+ stack_buffer_free(vacposting);
/* advance to next xl_btree_update from array */
updates = (xl_btree_update *)
--
2.53.0
[text/x-patch] v2-0010-Use-stack_buffer-in-LLVM-code.patch (3.7K, 12-v2-0010-Use-stack_buffer-in-LLVM-code.patch)
download | inline diff:
From ced3e5fd1809e38986e769384f13c2b689cd5333 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Tue, 10 Mar 2026 17:35:17 +1300
Subject: [PATCH v2 10/19] Use stack_buffer in LLVM code.
---
src/backend/jit/llvm/llvmjit.c | 21 ++++++++++++++-------
src/backend/jit/llvm/llvmjit_expr.c | 7 +++++--
2 files changed, 19 insertions(+), 9 deletions(-)
diff --git a/src/backend/jit/llvm/llvmjit.c b/src/backend/jit/llvm/llvmjit.c
index 2e8aa4749db..957241c64b5 100644
--- a/src/backend/jit/llvm/llvmjit.c
+++ b/src/backend/jit/llvm/llvmjit.c
@@ -41,6 +41,7 @@
#include "storage/ipc.h"
#include "utils/memutils.h"
#include "utils/resowner.h"
+#include "utils/stack_buffer.h"
#define LLVMJIT_LLVM_CONTEXT_REUSE_MAX 100
@@ -495,18 +496,20 @@ llvm_copy_attributes_at_index(LLVMValueRef v_from, LLVMValueRef v_to, uint32 ind
int num_attributes;
LLVMAttributeRef *attrs;
+ DECLARE_STACK_BUFFER();
+
num_attributes = LLVMGetAttributeCountAtIndex(v_from, index);
if (num_attributes == 0)
return;
- attrs = palloc_array(LLVMAttributeRef, num_attributes);
+ attrs = stack_buffer_alloc_array(LLVMAttributeRef, num_attributes);
LLVMGetAttributesAtIndex(v_from, index, attrs);
for (int attno = 0; attno < num_attributes; attno++)
LLVMAddAttributeAtIndex(v_to, index, attrs[attno]);
- pfree(attrs);
+ stack_buffer_free(attrs);
}
/*
@@ -1126,13 +1129,17 @@ llvm_resolve_symbols(LLVMOrcDefinitionGeneratorRef GeneratorObj, void *Ctx,
LLVMOrcJITDylibRef JD, LLVMOrcJITDylibLookupFlags JDLookupFlags,
LLVMOrcCLookupSet LookupSet, size_t LookupSetSize)
{
+ LLVMErrorRef error;
+ LLVMOrcMaterializationUnitRef mu;
+ LLVMOrcCSymbolMapPairs symbols;
+
+ DECLARE_STACK_BUFFER();
+
#if LLVM_VERSION_MAJOR > 14
- LLVMOrcCSymbolMapPairs symbols = palloc0_array(LLVMOrcCSymbolMapPair, LookupSetSize);
+ symbols = stack_buffer_alloc0_array(LLVMOrcCSymbolMapPair, LookupSetSize);
#else
- LLVMOrcCSymbolMapPairs symbols = palloc0_array(LLVMJITCSymbolMapPair, LookupSetSize);
+ symbols = stack_buffer_alloc0_array(LLVMJITCSymbolMapPair, LookupSetSize);
#endif
- LLVMErrorRef error;
- LLVMOrcMaterializationUnitRef mu;
for (int i = 0; i < LookupSetSize; i++)
{
@@ -1149,7 +1156,7 @@ llvm_resolve_symbols(LLVMOrcDefinitionGeneratorRef GeneratorObj, void *Ctx,
if (error != LLVMErrorSuccess)
LLVMOrcDisposeMaterializationUnit(mu);
- pfree(symbols);
+ stack_buffer_free(symbols);
return error;
}
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index 650f1d42a93..411e4746ee9 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -41,6 +41,7 @@
#include "utils/fmgrtab.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
+#include "utils/stack_buffer.h"
#include "utils/timestamp.h"
#include "utils/typcache.h"
#include "utils/xml.h"
@@ -3063,12 +3064,14 @@ build_EvalXFuncInt(LLVMBuilderRef b, LLVMModuleRef mod, const char *funcname,
int argno = 0;
LLVMValueRef v_ret;
+ DECLARE_STACK_BUFFER();
+
/* cheap pre-check as llvm just asserts out */
if (LLVMCountParams(v_fn) != (nargs + 2))
elog(ERROR, "parameter mismatch: %s expects %d passed %d",
funcname, LLVMCountParams(v_fn), nargs + 2);
- params = palloc_array(LLVMValueRef, (2 + nargs));
+ params = stack_buffer_alloc_array(LLVMValueRef, (2 + nargs));
params[argno++] = v_state;
params[argno++] = l_ptr_const(op, l_ptr(StructExprEvalStep));
@@ -3078,7 +3081,7 @@ build_EvalXFuncInt(LLVMBuilderRef b, LLVMModuleRef mod, const char *funcname,
v_ret = l_call(b, LLVMGetFunctionType(v_fn), v_fn, params, argno, "");
- pfree(params);
+ stack_buffer_free(params);
return v_ret;
}
--
2.53.0
[text/x-patch] v2-0011-Use-stack-buffer-in-transam-xact.c.patch (2.5K, 13-v2-0011-Use-stack-buffer-in-transam-xact.c.patch)
download | inline diff:
From 4a3c54cadd459d4ac6b65e7b323775d84714649b Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Tue, 10 Mar 2026 17:37:29 +1300
Subject: [PATCH v2 11/19] Use stack buffer in transam/xact.c.
---
src/backend/access/transam/xact.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index aafc53e0164..91adced5fb9 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -69,6 +69,7 @@
#include "utils/memutils.h"
#include "utils/relmapper.h"
#include "utils/snapmgr.h"
+#include "utils/stack_buffer.h"
#include "utils/timeout.h"
#include "utils/timestamp.h"
#include "utils/typcache.h"
@@ -640,6 +641,8 @@ AssignTransactionId(TransactionState s)
ResourceOwner currentOwner;
bool log_unknown_top = false;
+ DECLARE_STACK_BUFFER();
+
/* Assert that caller didn't screw up */
Assert(!FullTransactionIdIsValid(s->fullTransactionId));
Assert(s->state == TRANS_INPROGRESS);
@@ -665,7 +668,7 @@ AssignTransactionId(TransactionState s)
TransactionState *parents;
size_t parentOffset = 0;
- parents = palloc_array(TransactionState, s->nestingLevel);
+ parents = stack_buffer_alloc_array(TransactionState, s->nestingLevel);
while (p != NULL && !FullTransactionIdIsValid(p->fullTransactionId))
{
parents[parentOffset++] = p;
@@ -679,7 +682,7 @@ AssignTransactionId(TransactionState s)
while (parentOffset != 0)
AssignTransactionId(parents[--parentOffset]);
- pfree(parents);
+ stack_buffer_free(parents);
}
/*
@@ -5568,6 +5571,8 @@ SerializeTransactionState(Size maxsize, char *start_address)
TransactionId *workspace;
SerializedTransactionState *result;
+ DECLARE_STACK_BUFFER();
+
result = (SerializedTransactionState *) start_address;
result->xactIsoLevel = XactIsoLevel;
@@ -5604,7 +5609,7 @@ SerializeTransactionState(Size maxsize, char *start_address)
<= maxsize);
/* Copy them to our scratch space. */
- workspace = palloc(nxids * sizeof(TransactionId));
+ workspace = stack_buffer_alloc_array(TransactionId, nxids);
for (s = CurrentTransactionState; s != NULL; s = s->parent)
{
if (FullTransactionIdIsValid(s->fullTransactionId))
@@ -5623,6 +5628,8 @@ SerializeTransactionState(Size maxsize, char *start_address)
result->nParallelCurrentXids = nxids;
memcpy(&result->parallelCurrentXids[0], workspace,
nxids * sizeof(TransactionId));
+
+ stack_buffer_free(workspace);
}
/*
--
2.53.0
[text/x-patch] v2-0012-Use-stack-buffer-in-a-few-places-in-the-parser.patch (4.0K, 14-v2-0012-Use-stack-buffer-in-a-few-places-in-the-parser.patch)
download | inline diff:
From e07593473fad042cfa9da6f5b565443728af7656 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Tue, 10 Mar 2026 17:41:49 +1300
Subject: [PATCH v2 12/19] Use stack buffer in a few places in the parser.
Mechanical C string conversions and a small temporary array.
---
src/backend/parser/parse_clause.c | 7 +++++--
src/backend/parser/parse_func.c | 7 +++++--
src/backend/parser/parse_type.c | 7 +++++--
3 files changed, 15 insertions(+), 6 deletions(-)
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 06b65d4a605..7360da2b04b 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -45,6 +45,7 @@
#include "utils/catcache.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
+#include "utils/stack_buffer.h"
#include "utils/syscache.h"
@@ -693,6 +694,8 @@ transformRangeTableFunc(ParseState *pstate, RangeTableFunc *rtf)
char **names;
int colno;
+ DECLARE_STACK_BUFFER();
+
/*
* Currently we only support XMLTABLE here. See transformJsonTable() for
* JSON_TABLE support.
@@ -733,7 +736,7 @@ transformRangeTableFunc(ParseState *pstate, RangeTableFunc *rtf)
tf->ordinalitycol = -1;
/* Process column specs */
- names = palloc_array(char *, list_length(rtf->columns));
+ names = stack_buffer_alloc_array(char *, list_length(rtf->columns));
colno = 0;
foreach(col, rtf->columns)
@@ -825,7 +828,7 @@ transformRangeTableFunc(ParseState *pstate, RangeTableFunc *rtf)
colno++;
}
- pfree(names);
+ stack_buffer_free(names);
/* Namespaces, if any, also need to be transformed */
if (rtf->namespaces != NIL)
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 24f6745923b..2f63ce55a23 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -32,6 +32,7 @@
#include "parser/parse_type.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
+#include "utils/stack_buffer.h"
#include "utils/syscache.h"
@@ -1759,15 +1760,17 @@ func_get_detail(List *funcname,
char *str;
List *defaults;
+ DECLARE_STACK_BUFFER();
+
/* shouldn't happen, FuncnameGetCandidates messed up */
if (best_candidate->ndargs > pform->pronargdefaults)
elog(ERROR, "not enough default arguments");
proargdefaults = SysCacheGetAttrNotNull(PROCOID, ftup,
Anum_pg_proc_proargdefaults);
- str = TextDatumGetCString(proargdefaults);
+ str = stack_buffer_text_datum_to_cstring(proargdefaults);
defaults = castNode(List, stringToNode(str));
- pfree(str);
+ stack_buffer_free(str);
/* Delete any unused defaults from the returned list */
if (best_candidate->argnumbers != NULL)
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index bb7eccde9fd..35234331070 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -24,6 +24,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
+#include "utils/stack_buffer.h"
#include "utils/syscache.h"
static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName,
@@ -339,6 +340,8 @@ typenameTypeMod(ParseState *pstate, const TypeName *typeName, Type typ)
ArrayType *arrtypmod;
ParseCallbackState pcbstate;
+ DECLARE_STACK_BUFFER();
+
/* Return prespecified typmod if no typmod expressions */
if (typeName->typmods == NIL)
return typeName->typemod;
@@ -369,7 +372,7 @@ typenameTypeMod(ParseState *pstate, const TypeName *typeName, Type typ)
* Currently, we allow simple numeric constants, string literals, and
* identifiers; possibly this list could be extended.
*/
- datums = palloc_array(Datum, list_length(typeName->typmods));
+ datums = stack_buffer_alloc_array(Datum, list_length(typeName->typmods));
n = 0;
foreach(l, typeName->typmods)
{
@@ -421,7 +424,7 @@ typenameTypeMod(ParseState *pstate, const TypeName *typeName, Type typ)
cancel_parser_errposition_callback(&pcbstate);
- pfree(datums);
+ stack_buffer_free(datums);
pfree(arrtypmod);
return result;
--
2.53.0
[text/x-patch] v2-0013-Use-stack-buffer-in-fmgr.c.patch (3.0K, 15-v2-0013-Use-stack-buffer-in-fmgr.c.patch)
download | inline diff:
From 674b354dc120e3fd938c2b9f6f8a8dbb364c8ed7 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Tue, 10 Mar 2026 17:42:46 +1300
Subject: [PATCH v2 13/19] Use stack buffer in fmgr.c.
Mechanical C string conversions.
---
src/backend/utils/fmgr/fmgr.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 4e26df7c63a..1614a683877 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -32,6 +32,7 @@
#include "utils/fmgrtab.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
+#include "utils/stack_buffer.h"
#include "utils/syscache.h"
/*
@@ -154,6 +155,8 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt,
Datum prosrcdatum;
char *prosrc;
+ DECLARE_STACK_BUFFER();
+
/*
* fn_oid *must* be filled in last. Some code assumes that if fn_oid is
* valid, the whole struct is valid. Some FmgrInfo struct's do survive
@@ -229,14 +232,14 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt,
*/
prosrcdatum = SysCacheGetAttrNotNull(PROCOID, procedureTuple,
Anum_pg_proc_prosrc);
- prosrc = TextDatumGetCString(prosrcdatum);
+ prosrc = stack_buffer_text_datum_to_cstring(prosrcdatum);
fbp = fmgr_lookupByName(prosrc);
if (fbp == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("internal function \"%s\" is not in internal lookup table",
prosrc)));
- pfree(prosrc);
+ stack_buffer_free(prosrc);
/* Should we check that nargs, strict, retset match the table? */
finfo->fn_addr = fbp->func;
/* note this policy is also assumed in fast path above */
@@ -370,6 +373,8 @@ fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple)
*probinstring;
void *libraryhandle;
+ DECLARE_STACK_BUFFER();
+
/*
* Get prosrc and probin strings (link symbol and library filename).
* While in general these columns might be null, that's not allowed
@@ -377,11 +382,11 @@ fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple)
*/
prosrcattr = SysCacheGetAttrNotNull(PROCOID, procedureTuple,
Anum_pg_proc_prosrc);
- prosrcstring = TextDatumGetCString(prosrcattr);
+ prosrcstring = stack_buffer_text_datum_to_cstring(prosrcattr);
probinattr = SysCacheGetAttrNotNull(PROCOID, procedureTuple,
Anum_pg_proc_probin);
- probinstring = TextDatumGetCString(probinattr);
+ probinstring = stack_buffer_text_datum_to_cstring(probinattr);
/* Look up the function itself */
user_fn = load_external_function(probinstring, prosrcstring, true,
@@ -393,8 +398,8 @@ fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple)
/* Cache the addresses for later calls */
record_C_func(procedureTuple, user_fn, inforec);
- pfree(prosrcstring);
- pfree(probinstring);
+ stack_buffer_free(prosrcstring);
+ stack_buffer_free(probinstring);
}
switch (inforec->api_version)
--
2.53.0
[text/x-patch] v2-0014-Use-stack-buffer-in-logical-replication.patch (3.3K, 16-v2-0014-Use-stack-buffer-in-logical-replication.patch)
download | inline diff:
From b94fed5ea689c680329a6b436e5e16171e0a5c7b Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Mon, 2 Mar 2026 04:19:54 +1300
Subject: [PATCH v2 14/19] Use stack buffer in logical replication.
Mechanical changes to temporary datum and null arrays.
---
src/backend/replication/logical/reorderbuffer.c | 16 +++++++++-------
src/backend/replication/logical/worker.c | 10 ++++++++--
2 files changed, 17 insertions(+), 9 deletions(-)
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index 4c230bcc8e4..5f0888fc159 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -113,6 +113,7 @@
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/relfilenumbermap.h"
+#include "utils/stack_buffer.h"
#include "utils/wait_event.h"
/*
@@ -5092,6 +5093,8 @@ ReorderBufferToastReplace(ReorderBuffer *rb, ReorderBufferTXN *txn,
HeapTuple newtup;
Size old_size;
+ DECLARE_STACK_BUFFER();
+
/* no toast tuples changed */
if (txn->toast_hash == NULL)
return;
@@ -5122,10 +5125,9 @@ ReorderBufferToastReplace(ReorderBuffer *rb, ReorderBufferTXN *txn,
toast_desc = RelationGetDescr(toast_rel);
- /* should we allocate from stack instead? */
- attrs = palloc0_array(Datum, desc->natts);
- isnull = palloc0_array(bool, desc->natts);
- free = palloc0_array(bool, desc->natts);
+ attrs = stack_buffer_alloc0_array(Datum, desc->natts);
+ isnull = stack_buffer_alloc0_array(bool, desc->natts);
+ free = stack_buffer_alloc0_array(bool, desc->natts);
newtup = change->data.tp.newtuple;
@@ -5247,9 +5249,9 @@ ReorderBufferToastReplace(ReorderBuffer *rb, ReorderBufferTXN *txn,
if (free[natt])
pfree(DatumGetPointer(attrs[natt]));
}
- pfree(attrs);
- pfree(free);
- pfree(isnull);
+ stack_buffer_free(attrs);
+ stack_buffer_free(free);
+ stack_buffer_free(isnull);
MemoryContextSwitchTo(oldcontext);
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 033858752d9..54828cb7e0e 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -294,6 +294,7 @@
#include "utils/rel.h"
#include "utils/rls.h"
#include "utils/snapmgr.h"
+#include "utils/stack_buffer.h"
#include "utils/syscache.h"
#include "utils/usercontext.h"
#include "utils/wait_event.h"
@@ -972,14 +973,16 @@ slot_fill_defaults(LogicalRepRelMapEntry *rel, EState *estate,
ExprState **defexprs;
ExprContext *econtext;
+ DECLARE_STACK_BUFFER();
+
econtext = GetPerTupleExprContext(estate);
/* We got all the data via replication, no need to evaluate anything. */
if (num_phys_attrs == rel->remoterel.natts)
return;
- defmap = palloc_array(int, num_phys_attrs);
- defexprs = palloc_array(ExprState *, num_phys_attrs);
+ defmap = stack_buffer_alloc_array(int, num_phys_attrs);
+ defexprs = stack_buffer_alloc_array(ExprState *, num_phys_attrs);
Assert(rel->attrmap->maplen == num_phys_attrs);
for (attnum = 0; attnum < num_phys_attrs; attnum++)
@@ -1010,6 +1013,9 @@ slot_fill_defaults(LogicalRepRelMapEntry *rel, EState *estate,
for (i = 0; i < num_defaults; i++)
slot->tts_values[defmap[i]] =
ExecEvalExpr(defexprs[i], econtext, &slot->tts_isnull[defmap[i]]);
+
+ stack_buffer_free(defmap);
+ stack_buffer_free(defexprs);
}
/*
--
2.53.0
[text/x-patch] v2-0015-Use-stack-buffer-in-some-FDW-code.patch (2.9K, 17-v2-0015-Use-stack-buffer-in-some-FDW-code.patch)
download | inline diff:
From dcc2eb693c70936627e8c9af32b93bcc35d6cd5e Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Mon, 2 Mar 2026 04:21:02 +1300
Subject: [PATCH v2 15/19] Use stack buffer in some FDW code.
Mechanical changes to temporary datum and null arrays.
---
contrib/file_fdw/file_fdw.c | 11 +++++++----
contrib/postgres_fdw/postgres_fdw.c | 9 +++++++--
2 files changed, 14 insertions(+), 6 deletions(-)
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 33a37d832ce..a6958d6a4c0 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -40,6 +40,7 @@
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/sampling.h"
+#include "utils/stack_buffer.h"
#include "utils/varlena.h"
PG_MODULE_MAGIC_EXT(
@@ -1202,12 +1203,14 @@ file_acquire_sample_rows(Relation onerel, int elevel,
MemoryContext oldcontext = CurrentMemoryContext;
MemoryContext tupcontext;
+ DECLARE_STACK_BUFFER();
+
Assert(onerel);
Assert(targrows > 0);
tupDesc = RelationGetDescr(onerel);
- values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
- nulls = (bool *) palloc(tupDesc->natts * sizeof(bool));
+ values = stack_buffer_alloc_array(Datum, tupDesc->natts);
+ nulls = stack_buffer_alloc_array(bool, tupDesc->natts);
/* Fetch options of foreign table */
fileGetOptions(RelationGetRelid(onerel), &filename, &is_program, &options);
@@ -1324,8 +1327,8 @@ file_acquire_sample_rows(Relation onerel, int elevel,
EndCopyFrom(cstate);
- pfree(values);
- pfree(nulls);
+ stack_buffer_free(values);
+ stack_buffer_free(nulls);
/*
* Emit some interesting relation info
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 60d90329a65..c817b718616 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -48,6 +48,7 @@
#include "utils/rel.h"
#include "utils/sampling.h"
#include "utils/selfuncs.h"
+#include "utils/stack_buffer.h"
PG_MODULE_MAGIC_EXT(
.name = "postgres_fdw",
@@ -7507,6 +7508,8 @@ make_tuple_from_result_row(PGresult *res,
ListCell *lc;
int j;
+ DECLARE_STACK_BUFFER();
+
Assert(row < PQntuples(res));
/*
@@ -7528,8 +7531,8 @@ make_tuple_from_result_row(PGresult *res,
tupdesc = fsstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
}
- values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
- nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
+ values = stack_buffer_alloc0_array(Datum, tupdesc->natts);
+ nulls = stack_buffer_alloc_array(bool, tupdesc->natts);
/* Initialize to nulls for any columns not present in result */
memset(nulls, true, tupdesc->natts * sizeof(bool));
@@ -7631,6 +7634,8 @@ make_tuple_from_result_row(PGresult *res,
HeapTupleHeaderSetCmin(tuple->t_data, InvalidTransactionId);
/* Clean up */
+ stack_buffer_free(values);
+ stack_buffer_free(nulls);
MemoryContextReset(temp_context);
return tuple;
--
2.53.0
[text/x-patch] v2-0016-Use-stack-buffer-in-partition-pruning.patch (10.3K, 18-v2-0016-Use-stack-buffer-in-partition-pruning.patch)
download | inline diff:
From 6e8968cbadeec651963ed9b310551685799bf364 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Mon, 2 Mar 2026 04:44:33 +1300
Subject: [PATCH v2 16/19] Use stack buffer in partition pruning.
---
src/backend/partitioning/partbounds.c | 60 +++++++++++++++++----------
src/backend/partitioning/partprune.c | 17 +++++---
2 files changed, 49 insertions(+), 28 deletions(-)
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 0ca312ac27d..fc0ae489aaa 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -39,6 +39,7 @@
#include "utils/partcache.h"
#include "utils/ruleutils.h"
#include "utils/snapmgr.h"
+#include "utils/stack_buffer.h"
#include "utils/syscache.h"
/*
@@ -354,13 +355,15 @@ create_hash_bounds(PartitionBoundSpec **boundspecs, int nparts,
int greatest_modulus;
Datum *boundDatums;
+ DECLARE_STACK_BUFFER();
+
boundinfo = palloc0_object(PartitionBoundInfoData);
boundinfo->strategy = key->strategy;
/* No special hash partitions. */
boundinfo->null_index = -1;
boundinfo->default_index = -1;
- hbounds = palloc_array(PartitionHashBound, nparts);
+ hbounds = stack_buffer_alloc_array(PartitionHashBound, nparts);
/* Convert from node to the internal representation */
for (i = 0; i < nparts; i++)
@@ -422,7 +425,7 @@ create_hash_bounds(PartitionBoundSpec **boundspecs, int nparts,
(*mapping)[hbounds[i].index] = i;
}
- pfree(hbounds);
+ stack_buffer_free(hbounds);
return boundinfo;
}
@@ -471,6 +474,8 @@ create_list_bounds(PartitionBoundSpec **boundspecs, int nparts,
int null_index = -1;
Datum *boundDatums;
+ DECLARE_STACK_BUFFER();
+
boundinfo = palloc0_object(PartitionBoundInfoData);
boundinfo->strategy = key->strategy;
/* Will be set correctly below. */
@@ -479,7 +484,7 @@ create_list_bounds(PartitionBoundSpec **boundspecs, int nparts,
ndatums = get_non_null_list_datum_count(boundspecs, nparts);
all_values = (PartitionListValue *)
- palloc(ndatums * sizeof(PartitionListValue));
+ stack_buffer_alloc_array(PartitionListValue, ndatums);
/* Create a unified list of non-null values across all partitions. */
for (j = 0, i = 0; i < nparts; i++)
@@ -566,7 +571,7 @@ create_list_bounds(PartitionBoundSpec **boundspecs, int nparts,
boundinfo->indexes[i] = (*mapping)[orig_index];
}
- pfree(all_values);
+ stack_buffer_free(all_values);
/*
* Set the canonical value for null_index, if any.
@@ -688,6 +693,8 @@ create_range_bounds(PartitionBoundSpec **boundspecs, int nparts,
Datum *boundDatums;
PartitionRangeDatumKind *boundKinds;
+ DECLARE_STACK_BUFFER();
+
boundinfo = palloc0_object(PartitionBoundInfoData);
boundinfo->strategy = key->strategy;
/* There is no special null-accepting range partition. */
@@ -695,7 +702,7 @@ create_range_bounds(PartitionBoundSpec **boundspecs, int nparts,
/* Will be set correctly below. */
boundinfo->default_index = -1;
- all_bounds = palloc0_array(PartitionRangeBound *, 2 * nparts);
+ all_bounds = stack_buffer_alloc0_array(PartitionRangeBound *, 2 * nparts);
/* Create a unified list of range bounds across all the partitions. */
ndatums = 0;
@@ -735,8 +742,7 @@ create_range_bounds(PartitionBoundSpec **boundspecs, int nparts,
key);
/* Save distinct bounds from all_bounds into rbounds. */
- rbounds = (PartitionRangeBound **)
- palloc(ndatums * sizeof(PartitionRangeBound *));
+ rbounds = stack_buffer_alloc_array(PartitionRangeBound *, ndatums);
k = 0;
prev = NULL;
for (i = 0; i < ndatums; i++)
@@ -785,7 +791,7 @@ create_range_bounds(PartitionBoundSpec **boundspecs, int nparts,
prev = cur;
}
- pfree(all_bounds);
+ stack_buffer_free(all_bounds);
/* Update ndatums to hold the count of distinct datums. */
ndatums = k;
@@ -858,7 +864,7 @@ create_range_bounds(PartitionBoundSpec **boundspecs, int nparts,
}
}
- pfree(rbounds);
+ stack_buffer_free(rbounds);
/* Set the canonical value for default_index, if any. */
if (default_index != -1)
@@ -2384,9 +2390,11 @@ fix_merged_indexes(PartitionMap *outer_map, PartitionMap *inner_map,
int i;
ListCell *lc;
+ DECLARE_STACK_BUFFER();
+
Assert(nmerged > 0);
- new_indexes = palloc_array(int, nmerged);
+ new_indexes = stack_buffer_alloc_array(int, nmerged);
for (i = 0; i < nmerged; i++)
new_indexes[i] = -1;
@@ -2419,7 +2427,7 @@ fix_merged_indexes(PartitionMap *outer_map, PartitionMap *inner_map,
lfirst_int(lc) = new_indexes[merged_index];
}
- pfree(new_indexes);
+ stack_buffer_free(new_indexes);
}
/*
@@ -2442,12 +2450,14 @@ generate_matching_part_pairs(RelOptInfo *outer_rel, RelOptInfo *inner_rel,
int max_nparts;
int i;
+ DECLARE_STACK_BUFFER();
+
Assert(nmerged > 0);
Assert(*outer_parts == NIL);
Assert(*inner_parts == NIL);
- outer_indexes = palloc_array(int, nmerged);
- inner_indexes = palloc_array(int, nmerged);
+ outer_indexes = stack_buffer_alloc_array(int, nmerged);
+ inner_indexes = stack_buffer_alloc_array(int, nmerged);
for (i = 0; i < nmerged; i++)
outer_indexes[i] = inner_indexes[i] = -1;
@@ -2500,8 +2510,8 @@ generate_matching_part_pairs(RelOptInfo *outer_rel, RelOptInfo *inner_rel,
inner_rel->part_rels[inner_index] : NULL);
}
- pfree(outer_indexes);
- pfree(inner_indexes);
+ stack_buffer_free(outer_indexes);
+ stack_buffer_free(inner_indexes);
}
/*
@@ -5104,6 +5114,8 @@ calculate_partition_bound_for_merge(Relation parent,
PartitionKey key = RelationGetPartitionKey(parent);
PartitionBoundSpec *bound;
+ DECLARE_STACK_BUFFER();
+
Assert(!spec->is_default);
switch (key->strategy)
@@ -5115,7 +5127,7 @@ calculate_partition_bound_for_merge(Relation parent,
int nparts = list_length(partOids);
List *bounds = NIL;
- lower_bounds = palloc0_array(PartitionRangeBound *, nparts);
+ lower_bounds = stack_buffer_alloc0_array(PartitionRangeBound *, nparts);
/*
* Create an array of lower bounds and a list of
@@ -5164,7 +5176,7 @@ calculate_partition_bound_for_merge(Relation parent,
spec->upperdatums =
((PartitionBoundSpec *) list_nth(bounds, lower_bounds[nparts - 1]->index))->upperdatums;
- pfree(lower_bounds);
+ stack_buffer_free(lower_bounds);
list_free(bounds);
break;
}
@@ -5741,6 +5753,8 @@ check_partitions_for_split(Relation parent,
*/
int nparts = 0;
+ DECLARE_STACK_BUFFER();
+
key = RelationGetPartitionKey(parent);
strategy = get_partition_strategy(key);
@@ -5754,7 +5768,7 @@ check_partitions_for_split(Relation parent,
* Make an array new_parts with new partitions except the DEFAULT
* partition.
*/
- new_parts = palloc0_array(SinglePartitionSpec *, list_length(partlist));
+ new_parts = stack_buffer_alloc0_array(SinglePartitionSpec *, list_length(partlist));
/* isSplitPartDefault flag: is split partition a DEFAULT partition? */
isSplitPartDefault = (defaultPartOid == splitPartOid);
@@ -5784,7 +5798,7 @@ check_partitions_for_split(Relation parent,
* all partitions in ascending order of their bounds (we compare the
* lower bound only).
*/
- lower_bounds = palloc0_array(PartitionRangeBound *, nparts);
+ lower_bounds = stack_buffer_alloc0_array(PartitionRangeBound *, nparts);
/* Create an array of lower bounds. */
for (i = 0; i < nparts; i++)
@@ -5799,12 +5813,12 @@ check_partitions_for_split(Relation parent,
/* Reorder the array of partitions. */
tmp_new_parts = new_parts;
- new_parts = palloc0_array(SinglePartitionSpec *, nparts);
+ new_parts = stack_buffer_alloc0_array(SinglePartitionSpec *, nparts);
for (i = 0; i < nparts; i++)
new_parts[i] = tmp_new_parts[lower_bounds[i]->index];
- pfree(tmp_new_parts);
- pfree(lower_bounds);
+ stack_buffer_free(tmp_new_parts);
+ stack_buffer_free(lower_bounds);
}
for (i = 0; i < nparts; i++)
@@ -5871,5 +5885,5 @@ check_partitions_for_split(Relation parent,
new_parts, nparts, pstate);
}
- pfree(new_parts);
+ stack_buffer_free(new_parts);
}
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c
index 6d979a08fd3..8c4bf1d657e 100644
--- a/src/backend/partitioning/partprune.c
+++ b/src/backend/partitioning/partprune.c
@@ -54,6 +54,7 @@
#include "partitioning/partprune.h"
#include "utils/array.h"
#include "utils/lsyscache.h"
+#include "utils/stack_buffer.h"
/*
@@ -233,6 +234,8 @@ make_partition_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
ListCell *lc;
int i;
+ DECLARE_STACK_BUFFER();
+
/*
* Scan the subpaths to see which ones are scans of partition child
* relations, and identify their parent partitioned rels. (Note: we must
@@ -246,7 +249,8 @@ make_partition_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
* that zero can represent an un-filled array entry.
*/
allpartrelids = NIL;
- relid_subplan_map = palloc0_array(int, root->simple_rel_array_size);
+ relid_subplan_map = stack_buffer_alloc0_array(int,
+ root->simple_rel_array_size);
i = 1;
foreach(lc, subpaths)
@@ -327,7 +331,7 @@ make_partition_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
}
}
- pfree(relid_subplan_map);
+ stack_buffer_free(relid_subplan_map);
/*
* If none of the partition hierarchies had any useful run-time pruning
@@ -457,6 +461,8 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
int rti;
int i;
+ DECLARE_STACK_BUFFER();
+
/*
* Examine each partitioned rel, constructing a temporary array to map
* from planner relids to index of the partitioned rel, and building a
@@ -465,7 +471,8 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
* In this phase we discover whether runtime pruning is needed at all; if
* not, we can avoid doing further work.
*/
- relid_subpart_map = palloc0_array(int, root->simple_rel_array_size);
+ relid_subpart_map = stack_buffer_alloc0_array(int,
+ root->simple_rel_array_size);
i = 1;
rti = -1;
@@ -624,7 +631,7 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
if (!doruntimeprune)
{
/* No run-time pruning required. */
- pfree(relid_subpart_map);
+ stack_buffer_free(relid_subpart_map);
return NIL;
}
@@ -719,7 +726,7 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
pinfo->leafpart_rti_map = leafpart_rti_map;
}
- pfree(relid_subpart_map);
+ stack_buffer_free(relid_subpart_map);
*matchedsubplans = subplansfound;
--
2.53.0
[text/x-patch] v2-0017-Use-stack-buffer-for-simple-arrays-in-planner.patch (16.7K, 19-v2-0017-Use-stack-buffer-for-simple-arrays-in-planner.patch)
download | inline diff:
From 5cef70069ca8e0b0e5a2e45bdd7cf177404a882d Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Mon, 2 Mar 2026 03:39:11 +1300
Subject: [PATCH v2 17/19] Use stack buffer for simple arrays in planner.
---
src/backend/optimizer/path/allpaths.c | 7 +++--
src/backend/optimizer/path/equivclass.c | 9 ++++--
src/backend/optimizer/path/indxpath.c | 14 ++++++---
src/backend/optimizer/path/pathkeys.c | 15 +++++----
src/backend/optimizer/plan/analyzejoins.c | 7 ++++-
src/backend/optimizer/plan/planner.c | 37 ++++++++++++++---------
src/backend/optimizer/prep/prepagg.c | 7 +++--
src/backend/optimizer/prep/prepunion.c | 7 +++--
src/backend/optimizer/util/clauses.c | 17 +++++++----
src/backend/optimizer/util/plancat.c | 7 +++--
10 files changed, 84 insertions(+), 43 deletions(-)
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 5eceb321828..1c8efc5e3cd 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -49,6 +49,7 @@
#include "rewrite/rewriteManip.h"
#include "utils/lsyscache.h"
#include "utils/selfuncs.h"
+#include "utils/stack_buffer.h"
/* Bitmask flags for pushdown_safety_info.unsafeFlags */
@@ -1021,6 +1022,8 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
int nattrs;
ListCell *l;
+ DECLARE_STACK_BUFFER();
+
/* Guard against stack overflow due to overly deep inheritance tree. */
check_stack_depth();
@@ -1065,7 +1068,7 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
parent_rows = 0;
parent_size = 0;
nattrs = rel->max_attr - rel->min_attr + 1;
- parent_attrsizes = (double *) palloc0(nattrs * sizeof(double));
+ parent_attrsizes = stack_buffer_alloc0_array(double, nattrs);
foreach(l, root->append_rel_list)
{
@@ -1296,7 +1299,7 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
set_dummy_rel_pathlist(rel);
}
- pfree(parent_attrsizes);
+ stack_buffer_free(parent_attrsizes);
}
/*
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index e3697df51a2..28a3162b98e 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -32,6 +32,7 @@
#include "optimizer/restrictinfo.h"
#include "rewrite/rewriteManip.h"
#include "utils/lsyscache.h"
+#include "utils/stack_buffer.h"
static EquivalenceMember *make_eq_member(EquivalenceClass *ec,
@@ -1373,6 +1374,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
EquivalenceMember **prev_ems;
ListCell *lc;
+ DECLARE_STACK_BUFFER();
+
/*
* We scan the EC members once and track the last-seen member for each
* base relation. When we see another member of the same base relation,
@@ -1381,8 +1384,8 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
* ordering would succeed. XXX FIXME: use a UNION-FIND algorithm similar
* to the way we build merged ECs. (Use a list-of-lists for each rel.)
*/
- prev_ems = (EquivalenceMember **)
- palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));
+ prev_ems = stack_buffer_alloc0_array(EquivalenceMember *,
+ root->simple_rel_array_size);
/* We don't expect any children yet */
Assert(ec->ec_childmembers == NULL);
@@ -1444,7 +1447,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
prev_ems[relid] = cur_em;
}
- pfree(prev_ems);
+ stack_buffer_free(prev_ems);
/*
* We also have to make sure that all the Vars used in the member clauses
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 67d9dc35f44..b01c48650c8 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -34,6 +34,7 @@
#include "optimizer/restrictinfo.h"
#include "utils/lsyscache.h"
#include "utils/selfuncs.h"
+#include "utils/stack_buffer.h"
/* XXX see PartCollMatchesExprColl */
@@ -1281,6 +1282,8 @@ group_similar_or_args(PlannerInfo *root, RelOptInfo *rel, RestrictInfo *rinfo)
List *result = NIL;
Index relid = rel->relid;
+ DECLARE_STACK_BUFFER();
+
Assert(IsA(rinfo->orclause, BoolExpr));
orargs = ((BoolExpr *) rinfo->orclause)->args;
n = list_length(orargs);
@@ -1291,7 +1294,7 @@ group_similar_or_args(PlannerInfo *root, RelOptInfo *rel, RestrictInfo *rinfo)
* which will be used to sort these arguments at the next step.
*/
i = -1;
- matches = palloc_array(OrArgIndexMatch, n);
+ matches = stack_buffer_alloc_array(OrArgIndexMatch, n);
foreach(lc, orargs)
{
Node *arg = lfirst(lc);
@@ -1421,7 +1424,7 @@ group_similar_or_args(PlannerInfo *root, RelOptInfo *rel, RestrictInfo *rinfo)
*/
if (!matched)
{
- pfree(matches);
+ stack_buffer_free(matches);
return orargs;
}
@@ -1526,7 +1529,7 @@ group_similar_or_args(PlannerInfo *root, RelOptInfo *rel, RestrictInfo *rinfo)
group_start = i;
}
}
- pfree(matches);
+ stack_buffer_free(matches);
return result;
}
@@ -1794,6 +1797,8 @@ choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths)
j;
ListCell *l;
+ DECLARE_STACK_BUFFER();
+
Assert(npaths > 0); /* else caller error */
if (npaths == 1)
return (Path *) linitial(paths); /* easy case */
@@ -1853,7 +1858,7 @@ choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths)
* same set of clauses; keep only the cheapest-to-scan of any such groups.
* The surviving paths are put into an array for qsort'ing.
*/
- pathinfoarray = palloc_array(PathClauseUsage *, npaths);
+ pathinfoarray = stack_buffer_alloc_array(PathClauseUsage *, npaths);
clauselist = NIL;
npaths = 0;
foreach(l, paths)
@@ -1979,6 +1984,7 @@ choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths)
/* some easy cleanup (we don't try real hard though) */
list_free(qualsofar);
}
+ stack_buffer_free(pathinfoarray);
if (list_length(bestpaths) == 1)
return (Path *) linitial(bestpaths); /* no need for AND */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 5eb71635d15..a0e3194f6fd 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -26,6 +26,7 @@
#include "optimizer/paths.h"
#include "partitioning/partbounds.h"
#include "rewrite/rewriteManip.h"
+#include "utils/stack_buffer.h"
#include "utils/lsyscache.h"
/* Consider reordering of GROUP BY keys? */
@@ -1668,6 +1669,8 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
ListCell *lc;
int j;
+ DECLARE_STACK_BUFFER();
+
/* Might have no mergeclauses */
if (nClauses == 0)
return NIL;
@@ -1676,8 +1679,8 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
* Make arrays of the ECs used by the mergeclauses (dropping any
* duplicates) and their "popularity" scores.
*/
- ecs = (EquivalenceClass **) palloc(nClauses * sizeof(EquivalenceClass *));
- scores = (int *) palloc(nClauses * sizeof(int));
+ ecs = stack_buffer_alloc_array(EquivalenceClass *, nClauses);
+ scores = stack_buffer_alloc_array(int, nClauses);
necs = 0;
foreach(lc, mergeclauses)
@@ -1784,8 +1787,8 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
pathkeys = list_copy_head(root->query_pathkeys, matches);
/* we have all of the join pathkeys, so nothing more to do */
- pfree(ecs);
- pfree(scores);
+ stack_buffer_free(ecs);
+ stack_buffer_free(scores);
return pathkeys;
}
@@ -1827,8 +1830,8 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
pathkeys = lappend(pathkeys, pathkey);
}
- pfree(ecs);
- pfree(scores);
+ stack_buffer_free(ecs);
+ stack_buffer_free(scores);
return pathkeys;
}
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 12e9ed0d0c7..e3d462af416 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -34,6 +34,7 @@
#include "parser/parse_agg.h"
#include "rewrite/rewriteManip.h"
#include "utils/lsyscache.h"
+#include "utils/stack_buffer.h"
/*
* Utility structure. A sorting procedure is needed to simplify the search
@@ -2314,6 +2315,8 @@ remove_self_joins_recurse(PlannerInfo *root, List *joinlist, Relids toRemove)
int j;
int numRels;
+ DECLARE_STACK_BUFFER();
+
/* Collect indexes of base relations of the join tree */
foreach(jl, joinlist)
{
@@ -2363,7 +2366,7 @@ remove_self_joins_recurse(PlannerInfo *root, List *joinlist, Relids toRemove)
* In order to find relations with the same oid we first build an array of
* candidates and then sort it by oid.
*/
- candidates = palloc_array(SelfJoinCandidate, numRels);
+ candidates = stack_buffer_alloc_array(SelfJoinCandidate, numRels);
i = -1;
j = 0;
while ((i = bms_next_member(relids, i)) >= 0)
@@ -2431,6 +2434,8 @@ remove_self_joins_recurse(PlannerInfo *root, List *joinlist, Relids toRemove)
Assert(bms_is_empty(relids));
+ stack_buffer_free(candidates);
+
return toRemove;
}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 42604a0f75c..24ad41d4ec6 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -63,6 +63,7 @@
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/selfuncs.h"
+#include "utils/stack_buffer.h"
/* GUC parameters */
double cursor_tuple_fraction = DEFAULT_CURSOR_TUPLE_FRACTION;
@@ -3020,6 +3021,8 @@ extract_rollup_sets(List *groupingSets)
ListCell *lc1 = list_head(groupingSets);
ListCell *lc;
+ DECLARE_STACK_BUFFER();
+
/*
* Start by stripping out empty sets. The algorithm doesn't require this,
* but the planner currently needs all empty sets to be returned in the
@@ -3053,10 +3056,10 @@ extract_rollup_sets(List *groupingSets)
* to leave 0 free for the NIL node in the graph algorithm.
*----------
*/
- orig_sets = palloc0((num_sets_raw + 1) * sizeof(List *));
- set_masks = palloc0((num_sets_raw + 1) * sizeof(Bitmapset *));
- adjacency = palloc0((num_sets_raw + 1) * sizeof(short *));
- adjacency_buf = palloc((num_sets_raw + 1) * sizeof(short));
+ orig_sets = stack_buffer_alloc0_array(List *, num_sets_raw + 1);
+ set_masks = stack_buffer_alloc0_array(Bitmapset *, num_sets_raw + 1);
+ adjacency = stack_buffer_alloc0_array(short *, num_sets_raw + 1);
+ adjacency_buf = stack_buffer_alloc_array(short, num_sets_raw + 1);
j_size = 0;
j = 0;
@@ -3141,7 +3144,7 @@ extract_rollup_sets(List *groupingSets)
* pair_vu[v] = u (both will be true, but we check both so that we can do
* it in one pass)
*/
- chains = palloc0((num_sets + 1) * sizeof(int));
+ chains = stack_buffer_alloc0_array(int, num_sets + 1);
for (i = 1; i <= num_sets; ++i)
{
@@ -3157,7 +3160,7 @@ extract_rollup_sets(List *groupingSets)
}
/* build result lists. */
- results = palloc0((num_chains + 1) * sizeof(List *));
+ results = stack_buffer_alloc0_array(List *, (num_chains + 1));
for (i = 1; i <= num_sets; ++i)
{
@@ -3183,17 +3186,17 @@ extract_rollup_sets(List *groupingSets)
* tied up a nontrivial amount of memory.)
*/
BipartiteMatchFree(state);
- pfree(results);
- pfree(chains);
+ stack_buffer_free(results);
+ stack_buffer_free(chains);
for (i = 1; i <= num_sets; ++i)
if (adjacency[i])
pfree(adjacency[i]);
- pfree(adjacency);
- pfree(adjacency_buf);
- pfree(orig_sets);
+ stack_buffer_free(adjacency);
+ stack_buffer_free(adjacency_buf);
+ stack_buffer_free(orig_sets);
for (i = 1; i <= num_sets; ++i)
bms_free(set_masks[i]);
- pfree(set_masks);
+ stack_buffer_free(set_masks);
return result;
}
@@ -6033,8 +6036,12 @@ select_active_windows(PlannerInfo *root, WindowFuncLists *wflists)
List *result = NIL;
ListCell *lc;
int nActive = 0;
- WindowClauseSortData *actives = palloc_array(WindowClauseSortData,
- list_length(windowClause));
+ WindowClauseSortData *actives;
+
+ DECLARE_STACK_BUFFER();
+
+ actives = stack_buffer_alloc_array(WindowClauseSortData,
+ list_length(windowClause));
/* First, construct an array of the active windows */
foreach(lc, windowClause)
@@ -6093,7 +6100,7 @@ select_active_windows(PlannerInfo *root, WindowFuncLists *wflists)
for (int i = 0; i < nActive; i++)
result = lappend(result, actives[i].wc);
- pfree(actives);
+ stack_buffer_free(actives);
return result;
}
diff --git a/src/backend/optimizer/prep/prepagg.c b/src/backend/optimizer/prep/prepagg.c
index 3737cc15ba1..f1d0466504a 100644
--- a/src/backend/optimizer/prep/prepagg.c
+++ b/src/backend/optimizer/prep/prepagg.c
@@ -49,6 +49,7 @@
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
+#include "utils/stack_buffer.h"
#include "utils/syscache.h"
static bool preprocess_aggrefs_walker(Node *node, PlannerInfo *root);
@@ -525,11 +526,13 @@ GetAggInitVal(Datum textInitVal, Oid transtype)
char *strInitVal;
Datum initVal;
+ DECLARE_STACK_BUFFER();
+
getTypeInputInfo(transtype, &typinput, &typioparam);
- strInitVal = TextDatumGetCString(textInitVal);
+ strInitVal = stack_buffer_text_datum_to_cstring(textInitVal);
initVal = OidInputFunctionCall(typinput, strInitVal,
typioparam, -1);
- pfree(strInitVal);
+ stack_buffer_free(strInitVal);
return initVal;
}
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index f50c296e3d9..9816e315bab 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -38,6 +38,7 @@
#include "optimizer/tlist.h"
#include "parser/parse_coerce.h"
#include "utils/selfuncs.h"
+#include "utils/stack_buffer.h"
static RelOptInfo *recurse_set_operations(Node *setOp, PlannerInfo *root,
@@ -1628,13 +1629,15 @@ generate_append_tlist(List *colTypes, List *colCollations,
ListCell *tlistl;
int32 *colTypmods;
+ DECLARE_STACK_BUFFER();
+
/*
* First extract typmods to use.
*
* If the inputs all agree on type and typmod of a particular column, use
* that typmod; else use -1.
*/
- colTypmods = palloc_array(int32, list_length(colTypes));
+ colTypmods = stack_buffer_alloc_array(int32, list_length(colTypes));
foreach(tlistl, input_tlists)
{
@@ -1705,7 +1708,7 @@ generate_append_tlist(List *colTypes, List *colCollations,
tlist = lappend(tlist, tle);
}
- pfree(colTypmods);
+ stack_buffer_free(colTypmods);
return tlist;
}
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index f0f8e2515ec..4b50c56991d 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -59,6 +59,7 @@
#include "utils/jsonpath.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
+#include "utils/stack_buffer.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
@@ -5069,11 +5070,13 @@ fetch_function_defaults(HeapTuple func_tuple)
Datum proargdefaults;
char *str;
+ DECLARE_STACK_BUFFER();
+
proargdefaults = SysCacheGetAttrNotNull(PROCOID, func_tuple,
Anum_pg_proc_proargdefaults);
- str = TextDatumGetCString(proargdefaults);
+ str = stack_buffer_text_datum_to_cstring(proargdefaults);
defaults = castNode(List, stringToNode(str));
- pfree(str);
+ stack_buffer_free(str);
return defaults;
}
@@ -6273,10 +6276,12 @@ make_SAOP_expr(Oid oper, Node *leftexpr, Oid coltype, Oid arraycollid,
int dims[1] = {list_length(exprs)};
int lbs[1] = {1};
+ DECLARE_STACK_BUFFER();
+
get_typlenbyvalalign(coltype, &typlen, &typbyval, &typalign);
- elems = palloc_array(Datum, list_length(exprs));
- nulls = palloc_array(bool, list_length(exprs));
+ elems = stack_buffer_alloc_array(Datum, list_length(exprs));
+ nulls = stack_buffer_alloc_array(bool, list_length(exprs));
foreach_node(Const, value, exprs)
{
elems[i] = value->constvalue;
@@ -6289,8 +6294,8 @@ make_SAOP_expr(Oid oper, Node *leftexpr, Oid coltype, Oid arraycollid,
-1, PointerGetDatum(arrayConst),
false, false);
- pfree(elems);
- pfree(nulls);
+ stack_buffer_free(elems);
+ stack_buffer_free(nulls);
list_free(exprs);
}
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index b2fbd6a082b..a080ba0f241 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -51,6 +51,7 @@
#include "utils/lsyscache.h"
#include "utils/partcache.h"
#include "utils/rel.h"
+#include "utils/stack_buffer.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
@@ -1788,9 +1789,11 @@ get_relation_statistics(PlannerInfo *root, RelOptInfo *rel,
{
char *exprsString;
- exprsString = TextDatumGetCString(datum);
+ DECLARE_STACK_BUFFER();
+
+ exprsString = stack_buffer_text_datum_to_cstring(datum);
exprs = (List *) stringToNode(exprsString);
- pfree(exprsString);
+ stack_buffer_free(exprsString);
/*
* Modify the copies we obtain from the relcache to have the
--
2.53.0
[text/x-patch] v2-0018-Use-stack-buffer-for-tlist-indexes-in-planner.patch (8.9K, 20-v2-0018-Use-stack-buffer-for-tlist-indexes-in-planner.patch)
download | inline diff:
From 9a8d0bd1806694651b02cc994810bc973dcfc026 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Mon, 2 Mar 2026 02:35:03 +1300
Subject: [PATCH v2 18/19] Use stack buffer for tlist indexes in planner.
---
src/backend/optimizer/plan/setrefs.c | 86 +++++++++++++++++++---------
1 file changed, 58 insertions(+), 28 deletions(-)
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 1b5b9b5ed9c..bc048c397a2 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -28,6 +28,7 @@
#include "parser/parse_relation.h"
#include "rewrite/rewriteManip.h"
#include "tcop/utility.h"
+#include "utils/stack_buffer.h"
#include "utils/syscache.h"
@@ -166,7 +167,15 @@ static void set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset);
static void set_param_references(PlannerInfo *root, Plan *plan);
static Node *convert_combining_aggrefs(Node *node, void *context);
static void set_dummy_tlist_references(Plan *plan, int rtoffset);
-static indexed_tlist *build_tlist_index(List *tlist);
+static size_t tlist_index_size(List *tlist);
+static indexed_tlist *fill_tlist_index(indexed_tlist *itlist, List *tlist);
+#define build_tlist_index(tlist) \
+ fill_tlist_index(stack_buffer_alloc(tlist_index_size(tlist)), (tlist))
+#define build_tlist_index_other_vars(tlist, ignore_rel) \
+ fill_tlist_index_other_vars(stack_buffer_alloc(tlist_index_size(tlist)), \
+ (tlist), (ignore_rel))
+#define free_tlist_index(itlist) \
+ stack_buffer_free(itlist)
static Var *search_indexed_tlist_for_var(Var *var,
indexed_tlist *itlist,
int newvarno,
@@ -643,6 +652,8 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
{
ListCell *l;
+ DECLARE_STACK_BUFFER();
+
if (plan == NULL)
return NULL;
@@ -1182,7 +1193,7 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
linitial_int(splan->resultRelations),
rtoffset, NRM_EQUAL, NUM_EXEC_QUAL(plan));
- pfree(itlist);
+ free_tlist_index(itlist);
splan->exclRelTlist =
fix_scan_list(root, splan->exclRelTlist, rtoffset, 1);
@@ -1383,6 +1394,8 @@ set_indexonlyscan_references(PlannerInfo *root,
List *stripped_indextlist;
ListCell *lc;
+ DECLARE_STACK_BUFFER();
+
/*
* Vars in the plan node's targetlist, qual, and recheckqual must only
* reference columns that the index AM can actually return. To ensure
@@ -1436,7 +1449,7 @@ set_indexonlyscan_references(PlannerInfo *root,
plan->indextlist = fix_scan_list(root, plan->indextlist,
rtoffset, NUM_EXEC_TLIST((Plan *) plan));
- pfree(index_itlist);
+ free_tlist_index(index_itlist);
return (Plan *) plan;
}
@@ -1643,6 +1656,8 @@ set_foreignscan_references(PlannerInfo *root,
ForeignScan *fscan,
int rtoffset)
{
+ DECLARE_STACK_BUFFER();
+
/* Adjust scanrelid if it's valid */
if (fscan->scan.scanrelid > 0)
fscan->scan.scanrelid += rtoffset;
@@ -1687,7 +1702,7 @@ set_foreignscan_references(PlannerInfo *root,
rtoffset,
NRM_EQUAL,
NUM_EXEC_QUAL((Plan *) fscan));
- pfree(itlist);
+ free_tlist_index(itlist);
/* fdw_scan_tlist itself just needs fix_scan_list() adjustments */
fscan->fdw_scan_tlist =
fix_scan_list(root, fscan->fdw_scan_tlist,
@@ -1732,6 +1747,8 @@ set_customscan_references(PlannerInfo *root,
{
ListCell *lc;
+ DECLARE_STACK_BUFFER();
+
/* Adjust scanrelid if it's valid */
if (cscan->scan.scanrelid > 0)
cscan->scan.scanrelid += rtoffset;
@@ -1765,7 +1782,7 @@ set_customscan_references(PlannerInfo *root,
rtoffset,
NRM_EQUAL,
NUM_EXEC_QUAL((Plan *) cscan));
- pfree(itlist);
+ free_tlist_index(itlist);
/* custom_scan_tlist itself just needs fix_scan_list() adjustments */
cscan->custom_scan_tlist =
fix_scan_list(root, cscan->custom_scan_tlist,
@@ -2028,6 +2045,8 @@ set_hash_references(PlannerInfo *root, Plan *plan, int rtoffset)
Plan *outer_plan = plan->lefttree;
indexed_tlist *outer_itlist;
+ DECLARE_STACK_BUFFER();
+
/*
* Hash's hashkeys are used when feeding tuples into the hashtable,
* therefore have them reference Hash's outer plan (which itself is the
@@ -2042,6 +2061,7 @@ set_hash_references(PlannerInfo *root, Plan *plan, int rtoffset)
rtoffset,
NRM_EQUAL,
NUM_EXEC_QUAL(plan));
+ free_tlist_index(outer_itlist);
/* Hash doesn't project */
set_dummy_tlist_references(plan, rtoffset);
@@ -2408,6 +2428,8 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
indexed_tlist *outer_itlist;
indexed_tlist *inner_itlist;
+ DECLARE_STACK_BUFFER();
+
outer_itlist = build_tlist_index(outer_plan->targetlist);
inner_itlist = build_tlist_index(inner_plan->targetlist);
@@ -2527,8 +2549,8 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
(join->jointype == JOIN_INNER ? NRM_EQUAL : NRM_SUPERSET),
NUM_EXEC_QUAL((Plan *) join));
- pfree(outer_itlist);
- pfree(inner_itlist);
+ free_tlist_index(outer_itlist);
+ free_tlist_index(inner_itlist);
}
/*
@@ -2557,6 +2579,8 @@ set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset)
List *output_targetlist;
ListCell *l;
+ DECLARE_STACK_BUFFER();
+
subplan_itlist = build_tlist_index(subplan->targetlist);
/*
@@ -2627,7 +2651,7 @@ set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset)
NRM_EQUAL,
NUM_EXEC_QUAL(plan));
- pfree(subplan_itlist);
+ free_tlist_index(subplan_itlist);
}
/*
@@ -2815,7 +2839,19 @@ set_dummy_tlist_references(Plan *plan, int rtoffset)
/*
- * build_tlist_index --- build an index data structure for a child tlist
+ * tlist_index_size --- compute memory required to an indexed_tlist.
+ */
+static size_t
+tlist_index_size(List *tlist)
+{
+ /* Require data structure with enough slots for all tlist entries */
+ return offsetof(indexed_tlist, vars) +
+ list_length(tlist) * sizeof(tlist_vinfo);
+}
+
+
+/*
+ * fill_tlist_index --- build an index data structure for a child tlist
*
* In most cases, subplan tlists will be "flat" tlists with only Vars,
* so we try to optimize that case by extracting information about Vars
@@ -2825,20 +2861,16 @@ set_dummy_tlist_references(Plan *plan, int rtoffset)
*
* The result of this function is an indexed_tlist struct to pass to
* search_indexed_tlist_for_var() and siblings.
- * When done, the indexed_tlist may be freed with a single pfree().
+ *
+ * The macro build_tlist_index() can be used to allocate memory in a stack
+ * buffer. When done, the indexed_tlist may be freed with free_tlist_index().
*/
static indexed_tlist *
-build_tlist_index(List *tlist)
+fill_tlist_index(indexed_tlist *itlist, List *tlist)
{
- indexed_tlist *itlist;
tlist_vinfo *vinfo;
ListCell *l;
- /* Create data structure with enough slots for all tlist entries */
- itlist = (indexed_tlist *)
- palloc(offsetof(indexed_tlist, vars) +
- list_length(tlist) * sizeof(tlist_vinfo));
-
itlist->tlist = tlist;
itlist->has_ph_vars = false;
itlist->has_non_vars = false;
@@ -2871,25 +2903,19 @@ build_tlist_index(List *tlist)
}
/*
- * build_tlist_index_other_vars --- build a restricted tlist index
+ * fill_tlist_index_other_vars --- build a restricted tlist index
*
- * This is like build_tlist_index, but we only index tlist entries that
+ * This is like fill_tlist_index, but we only index tlist entries that
* are Vars belonging to some rel other than the one specified. We will set
* has_ph_vars (allowing PlaceHolderVars to be matched), but not has_non_vars
* (so nothing other than Vars and PlaceHolderVars can be matched).
*/
static indexed_tlist *
-build_tlist_index_other_vars(List *tlist, int ignore_rel)
+fill_tlist_index_other_vars(indexed_tlist *itlist, List *tlist, int ignore_rel)
{
- indexed_tlist *itlist;
tlist_vinfo *vinfo;
ListCell *l;
- /* Create data structure with enough slots for all tlist entries */
- itlist = (indexed_tlist *)
- palloc(offsetof(indexed_tlist, vars) +
- list_length(tlist) * sizeof(tlist_vinfo));
-
itlist->tlist = tlist;
itlist->has_ph_vars = false;
itlist->has_non_vars = false;
@@ -3476,6 +3502,8 @@ set_returning_clause_references(PlannerInfo *root,
{
indexed_tlist *itlist;
+ DECLARE_STACK_BUFFER();
+
/*
* We can perform the desired Var fixup by abusing the fix_join_expr
* machinery that formerly handled inner indexscan fixup. We search the
@@ -3501,7 +3529,7 @@ set_returning_clause_references(PlannerInfo *root,
NRM_EQUAL,
NUM_EXEC_TLIST(topplan));
- pfree(itlist);
+ free_tlist_index(itlist);
return rlist;
}
@@ -3570,11 +3598,13 @@ set_windowagg_runcondition_references(PlannerInfo *root,
List *newlist;
indexed_tlist *itlist;
+ DECLARE_STACK_BUFFER();
+
itlist = build_tlist_index(plan->targetlist);
newlist = fix_windowagg_condition_expr(root, runcondition, itlist);
- pfree(itlist);
+ free_tlist_index(itlist);
return newlist;
}
--
2.53.0
[text/x-patch] v2-0019-Use-stack-buffer-for-AppendRelInfos-in-planner.patch (12.9K, 21-v2-0019-Use-stack-buffer-for-AppendRelInfos-in-planner.patch)
download | inline diff:
From 5e889d2f52c9f4b5f4a76f6f6d89afbe091d4f1a Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Mon, 2 Mar 2026 11:24:34 +1300
Subject: [PATCH v2 19/19] Use stack buffer for AppendRelInfos in planner.
---
src/backend/optimizer/path/joinrels.c | 18 ++++++---
src/backend/optimizer/plan/planner.c | 23 ++++++++----
src/backend/optimizer/util/appendinfo.c | 32 ++++++++++------
src/backend/partitioning/partprune.c | 13 ++++---
src/include/optimizer/appendinfo.h | 49 ++++++++++++++++++++++++-
src/tools/pgindent/typedefs.list | 1 +
6 files changed, 105 insertions(+), 31 deletions(-)
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 443e2dca7c0..c54d771eda6 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -23,6 +23,7 @@
#include "optimizer/planner.h"
#include "partitioning/partbounds.h"
#include "utils/memutils.h"
+#include "utils/stack_buffer.h"
static void make_rels_by_clause_joins(PlannerInfo *root,
@@ -1619,6 +1620,9 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
ListCell *lcr1 = NULL;
ListCell *lcr2 = NULL;
int cnt_parts;
+ AppendRelInfoBuffer appinfos_buffer = {0};
+
+ DECLARE_STACK_BUFFER();
/* Guard against stack overflow due to overly deep partition hierarchy. */
check_stack_depth();
@@ -1774,8 +1778,10 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
/* Find the AppendRelInfo structures */
child_relids = bms_union(child_rel1->relids, child_rel2->relids);
- appinfos = find_appinfos_by_relids(root, child_relids,
- &nappinfos);
+ appinfos = find_appinfos_by_relids_with_buffer(&appinfos_buffer,
+ root,
+ child_relids,
+ &nappinfos);
/*
* Construct restrictions applicable to the child join from those
@@ -1820,10 +1826,10 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
* are only needed within the loop. Free these local objects eagerly
* at the end of each iteration.
*/
- pfree(appinfos);
bms_free(child_relids);
free_child_join_sjinfo(child_sjinfo, parent_sjinfo);
}
+ free_appinfos_buffer(&appinfos_buffer);
}
/*
@@ -1844,6 +1850,8 @@ build_child_join_sjinfo(PlannerInfo *root, SpecialJoinInfo *parent_sjinfo,
AppendRelInfo **right_appinfos;
int right_nappinfos;
+ DECLARE_STACK_BUFFER();
+
/* Dummy SpecialJoinInfos can be created without any translation. */
if (parent_sjinfo->jointype == JOIN_INNER)
{
@@ -1874,8 +1882,8 @@ build_child_join_sjinfo(PlannerInfo *root, SpecialJoinInfo *parent_sjinfo,
right_nappinfos,
right_appinfos);
- pfree(left_appinfos);
- pfree(right_appinfos);
+ free_appinfos(left_appinfos);
+ free_appinfos(right_appinfos);
return sjinfo;
}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 24ad41d4ec6..64b4e98638f 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -7966,6 +7966,8 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
PathTarget *scanjoin_target;
ListCell *lc;
+ DECLARE_STACK_BUFFER();
+
/* This recurses, so be paranoid. */
check_stack_depth();
@@ -8111,6 +8113,7 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
{
List *live_children = NIL;
int i;
+ AppendRelInfoBuffer appinfos_buffer = {0};
/* Adjust each partition. */
i = -1;
@@ -8128,8 +8131,10 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
continue;
/* Translate scan/join targets for this child. */
- appinfos = find_appinfos_by_relids(root, child_rel->relids,
- &nappinfos);
+ appinfos = find_appinfos_by_relids_with_buffer(&appinfos_buffer,
+ root,
+ child_rel->relids,
+ &nappinfos);
foreach(lc, scanjoin_targets)
{
PathTarget *target = lfirst_node(PathTarget, lc);
@@ -8142,7 +8147,6 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
child_scanjoin_targets = lappend(child_scanjoin_targets,
target);
}
- pfree(appinfos);
/* Recursion does the real work. */
apply_scanjoin_target_to_paths(root, child_rel,
@@ -8155,6 +8159,7 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
if (!IS_DUMMY_REL(child_rel))
live_children = lappend(live_children, child_rel);
}
+ free_appinfos_buffer(&appinfos_buffer);
/* Build new paths for this relation by appending child paths. */
add_paths_to_append_rel(root, rel, live_children);
@@ -8210,6 +8215,9 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
PathTarget *target = grouped_rel->reltarget;
bool partial_grouping_valid = true;
int i;
+ AppendRelInfoBuffer appinfos_buffer = {0};
+
+ DECLARE_STACK_BUFFER();
Assert(patype != PARTITIONWISE_AGGREGATE_NONE);
Assert(patype != PARTITIONWISE_AGGREGATE_PARTIAL ||
@@ -8241,8 +8249,10 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
*/
memcpy(&child_extra, extra, sizeof(child_extra));
- appinfos = find_appinfos_by_relids(root, child_input_rel->relids,
- &nappinfos);
+ appinfos = find_appinfos_by_relids_with_buffer(&appinfos_buffer,
+ root,
+ child_input_rel->relids,
+ &nappinfos);
child_target->exprs = (List *)
adjust_appendrel_attrs(root,
@@ -8296,9 +8306,8 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
grouped_live_children = lappend(grouped_live_children,
child_grouped_rel);
}
-
- pfree(appinfos);
}
+ free_appinfos_buffer(&appinfos_buffer);
/*
* Try to create append paths for partially grouped children. For full
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index eadecd0bb92..10760235697 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -25,6 +25,7 @@
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
+#include "utils/stack_buffer.h"
#include "utils/syscache.h"
@@ -600,6 +601,8 @@ adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,
AppendRelInfo **appinfos;
int nappinfos;
+ DECLARE_STACK_BUFFER();
+
/* Recurse if immediate parent is not the top parent. */
if (childrel->parent != parentrel)
{
@@ -612,11 +615,12 @@ adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,
}
/* Now translate for this child. */
- appinfos = find_appinfos_by_relids(root, childrel->relids, &nappinfos);
+ appinfos = find_appinfos_by_relids(root, childrel->relids,
+ &nappinfos);
node = adjust_appendrel_attrs(root, node, nappinfos, appinfos);
- pfree(appinfos);
+ free_appinfos(appinfos);
return node;
}
@@ -667,6 +671,8 @@ adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,
AppendRelInfo **appinfos;
int nappinfos;
+ DECLARE_STACK_BUFFER();
+
/*
* If the given relids set doesn't contain any of the parent relids, it
* will remain unchanged.
@@ -686,11 +692,13 @@ adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,
}
/* Now translate for this child. */
- appinfos = find_appinfos_by_relids(root, childrel->relids, &nappinfos);
+ appinfos = find_appinfos_by_relids(root,
+ childrel->relids,
+ &nappinfos);
relids = adjust_child_relids(relids, nappinfos, appinfos);
- pfree(appinfos);
+ free_appinfos(appinfos);
return relids;
}
@@ -801,19 +809,19 @@ get_translated_update_targetlist(PlannerInfo *root, Index relid,
* include outer-join RT indexes in addition to baserels. We silently
* ignore the outer joins.
*
- * The AppendRelInfos are returned in an array, which can be pfree'd by the
- * caller. *nappinfos is set to the number of entries in the array.
+ * A caller-supplied output array must have enough space for
+ * bms_num_members(relids) AppendRelInfo pointer. The destination address is
+ * returned.
*/
AppendRelInfo **
-find_appinfos_by_relids(PlannerInfo *root, Relids relids, int *nappinfos)
+find_appinfos_by_relids_in_place(AppendRelInfo **appinfos,
+ PlannerInfo *root,
+ Relids relids,
+ int *nappinfos)
{
- AppendRelInfo **appinfos;
int cnt = 0;
int i;
- /* Allocate an array that's certainly big enough */
- appinfos = palloc_array(AppendRelInfo *, bms_num_members(relids));
-
i = -1;
while ((i = bms_next_member(relids, i)) >= 0)
{
@@ -830,11 +838,11 @@ find_appinfos_by_relids(PlannerInfo *root, Relids relids, int *nappinfos)
appinfos[cnt++] = appinfo;
}
+
*nappinfos = cnt;
return appinfos;
}
-
/*****************************************************************************
*
* ROW-IDENTITY VARIABLE MANAGEMENT
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c
index 8c4bf1d657e..58478747513 100644
--- a/src/backend/partitioning/partprune.c
+++ b/src/backend/partitioning/partprune.c
@@ -460,6 +460,7 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
ListCell *lc;
int rti;
int i;
+ AppendRelInfoBuffer appinfos_buffer = {0};
DECLARE_STACK_BUFFER();
@@ -517,16 +518,17 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
if (!bms_equal(parentrel->relids, subpart->relids))
{
int nappinfos;
- AppendRelInfo **appinfos = find_appinfos_by_relids(root,
- subpart->relids,
- &nappinfos);
+ AppendRelInfo **appinfos;
+
+ appinfos = find_appinfos_by_relids_with_buffer(&appinfos_buffer,
+ root,
+ subpart->relids,
+ &nappinfos);
prunequal = (List *) adjust_appendrel_attrs(root, (Node *)
prunequal,
nappinfos,
appinfos);
-
- pfree(appinfos);
}
partprunequal = prunequal;
@@ -544,6 +546,7 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
subpart,
targetpart);
}
+ free_appinfos_buffer(&appinfos_buffer);
/*
* Convert pruning qual to pruning steps. We may need to do this
diff --git a/src/include/optimizer/appendinfo.h b/src/include/optimizer/appendinfo.h
index b59a6218853..03d44c5ba74 100644
--- a/src/include/optimizer/appendinfo.h
+++ b/src/include/optimizer/appendinfo.h
@@ -38,8 +38,10 @@ extern List *adjust_inherited_attnums_multilevel(PlannerInfo *root,
extern void get_translated_update_targetlist(PlannerInfo *root, Index relid,
List **processed_tlist,
List **update_colnos);
-extern AppendRelInfo **find_appinfos_by_relids(PlannerInfo *root,
- Relids relids, int *nappinfos);
+extern AppendRelInfo **find_appinfos_by_relids_in_place(AppendRelInfo **appinfos,
+ PlannerInfo *root,
+ Relids relids,
+ int *nappinfos);
extern void add_row_identity_var(PlannerInfo *root, Var *orig_var,
Index rtindex, const char *rowid_name);
extern void add_row_identity_columns(PlannerInfo *root, Index rtindex,
@@ -47,4 +49,47 @@ extern void add_row_identity_columns(PlannerInfo *root, Index rtindex,
Relation target_relation);
extern void distribute_row_identity_vars(PlannerInfo *root);
+typedef struct AppendRelInfoBuffer
+{
+ AppendRelInfo **appinfos;
+ int capacity;
+ int want;
+} AppendRelInfoBuffer;
+
+/*
+ * The following are written as a macros so they can use a stack buffer to
+ * place the results in the calling stack frame if possible. A stack buffer
+ * must be declared in the current scope to use them.
+ */
+
+#define find_appinfos_by_relids(root, relids, nappinfos) \
+ find_appinfos_by_relids_in_place(stack_buffer_alloc_array(AppendRelInfo *, \
+ bms_num_members(relids)), \
+ (root), \
+ (relids), \
+ (nappinfos))
+#define free_appinfos(appinfos) \
+ if (appinfos) \
+ stack_buffer_free(appinfos)
+
+#define find_appinfos_by_relids_with_buffer(buffer, root, relids, nappinfos) \
+ ((buffer)->want = bms_num_members(relids), \
+ (buffer)->appinfos = ((buffer)->capacity >= (buffer)->want ? \
+ (buffer)->appinfos : \
+ ((buffer)->capacity = (buffer)->want, \
+ stack_buffer_alloc_array(AppendRelInfo *, \
+ (buffer)->capacity))), \
+ find_appinfos_by_relids_in_place((buffer)->appinfos, \
+ (root), \
+ (relids), \
+ (nappinfos)))
+
+#define free_appinfos_buffer(buffer) \
+ if ((buffer)->appinfos) \
+ { \
+ stack_buffer_free((buffer)->appinfos); \
+ (buffer)->appinfos = NULL; \
+ (buffer)->capacity = 0; \
+ }
+
#endif /* APPENDINFO_H */
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 141b9d6e077..5b9b56d6a0e 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -127,6 +127,7 @@ Append
AppendPath
AppendPathInput
AppendRelInfo
+AppendRelInfoBuffer
AppendState
ApplyErrorCallbackArg
ApplyExecutionData
--
2.53.0
view thread (6+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected]
Subject: Re: A stack allocation API
In-Reply-To: <CA+hUKG+Shdfa_QcVWXrHyhWGDJcPsw2aXWRQxnaUpyUa2fMffA@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox