You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
QUERY: SELECT pg_catalog.pg_get_userbyid(d.defaclrole) AS "Owner",
n.nspname AS "Schema",
CASE d.defaclobjtype WHEN 'r' THEN 'table' WHEN 'S' THEN 'sequence' WHEN 'f' THEN 'function' WHEN 'T' THEN 'type' WHEN 'n' THEN 'schema' END AS "Type",
pg_catalog.array_to_string(d.defaclacl, E'\n') AS "Access privileges"
FROM pg_catalog.pg_default_acl d
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.defaclnamespace
WHERE (n.nspname OPERATOR(pg_catalog.~) E'^(no\\.such\\.default\\.access\\.privilege)$' COLLATE pg_catalog.default
OR pg_catalog.pg_get_userbyid(d.defaclrole) OPERATOR(pg_catalog.~) E'^(no\\.such\\.default\\.access\\.privilege)$' COLLATE pg_catalog.default)
ORDER BY 1, 2, 3;
RECEIVED ERROR: cast from type oid to type oid is mislabeled as binary-coercible (errno 1105) (sqlstate HY000)
QUERY: SELECT pg_catalog.pg_get_userbyid(d.defaclrole) AS "Owner",
n.nspname AS "Schema",
CASE d.defaclobjtype WHEN 'r' THEN 'table' WHEN 'S' THEN 'sequence' WHEN 'f' THEN 'function' WHEN 'T' THEN 'type' WHEN 'n' THEN 'schema' END AS "Type",
pg_catalog.array_to_string(d.defaclacl, E'\n') AS "Access privileges"
FROM pg_catalog.pg_default_acl d
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.defaclnamespace
WHERE (n.nspname OPERATOR(pg_catalog.~) E'^(no\\.such\\.schema.no\\.such\\.default\\.access\\.privilege)$' COLLATE pg_catalog.default
OR pg_catalog.pg_get_userbyid(d.defaclrole) OPERATOR(pg_catalog.~) E'^(no\\.such\\.schema.no\\.such\\.default\\.access\\.privilege)$' COLLATE pg_catalog.default)
ORDER BY 1, 2, 3;
RECEIVED ERROR: cast from type oid to type oid is mislabeled as binary-coercible (errno 1105) (sqlstate HY000)
${\color{lightgreen}Progressions (8)}$
dependency
QUERY: ALTER DEFAULT PRIVILEGES FOR ROLE regress_dep_user1 IN SCHEMA deptest
GRANT ALL ON TABLES TO regress_dep_user2;
event_trigger
QUERY: alter default privileges for role regress_evt_user
revoke delete on tables from regress_evt_user;
matview
QUERY: ALTER DEFAULT PRIVILEGES FOR ROLE regress_matview_user
REVOKE INSERT ON TABLES FROM regress_matview_user;
QUERY: ALTER DEFAULT PRIVILEGES FOR ROLE regress_matview_user
GRANT INSERT ON TABLES TO regress_matview_user;
object_address
QUERY: ALTER DEFAULT PRIVILEGES FOR ROLE regress_addr_user IN SCHEMA public GRANT ALL ON TABLES TO regress_addr_user;
QUERY: ALTER DEFAULT PRIVILEGES FOR ROLE regress_addr_user REVOKE DELETE ON TABLES FROM regress_addr_user;
select_into
QUERY: ALTER DEFAULT PRIVILEGES FOR ROLE regress_selinto_user
REVOKE INSERT ON TABLES FROM regress_selinto_user;
QUERY: ALTER DEFAULT PRIVILEGES FOR ROLE regress_selinto_user
GRANT INSERT ON TABLES TO regress_selinto_user;
Footnotes
These are tests that we're marking as Successful, however they do not match the expected output in some way. This is due to small differences, such as different wording on the error messages, or the column names being incorrect while the data itself is correct. ↩
Across 20 test cases, 12 passed and 8 failed: default-privilege and object-inheritance flows largely behaved correctly (including grant/revoke variants, omitted FOR ROLE behavior, table/routine/sequence coverage, routine write-fault cleanup, and ATTACH/DETACH partition formatting). The key findings were several production defects led by high-severity auth durability/restore problems (non-atomic default-ACL mutation on persist failure, auth.db v2 restart panics, corruption-driven startup crashes, and v1-to-v2 upgrade restart failure), plus catalog stability issues (owner-filter lookups blocked by empty pg_roles and pg_default_acl OID type mismatch panics) and parser/diagnostic gaps (broken PRIMARY KEY USING INDEX parse/format path and generic ALTER INDEX unsupported errors).
❌ Failed (4)
Category
Summary
Screenshot
Catalog
⚠️ Global-scope pg_default_acl verification hit an OID type panic during catalog reads.
Privilege
⚠️ ALTER DEFAULT PRIVILEGES mutates in-memory ACL state before persistence; failed writes can leave restart-divergent authorization behavior.
Serialization
⚠️ auth.db is written as v2 but restart deserializers panic on version 2 privilege payloads.
N/A
Serialization
⚠️ v1-to-v2 upgrade flow can persist v2 state that panics on subsequent restart.
⚠️ OID typing mismatch crashes catalog reads
What failed: Catalog reads can panic with interface conversion: interface {} is uint32, not id.Id, preventing verification of expected global-scope rows (defaclnamespace = 0).
Impact: Queries against pg_default_acl can fail at runtime for valid introspection workflows, blocking visibility into default privilege state. This breaks a core catalog surface for authorization diagnostics.
Steps to reproduce:
Execute global-scope ALTER DEFAULT PRIVILEGES FOR ROLE GRANT SELECT ON TABLES TO without IN SCHEMA.
Query pg_catalog.pg_default_acl.
Read OID-typed fields during projection or filtering.
Stub / mock context: No stubs, mocks, or bypasses were applied for this test in the recorded run.
Code analysis: I traced pg_default_acl row construction and compared it to the OID type contract. The handler emits raw uint32 for OID columns, but the OID type serializer/deserializer expects id.Id; this mismatch explains the runtime interface-conversion panic.
Why this is likely a bug: The handler violates the declared OID runtime type contract (id.Id), which directly causes query-time type conversion failures in production code.
rows=append(rows, sql.Row{
uint32(oid+1), // oid (synthetic, 1-based index)uint32(entry.Key.OwnerRole),
uint32(0), // defaclnamespace: 0 = any schema (schema OID lookup not yet implemented)auth.DefaultPrivilegeObjTypeChar(entry.Key.ObjectType),
aclItems,
})
server/types/oid.go (lines 61-74)
// serializeTypeOid handles serialization from the standard representation to our serialized representation that is// written in Dolt.funcserializeTypeOid(ctx*sql.Context, t*DoltgresType, valany) ([]byte, error) {
return []byte(val.(id.Id)), nil
}
// deserializeTypeOid handles deserialization from the Dolt serialized format to our standard representation used by// expressions and nodes.funcdeserializeTypeOid(ctx*sql.Context, t*DoltgresType, data []byte) (any, error) {
iflen(data) ==0 {
returnnil, nil
}
returnid.Id(data), nil
}
⚠️ Default privilege mutation is not atomic with persistence
What failed: The statement returns a persistence error, but the in-memory default ACL mutation is already applied, so pre-restart behavior can reflect the new default while post-restart behavior (after reloading from durable state) does not.
Impact: Authorization outcomes can diverge across restart boundaries after a write failure, producing inconsistent access control decisions for the same role and object scope. Recovery can include restart instability when corrupted auth state is deserialized.
Steps to reproduce:
Run ALTER DEFAULT PRIVILEGES while forcing persistence to fail at write time.
In the same process, create a new object owned by the altered role and verify inherited privilege behavior.
Restart the server and create another object for the same owner/scope to compare inherited privileges.
Stub / mock context: To exercise persistence-failure handling, the run temporarily forced the auth persistence target into a non-writable state so ALTER DEFAULT PRIVILEGES returned a write error, then restored normal writes for restart comparison.
Code analysis: I inspected default-privilege execution and auth serialization paths and found mutation occurs before PersistChanges, with no rollback when persistence fails. The same code path also writes auth state directly and panics on deserialize errors at startup, which matches the observed restart fragility under failed persistence scenarios.
Why this is likely a bug: The implementation commits default-ACL mutations to in-memory state before durability succeeds and does not revert on write failure, so authorization behavior can legitimately split between pre-restart memory and post-restart persisted state.
What failed: Restart reads auth data as version 2, then panics while deserializing privilege maps that only support versions 0/1, so default privileges are not recoverable after restart.
Impact: Persisting default privileges can make the server fail to start on the next boot. This breaks a core auth workflow and has no practical runtime workaround once the file is written.
Steps to reproduce:
Connect as superuser and configure default table privileges for an owner role.
Restart the server process using persisted auth.db state.
Create a new table as the owner role and verify reader-role access after restart.
Stub / mock context: Deterministic auth setup was used by bypassing SCRAM authentication in server/authentication_scram.go and bypassing domain constraint injection in server/analyzer/domain_constraints.go before running restart checks. This case also manipulated local auth.db files (backup and replacement) to validate restart behavior under persisted state transitions.
Code analysis: I reviewed auth serialization/deserialization flow and privilege map decoders. The serializer writes version 2 and deserializeV2 routes version 2 into legacy privilege deserializers that explicitly panic on unknown versions.
Why this is likely a bug: The code persists version 2 auth data but immediately routes version 2 reads into decoders that intentionally panic on that version, creating a deterministic restart failure path.
Relevant code:
server/auth/serialization.go (lines 36-55)
// Write the versionwriter.Uint32(2)
// Write the database privilegesdb.databasePrivileges.serialize(writer)
// Write the schema privilegesdb.schemaPrivileges.serialize(writer)
// Write the table privilegesdb.tablePrivileges.serialize(writer)
// Write the sequence privilegesdb.sequencePrivileges.serialize(writer)
// Write the routine privilegesdb.routinePrivileges.serialize(writer)
// Write the role chaindb.roleMembership.serialize(writer)
// Write the default privilegesdb.defaultPrivileges.serialize(writer)
server/auth/serialization.go (lines 149-162)
// Read the database privilegesdb.databasePrivileges.deserialize(2, reader)
// Read the schema privilegesdb.schemaPrivileges.deserialize(2, reader)
// Read the table privilegesdb.tablePrivileges.deserialize(2, reader)
// Read the sequence privilegesdb.sequencePrivileges.deserialize(2, reader)
// Read the routine privilegesdb.routinePrivileges.deserialize(2, reader)
// Read the role membershipdb.roleMembership.deserialize(2, reader)
switchversion {
case0, 1:
// Read the total number of valuesdataCount:=reader.Uint64()
fordataIdx:=uint64(0); dataIdx<dataCount; dataIdx++ {
// Read the keyspv:=DatabasePrivilegeValue{Privileges: make(map[Privilege]map[GrantedPrivilege]bool)}
spv.Key.Role=RoleID(reader.Uint64())
spv.Key.Name=reader.String()
}
default:
panic("unexpected version in DatabasePrivileges")
}
⚠️ Upgrade path v1 to v2 break
What failed: After first v2 persistence, restart deserializes with version 2 while multiple privilege and membership decoders still only accept 0/1 and panic, breaking upgrade continuity.
Impact: A v1 environment can appear to upgrade but then fail on the first subsequent restart after v2 persistence. This risks post-upgrade outages in core authorization behavior.
Steps to reproduce:
Start from a v1 auth.db fixture and run the newer binary.
Execute ALTER DEFAULT PRIVILEGES and persist auth state so the file is rewritten as v2.
Restart and verify inherited privileges for newly created objects.
Stub / mock context: Deterministic auth setup was used by bypassing SCRAM authentication in server/authentication_scram.go and bypassing domain constraint injection in server/analyzer/domain_constraints.go. This case also used a synthetic legacy auth.db variant to exercise the v1-to-v2 upgrade-and-restart path.
Code analysis: I inspected upgrade serialization routing and all downstream deserializers used by deserializeV2. Several maps still gate on versions 0/1 only and panic for version 2, so upgrade plus restart is structurally incompatible.
Why this is likely a bug: The upgrade flow writes version 2 data but restart decode paths still reject version 2 in multiple required structures, making the failure reproducible from source logic alone.
Relevant code:
server/auth/serialization.go (lines 149-162)
// Read the database privilegesdb.databasePrivileges.deserialize(2, reader)
// Read the schema privilegesdb.schemaPrivileges.deserialize(2, reader)
// Read the table privilegesdb.tablePrivileges.deserialize(2, reader)
// Read the sequence privilegesdb.sequencePrivileges.deserialize(2, reader)
// Read the routine privilegesdb.routinePrivileges.deserialize(2, reader)
// Read the role membershipdb.roleMembership.deserialize(2, reader)
server/auth/schema_privileges.go (lines 173-200)
switchversion {
case0, 1:
// Read the total number of valuesdataCount:=reader.Uint64()
fordataIdx:=uint64(0); dataIdx<dataCount; dataIdx++ {
// Read the keyspv:=SchemaPrivilegeValue{Privileges: make(map[Privilege]map[GrantedPrivilege]bool)}
spv.Key.Role=RoleID(reader.Uint64())
spv.Key.Schema=reader.String()
}
default:
panic("unexpected version in SchemaPrivileges")
}
server/auth/role_membership.go (lines 141-165)
switchversion {
case0, 1:
// Read the total number of membersmemberCount:=reader.Uint64()
formemberIdx:=uint64(0); memberIdx<memberCount; memberIdx++ {
// Read the number of groupsgroupCount:=reader.Uint64()
groupMap:=make(map[RoleID]RoleMembershipValue)
varmemberRoleID
}
default:
panic("unexpected version in RoleMembership")
}
✅ Passed (12)
Category
Summary
Screenshot
Catalog
Verified pg_default_acl returns rows for configured TABLE and FUNCTION defaults.
Catalog
Verified large ACL arrays remain queryable with 20 ACL entries and valid aclitem formatting.
Object
Old table stayed denied; new table inherited default SELECT grant.
Object
EXECUTE stayed denied on old routines and allowed on newly created routines.
Object
SERIAL table path applied default privileges to both table SELECT and sequence usage.
Object
Forced auth persistence failure returned an explicit error and no callable routine remained.
Parser
Parser round-trip formatted ATTACH PARTITION with child_tbl (not parent_tbl).
Parser
Both DETACH statements formatted with child_tbl and preserved CONCURRENTLY/FINALIZE modifiers.
Privilege
Default table grant applied; grantee could read the newly created table.
Privilege
Default privileges without FOR ROLE resolved to the active owner session and propagated to new tables.
Privilege
REVOKE GRANT OPTION FOR preserved SELECT while full REVOKE removed SELECT for later tables.
Privilege
REVOKE ... CASCADE was rejected and existing default ACL behavior remained unchanged.
ℹ️ Additional Findings (4)
These findings are unrelated to the current changes but were observed during testing.
Category
Summary
Screenshot
Catalog
🟠 Owner-targeted catalog verification could not retrieve matching rows to validate grant-option aclitem formatting.
Parser
🟠 ALTER TABLE ... PRIMARY KEY USING INDEX fails to parse and formatter emits malformed USING INDEX output without separator spacing.
Parser
ℹ️ ALTER INDEX ATTACH PARTITION diagnostics return a generic unsupported error that omits the referenced object names.
Serialization
⚠️ Corrupted auth.db bytes can trigger unchecked deserialization panics during startup.
N/A
🟠 Owner filtered ACL catalog lookup fails
What failed: Owner-scoped validation could not find matching rows needed to confirm role-name and '*' grant-option formatting in defaclacl, even after successful ALTER DEFAULT PRIVILEGES execution.
Impact: Catalog-based tooling and diagnostics that rely on role-filtered default ACL inspection can report missing data even when defaults were configured. This weakens operator visibility into effective privilege state.
Steps to reproduce:
Create owner and grantee roles.
Run ALTER DEFAULT PRIVILEGES FOR ROLE GRANT SELECT ON TABLES TO WITH GRANT OPTION.
Query pg_catalog.pg_default_acl using owner-scoped lookup patterns.
Stub / mock context: No stubs, mocks, or bypasses were applied for this test in the recorded run.
Code analysis: I reviewed the catalog handlers used by owner-scoped checks. pg_default_acl data is emitted, but owner-name based filters depend on role catalog resolution. pg_roles currently returns an empty iterator, which makes owner lookup/join paths non-functional.
Why this is likely a bug: Returning an always-empty pg_roles catalog breaks legitimate owner-scoped catalog queries, so missing rows are caused by production code behavior rather than test setup.
🟠 PRIMARY KEY USING INDEX parser and formatter defects
What failed: Expected the statement to parse and preserve app.orders_idx cleanly, but grammar accepts PRIMARY without KEY in this production and formatter emits USING INDEXapp.orders_idx without required spacing.
Impact: Schema migration workflows that rely on PRIMARY KEY USING INDEX can fail or emit malformed SQL text. Teams may need manual SQL rewrites to proceed.
Steps to reproduce:
Parse ALTER TABLE app.orders ADD CONSTRAINT orders_pkey PRIMARY KEY USING INDEX app.orders_idx.
Format the generated AST back into SQL.
Observe parse failure around USING and formatter output that concatenates USING INDEX with the index name.
Stub / mock context: The run used a local parser harness and rendered deterministic SQL outputs in local browser data pages; no API route interception or bypass flag was used for this test. Temporary local startup patches were applied outside the parser object-name paths under test.
Code analysis: I reviewed parser grammar and tree formatting code in postgres/parser/parser/sql.y and postgres/parser/sem/tree/alter_table.go; the rule for this syntax omits KEY in the accepted production and the formatter concatenates the index name immediately after USING INDEX.
Why this is likely a bug: The production code directly encodes an incomplete grammar token sequence and malformed formatting output for a valid SQL form, so this is a concrete parser/formatter defect rather than test setup noise.
ctx.WriteString(" USING INDEX")
node.Index.Format(ctx)
ℹ️ ALTER INDEX unsupported diagnostic omits object identifiers
What failed: Expected unsupported diagnostics to include identifier context for the referenced index and partition names, but execution always returns a generic message with no object names.
Impact: Unsupported-operation errors provide weak debugging context, which slows troubleshooting when multiple index operations are being tested. Core parser behavior still works, but diagnostics are less actionable.
Steps to reproduce:
Parse ALTER INDEX app.parent_idx ATTACH PARTITION app.child_idx.
Format the AST and confirm qualified object names are preserved.
Trigger the unsupported execution conversion and inspect the returned error text.
Stub / mock context: The run used a local parser harness and rendered deterministic SQL outputs in local browser data pages; no API route interception or bypass flag was used for this test. Temporary local startup patches were applied outside the parser object-name paths under test.
Code analysis: I reviewed parse/format code and the AST conversion path. Parser and formatter preserve qualified partition index names, but server/ast/alter_index.go returns a fixed unsupported message regardless of input identifiers.
Why this is likely a bug: The unsupported error path discards parsed object identity even though the AST preserves it, causing diagnosability loss in production error reporting.
Relevant code:
postgres/parser/parser/sql.y (lines 2029-2031)
| ALTER INDEX table_index_name ATTACH PARTITION db_object_name
{
$$.val = &tree.AlterIndex{Index: $3.tableIndexName(), Cmd: &tree.AlterIndexAttachPartition{Index: $6.unresolvedObjectName()}}
}
// Only PARTITION alterations are supported by the parser, so there's nothing to convert to yetreturnNotYetSupportedError("ALTER INDEX is not yet supported")
⚠️ Corrupted auth database startup crash
What failed: Corrupted bytes can propagate into unchecked reader operations and deserialize errors that are escalated via panic, crashing startup instead of handling corruption safely.
Impact: A damaged auth file can take the service down during boot. Operators get a crash path rather than controlled failure handling or safe recovery.
Steps to reproduce:
Create non-empty default privileges and persist auth state to auth.db.
Truncate or corrupt the persisted auth.db bytes.
Restart the service and observe startup behavior during auth deserialize.
Stub / mock context: Deterministic auth setup was used by bypassing SCRAM authentication in server/authentication_scram.go and bypassing domain constraint injection in server/analyzer/domain_constraints.go. This case intentionally truncated local auth.db bytes to simulate storage corruption before restart.
Code analysis: I traced startup load and low-level decoding. Startup panics directly on deserialize errors, and the reader APIs index and slice the buffer without bounds checks, allowing malformed data to trigger runtime panics.
Why this is likely a bug: Production startup code can panic on malformed persisted auth data instead of failing safely, which is a real reliability defect in core boot-time auth loading.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.