refactor: PrestConf, Postgres adapter and connection management#972
Conversation
- Added a new Postgres adapter with connection pooling and database interaction capabilities. - Implemented interfaces for database connection, access, and pinging. - Refactored existing mock tests to utilize the new adapter structure. - Enhanced error handling and configuration management for database connections. - Introduced integration tests for the Postgres adapter to ensure functionality and reliability.
- Introduced logsafe package to redact sensitive information in error messages. - Updated error logging across the Postgres adapter to use logsafe for improved security. - Refactored connection management to utilize singleflight for deduplication of database connection requests. - Added unit tests for logsafe functionality to ensure proper redaction of sensitive data. - Improved connection pool management with RWMutex for better concurrency handling.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
💤 Files with no reviewable changes (1)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughThis PR removes global mutable configuration and package-level Postgres/middleware state, replacing them with config-scoped constructors, instance-backed adapters, and a new app composition root. It also updates CLI wiring, controllers, router, plugins, and integration tests to use the new dependency flow. ChangesGlobal config removal and dependency-injected wiring
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks 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 |
There was a problem hiding this comment.
Actionable comments posted: 19
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
cmd/root.go (1)
53-55: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winRedact command errors before sending them to
slog.Subcommands can still return raw DB/bootstrap errors here, so this path can leak credentials into stderr or CI logs unless it goes through
logsafe.Error.💡 Local fix
+ "github.com/prest/prest/v2/internal/logsafe" + if err := RootCmd.Execute(); err != nil { - slog.Error("executing root command", "err", err) + slog.Error("executing root command", "err", logsafe.Error(err)) os.Exit(1) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cmd/root.go` around lines 53 - 55, RootCmd.Execute currently logs raw errors with slog.Error, which can leak sensitive DB/bootstrap details. Update the error handling in RootCmd.Execute’s failure path to pass the error through logsafe.Error before logging, and keep the existing command-execution context in the message while using the redacted value for the error field.integration/adapters/postgres/postgres_test.go (1)
2456-2481: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winReset
AccessConf.Usersper case instead of appending to it.Only
Tablesis cleared here.Userskeeps growing with duplicatefoo/barentries, so later cases can resolve permissions from stale earlier definitions instead of the case under test.💡 Local fix
- testCfg.AccessConf.Tables = []config.TablesConf{} - testCfg.AccessConf.Tables = append(testCfg.AccessConf.Tables, - config.TablesConf{ - Name: "test_field_permission", - Permissions: []string{"read", "write", "delete"}, - Fields: tt.args.fields, - }) - - testCfg.AccessConf.Users = append(testCfg.AccessConf.Users, - config.UsersConf{ + testCfg.AccessConf.Tables = []config.TablesConf{{ + Name: "test_field_permission", + Permissions: []string{"read", "write", "delete"}, + Fields: tt.args.fields, + }} + + testCfg.AccessConf.Users = []config.UsersConf{ + { Name: "foo", Tables: []config.TablesConf{{ Name: "test_field_permission", Permissions: []string{"read", "write", "delete"}, Fields: tt.args.userFields, }}, }, - config.UsersConf{ + { Name: "bar", Tables: []config.TablesConf{{ Name: "test_field_permission", Permissions: []string{"read", "write", "delete"}, Fields: tt.args.user2Fields, }}, - }) + }, + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@integration/adapters/postgres/postgres_test.go` around lines 2456 - 2481, Reset AccessConf.Users for each test case instead of appending to the existing slice. In the postgres test setup around the field-permission cases, clear testCfg.AccessConf.Users alongside testCfg.AccessConf.Tables before rebuilding the per-case config, so the UsersConf entries for foo and bar come only from tt.args.userFields and tt.args.user2Fields and do not carry over stale definitions between cases.adapters/postgres/queries.go (1)
126-145: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
WriteSQLCtxdoes not execute with the supplied context.After
adapter.dbFromCtx(ctx), this path prepares and executes via non-context calls, soExecuteScriptsCtxwill not respect cancellation or deadlines for write scripts.Also applies to: 183-188
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@adapters/postgres/queries.go` around lines 126 - 145, WriteSQLCtx is ignoring the supplied context after dbFromCtx(ctx), so prepare/exec won’t honor cancellation or deadlines. Update WriteSQLCtx (and the similar ExecuteScriptsCtx path referenced in the review) to use context-aware database/statement operations throughout, keeping the context from the call all the way through prepare and execution. Use the existing postgres methods and scanner.PrestScanner error handling, but switch the non-context calls in WriteSQLCtx to their ctx-aware equivalents so cancellation propagates correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@adapters/postgres/internal/connection/conn_test.go`:
- Around line 65-67: The dbConnect test stub in conn_test.go is calling
require.Equal inside a spawned goroutine, which can trigger unsafe FailNow
behavior. Update the dbConnect replacement used by the test so it only captures
the driverName/dataSourceName values or any error inside the goroutine, then
perform the require.Equal assertions back in the parent test goroutine around
the connTest/connection flow.
In `@adapters/postgres/internal/connection/conn.go`:
- Around line 25-30: `Manager` has unsynchronized mutable fields, so concurrent
calls can race when `getPool()` initializes `m.pool` and when `currDatabase` is
read or updated during database switching. Add synchronization in `Manager` (or
guard these fields with the existing pool locking strategy) so `getPool`, the
database selection path, and any methods that read/write `currDatabase` access
shared state safely; use the `Manager` type and `getPool()` as the main fix
points.
- Around line 164-166: The error handling in the database connection path logs a
sanitized error but still panics with the raw err, which can leak sensitive
details. In the connection logic around the existing slog.Error call in conn.go,
update the panic in the same err != nil block to use the redacted/logsafe
version of the error as well, keeping the panic message consistent with the
sanitized logging.
In `@adapters/postgres/postgres.go`:
- Around line 98-105: The lazy initialization in postgres.getStmts() can race
when multiple requests hit the shared adapter instance at first use. Guard the
p.stmts creation with synchronization (for example, reuse an existing mutex on
postgres or add a dedicated init lock) so only one goroutine can initialize the
Stmt struct. Keep the change localized to getStmts() and the postgres / Stmt
fields involved.
- Around line 179-185: The `...Ctx` transaction/write paths still drop the
request context after selecting the DB, so canceled requests can continue via
non-context calls. Update `GetTransactionCtx` and the other affected `...Ctx`
helpers to pass `ctx` through to `BeginTx`, `ExecContext`, `QueryContext`, and
`QueryRowContext` instead of `db.Begin`, `stmt.Exec`, `stmt.Query`, or helper
methods that use non-context variants. Make the same change in the related
write/transaction entrypoints referenced by the review so all context-aware
paths in `postgres` honor cancellation consistently.
- Around line 904-909: The read-path error logging in postgres still emits raw
connection errors, which can leak DSN credentials. Update the error handling in
QueryCtx and the other affected read paths (including the dbFromCtx/conn.Get
call sites in the postgres adapter) to use the same redacted logging approach as
adapters/postgres/queries.go, replacing direct slog.Error(err) logging with
logsafe.Error(err) or equivalent safe redaction. Keep the existing return
behavior unchanged, but ensure the logged error is sanitized wherever these
connection failures can occur.
In `@app/app.go`:
- Around line 26-31: New currently always creates and connects a new Postgres
adapter, which overwrites any adapter already present on cfg.Adapter. Update New
to first check cfg.Adapter and reuse it when non-nil, only calling postgres.New
and postgres.Connect when no adapter has been injected. Keep the cfg.Adapter
assignment in the connect path so the existing adapter reference is preserved
and the new adapter is only created when needed.
- Around line 70-73: The error handling in app initialization is too broad: in
the db, err := postgres.DB(cfg.Adapter) path, only the sentinel type-assertion
failure should become ErrAdapterNotPostgres. Update the error translation in the
DB() setup logic to detect that specific sentinel and return it, while
preserving and returning any other error from the adapter’s DB() implementation
unchanged.
In `@cmd/prestd/main.go`:
- Around line 19-25: The startup flow in main currently calls app.New(cfg)
before Cobra command selection, which eagerly connects to Postgres and breaks
non-serve subcommands. Move app.New creation out of the top-level startup path
and initialize it only in the serve execution path, while keeping DB-dependent
subcommands like migrate and version working off the parsed cfg after Cobra has
selected the command. Use the existing main, cmd.Execute, and app.New symbols to
relocate the initialization lazily.
- Around line 19-22: The startup error logging in main should not write err
directly because it can expose DSNs or credentials. Update the app
initialization failure path around app.New in main to pass the error through the
internal/logsafe redaction helper before calling slog.Error, and keep the
existing exit behavior unchanged.
In `@config/config.go`:
- Around line 134-149: The Load path still only logs when required directories
cannot be created, so startup can continue with an unusable queries/cache setup.
Update the directory preparation logic in Load to fail fast by returning the
MkdirAll error for the queries directory and the cache storage directory,
instead of just calling slog.Error and continuing; if any directory is meant to
be optional, make that behavior explicit in the cfg handling before setupLogger
is called.
- Around line 130-153: The Load flow still relies on the package-level Viper
singleton, so config paths can leak across repeated calls. Update Load to create
a fresh Viper instance with viper.New(), then pass that instance into Parse and
any helper that reads config so each call is isolated. Keep the existing Load,
viperCfg, and Parse flow intact otherwise, but ensure the new instance is used
for all config resolution instead of the shared global state.
In `@controllers/health_db.go`:
- Around line 7-12: The health check setup currently leaves DefaultCheckList
empty, so controllers/deps.go can build a health handler with no DB verification
when NewHandlersFromConfig/NewDepsFromConfig is used outside app.New. Restore
the ping-based DB check by wiring CheckDBHealth(pinger.Ping) into the
dependency/handler construction path, or repopulate DefaultCheckList for
DatabasePinger-backed adapters so every health endpoint includes the DB check.
Use the existing symbols DefaultCheckList, CheckDBHealth, NewHandlersFromConfig,
and NewDepsFromConfig to place the fix.
In `@integration/adapters/postgres/postgres_test.go`:
- Around line 30-44: The shared test config/adapter setup in setupTestDB and
testAdapter is reusing the same mutable *config.Prest and Adapter instance from
helpers.LoadTestConfig(t), which makes the postgres tests order-dependent and
unsafe for parallel execution. Update setupTestDB to clone the loaded config for
each test and construct a fresh adapter instance from that copy instead of
returning testCfg.Adapter directly, so each test gets isolated AccessConf,
cache, and statement-cache state.
In `@integration/helpers/setup.go`:
- Around line 67-98: LoadTestConfig is returning a shared cached *config.Prest
from the package-level loadedCfg, which lets per-test mutations leak across
callers and makes tests order-dependent. Keep the one-time config/database setup
in loadOnce, but have LoadTestConfig create and return a fresh copy of the
initialized config on each call (including the Adapter and Debug settings)
instead of handing out loadedCfg directly. Use the LoadTestConfig function and
the loadedCfg/loadOnce state as the main places to update.
In `@internal/logsafe/logsafe.go`:
- Around line 9-10: The password redaction regex in passwordKV currently stops
matching when the value starts with quotes, so quoted DSN passwords can leak.
Update the pattern and any related masking logic in logsafe.go so values like
password='...' and password="..." are also fully redacted, while preserving the
existing pgURLCreds handling for URL-style credentials. Make sure the change is
applied where the regexes are defined and verified against quoted and unquoted
password forms.
In `@plugins/plugins.go`:
- Around line 67-87: Return an error instead of panicking when plugin symbols
are malformed in plugins.go. In the code path that looks up HTTPVars, URLQuery,
and the handler symbol via p.Lookup and then asserts to func() string / func()
(string, int), switch to the same checked type-assertion pattern used in the
middleware loader so bad plugins flow through the handler error path. Update the
logic around the plugin lookup and the handler selection in the relevant plugin
execution function to validate each symbol before dereferencing or calling it,
and surface a descriptive error when the expected type is not present.
- Around line 43-47: The plugin cache maps in `loadedFunc` and
`loadedMiddlewareFunc` are accessed concurrently by `Handler()` and
`Middleware()` without synchronization, which can trigger concurrent map access
panics. Add a small mutex guard around each cache’s read/write path in the
plugin loading flow, keeping the locking local to the loader logic that consults
and updates these maps.
- Around line 66-87: The request-handling code in the plugin lookup path is
mutating shared plugin globals by assigning mux Vars and URL query data into
HTTPVars and URLQuery, which can be overwritten by concurrent requests. Update
the plugin invocation flow around the Lookup calls in the handler dispatch logic
to avoid writing request-specific data into package-level symbols; instead pass
vars/query into the per-request handler call or protect the entire plugin
invocation with serialization so each request sees isolated data.
---
Outside diff comments:
In `@adapters/postgres/queries.go`:
- Around line 126-145: WriteSQLCtx is ignoring the supplied context after
dbFromCtx(ctx), so prepare/exec won’t honor cancellation or deadlines. Update
WriteSQLCtx (and the similar ExecuteScriptsCtx path referenced in the review) to
use context-aware database/statement operations throughout, keeping the context
from the call all the way through prepare and execution. Use the existing
postgres methods and scanner.PrestScanner error handling, but switch the
non-context calls in WriteSQLCtx to their ctx-aware equivalents so cancellation
propagates correctly.
In `@cmd/root.go`:
- Around line 53-55: RootCmd.Execute currently logs raw errors with slog.Error,
which can leak sensitive DB/bootstrap details. Update the error handling in
RootCmd.Execute’s failure path to pass the error through logsafe.Error before
logging, and keep the existing command-execution context in the message while
using the redacted value for the error field.
In `@integration/adapters/postgres/postgres_test.go`:
- Around line 2456-2481: Reset AccessConf.Users for each test case instead of
appending to the existing slice. In the postgres test setup around the
field-permission cases, clear testCfg.AccessConf.Users alongside
testCfg.AccessConf.Tables before rebuilding the per-case config, so the
UsersConf entries for foo and bar come only from tt.args.userFields and
tt.args.user2Fields and do not carry over stale definitions between cases.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: fee4fe9e-331f-4e46-9499-e73ad614bacd
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (57)
adapters/connection.goadapters/mock/mock.goadapters/mock/mock_test.goadapters/postgres/adapter.goadapters/postgres/connection.goadapters/postgres/internal/connection/conn.goadapters/postgres/internal/connection/conn_test.goadapters/postgres/internal/connection/test_exports.goadapters/postgres/postgres.goadapters/postgres/postgres_exec_test.goadapters/postgres/postgres_test.goadapters/postgres/queries.goadapters/postgres/queries_test.goadapters/postgres/test_exports.goapp/app.goapp/app_test.goapp/errors.gocmd/auth.gocmd/migrate.gocmd/prestd/main.gocmd/root.goconfig/config.goconfig/config_test.gocontrollers/auth.gocontrollers/auth_test.gocontrollers/deps.gocontrollers/health_db.gocontrollers/healthcheck.gogo.modintegration/adapters/connection/conn_test.gointegration/adapters/postgres/postgres_test.gointegration/adapters/postgres/queries_test.gointegration/controllers/auth_test.gointegration/controllers/crud_test.gointegration/controllers/scripts_test.gointegration/helpers/setup.gointegration/middlewares/config_test.gointegration/plugins/handler_test.gointegration/plugins/middleware_test.gointegration/router/routes_test.gointernal/logsafe/logsafe.gointernal/logsafe/logsafe_test.gomiddlewares/cache.gomiddlewares/cache_test.gomiddlewares/config.gomiddlewares/config_testhooks.gomiddlewares/crud_stack.gomiddlewares/crud_stack_test.gomiddlewares/middlewares.gomiddlewares/middlewares_test.gomiddlewares/test_exports.gomiddlewares/utils.gomiddlewares/utils_test.goplugins/handler.goplugins/middleware.goplugins/plugins.gorouter/router.go
💤 Files with no reviewable changes (2)
- plugins/middleware.go
- plugins/handler.go
… handling - Introduced comprehensive unit tests for plugin HTTP variable and URL query assignment. - Added tests for invoking plugin handlers with different return types. - Implemented concurrency tests to ensure thread-safe access to plugin mutexes and loaded function caches. - Enhanced the plugin loading mechanism with mutexes to prevent race conditions during concurrent access.
- Updated LoadTestConfig to return fresh shallow copies of the configuration to prevent test interference. - Enhanced the logsafe package to support redaction of passwords in various quoted formats within error messages. - Added comprehensive unit tests for the logsafe functionality to ensure proper handling of sensitive data.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
config/config.go (1)
163-246: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
configFileremains a mutated package-level global despite the Viper-singleton fix.
viperCfg()still assigns to the package-levelconfigFilevar on every call, later read unsynchronized inParse()(line 262). This is inconsistent with the PR's goal of eliminating global config state and is a latent data race ifLoad()/viperCfg()is ever called concurrently (e.g. parallel tests). Consider returning the resolved path fromviperCfg()(or storing it on the*Prestbeing built) instead of a package var.♻️ Possible approach
-func viperCfg() *viper.Viper { +func viperCfg() (*viper.Viper, string) { v := viper.New() - configFile = getPrestConfFile(os.Getenv("PREST_CONF")) + configFile := getPrestConfFile(os.Getenv("PREST_CONF")) ... - return v + return v, configFile }Then thread
configFileintoParse(v, cfg, configFile)instead of reading the package var.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@config/config.go` around lines 163 - 246, The config path is still stored in the package-level configFile global inside viperCfg(), which leaves mutable shared state and can race with Parse() reading it later. Stop assigning to the package var in viperCfg(); instead return the resolved config file path from viperCfg() (or attach it to the Prest/config object being built) and pass that value into Parse() explicitly so Load(), viperCfg(), and Parse() no longer depend on unsynchronized global state.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@config/config.go`:
- Around line 163-246: The config path is still stored in the package-level
configFile global inside viperCfg(), which leaves mutable shared state and can
race with Parse() reading it later. Stop assigning to the package var in
viperCfg(); instead return the resolved config file path from viperCfg() (or
attach it to the Prest/config object being built) and pass that value into
Parse() explicitly so Load(), viperCfg(), and Parse() no longer depend on
unsynchronized global state.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 81d586c8-42df-4822-854e-0541912e6ed4
📒 Files selected for processing (18)
adapters/postgres/internal/connection/conn.goadapters/postgres/internal/connection/conn_test.goadapters/postgres/internal/connection/test_exports.goadapters/postgres/postgres.goadapters/postgres/queries.goapp/app.gocmd/prestd/main.gocmd/root.goconfig/config.goconfig/config_test.gocontrollers/deps.gocontrollers/health_db.gointegration/adapters/postgres/postgres_test.gointegration/helpers/setup.gointernal/logsafe/logsafe.gointernal/logsafe/logsafe_test.goplugins/plugins.goplugins/plugins_test.go
🚧 Files skipped from review as they are similar to previous changes (10)
- internal/logsafe/logsafe.go
- adapters/postgres/internal/connection/test_exports.go
- controllers/health_db.go
- adapters/postgres/internal/connection/conn_test.go
- integration/helpers/setup.go
- adapters/postgres/queries.go
- adapters/postgres/internal/connection/conn.go
- internal/logsafe/logsafe_test.go
- integration/adapters/postgres/postgres_test.go
- adapters/postgres/postgres.go
…t support - Introduced PrepareContext method in the Postgres adapter to allow statement preparation with context for cancellation and deadline support. - Updated existing WriteSQLCtx function to utilize the new PrepareContext method for improved error handling and context management. - Enhanced the Postgres adapter's concurrency handling by implementing caching for prepared statements.
- Modified the configuration parsing functions to return the configuration path alongside the viper instance. - Updated all relevant tests to utilize the new configuration path parameter, ensuring consistency in configuration handling. - Enhanced logging to reflect the correct configuration file being used during parsing.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@adapters/postgres/postgres.go`:
- Around line 158-180: The prepared-statement cache in Stmt.PrepareContext is
keyed only by SQL, so statements can be reused across different selected
databases. Update the cache to be DB-scoped (for example by changing PrepareMap
to use a per-DB key or per-DB nested map) and keep the lookup/insert logic
inside PrepareContext aligned with that key. Also re-check the cache after
preparing under lock, and if another goroutine already stored a statement, close
the duplicate prepared statement before returning the cached one to avoid leaks.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: cef55b8c-ab6a-4f85-ac56-69b868568c0f
📒 Files selected for processing (4)
adapters/postgres/postgres.goadapters/postgres/queries.gocmd/root.gointegration/adapters/postgres/postgres_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
- adapters/postgres/queries.go
- cmd/root.go
- integration/adapters/postgres/postgres_test.go
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
config/config.go (2)
129-142: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winReturn non-ENOENT
os.Staterrors.
os.Statfailures likeEACCESare notIsNotExist, soLoad()can continue with an inaccessible queries/cache path.Proposed fix
- if _, err := os.Stat(cfg.QueriesPath); os.IsNotExist(err) { + if _, err := os.Stat(cfg.QueriesPath); err != nil { + if !os.IsNotExist(err) { + return nil, fmt.Errorf("stat queries directory %q: %w", cfg.QueriesPath, err) + } if err = os.MkdirAll(cfg.QueriesPath, 0700); err != nil { return nil, fmt.Errorf("create queries directory %q: %w", cfg.QueriesPath, err) } } @@ - if _, err := os.Stat(cfg.Cache.StoragePath); os.IsNotExist(err) { + if _, err := os.Stat(cfg.Cache.StoragePath); err != nil { + if !os.IsNotExist(err) { + return nil, fmt.Errorf("stat cache directory %q: %w", cfg.Cache.StoragePath, err) + } if err = os.MkdirAll(cfg.Cache.StoragePath, 0700); err != nil { return nil, fmt.Errorf("create cache directory %q: %w", cfg.Cache.StoragePath, err) } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@config/config.go` around lines 129 - 142, Handle non-ENOENT failures from the os.Stat checks in Load so inaccessible queries/cache paths do not get ignored. In the cfg.QueriesPath and cfg.Cache.StoragePath existence checks, only create the directory when os.IsNotExist(err) is true; if Stat returns any other error, return it immediately instead of continuing. Keep the existing setupLogger flow unchanged, and apply the fix in the Load path around the os.Stat/MkdirAll logic.
237-242: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winSet a fallback
queries.locationwhen home lookup fails.Without this,
cfg.QueriesPathcan remain empty and make default startup fail in environments without a resolvable home directory.Proposed fix
hDir, err := homedir.Dir() if err != nil { slog.Error("could not find homedir", "err", err) + v.SetDefault("queries.location", "./queries") } else { v.SetDefault("queries.location", filepath.Join(hDir, "queries")) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@config/config.go` around lines 237 - 242, The homedir lookup in the config initialization leaves queries.location unset on failure, which can later make cfg.QueriesPath empty. Update the logic around homedir.Dir() in the config setup to always set a fallback default for queries.location when the home directory cannot be resolved, while still logging the lookup failure with slog.Error. Use the existing configuration flow that sets v.SetDefault("queries.location", ...) so startup has a usable path even without a resolvable home directory.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@config/config.go`:
- Line 128: Load() is swallowing config read/parse failures by calling Parse(v,
cfg, configPath) and then continuing, which lets malformed or unreadable config
files fall back to defaults. Update Load() in config/config.go to detect and
return an error for any config read/parse failure, while still allowing only the
missing-config case to proceed with defaults; use the Parse call site and the
Load() flow to locate where to thread the error back to the caller instead of
logging and continuing.
---
Outside diff comments:
In `@config/config.go`:
- Around line 129-142: Handle non-ENOENT failures from the os.Stat checks in
Load so inaccessible queries/cache paths do not get ignored. In the
cfg.QueriesPath and cfg.Cache.StoragePath existence checks, only create the
directory when os.IsNotExist(err) is true; if Stat returns any other error,
return it immediately instead of continuing. Keep the existing setupLogger flow
unchanged, and apply the fix in the Load path around the os.Stat/MkdirAll logic.
- Around line 237-242: The homedir lookup in the config initialization leaves
queries.location unset on failure, which can later make cfg.QueriesPath empty.
Update the logic around homedir.Dir() in the config setup to always set a
fallback default for queries.location when the home directory cannot be
resolved, while still logging the lookup failure with slog.Error. Use the
existing configuration flow that sets v.SetDefault("queries.location", ...) so
startup has a usable path even without a resolvable home directory.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: bc2c4847-b4eb-4879-8ade-05d7dafc1019
📒 Files selected for processing (2)
config/config.goconfig/config_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
- config/config_test.go
…d sanitization - Updated the Postgres adapter to support caching of prepared statements on a per-database basis, improving performance and resource management. - Introduced a new method, CacheKeyForDB, to generate stable cache keys for database connections. - Added unit tests for the new caching behavior and for sanitizing script parameters to prevent SQL injection vulnerabilities. - Enhanced the extractQueryParameters function to sanitize unsafe query values, ensuring better security in handling user input.
- Improved the Load and Parse functions to handle errors more gracefully, returning specific error messages for configuration issues. - Added unit tests for handling malformed configuration files and permission errors when accessing directories. - Refactored tests to utilize a helper function for parsing configurations, ensuring consistency and reducing code duplication. - Updated documentation for the Load function to clarify its behavior and error handling.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
adapters/postgres/postgres_exec_test.go (1)
267-288: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winTest verifies per-DB routing but not actual cache reuse.
TestQuery_WithStatementCachePerDatabaseconfirmsQueryandQueryCtxhit the correct mock DB, but each mock only expects a singlePrepare/Querypair, so it doesn't actually prove the statement is cached and reused across repeated calls to the same database. Consider callingadapter.Query("SELECT 1")twice againstdefaultMockwith only oneExpectPrepareto assert the cache is actually hit on the second call.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@adapters/postgres/postgres_exec_test.go` around lines 267 - 288, The per-database cache test in TestQuery_WithStatementCachePerDatabase only checks routing to defaultMock and ctxMock once each, so it does not verify statement reuse. Update the test to call adapter.Query("SELECT 1") twice for the same database with only one ExpectPrepare on defaultMock, and similarly ensure QueryCtx against contextMockDB can reuse the prepared statement on a repeated call. Keep the existing Query and QueryCtx paths, but make the expectations prove the cache hit occurs by not allowing a second prepare on the same mock.config/config_test.go (1)
56-89: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winPermission-denial test may be flaky under root or on Windows.
inaccessiblePathrelies onos.Chmod(restricted, 0000)to force a permission-deniedos.Stat. This breaks down when the test process runs as root (common in Docker-based CI images), since root bypasses Unix permission bits and theStatwould succeed instead of failing withos.ErrPermission; it's also unreliable on Windows, which doesn't enforce POSIX permission bits the same way. Consider skipping these subtests whenos.Geteuid() == 0(Unix) or gating them behindruntime.GOOS != "windows".🔧 Suggested guard
func TestLoadStatErrors(t *testing.T) { + if runtime.GOOS == "windows" || os.Geteuid() == 0 { + t.Skip("permission-denial simulation not reliable as root or on windows") + } inaccessiblePath := func(t *testing.T) string {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@config/config_test.go` around lines 56 - 89, The permission-denial assertions in TestLoadStatErrors are platform-dependent because inaccessiblePath uses os.Chmod to simulate os.ErrPermission. Update the test setup so the subtests are skipped or gated when running as root on Unix (using os.Geteuid() == 0) and when the runtime is Windows, and keep the Load-based error checks only for environments where permission bits are enforced. Use the existing TestLoadStatErrors and inaccessiblePath helpers to place the guard cleanly around both subtests.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@adapters/postgres/postgres_exec_test.go`:
- Around line 267-288: The per-database cache test in
TestQuery_WithStatementCachePerDatabase only checks routing to defaultMock and
ctxMock once each, so it does not verify statement reuse. Update the test to
call adapter.Query("SELECT 1") twice for the same database with only one
ExpectPrepare on defaultMock, and similarly ensure QueryCtx against
contextMockDB can reuse the prepared statement on a repeated call. Keep the
existing Query and QueryCtx paths, but make the expectations prove the cache hit
occurs by not allowing a second prepare on the same mock.
In `@config/config_test.go`:
- Around line 56-89: The permission-denial assertions in TestLoadStatErrors are
platform-dependent because inaccessiblePath uses os.Chmod to simulate
os.ErrPermission. Update the test setup so the subtests are skipped or gated
when running as root on Unix (using os.Geteuid() == 0) and when the runtime is
Windows, and keep the Load-based error checks only for environments where
permission bits are enforced. Use the existing TestLoadStatErrors and
inaccessiblePath helpers to place the guard cleanly around both subtests.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 256e217d-7bda-4684-998b-fb8dda1af160
📒 Files selected for processing (8)
adapters/postgres/internal/connection/conn.goadapters/postgres/postgres.goadapters/postgres/postgres_exec_test.goconfig/config.goconfig/config_test.gocontrollers/script.gocontrollers/script_test.gointegration/adapters/postgres/postgres_test.go
🚧 Files skipped from review as they are similar to previous changes (4)
- adapters/postgres/internal/connection/conn.go
- config/config.go
- adapters/postgres/postgres.go
- integration/adapters/postgres/postgres_test.go
- Introduced a new test suite for the New function, covering scenarios with both existing and nil adapters. - Enhanced error handling tests to ensure proper behavior when the database connection cannot be established. - Updated documentation for the New function to clarify its behavior regarding adapter handling and error conditions.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
adapters/postgres/postgres.go (1)
1167-1219: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winPropagate ctx into prepare on the write paths.
BatchInsertCopyCtxstill usestx.Prepare(...), andInsertCtx/DeleteCtx/UpdateCtxstill flow throughfullInsert/Prepare(...)instead ofPrepareContext(...), so cancellation can still be dropped before execution.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@adapters/postgres/postgres.go` around lines 1167 - 1219, BatchInsertCopyCtx and the other write helpers are still preparing statements without honoring cancellation from the passed context. Update BatchInsertCopyCtx to use the context-aware prepare path on the tx, and make InsertCtx, DeleteCtx, UpdateCtx, and the shared fullInsert/Prepare flow use PrepareContext instead of Prepare so ctx cancellation can propagate before execution. Use the existing symbols BatchInsertCopyCtx, fullInsert, and the Prepare calls to locate and adjust the write paths consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@adapters/postgres/postgres.go`:
- Around line 1167-1219: BatchInsertCopyCtx and the other write helpers are
still preparing statements without honoring cancellation from the passed
context. Update BatchInsertCopyCtx to use the context-aware prepare path on the
tx, and make InsertCtx, DeleteCtx, UpdateCtx, and the shared fullInsert/Prepare
flow use PrepareContext instead of Prepare so ctx cancellation can propagate
before execution. Use the existing symbols BatchInsertCopyCtx, fullInsert, and
the Prepare calls to locate and adjust the write paths consistently.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 89b57364-4629-4c7f-bae9-dc1897c88b66
📒 Files selected for processing (13)
adapters/postgres/internal/connection/conn.goadapters/postgres/postgres.goadapters/postgres/postgres_conn_test.goadapters/postgres/postgres_exec_test.goadapters/postgres/postgres_test.goadapters/postgres/queries_test.goapp/app.goapp/app_test.goconfig/config.goconfig/config_test.gocontrollers/script.gocontrollers/script_test.gointegration/adapters/postgres/postgres_test.go
🚧 Files skipped from review as they are similar to previous changes (9)
- controllers/script_test.go
- app/app.go
- controllers/script.go
- adapters/postgres/internal/connection/conn.go
- adapters/postgres/postgres_exec_test.go
- config/config_test.go
- config/config.go
- adapters/postgres/postgres_test.go
- integration/adapters/postgres/postgres_test.go
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@adapters/postgres/testmain_test.go`:
- Around line 10-22: The TestMain timeout in testmain_test.go is duplicating the
same 30s limit already enforced by go test -timeout, causing a race between the
two shutdown paths. Update TestMain’s testSuiteTimeout to leave clear headroom
below the CLI timeout, or remove the custom timeout entirely, so the native go
test timeout remains the primary failure mechanism. Keep the change localized to
TestMain and the testSuiteTimeout constant.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 197646e8-46f4-41a4-8513-ea0fc3783a6d
📒 Files selected for processing (11)
.github/copilot-instructions.mdMakefileadapters/adapter.goadapters/connection.goadapters/postgres/internal/connection/test_exports.goadapters/postgres/postgres.goadapters/postgres/postgres_conn_test.goadapters/postgres/postgres_exec_test.goadapters/postgres/postgres_test.goadapters/postgres/testmain_test.goadapters/query_executor.go
✅ Files skipped from review due to trivial changes (4)
- adapters/connection.go
- adapters/adapter.go
- adapters/query_executor.go
- .github/copilot-instructions.md
🚧 Files skipped from review as they are similar to previous changes (5)
- adapters/postgres/postgres_conn_test.go
- adapters/postgres/postgres_exec_test.go
- adapters/postgres/internal/connection/test_exports.go
- adapters/postgres/postgres_test.go
- adapters/postgres/postgres.go
- Updated the Makefile to allow parallel test execution based on GOMAXPROCS, improving test performance. - Enhanced the Postgres adapter by introducing context support in various methods, allowing for better cancellation and deadline management. - Added a new test suite for connection handling, ensuring proper error reporting for connection failures. - Improved documentation for adapter interfaces to clarify the separation of connection lifecycle methods from the main Adapter interface.
- Updated command files to utilize a centralized configuration retrieval method, enhancing consistency and reducing code duplication. - Ensured that all commands now derive configuration from the command context, promoting a stateless design. - Added a new context key for storing the Prest configuration, facilitating better context management across commands.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
cmd/root.go (1)
63-63: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueHardcoded
context.Background()loses room for cancellation/graceful shutdown.
Executebuilds a freshcontext.Background()rather than accepting actxparameter, which prevents callers (e.g.,main.go) from propagating a cancellable context for graceful server shutdown down the line. Not a functional bug today, but worth considering if graceful shutdown is ever added.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cmd/root.go` at line 63, Update Execute in RootCmd so it accepts a caller-provided ctx instead of always creating a fresh context.Background(), then pass that ctx into withConfig before SetContext. This lets main.go or other callers propagate cancellation for future graceful shutdown support while keeping the RootCmd setup flow unchanged.cmd/auth.go (1)
18-27: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRedundant
EnsureAdaptercall beforePostgresDB.
app.PostgresDB(cfg)already callsEnsureAdapter(cfg)internally before opening the connection, so the explicit call on Line 19 is redundant. It's harmless sinceEnsureAdapteris idempotent (returns early ifcfg.Adapter != nil), but it duplicates the same guard logic unnecessarily.♻️ Proposed simplification
cfg := configFrom(cmd) - if err := app.EnsureAdapter(cfg); err != nil { - return err - } db, err := app.PostgresDB(cfg) if err != nil { fmt.Fprint(os.Stdout, err.Error()) return err }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cmd/auth.go` around lines 18 - 27, The auth command is calling app.EnsureAdapter(cfg) twice because app.PostgresDB(cfg) already performs that check internally. Remove the explicit EnsureAdapter call in cmd/auth.go and keep the adapter setup handled by PostgresDB, using configFrom(cmd), app.PostgresDB, and the subsequent db.Exec flow as the single path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@cmd/auth.go`:
- Around line 18-27: The auth command is calling app.EnsureAdapter(cfg) twice
because app.PostgresDB(cfg) already performs that check internally. Remove the
explicit EnsureAdapter call in cmd/auth.go and keep the adapter setup handled by
PostgresDB, using configFrom(cmd), app.PostgresDB, and the subsequent db.Exec
flow as the single path.
In `@cmd/root.go`:
- Line 63: Update Execute in RootCmd so it accepts a caller-provided ctx instead
of always creating a fresh context.Background(), then pass that ctx into
withConfig before SetContext. This lets main.go or other callers propagate
cancellation for future graceful shutdown support while keeping the RootCmd
setup flow unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: fd1543c4-9827-4b60-894f-9c4d7fc3dbbe
📒 Files selected for processing (5)
.github/copilot-instructions.mdcmd/auth.gocmd/migrate.gocmd/root.gocontext/keys.go
✅ Files skipped from review due to trivial changes (1)
- .github/copilot-instructions.md
🚧 Files skipped from review as they are similar to previous changes (1)
- cmd/migrate.go
- Removed redundant adapter checks in auth command execution, simplifying the code. - Updated the Execute function to accept a context parameter, enhancing flexibility in command execution. - Adjusted main function to pass a context to the Execute method, promoting better context management across commands.
Merging this branch will increase overall coverage
Coverage by fileChanged files (no unit tests)
Please note that the "Total", "Covered", and "Missed" counts above refer to code statements instead of lines of code. The value in brackets refers to the test coverage of that file in the old version of the code. Changed unit test files
|
Summary by CodeRabbit