Skip to content

Commit c119ffa

Browse files
committed
fix(postgresql): lazy-init cloudsync context per call
Avoid relcache/snapshot leaks during CREATE EXTENSION by moving SPI-dependent init to normal function calls. Add cloudsync_pg_ensure_initialized helper, drop SPI work from _PG_init, and wire initialization into SQL entry points so context loads on demand.
1 parent ce4cdfb commit c119ffa

File tree

1 file changed

+50
-29
lines changed

1 file changed

+50
-29
lines changed

src/postgresql/cloudsync_postgresql.c

Lines changed: 50 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -62,42 +62,47 @@ static cloudsync_context *get_cloudsync_context(void) {
6262

6363
// MARK: - Extension Entry Points -
6464

65-
void _PG_init (void) {
66-
// Extension initialization
67-
// SPI will be connected per-function call
68-
elog(DEBUG1, "CloudSync extension loading");
69-
70-
// Initialize memory debugger (NOOP in production)
71-
cloudsync_memory_init(1);
72-
73-
// load config, if exists
74-
cloudsync_context *data = get_cloudsync_context();
75-
76-
int spi_rc = SPI_connect();
77-
if (spi_rc != SPI_OK_CONNECT) {
78-
ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("SPI_connect failed: %d", spi_rc)));
65+
static void cloudsync_pg_ensure_initialized (cloudsync_context *data, bool spi_connected) {
66+
if (!data) return;
67+
if (data->site_id[0] != 0) return;
68+
69+
if (!spi_connected) {
70+
int spi_rc = SPI_connect();
71+
if (spi_rc != SPI_OK_CONNECT) {
72+
ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("SPI_connect failed: %d", spi_rc)));
73+
}
7974
}
80-
75+
8176
PG_TRY();
8277
{
8378
if (cloudsync_config_exists(data)) {
8479
if (cloudsync_context_init(data) == NULL) {
8580
ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("An error occurred while trying to initialize context")));
8681
}
87-
82+
8883
// make sure to update internal version to current version
8984
dbutils_settings_set_key_value(data, CLOUDSYNC_KEY_LIBVERSION, CLOUDSYNC_VERSION);
9085
}
91-
SPI_finish();
86+
87+
if (!spi_connected) SPI_finish();
9288
}
9389
PG_CATCH();
9490
{
95-
SPI_finish();
91+
if (!spi_connected) SPI_finish();
9692
PG_RE_THROW();
9793
}
9894
PG_END_TRY();
9995
}
10096

97+
void _PG_init (void) {
98+
// Extension initialization
99+
// SPI will be connected per-function call
100+
elog(DEBUG1, "CloudSync extension loading");
101+
102+
// Initialize memory debugger (NOOP in production)
103+
cloudsync_memory_init(1);
104+
}
105+
101106
void _PG_fini (void) {
102107
// Extension cleanup
103108
elog(DEBUG1, "CloudSync extension unloading");
@@ -124,6 +129,7 @@ Datum pg_cloudsync_siteid (PG_FUNCTION_ARGS) {
124129
UNUSED_PARAMETER(fcinfo);
125130

126131
cloudsync_context *data = get_cloudsync_context();
132+
cloudsync_pg_ensure_initialized(data, false);
127133
const void *siteid = cloudsync_siteid(data);
128134

129135
if (!siteid) {
@@ -169,6 +175,7 @@ Datum cloudsync_db_version (PG_FUNCTION_ARGS) {
169175

170176
PG_TRY();
171177
{
178+
cloudsync_pg_ensure_initialized(data, true);
172179
int rc = cloudsync_dbversion_check_uptodate(data);
173180
if (rc != DBRES_OK) {
174181
ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("Unable to retrieve db_version (%s)", database_errmsg(data))));
@@ -205,6 +212,7 @@ Datum cloudsync_db_version_next (PG_FUNCTION_ARGS) {
205212

206213
PG_TRY();
207214
{
215+
cloudsync_pg_ensure_initialized(data, true);
208216
int64_t next_version = cloudsync_dbversion_next(data, merging_version);
209217
SPI_finish();
210218

@@ -233,6 +241,7 @@ static bytea *cloudsync_init_internal (cloudsync_context *data, const char *tabl
233241

234242
PG_TRY();
235243
{
244+
cloudsync_pg_ensure_initialized(data, true);
236245
// Begin savepoint for transactional init
237246
int rc = database_begin_savepoint(data, "cloudsync_init");
238247
if (rc != DBRES_OK) {
@@ -258,11 +267,10 @@ static bytea *cloudsync_init_internal (cloudsync_context *data, const char *tabl
258267

259268
cloudsync_update_schema_hash(data);
260269

261-
// Build site_id as TEXT to return
262-
char buffer[UUID_STR_MAXLEN];
263-
cloudsync_uuid_v7_stringify(cloudsync_siteid(data), buffer, false);
264-
result = cstring_to_text(buffer);
265-
ereport(DEBUG1, (errmsg("cloudsync_init_internal uuid %s", buffer)));
270+
// Build site_id as bytea to return
271+
result = (bytea *)palloc(UUID_LEN + VARHDRSZ);
272+
SET_VARSIZE(result, UUID_LEN + VARHDRSZ);
273+
memcpy(VARDATA(result), cloudsync_siteid(data), UUID_LEN);
266274

267275
SPI_finish();
268276
}
@@ -325,6 +333,8 @@ Datum cloudsync_enable (PG_FUNCTION_ARGS) {
325333
}
326334

327335
const char *table = text_to_cstring(PG_GETARG_TEXT_PP(0));
336+
cloudsync_context *data = get_cloudsync_context();
337+
cloudsync_pg_ensure_initialized(data, false);
328338
cloudsync_enable_disable(table, true);
329339
PG_RETURN_BOOL(true);
330340
}
@@ -337,6 +347,8 @@ Datum cloudsync_disable (PG_FUNCTION_ARGS) {
337347
}
338348

339349
const char *table = text_to_cstring(PG_GETARG_TEXT_PP(0));
350+
cloudsync_context *data = get_cloudsync_context();
351+
cloudsync_pg_ensure_initialized(data, false);
340352
cloudsync_enable_disable(table, false);
341353
PG_RETURN_BOOL(true);
342354
}
@@ -349,6 +361,7 @@ Datum cloudsync_is_enabled (PG_FUNCTION_ARGS) {
349361
}
350362

351363
cloudsync_context *data = get_cloudsync_context();
364+
cloudsync_pg_ensure_initialized(data, false);
352365
const char *table_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
353366
cloudsync_table_context *table = table_lookup(data, table_name);
354367

@@ -375,6 +388,7 @@ Datum pg_cloudsync_cleanup (PG_FUNCTION_ARGS) {
375388

376389
PG_TRY();
377390
{
391+
cloudsync_pg_ensure_initialized(data, true);
378392
int rc = cloudsync_cleanup(data, table);
379393
SPI_finish();
380394

@@ -406,6 +420,7 @@ Datum pg_cloudsync_terminate (PG_FUNCTION_ARGS) {
406420

407421
PG_TRY();
408422
{
423+
cloudsync_pg_ensure_initialized(data, true);
409424
int rc = cloudsync_terminate(data);
410425
SPI_finish();
411426
PG_RETURN_INT32(rc);
@@ -449,6 +464,7 @@ Datum cloudsync_set (PG_FUNCTION_ARGS) {
449464

450465
PG_TRY();
451466
{
467+
cloudsync_pg_ensure_initialized(data, true);
452468
dbutils_settings_set_key_value(data, key, value);
453469
SPI_finish();
454470
PG_RETURN_BOOL(true);
@@ -487,6 +503,7 @@ Datum cloudsync_set_table (PG_FUNCTION_ARGS) {
487503

488504
PG_TRY();
489505
{
506+
cloudsync_pg_ensure_initialized(data, true);
490507
dbutils_table_settings_set_key_value(data, tbl, "*", key, value);
491508
SPI_finish();
492509
PG_RETURN_BOOL(true);
@@ -531,6 +548,7 @@ Datum cloudsync_set_column (PG_FUNCTION_ARGS) {
531548

532549
PG_TRY();
533550
{
551+
cloudsync_pg_ensure_initialized(data, true);
534552
dbutils_table_settings_set_key_value(data, tbl, col, key, value);
535553
SPI_finish();
536554
PG_RETURN_BOOL(true);
@@ -562,6 +580,7 @@ Datum pg_cloudsync_begin_alter (PG_FUNCTION_ARGS) {
562580

563581
PG_TRY();
564582
{
583+
cloudsync_pg_ensure_initialized(data, true);
565584
int rc = cloudsync_begin_alter(data, table_name);
566585
SPI_finish();
567586

@@ -598,6 +617,7 @@ Datum pg_cloudsync_commit_alter (PG_FUNCTION_ARGS) {
598617

599618
PG_TRY();
600619
{
620+
cloudsync_pg_ensure_initialized(data, true);
601621
int rc = cloudsync_commit_alter(data, table_name);
602622
SPI_finish();
603623

@@ -639,6 +659,7 @@ Datum cloudsync_payload_encode_transfn (PG_FUNCTION_ARGS) {
639659

640660
int argc = 0;
641661
cloudsync_context *data = get_cloudsync_context();
662+
cloudsync_pg_ensure_initialized(data, false);
642663
pgvalue_t **argv = pgvalues_from_args(fcinfo, 1, &argc);
643664

644665
// Wrap variadic args into pgvalue_t so pk/payload helpers can read types safely.
@@ -667,6 +688,7 @@ Datum cloudsync_payload_encode_finalfn (PG_FUNCTION_ARGS) {
667688

668689
cloudsync_payload_context *payload = (cloudsync_payload_context *)PG_GETARG_POINTER(0);
669690
cloudsync_context *data = get_cloudsync_context();
691+
cloudsync_pg_ensure_initialized(data, false);
670692

671693
int rc = cloudsync_payload_encode_final(payload, data);
672694
if (rc != DBRES_OK) {
@@ -716,6 +738,7 @@ Datum cloudsync_payload_decode (PG_FUNCTION_ARGS) {
716738

717739
PG_TRY();
718740
{
741+
cloudsync_pg_ensure_initialized(data, true);
719742
int nrows = 0;
720743
int rc = cloudsync_payload_apply(data, payload, blen, &nrows);
721744
SPI_finish();
@@ -777,6 +800,7 @@ static void cloudsync_pg_cleanup(int code, Datum arg) {
777800
PG_FUNCTION_INFO_V1(cloudsync_is_sync);
778801
Datum cloudsync_is_sync (PG_FUNCTION_ARGS) {
779802
cloudsync_context *data = get_cloudsync_context();
803+
cloudsync_pg_ensure_initialized(data, false);
780804

781805
if (cloudsync_insync(data)) {
782806
PG_RETURN_BOOL(true);
@@ -799,6 +823,7 @@ Datum cloudsync_seq (PG_FUNCTION_ARGS) {
799823
UNUSED_PARAMETER(fcinfo);
800824

801825
cloudsync_context *data = get_cloudsync_context();
826+
cloudsync_pg_ensure_initialized(data, false);
802827
int seq = cloudsync_bumpseq(data);
803828

804829
PG_RETURN_INT32(seq);
@@ -930,9 +955,7 @@ Datum cloudsync_insert (PG_FUNCTION_ARGS) {
930955

931956
PG_ENSURE_ERROR_CLEANUP(cloudsync_pg_cleanup, PointerGetDatum(&cleanup));
932957
{
933-
if (cloudsync_context_init(data) == NULL) {
934-
ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("Unable to initialize cloudsync context")));
935-
}
958+
cloudsync_pg_ensure_initialized(data, true);
936959

937960
// Lookup table (load from settings if needed)
938961
cloudsync_table_context *table = table_lookup(data, table_name);
@@ -1029,9 +1052,7 @@ Datum cloudsync_delete (PG_FUNCTION_ARGS) {
10291052

10301053
PG_ENSURE_ERROR_CLEANUP(cloudsync_pg_cleanup, PointerGetDatum(&cleanup));
10311054
{
1032-
if (cloudsync_context_init(data) == NULL) {
1033-
ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("Unable to initialize cloudsync context")));
1034-
}
1055+
cloudsync_pg_ensure_initialized(data, true);
10351056

10361057
cloudsync_table_context *table = table_lookup(data, table_name);
10371058
if (!table) {

0 commit comments

Comments
 (0)