Conversation
A bulk payload sends the union of every row's keys as `columns`, so a column some rows omit was always written as `null` — overwriting its database DEFAULT. `defaultToNull: false` sends `Prefer: missing=default` instead. Sent whenever the option is false, for a single row or an array, matching supabase-js: there the payload-shape check gates only the `columns` parameter. The preference is appended to `Prefer` rather than replacing it, so it composes with `resolution=`, `return=`, `count=` and any caller-supplied value. Scope matches supabase-js: insert and upsert only. PostgREST applies `missing` to PATCH too, but `update` is left alone. Adding the parameter renames both symbols in DocC, so nine existing symbol links across four files gained the new label. Fixes SDK-1613
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueThanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
ABI verdict: not breaking ✅
So adding a defaulted parameter to a public function on a shipped surface does not trip
Useful precedent for the next option added to these builders. Remaining Still a draft pending supabase/sdk#130 (the spec), which is the only thing gating this now. |
Coverage Report for CI Build 33515206474Coverage increased (+0.2%) to 87.54%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
|
The following capabilities are marked
The following capabilities are marked
These may have been renamed, removed, or never registered. Please update the capability matrix. |
Why
A bulk payload sends the union of every row's keys as the
columnsparameter, so a column that only some rows carry is written asnullfor the rows that omit it — overwriting its databaseDEFAULT. PostgREST'sPrefer: missing=defaultasks for the default instead, and Swift had no first-class way to send it. supabase-js exposes this asdefaultToNull.defaultToNull: Bool = trueon the legacyinsertandupsert;falseappendsmissing=defaulttoPrefer.Important
Draft — blocked on the spec PR that documents this capability: supabase/sdk#130 (awaiting CODEOWNERS). Opening as a draft so CI can answer the ABI question below while that review happens.
The ABI question this PR exists to answer
SDK-1613 flags that adding even a defaulted parameter to a public function can be reported as an
API breakage:because the mangled name changes — and this is a shipped surface (the legacy builder, present in v2.55.1), so it matters.I could not answer it locally.
scripts/check-for-breaking-api-changes.shreports❌ Breaking API changes detectedon a tree byte-identical to the baseline — all 8 modules fail to load with alibsecp256k1module redefinition, so every baseline has zero symbols and the non-zero exit is a false positive (rm -rf .buildfirst does not help). The gate is green in CI, so it is a local-environment problem, not a broken gate.So
api-stability.ymlon this PR is the verdict. If it flags the parameter, the fix is to swap it for a new overload rather than carry a!and aV3_MIGRATION.mdentry for a purely additive option — say the word and I will reshape it.Two deviations from the issue, both deliberate
1. The header is not gated on the payload shape. The issue suggested gating it on the existing
[[String: Any]]array check "for exact parity".postgrest-jsdoes the opposite —if (!defaultToNull) headers.append('Prefer', 'missing=default')runs unconditionally, andArray.isArraygates only thecolumnsparameter. Gating would therefore break parity, so the header is sent for a single row too. Harmless there: with nocolumnsparameter, omitted columns already take their default.2. Scope is insert + upsert only. PostgREST applies
missingtoPATCHas well, but supabase-js exposesdefaultToNullonly on these two, soupdateis untouched.Unplanned fallout worth a reviewer's eye
Adding the parameter renames both methods in DocC, which broke nine pre-existing symbol links across
PostgrestRequestBuilder.swift,PostgrestClient.swiftandTypes.swift—./scripts/test-docs.shfailed until each gaineddefaultToNull:. That is most of the non-test diff and the reason it touches four source files instead of one.I also dropped a cross-link I had added from
inserttoupsert: I wrote it against thePostgrestQueryBuildertypealias path, which DocC does not resolve (the type isPostgrestRequestBuilder). Replaced with a prose mention — a hardcoded symbol path that must be re-edited on every signature change is not worth the link.Test plan
New tests in
PostgrestQueryBuilderTests, each asserting the exact fullPreferheader via the existing curl inline snapshots (acontainscheck cannot catch a missing preference):PreferinsertDefaultToNullFalseSendsMissingDefaultreturn=minimal,missing=defaultinsertDefaultToNullFalseComposesWithReturningAndCountreturn=minimal,count=estimated,missing=defaultupsertDefaultToNullFalseKeepsEveryOtherPreferenceexisting=value,resolution=merge-duplicates,return=minimal,count=estimated,missing=defaultupsertDefaultToNullFalseComposesWithIgnoreDuplicatesresolution=ignore-duplicates,return=representation,missing=defaultThe third is the one that matters most: it proves the new preference composes with a caller-supplied value,
resolution=,return=andcount=without dropping any — the failure mode that made #1308 (SDK-1626) a silent data bug."Option off" is covered by the pre-existing
insert/upsert/upsertIgnoreDuplicatessnapshots, which still assert their exactPrefervalues with nomissing=. That they pass unchanged is also the evidence for "existing call sites unchanged".swift test— 1441 tests in 148 suites passed (12 known issues)./scripts/format.sh,./scripts/spell-check.sh,./scripts/test-docs.shall cleanapi-stability.yml— the open question; see abovesdk-compliance.yamlunchanged — it lists symbols without parameter labels (PostgrestRequestBuilder.insert), and no new symbol landsFixes SDK-1613