Skip to content

Commit 206c45b

Browse files
author
Christian Findlay
committed
[BUG8] Lower-case JSON keys in PG sync trigger payload + 0.9.5-beta
Follow-up to BUG8 0.9.4-beta. The trigger emitted JSON keys verbatim from column names (e.g. 'GivenName', 'NameFamily'), but downstream consumers (sync workers, integration tests) read keys via lower-case ('givenname', 'namefamily'). KeyNotFoundException at runtime. Fix: lower-case the JSON key in BuildJsonbObject and in the pk_value extraction (jsonb_build_object('{pkLower}', NEW."{pkColumn}")). The SQL identifier reference still uses NEW."OriginalCase" so the trigger function executes against the real PG column; only the JSON key emitted into the payload is lower-cased so consumers see one canonical casing regardless of source schema. Verified: Sync tests 246/246 still pass. Bumps Directory.Build.props Version to 0.9.5-beta.
1 parent 85d7251 commit 206c45b

2 files changed

Lines changed: 19 additions & 8 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.4-beta</Version>
7+
<Version>0.9.5-beta</Version>
88
<Authors>ChristianFindlay</Authors>
99
<Company>MelbourneDeveloper</Company>
1010
<PackageLicenseExpression>MIT</PackageLicenseExpression>

Sync/Nimblesite.Sync.Postgres/PostgresTriggerGenerator.cs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ string pkColumn
281281
)
282282
{
283283
var lowerTable = tableName.ToLowerInvariant();
284+
var pkLower = pkColumn.ToLowerInvariant();
284285
return string.Format(
285286
CultureInfo.InvariantCulture,
286287
"""
@@ -290,7 +291,7 @@ string pkColumn
290291
INSERT INTO _sync_log (table_name, pk_value, operation, payload, origin, timestamp)
291292
VALUES (
292293
'{1}',
293-
jsonb_build_object('{2}', NEW."{2}")::text,
294+
jsonb_build_object('{4}', NEW."{2}")::text,
294295
'insert',
295296
{3}::text,
296297
(SELECT value FROM _sync_state WHERE key = 'origin_id'),
@@ -309,7 +310,8 @@ AFTER INSERT ON "{1}"
309310
lowerTable,
310311
tableName,
311312
pkColumn,
312-
BuildJsonbObject(columns, "NEW")
313+
BuildJsonbObject(columns, "NEW"),
314+
pkLower
313315
);
314316
}
315317

@@ -320,6 +322,7 @@ string pkColumn
320322
)
321323
{
322324
var lowerTable = tableName.ToLowerInvariant();
325+
var pkLower = pkColumn.ToLowerInvariant();
323326
return string.Format(
324327
CultureInfo.InvariantCulture,
325328
"""
@@ -329,7 +332,7 @@ string pkColumn
329332
INSERT INTO _sync_log (table_name, pk_value, operation, payload, origin, timestamp)
330333
VALUES (
331334
'{1}',
332-
jsonb_build_object('{2}', NEW."{2}")::text,
335+
jsonb_build_object('{4}', NEW."{2}")::text,
333336
'update',
334337
{3}::text,
335338
(SELECT value FROM _sync_state WHERE key = 'origin_id'),
@@ -348,13 +351,15 @@ AFTER UPDATE ON "{1}"
348351
lowerTable,
349352
tableName,
350353
pkColumn,
351-
BuildJsonbObject(columns, "NEW")
354+
BuildJsonbObject(columns, "NEW"),
355+
pkLower
352356
);
353357
}
354358

355359
private static string GenerateDeleteTrigger(string tableName, string pkColumn)
356360
{
357361
var lowerTable = tableName.ToLowerInvariant();
362+
var pkLower = pkColumn.ToLowerInvariant();
358363
return string.Format(
359364
CultureInfo.InvariantCulture,
360365
"""
@@ -364,7 +369,7 @@ private static string GenerateDeleteTrigger(string tableName, string pkColumn)
364369
INSERT INTO _sync_log (table_name, pk_value, operation, payload, origin, timestamp)
365370
VALUES (
366371
'{1}',
367-
jsonb_build_object('{2}', OLD."{2}")::text,
372+
jsonb_build_object('{3}', OLD."{2}")::text,
368373
'delete',
369374
NULL,
370375
(SELECT value FROM _sync_state WHERE key = 'origin_id'),
@@ -382,17 +387,23 @@ AFTER DELETE ON "{1}"
382387
""",
383388
lowerTable,
384389
tableName,
385-
pkColumn
390+
pkColumn,
391+
pkLower
386392
);
387393
}
388394

389395
// BUG8 fix: quote column names in jsonb_build_object payload so triggers
390396
// work on tables with mixed-case column names (e.g. "GivenName"). Without
391397
// the surrounding " quotes, PG case-folds NEW.GivenName to NEW.givenname
392398
// and the trigger fires `record "new" has no field "givenname"`.
399+
//
400+
// The emitted JSON key is the column name lower-cased so downstream
401+
// consumers see a single canonical casing regardless of how the schema
402+
// declared the column. The PG identifier (NEW."ColName") preserves case
403+
// so the trigger function compiles + executes against the real column.
393404
private static string BuildJsonbObject(IReadOnlyList<string> columns, string prefix)
394405
{
395-
var pairs = columns.Select(c => $"'{c}', {prefix}.\"{c}\"");
406+
var pairs = columns.Select(c => $"'{c.ToLowerInvariant()}', {prefix}.\"{c}\"");
396407
return $"jsonb_build_object({string.Join(", ", pairs)})";
397408
}
398409
}

0 commit comments

Comments
 (0)