Extension initialization
Use [PgInitialize] on one accessible, synchronous, non-generic, parameterless
static void method per extension assembly:
public static class Startup{ [PgInitialize] public static void Initialize() { PgLog.Write(PgLogLevel.Notice, "Extension initialized."); }}Ankus generates PostgreSQL’s _PG_init entry point. PostgreSQL calls it before
the first extension function. A library with only an initializer can load with:
LOAD 'Ankus.Examples.Initialization';Installing an extension does not load an initializer-only library.
Native hook and provider registration
Section titled “Native hook and provider registration”Use [PgModuleLoad] when native registration must happen as soon as PostgreSQL
loads the library, before a parallel worker’s first executor entry. It uses the
same method requirements as [PgInitialize]. An assembly may declare one method
for each phase; a single method cannot declare both attributes.
public static class Startup{ [PgModuleLoad] public static void Register() { PgLog.Write(PgLogLevel.Notice, "Extension module loaded."); }
[PgInitialize] public static void Initialize() { PgLog.Write(PgLogLevel.Notice, "Extension initialized."); }}For native registration, see the complete
executor hook example.
Ankus registers configuration settings before calling PgModuleLoad, then calls
PgInitialize. A parallel worker loads libraries before it restores the
leader’s settings, and PgModuleLoad runs during that load. On PostgreSQL
13–17, where the worker restores libraries and settings together before starting
its parallel transaction, PgInitialize waits until restoration finishes. On 18
and later it runs during the load, before the leader’s settings are restored, as
pgrx’s _PG_init does on every version. SQL is unavailable during early
registration, including in nested native callbacks. To see the leader’s session
settings on every version, read them when a function runs rather than during
initialization.
On PostgreSQL 13–16, worker SQL follows the parallel error boundary described in
SPI queries.
Each successful phase runs once. If PgModuleLoad fails, the next load retries it.
A callback that the failed attempt already installed also retries registration
before its handler runs. If PgInitialize fails after successful registration,
registration remains in place and only initialization retries. Installed native
hooks and managed state are not automatically rolled back; registration code owns
their cleanup. Record each hook installation as soon as it succeeds, so a retry
does not install the same hook again and save it as its own previous hook. See
idempotent hook installation
and the hooks sample.
Database access and failure
Section titled “Database access and failure”Spi works when PostgreSQL loads the library inside a transaction. It is not
available during postmaster startup. Transaction-dependent APIs throw
InvalidOperationException when no transaction exists. Logging and configuration
reads remain available.
Managed exceptions unwind finally blocks before Ankus raises a PostgreSQL
error. PgException preserves SQLSTATE, message, detail, and hint. Other managed
exceptions use SQLSTATE 38000.
Initialization can run again after a failed load. Managed static changes are not
rolled back. SQL changes follow the surrounding transaction or savepoint. Loading
the same library recursively raises SQLSTATE 55000.
Preloading
Section titled “Preloading”Use session_preload_libraries to initialize separately in each backend:
session_preload_libraries = 'Ankus.Examples.Initialization'PostgreSQL 15 and later load session libraries inside the startup transaction,
so an initializer can use Spi. PostgreSQL 13 and 14 load them after that
transaction ends: configuration reads, logging and memory operations remain
available, but SQL is rejected. Perform database work from a later function call
on those versions.
Use shared_preload_libraries to initialize when the server starts:
shared_preload_libraries = 'Ankus.Examples.Initialization'On Linux and macOS, the postmaster initializes once and each forked backend
receives its own copy of the initialized managed state. Windows starts each backend
as a new process, so every backend loads the library and runs PgModuleLoad and
PgInitialize again; shared-memory descriptors attach to the existing values.
Changes made in one backend do not change another backend or the postmaster. Tasks, timers,
garbage collection, finalizers, and managed configuration hooks continue to work
after startup. Extension projects use this automatically; no host setup is needed.
On Linux and macOS, the postmaster retires runtime service threads between managed callbacks so PostgreSQL can fork safely. Complete explicitly created threads before returning from a preload callback. Timers and queued work resume when the runtime reenters or a backend starts; a continuously running postmaster thread does not fit PostgreSQL’s Unix process model.
To share values across backends, register static PgShared<T>, PgLwLock<T> or PgAtomic<T> descriptors from
PgModuleLoad using PgSharedMemory.Initialize. See
shared memory, locks and atomics for storage and guard lifetimes.
To register a PostgreSQL process that runs a managed entry, use
PgBackgroundWorker.Register during shared preload. See
background workers for registration, signals and database transactions.
Declaration diagnostics
Section titled “Declaration diagnostics”Each initialization error identifies the declaration requirement and highlights
the relevant source. The same rules apply to [PgInitialize] and
[PgModuleLoad]. A valid callback remains a synchronous, accessible,
nongeneric, parameterless static void method with a managed implementation.
| Diagnostic | Correction |
|---|---|
ANKUS230 |
Declare separate methods for the two phases. |
ANKUS231 |
Use an ordinary named method instead of a local function, lambda or explicit interface implementation. |
ANKUS232, ANKUS233 |
Declare a static callback and complete its work synchronously on the backend thread. |
ANKUS234 |
Remove callback method type parameters. |
ANKUS235, ANKUS236, ANKUS237 |
Provide a concrete, nonvirtual managed implementation; callbacks cannot be abstract or extern. |
ANKUS238, ANKUS239 |
Return void and remove callback parameters. |
ANKUS240, ANKUS244 |
Make the method and enclosing types accessible to generated code: public, internal, or protected internal. |
ANKUS241 |
Implement the partial method definition. |
ANKUS242, ANKUS243 |
Use nongeneric enclosing types that are not file-local. |
ANKUS245, ANKUS246 |
Remove Conditional or UnmanagedCallersOnly; the callback must be callable unconditionally from managed code. |
ANKUS247, ANKUS248 |
Declare separate SQL exports and remove SQL result metadata from initialization. |
ANKUS249 |
Keep one callback per phase in the extension assembly. |
Editors offer Make static for ANKUS232, Make synchronous for
ANKUS233, Make internal for ANKUS240, ANKUS243 and ANKUS244, and
Remove for ANKUS245, ANKUS246 and ANKUS248.
Invalid initialization declarations do not remove unrelated valid SQL dispatchers. A rejected phase does not receive a PostgreSQL loader export.