Skip to content

Commit 85d7251

Browse files
author
Christian Findlay
committed
[BUG8] Quote NEW/OLD column refs + table name in PG sync trigger + 0.9.4-beta
PostgresTriggerGenerator emitted `NEW.{column}` / `OLD.{column}` without surrounding double-quotes in the generated trigger function body. Postgres case-folds unquoted identifiers to lowercase, so when a table has mixed-case columns (e.g. fhir_patient."GivenName") the trigger fires: ERROR: 42703: record "new" has no field "givenname" and the originating INSERT/UPDATE/DELETE on the source table fails with HTTP 500. Same root as BUG3/BUG6/BUG7: every emitted SQL identifier needs to be wrapped in " so it survives PG case-folding. Fix: - Quote NEW."{pkColumn}" / OLD."{pkColumn}" in jsonb_build_object pk_value extraction (insert/update/delete trigger functions). - Quote NEW."{c}" in BuildJsonbObject for the full payload. - Quote the table name in CREATE TRIGGER ... ON "{tableName}" and in the matching DROP TRIGGER IF EXISTS so triggers attach to mixed- case tables too. Verified: Sync tests 246/246 pass. Bumps Directory.Build.props Version to 0.9.4-beta.
1 parent 4b74ee5 commit 85d7251

2 files changed

Lines changed: 15 additions & 11 deletions

File tree

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<NuGetAudit>false</NuGetAudit>
55
<NuGetAuditMode>disabled</NuGetAuditMode>
66
<RestoreAuditProperties>false</RestoreAuditProperties>
7-
<Version>0.9.3-beta</Version>
7+
<Version>0.9.4-beta</Version>
88
<Authors>ChristianFindlay</Authors>
99
<Company>MelbourneDeveloper</Company>
1010
<PackageLicenseExpression>MIT</PackageLicenseExpression>

Sync/Nimblesite.Sync.Postgres/PostgresTriggerGenerator.cs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ string pkColumn
290290
INSERT INTO _sync_log (table_name, pk_value, operation, payload, origin, timestamp)
291291
VALUES (
292292
'{1}',
293-
jsonb_build_object('{2}', NEW.{2})::text,
293+
jsonb_build_object('{2}', NEW."{2}")::text,
294294
'insert',
295295
{3}::text,
296296
(SELECT value FROM _sync_state WHERE key = 'origin_id'),
@@ -301,9 +301,9 @@ INSERT INTO _sync_log (table_name, pk_value, operation, payload, origin, timesta
301301
END;
302302
$$ LANGUAGE plpgsql;
303303
304-
DROP TRIGGER IF EXISTS {0}_sync_insert ON {1};
304+
DROP TRIGGER IF EXISTS {0}_sync_insert ON "{1}";
305305
CREATE TRIGGER {0}_sync_insert
306-
AFTER INSERT ON {1}
306+
AFTER INSERT ON "{1}"
307307
FOR EACH ROW EXECUTE FUNCTION {0}_sync_insert_fn();
308308
""",
309309
lowerTable,
@@ -329,7 +329,7 @@ string pkColumn
329329
INSERT INTO _sync_log (table_name, pk_value, operation, payload, origin, timestamp)
330330
VALUES (
331331
'{1}',
332-
jsonb_build_object('{2}', NEW.{2})::text,
332+
jsonb_build_object('{2}', NEW."{2}")::text,
333333
'update',
334334
{3}::text,
335335
(SELECT value FROM _sync_state WHERE key = 'origin_id'),
@@ -340,9 +340,9 @@ INSERT INTO _sync_log (table_name, pk_value, operation, payload, origin, timesta
340340
END;
341341
$$ LANGUAGE plpgsql;
342342
343-
DROP TRIGGER IF EXISTS {0}_sync_update ON {1};
343+
DROP TRIGGER IF EXISTS {0}_sync_update ON "{1}";
344344
CREATE TRIGGER {0}_sync_update
345-
AFTER UPDATE ON {1}
345+
AFTER UPDATE ON "{1}"
346346
FOR EACH ROW EXECUTE FUNCTION {0}_sync_update_fn();
347347
""",
348348
lowerTable,
@@ -364,7 +364,7 @@ private static string GenerateDeleteTrigger(string tableName, string pkColumn)
364364
INSERT INTO _sync_log (table_name, pk_value, operation, payload, origin, timestamp)
365365
VALUES (
366366
'{1}',
367-
jsonb_build_object('{2}', OLD.{2})::text,
367+
jsonb_build_object('{2}', OLD."{2}")::text,
368368
'delete',
369369
NULL,
370370
(SELECT value FROM _sync_state WHERE key = 'origin_id'),
@@ -375,9 +375,9 @@ INSERT INTO _sync_log (table_name, pk_value, operation, payload, origin, timesta
375375
END;
376376
$$ LANGUAGE plpgsql;
377377
378-
DROP TRIGGER IF EXISTS {0}_sync_delete ON {1};
378+
DROP TRIGGER IF EXISTS {0}_sync_delete ON "{1}";
379379
CREATE TRIGGER {0}_sync_delete
380-
AFTER DELETE ON {1}
380+
AFTER DELETE ON "{1}"
381381
FOR EACH ROW EXECUTE FUNCTION {0}_sync_delete_fn();
382382
""",
383383
lowerTable,
@@ -386,9 +386,13 @@ AFTER DELETE ON {1}
386386
);
387387
}
388388

389+
// BUG8 fix: quote column names in jsonb_build_object payload so triggers
390+
// work on tables with mixed-case column names (e.g. "GivenName"). Without
391+
// the surrounding " quotes, PG case-folds NEW.GivenName to NEW.givenname
392+
// and the trigger fires `record "new" has no field "givenname"`.
389393
private static string BuildJsonbObject(IReadOnlyList<string> columns, string prefix)
390394
{
391-
var pairs = columns.Select(c => $"'{c}', {prefix}.{c}");
395+
var pairs = columns.Select(c => $"'{c}', {prefix}.\"{c}\"");
392396
return $"jsonb_build_object({string.Join(", ", pairs)})";
393397
}
394398
}

0 commit comments

Comments
 (0)