Conversation
Replace raw SQL with sqlc-generated code for the PostgresIdentityStore, providing compile-time SQL verification and type safety. Changes: - Add sqlc.yaml configuration and generated code (internal/db/) - Add queries/auth.sql with all query definitions - Add schema/auth.sql for sqlc (clean schema without goose markers) - Rewrite auth/postgres_store.go to use sqlc-generated queries - Update app/app.go to handle error from NewPostgresIdentityStore - Add sqlc to dev shell in flake.nix Benefits: - Compile-time SQL syntax validation - Type-safe query parameters and results - Query organization in queries/ directory - Better IDE support and autocomplete - No SQL string formatting in production code The schema_aware.go file provides schema qualification support and security validation for schema names.
kusold
left a comment
There was a problem hiding this comment.
🎨 Code Quality Review
Summary
Solid integration of sqlc for type-safe queries. The refactor eliminates manual SQL string construction, improving safety and maintainability. A few issues need attention around error handling patterns.
Findings
✅ Good:
- Type-safe queries via sqlc eliminates string interpolation SQL injection risks
- Clean separation of SQL into
queries/auth.sqland schema intoschema/auth.sql - Generated
Querierinterface (internal/db/querier.go) enables easy mocking/testing - Proper error wrapping with context (
fmt.Errorf("failed to ...: %w", err)) - Schema name validation added to prevent injection (
validateSchemaName) - Added sqlc to dev dependencies in flake.nix
-
auth/postgres_store.go:57-58and78-79- Error comparison uses fragile string matching:if err.Error() != "no rows in result set" {
Consider using a custom error type or checking if sqlc exposes a sentinel error. String comparison breaks if error messages change.
-
Duplicate validation logic -
validateSchemaNameexists in both:auth/postgres_store.go:209-219internal/db/schema_aware.go:40-53
Implementations differ slightly (postgres_store version checks if starts with digit, schema_aware doesn't). Consolidate into one location.
-
internal/db/schema_aware.go:32-36-WithSchemais a no-op. Either implement or remove if schema handling is fully via pool config. -
internal/db/schema_aware.go:55-57-QuoteIdentifieris defined but unused. Remove dead code. -
auth/postgres_store.go:73-80- Manual pgtype construction is verbose. Consider a helper for common conversions likepgtype.UUID.
🔴 Issues:
- Missing test coverage - The refactored auth store lacks tests. The
Querierinterface makes this straightforward - please add tests for the identity store using a mock or test database.
Overall direction is great. The string-based error check is the main concern to address before merge.
kusold
left a comment
There was a problem hiding this comment.
🔒 Security Review
Summary
This PR significantly improves security posture by replacing string-interpolated SQL queries with sqlc-generated parameterized queries. The changes eliminate the primary SQL injection vectors from the legacy code. A few minor issues were identified for cleanup.
Findings
🔴 Critical:
- None identified
- Fragile error handling: String comparison for "no rows" errors (
err.Error() != "no rows in result set") is brittle and could break across pgx versions. Consider usingpgx.ErrNoRowsor wrapping with sentinel errors. - Dead code with inconsistent validation:
internal/db/schema_aware.gocontains aValidateSchemaNamefunction that allows schema names starting with digits, whileauth/postgres_store.gohas a stricter version that doesn't. TheSchemaAwareQueriestype appears unused—consider removing this file to avoid confusion.
✅ Good:
- SQL injection eliminated: All queries now use parameterized statements via sqlc (
, , ...placeholders) - Schema name validation:
validateSchemaName()enforces alphanumeric + underscore, max 63 chars, can't start with digit - Role constraint: Database CHECK constraint limits roles to
owner,admin,member - Proper error propagation: Constructor now returns errors for invalid schema names
- Type safety: UUID types enforced at compile time, preventing type confusion attacks
- No secrets in code: Database credentials remain externalized in configuration
kusold
left a comment
There was a problem hiding this comment.
🧪 Testing Review
Summary
This PR introduces sqlc integration with significant refactoring of PostgresIdentityStore, but no new tests were added. The PR description claims "all existing tests pass" but the code paths being modified (PostgresIdentityStore, validateSchemaName) don't appear to have existing unit test coverage.
Findings
🔴 Missing Tests:
-
validateSchemaName() - New security-critical function with no tests. Edge cases to cover:
- Empty string (valid)
- Max length (63 chars) vs 64 chars
- Starting with digit (should fail)
- Special characters, spaces, hyphens (should fail)
- SQL injection attempts like
'; DROP TABLE-- - Unicode characters
-
NewPostgresIdentityStore() error handling - Constructor now returns error, but no tests verify:
- Invalid schema names are rejected
- Valid schema names are accepted
- Default tenant name fallback still works
-
Error handling in ResolveOrProvisionUser - Changed from
pgx.ErrNoRowsto string comparisonerr.Error() != "no rows in result set". This is fragile and untested:- What if the error message changes?
- Consider using errors.Is() or sqlc's :batchone with proper error types
-
firstTenantOrCreate() - Complex logic with multiple failure paths, no coverage for:
- Tenant creation failure scenarios
- Race conditions (two concurrent calls creating duplicate tenants)
-
Fragile error detection: Using
err.Error() != "no rows in result set"instead of proper error type checking. Consider defining a custom error or usingerrors.Is()pattern. -
Generated code testing: While sqlc generates correct code, the integration between PostgresIdentityStore and the generated queries should have integration tests.
✅ Good Coverage:
- Existing
handler_test.gotests forresolveSubjectFromClaimsremain intact - Type safety improvements from sqlc reduce runtime SQL errors
- sqlc-generated code is validated by sqlc's own test suite
Recommendation
Consider adding unit tests for at least validateSchemaName() and the constructor error handling before merging. Integration tests for the full user provisioning flow would be ideal but could be a follow-up.
kusold
left a comment
There was a problem hiding this comment.
📚 Documentation Review
Summary
The PR description is excellent with comprehensive documentation of changes, benefits, and migration path. However, the public Go APIs in this PR lack godoc documentation, which is important for a framework like gotchi that will be used by other developers.
Findings
🔴 Missing Docs:
PostgresStoreConfig- Struct fieldsSchemaandDefaultTenantNamelack documentationPostgresIdentityStore- Struct and its fields (pool,queries,cfg) are undocumentedNewPostgresIdentityStore- No godoc explaining what it does, when to use it, or what errors it returns (now returns error)ResolveOrProvisionUser- Public method lacks godoc explaining behavior (resolves existing user or creates new one)ListMemberships- No godoc explaining parameters and return valuesGetTenantDisplay- No godoc for this public methodfirstTenantOrCreate/createMembership- Private helper methods could use brief comments
- Error comparison pattern
err.Error() != "no rows in result set"is fragile (string comparison). Consider usingpgx.ErrNoRowsor adding a comment explaining why this pattern is used.
✅ Well Documented:
- PR description - Excellent comprehensive documentation
queries/auth.sql- sqlc query names are self-documenting (GetUserByIdentifier,InsertUser, etc.)schema/auth.sql- Schema is clear and self-documentingvalidateSchemaName- Has helpful comment explaining validation rules- Generated code (
internal/db/*.go) - Appropriately marked as generated, no additional docs needed internal/db/schema_aware.go- Has function-level comments for public API
Recommendation
Add godoc comments to the public types and methods in auth/postgres_store.go. This is especially important for a framework where users will interact with these APIs directly.
- Replace fragile string-based error comparison with proper pgx.ErrNoRows check - Add errors import for proper error handling with wrapped errors - Remove dead code in internal/db/schema_aware.go with weaker validation Security improvements: - Use errors.Is() for proper error comparison instead of string matching - Eliminate potential issues from string-based error checks - Remove duplicate validation function with weaker security
Add comprehensive tests for the schema name validation function to prevent SQL injection attacks via dynamic schema names. Test cases cover: - Valid schemas (empty, alphanumeric, underscores, max length) - Invalid schemas (starts with digit, special chars, SQL injection) - Edge cases (hyphens, spaces, dots, too long) Addresses testing review finding: missing tests for validateSchemaName(). Note: Integration tests for NewPostgresIdentityStore() and firstTenantOrCreate() require test database infrastructure - deferred for future PR.
Summary
Replace raw SQL with sqlc-generated code in the
PostgresIdentityStore, providing compile-time SQL verification and type safety.Changes
New Files
sqlc.yaml- sqlc configuration filequeries/auth.sql- SQL query definitions (sqlc format)schema/auth.sql- Clean schema for sqlc (without goose markers)internal/db/- sqlc-generated Go code (DO NOT EDIT manually)Modified Files
auth/postgres_store.go- Rewritten to use sqlc-generated queriesapp/app.go- Handle error fromNewPostgresIdentityStoreflake.nix- Add sqlc to dev shellDeleted Files
auth/postgres_store_legacy.go- Old implementation removedBenefits
Compile-Time Safety
Type Safety
Maintainability
queries/directoryDeveloper Experience
Migration Path
db.QueriesTesting
Implementation Details
Schema Qualification
The schema name is passed to sqlc during code generation.
When the PostgresIdentityStore connects to the database, it sets
the
search_pathto include the configured schema, ensuringqueries use the correct tables.
Security
validateSchemaName()function ensures schema names are safe:Generated Code
The generated code includes:
Future Enhancements
This integration enables:
[Optional] Checklist: