Skip to content

Allow explicit handling of allocation errors#2226

Merged
workingjubilee merged 4 commits into
pgcentralfoundation:developfrom
workingjubilee:oom-for-arrayallocerror
Dec 4, 2025
Merged

Allow explicit handling of allocation errors#2226
workingjubilee merged 4 commits into
pgcentralfoundation:developfrom
workingjubilee:oom-for-arrayallocerror

Conversation

@workingjubilee

@workingjubilee workingjubilee commented Dec 2, 2025

Copy link
Copy Markdown
Member

Right now, we have several fallible constructors for Postgres types. When this is an allocation error, we don't allow handling it. As I expect there to be more such fallible constructors in the future, and transient memory contexts can be arbitrarily limited in size, I think we should expose this problem in otherwise-fallible code. We can retain some infallible constructors elsewhere, though. If nothing else, this can allow better diagnostics via track_caller.

Resolves #2225

@workingjubilee

Copy link
Copy Markdown
Member Author

Huh. I expected it to be more intrusive than this, tbh. Feels hard to argue against.

@workingjubilee workingjubilee marked this pull request as ready for review December 3, 2025 20:24
@workingjubilee workingjubilee changed the title add OutOfMemory variant for ArrayAllocError Allow explicit handling of allocation errors Dec 3, 2025
Comment thread pgrx/src/memcx.rs
let ptr = unsafe { pg_sys::MemoryContextAlloc(self.ptr.as_ptr(), size).cast() };
// SAFETY: `pg_sys::MemoryContextAlloc` is not allowed to return NULL, it must error.
unsafe { NonNull::new_unchecked(ptr) }
pub fn alloc_bytes(&self, size: usize) -> Result<NonNull<u8>, OutOfMemory> {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just changed the signature on this one because breaking users of this "direct allocation" API is something I already did once this cycle: it's now NonNull<u8>, not *mut u8. Also, they probably want to be able to make decisions about error-handling-or-not themselves since they're allocating raw buffers!

Comment thread pgrx/src/palloc/pbox.rs
Comment on lines +36 to +38
#[track_caller]
pub fn new_in(val: T, memcx: &MemCx<'mcx>) -> Self {
PBox::try_new_in(val, memcx).unwrap()

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With track_caller, this way if it panics we point to the place someone called PBox::new_in.

let ndims = N;
if N == 0 {
return Ok(FlatArray::new_empty(memcx));
return FlatArray::new_empty(memcx);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making code like this simpler, even if it's subtle right now, seems like it will be an increasingly bigger win down the line.

Comment on lines 69 to 71
// TODO: remove `non_exhaustive` when the errors have been worked out
/// Errors occurring when constructing fresh arrays
#[non_exhaustive]

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this actually provides the full list of errors we can expect for this type, even in most future APIs.

Suggested change
/// Errors occurring when constructing fresh arrays

@workingjubilee workingjubilee Dec 3, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's one more possible variant, maybe: if we add ways to use a type without a guaranteed-to-be-a-valid-element pg_sys::Oid. But we can apply that as a restriction to implementations of T: Element, so I don't think so?

@workingjubilee workingjubilee merged commit 0d8e302 into pgcentralfoundation:develop Dec 4, 2025
14 checks passed
@workingjubilee workingjubilee deleted the oom-for-arrayallocerror branch December 4, 2025 09:20
Comment thread pgrx/src/memcx.rs
Comment on lines +71 to +80
#[derive(Debug)]
pub struct OutOfMemory {
_reserve: (),
}
impl OutOfMemory {
pub fn new() -> OutOfMemory {
OutOfMemory { _reserve: () }
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth it to have this be an actual Error and store the size it tried to allocate that it couldn't, and have that reported in its Display impl?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

definitely a possibility. I haven't added it yet because I figure the caller usually has that information, but maybe for bubbling up purposes?

Comment thread pgrx/src/memcx.rs
unsafe { NonNull::new_unchecked(ptr) }
pub fn alloc_bytes(&self, size: usize) -> Result<NonNull<u8>, OutOfMemory> {
let flags = (pg_sys::MCXT_ALLOC_NO_OOM) as i32;
let ptr = unsafe { pg_sys::MemoryContextAllocExtended(self.ptr.as_ptr(), size, flags) };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we preemptively check AllocSizeIsValid and/or AllocHugeSizeIsValid. If we don't, then MemoryContextAllocExtended can raise an ERROR.

And how do we expose MCXT_ALLOC_HUGE?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some types will never want to use MCXT_ALLOC_HUGE, so I think we should handle it as its own alloc_extended function and have a newtyped input for the flags.

@workingjubilee workingjubilee Dec 4, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also skip past Extended and expose MemoryContextAllocAligned as the "most complicated alloc" function, but have alignments over the limit return a (richer) Err type... so on pg16 that lets you go up to the 2.pow(26) limit, and on pg15 and under that's 8 (well, align_of::<ffi::c_double>() technically, I believe).

Comment thread pgrx/src/memcx.rs
unsafe { NonNull::new_unchecked(ptr) }
pub fn alloc_zeroed_bytes(&self, size: usize) -> Result<NonNull<u8>, OutOfMemory> {
let flags = (pg_sys::MCXT_ALLOC_NO_OOM | pg_sys::MCXT_ALLOC_ZERO) as i32;
let ptr = unsafe { pg_sys::MemoryContextAllocExtended(self.ptr.as_ptr(), size, flags) };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

unsafe {
let list: *mut pg_sys::List = mcx.alloc_bytes(list_size).cast().as_ptr();
let list: *mut pg_sys::List =
mcx.alloc_bytes(list_size).unwrap().cast().as_ptr();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we want to blindly .unwrap() here? Having the function return Result<&mut ListHead<'cx, T>, OutOfMemory> would be a little awkward but it would also indicate to the caller that it's possible it will allocate.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to rethink how this function works given I explicitly marked it as "unstable" as I didn't have much allocation infrastructure going at the time.

daamien pushed a commit to daamien/pgrx that referenced this pull request Dec 15, 2025
Right now, we have several fallible constructors for Postgres types.
When this is an allocation error, we don't allow handling it.
As I expect there to be more such fallible constructors in the future,
and transient memory contexts can be arbitrarily limited in size,
I think we should expose this problem in otherwise-fallible code.
We can retain some infallible constructors elsewhere, though.
If nothing else, this can allow better diagnostics via `track_caller`.

Resolves pgcentralfoundation#2225
eeeebbbbrrrr added a commit that referenced this pull request Feb 9, 2026
Welcome to pgrx v0.17.0. This is a new minor release that brings a bunch
of internal code refactoring, cleanup, new Postgres headers, and
additional `cargo-pgrx regress` CLI options.

As always, please install the latest `cargo-pgrx` with `cargo install
cargo-pgrx --version 0.17.0 --locked` and also update your extension
crate dependencies with `cargo pgrx upgrade`.

## What's Changed


### New Headers/Symbols

* include `access/tsmapi.h` by @usamoi in
#2155
* Include access/subtrans.h & access/commit_ts.h by @isdaniel in
#2147
* include `utils/guc_tables.h` by @usamoi in
#2243
* Include `scanner.h` in Rust bindings by @piki in
#2163
* Add `commands/publicationcmds.h` to generated `pg_sys` bindings by
@Sinjo in #2221
* Add the ClientAuthentication_hook by @daamien in
#2231
* add storage/dsm_*.h headers by @eeeebbbbrrrr in
#2244
* pg-sys: add catalog/heap.h bindings for PG18 by @charmitro in
#2150
* Add back `peer_dn` field to `Port` on `pg17..` by @workingjubilee in
#2200
* Add `repr(C)` to `struct Port` on `pg13..=16` by @workingjubilee in
#2201

### `cargo-pgrx` improvements

* Add a psql_verbosity parameter for cargo pgrx regress by @daamien in
#2230


### Code Cleanup

* Replace with let-chains in pgrx-bindgen by @workingjubilee in
#2168
* Replace with let-chains in pgrx-sql-entity-graph by @workingjubilee in
#2169
* Restore so-called needless lifetimes by @workingjubilee in
#2167
* Drop stray Postgres 12 support in cargo-pgrx by @workingjubilee in
#2173
* Remove deprecated enum##member constants by @workingjubilee in
#2170
* Remove `variadic!` macro by @workingjubilee in
#2171
* Semi-automated code cleanup by @workingjubilee in
#2189
* Deref instead of borrowing `Option<&String>` by @workingjubilee in
#2185
* Refactor schema.rs using a cargo command builder by @workingjubilee in
#2187
* Replace `impl AsRef<T>` with `&T` by @workingjubilee in
#2182
* Make `PgRelation::heap_relation` unsafe by @workingjubilee in
#2176
* Move `cargo_pgrx::env` to `cargo_pgrx::cargo` by @workingjubilee in
#2186
* Move `CargoProfile` into `cargo_pgrx::cargo` by @workingjubilee in
#2188
* Add Scalar trait for generic handling of primitive arrays by
@workingjubilee in #2153
* Set BGWORKER_SHMEM_ACCESS when building background workers by @cbandy
in #2161
* Stop referencing `pg_config` in schema generation by @workingjubilee
in #2174
* Show line number of failed asserts by @piki in
#2175
* Ghostbust `used_type.rs` a bit by @workingjubilee in
#2190
* Manually bind `static MyProcPort` and `struct Port` by @workingjubilee
in #2162
* Make datetime types import each other via `use super` by
@workingjubilee in #2198
* Migrate datetime types into `pgrx::datetime` by @workingjubilee in
#2199
* Use diagnostic attrs for ABI/SQL-related traits by @workingjubilee in
#2203
* Make `Scalar` provide `pg_sys::Oid` constant by @workingjubilee in
#2209
* Hoist varlena encoding fn by @workingjubilee in
#2208
* Recurse through `T` while anonymizing `&T` by @workingjubilee in
#2212
* `impl BorrowDatum for TimeTz` by @workingjubilee in
#2213
* Change `MemCx::alloc_bytes` to return `NonNull` by @workingjubilee in
#2214
* Add `MemCx<'current, T>` arguments and `PBox<'current, T>` returns by
@workingjubilee in #2210
* Further improve diagnostics for signatures with no SQL form by
@workingjubilee in #2215
* Add `array::Element` and `callconv::DatumPass` by @workingjubilee in
#2218
* Remove unused line in `memcxt_tests.rs` by @workingjubilee in
#2220
* Add `FlatArray<'_, T>` by @workingjubilee in
#2207
* Allow explicit handling of allocation errors by @workingjubilee in
#2226
* Place lbound before updating array len product by @workingjubilee in
#2236
* introduce `#[pg_guard(unsafe_entry_thread)]` by @eeeebbbbrrrr in
#2242


### Project Administrativa 

* Use Rust Edition 2024 by @workingjubilee in
#2165
* Remove testing for deprecated Intel Macs by @workingjubilee in
#2156
* Remove unused Cargo.lock for version-updater by @workingjubilee in
#2180
* Add rust-analyzer.toml by @workingjubilee in
#2179
* Note pgrx requires an unknown minimum Xcode version by @workingjubilee
in #2164

## New Contributors

* @isdaniel made their first contribution in
#2147
* @cbandy made their first contribution in
#2161
* @piki made their first contribution in
#2163
* @Sinjo made their first contribution in
#2221

**Full Changelog**:
v0.16.1...v0.17.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Should ArrayAllocError include an Oom variant?

2 participants