Skip to content

Conciseness cleanups - #30

Open
tonyalaribe wants to merge 316 commits into
masterfrom
claude/vibrant-bohr-sLKFW
Open

Conciseness cleanups#30
tonyalaribe wants to merge 316 commits into
masterfrom
claude/vibrant-bohr-sLKFW

Conversation

@tonyalaribe

Copy link
Copy Markdown
Contributor

Closes #

How to test

Checklist

  • Make sure you have described your changes and added all relevant screenshots or data.
  • Make sure your changes are tested (stories and/or unit, integration, or end-to-end tests).
  • Make sure to add/update documentation regarding your changes (or request one from the team).
  • You are NOT deprecating/removing a feature.

tonyalaribe and others added 28 commits May 29, 2026 21:35
DataFusion's CommonSubexprEliminate optimizer can wrap the UPDATE
assignment Projection in a second Projection that defines synthetic
`__common_expr_*` columns. extract_dml_info was overwriting the real
assignments with the inner Projection's contents (which contain pass-
throughs plus the CSE definition), so mem_buffer.update saw an
assignment for a column that doesn't exist in the table schema and
failed with 'Column __common_expr_1 not found'.

Now only the topmost Projection is treated as the source of assignments;
deeper Projections have their aliases inlined into the existing
assignment value exprs.
- mem_buffer::tests::test_delete_with_qualified_predicate
- mem_buffer::tests::test_update_with_qualified_predicate_and_assignment
  Verified to fail with 'FieldNotFound { field: Column { relation: Some(...) } }'
  before strip_column_qualifiers was added.

- test_dml_operations::test_update_with_common_subexpression
  Verified to fail (Bob's duration stays at 200 instead of 300) before
  inline_projection_aliases was added — DataFusion's CSE optimizer hoists
  'duration + 100' into a __common_expr_* synthetic column, and
  extract_dml_info used to overwrite the real assignments with that inner
  Projection's contents.
WAL UPDATE/DELETE entries serialize predicate + assignment exprs as SQL
strings. On recovery, mem_buffer.{update,delete}_by_sql re-parses them
via SqlToRel. The old EmptyContextProvider returned None from
get_function_meta — so any function reference (CAST is a built-in, but
coalesce/to_char/upper/variant_get/etc. are UDFs) failed planning with
'Internal error: No functions registered with this context' and the
entry was silently quarantined under a misleading 'WAL CORRUPTION' log.

In practice this dropped in-flight UPDATEs on every restart whenever the
SET/WHERE used a UDF — rows stayed at their pre-UPDATE values forever.

Fix:
- BufferedWriteLayer takes an Arc<dyn FunctionRegistry> via
  with_function_registry(); main.rs builds it from a SessionContext
  pre-populated with register_custom_functions before recover_from_wal.
- mem_buffer's parser also takes the table's DFSchema so column refs
  nested inside function args resolve (empty schema rejects them).
- Renamed misleading 'WAL CORRUPTION' log lines for replay-side failures
  to 'WAL REPLAY FAILED'. True deserialization corruption stays labeled.

Regression tests in mem_buffer::tests:
- parse_sql_predicate_without_registry_rejects_udf
- parse_sql_predicate_with_registry_handles_udf
- update_by_sql_with_udf_replays_when_registry_present
All three verified to fail when the registry plumbing is reverted.

Also adds .github/workflows/autoformat.yml: on push to master, runs
cargo fmt + cargo clippy --fix and pushes the result back, so PRs don't
need to ping-pong over format/clippy nits.
- Add timefusion::functions::function_registry() helper; collapse three
  call sites (main.rs + two tests) into one-line uses.
- Add type alias mem_buffer::FnRegistry for the dyn FunctionRegistry +
  Send + Sync bound. Drops six 60-char trait-object signatures.
- Inline parse_sql_expr — it was a one-line forwarder to
  parse_sql_predicate; remove the indirection.
- Trim narrative comments above field, setter, regression tests, and
  main.rs registry construction. Keep one-line WHY; drop WHAT/incident
  references that git blame already carries.
… reuse runtime SessionContext

Make BufferedWriteLayer's function_registry non-Option, passed positionally to
with_config(). Drops the with_function_registry setter, the Option field, the
runtime warn!, and the unenforceable 'MUST be called before recover_from_wal'
doc-contract — now a compile-time guarantee.

main.rs no longer builds a throwaway bootstrap SessionContext. Instead:
  1. Create the real SessionContext early.
  2. Database::setup_session_udfs() registers UDFs only (no buffered_layer dep).
  3. Hand its FunctionRegistry to the layer.
  4. recover_from_wal.
  5. Attach buffered_layer to db.
  6. Database::setup_session_tables() registers routing + stats + pg_settings.

Database::setup_session_context preserved for existing call sites; it just
calls the two new pieces in order. register_custom_functions runs once.

Updated all internal/external/bench test callers to pass a registry via
the timefusion::functions::function_registry() helper.
- Move FnRegistry alias from mem_buffer.rs (data-structure module) to
  functions.rs, alongside function_registry() — keeps the registry types
  in one place.
- function_registry() is now a process-wide OnceLock singleton. Test
  harnesses that build many BufferedWriteLayers no longer re-register
  ~20 UDFs per layer. Production builds it once at startup either way.
- Add timefusion::test_utils::test_helpers::test_layer(cfg) helper.
  Collapses 10 sites (lib tests, integration tests, benches) of
  BufferedWriteLayer::with_config(cfg, function_registry()?) into one
  call. Drops 3 now-unused imports.
- Trim main.rs's three-line narration above setup_session_tables (the
  method name carries it) and the 'Used by main.rs' clause from
  Database::setup_session_udfs' doc — couples API to a caller.
Monoscope's Log Explorer emits to_jsonb(...) (Postgres syntax); DataFusion
planning failed with 'Invalid function to_jsonb'. Both return JSON text
over PGWire here, so a name alias is sufficient.
`do_query` gated `Response::Execution` (CommandComplete) vs.
`Response::Query` (TuplesOk) on `matches!(statement, Statement::Insert(_))`.
Anything that didn't decode to exactly that AST variant — postgres-synonym
rewrites, plan-rewriter hooks, cached plans — fell through to the Query
branch, so pgwire shipped RowDescription + DataRow for what was really a
write. Clients expecting CommandComplete for INSERTs (e.g. row-count-only
decoders) saw `TuplesOk` and treated it as a hard error.

Replace both query paths (simple + extended) with `dml_completion`, which
gates on `LogicalPlan::Dml`/`LogicalPlan::Copy` from the executed
DataFrame and emits the right tag per WriteOp (INSERT/UPDATE/DELETE/
COPY/TRUNCATE/SELECT-via-CTAS). Driving off the plan keeps both query
paths consistent with what DataFusion actually runs.

Regression tests cover both paths:
- `dml_returns_command_complete` — simple-query end-to-end via do_query
- `dml_completion_survives_logical_optimisation` — extended-query gating
  after `state.optimize(...)` to confirm Dml survives the optimiser
Foundation for the Delta-derived cursor work in
docs/plans/zero-replay-shutdown-steps-5-6.md. Step 5 needs walrus to
expose its read-cursor position so we can snapshot it at bucket-seal
time, record it in Delta commit metadata, and fast-forward the cursor
on startup when Delta is ahead of locally-fsynced walrus state.

Vendors walrus-rust 0.2.0 under vendor/walrus-rust (Cargo.toml points
at the path) and adds two public APIs:

- Walrus::current_position(topic) -> WalPosition — snapshots the tail
  position for a topic without consuming entries.
- Walrus::set_persisted_read_position(topic, pos) -> io::Result<()> —
  fast-forwards the persisted-read cursor directly (atomic fsync via
  WalIndex::set), bypassing the read_next walk that advance_by_counts
  uses today.

Fixes two pre-existing walrus bugs uncovered while writing tests for
the new APIs:

1. The chain-fold block in read_next unconditionally cleared
   persisted_tail even when the sealed chain was empty (no fold
   happened). The tail-path else-branch then reset the cursor to
   offset 0 on every read. Now persisted_tail is only cleared after a
   successful fold.

2. The tail-path else-branch reset offset to 0 whenever no
   persisted_tail was present, even when in-memory state from a prior
   read on the same instance had advanced past 0. Subsequent reads
   would persist (block_id, 0) on top of a live cursor. Now the
   else-branch consults info.tail_block_id/tail_offset (also
   mirrored from the index at hydration time) before falling back to
   the offset-0 init.

Plus 5 walrus-side tests in tests/position.rs covering origin,
monotonicity, set-to-tail, set-to-origin, and snapshot-then-set
recovery. Existing walrus test suite (16 configuration tests +
others) still passes.

Wires the new APIs through src/wal.rs as WalManager::current_position
and WalManager::set_persisted_positions (per-shard variants over the
existing shards_per_topic infrastructure).

Docs include the original Step 4 plan and the new Step 5 + 6 plan
(steps-5-6).
Step 1 of the zero-replay-shutdown work: plumb a CancellationToken
through pgwire so its accept loop exits on shutdown signal, then await
in-flight connections to drain before the BufferedWriteLayer flush
fires. Without this, fresh INSERTs land in MemBuffer+WAL throughout
the drain — "flush everything" becomes a moving target and the next
container starts in a multi-GiB WAL replay.

Step 2: per-phase shutdown timeout default raised 5s → 180s (each of
pgwire drain, gRPC drain, buffered flush gets its own budget), and
the memory_mb/100 heuristic dropped from compute_shutdown_timeout
(never calibrated against real flush throughput, capped below
realistic flush time for 5 GiB+ buffers).

Vendor: datafusion-postgres serve_with_handlers takes an
`impl Future<Output = ()> + Send + 'static` shutdown parameter. Pass
std::future::pending() from anywhere that doesn't care.

Note: Step 4 (per-shard count snapshot, advance_by_counts) lives in
the same files (mem_buffer, wal, buffered_write_layer, main) as the
Step 5 work and is in the follow-up commit due to file-level overlap.
…data

Closes the crash-mid-flush window where Delta committed but
`advance_by_counts` didn't finish — without this, restart replays
entries already in Delta and the next flush double-writes them.

Step 4 (already in the prior commit's territory): each MemBuffer
TimeBucket accumulates per-shard WAL entry counts via
`record_wal_append`; at seal time these counts drive
`advance_by_counts` so the walrus cursor moves only past entries
belonging to the flushed bucket, never into the open follow-on
bucket.

Step 5 (this commit):

- TimeBucket also accumulates per-shard *positions* alongside counts.
  On every WAL append, `record_wal_append` queries
  `WalManager::current_position` and stores the post-append tail
  position on the bucket. At seal time both vectors snapshot into
  `FlushableBucket` (counts → advance, positions → watermark).
- `DeltaWriteCallback` signature gains a `DeltaWatermark` parameter
  carrying the per-shard positions for the bucket being flushed.
- `insert_records_batch` takes an `Option<&DeltaWatermark>` and, when
  present, attaches a `CommitProperties::with_metadata` map to the
  Delta write. `build_watermark_commit_properties` serializes the
  watermark under the `timefusion.wal_watermark` key in
  `commitInfo.info`. delta-rs inserts `Action::CommitInfo` at the
  head of the same actions vector as the Add file actions, so the
  metadata lands atomically in the same `_delta_log/N.json` write.
- On startup, `Database::derive_wal_cursors_from_delta` scans the
  most recent 16 commits per known WAL topic and takes the per-shard
  MAX watermark. For each shard where the Delta watermark exceeds
  walrus's locally-fsynced cursor, walrus is fast-forwarded via
  `set_persisted_read_position`. Runs before `recover_from_wal` so
  replay starts past any entries already durable in Delta. Pure
  best-effort — missing/older metadata falls back to the local
  walrus state (today's at-least-once behaviour), so this can't make
  recovery worse than before.

Walrus additions: `persisted_read_position` getter so the cursor
comparison can take `max(local, delta)` without blindly stepping
backward.

Plus: `flush_callback_receives_per_shard_watermark` regression test
proving the seal → callback pipeline carries the right per-shard
positions.

Existing `insert_records_batch` callsites updated to pass `None`
(no-op for any path that doesn't go through the flush callback).
Factors the watermark serialization out of build_watermark_commit_properties
and the parsing out of derive_wal_cursor_for_table into pure helpers
(serialize_watermark_to_json, parse_watermark_from_json,
max_watermark_across_commits) with a single WAL_WATERMARK_KEY constant
so writer and reader can't drift.

Four unit tests pin the format and aggregation:
- serialize → parse roundtrip preserves per-shard positions, including
  the absent-shard distinction needed for MAX aggregation.
- all-None watermark serializes to empty map (no metadata written),
  matching pre-feature commits on the recovery side.
- per-shard MAX across commits picks the furthest position per shard;
  commits missing the key (replay-derived) contribute nothing and
  cannot reset the MAX backward.
- out-of-range and malformed shard keys are dropped silently — a
  future writer with more shards than this reader configures won't
  crash recovery.

Live-Delta E2E tests (real commit metadata roundtrip, walrus cursor
fast-forward after simulated crash) need MinIO infrastructure and are
deferred to a follow-up.
- src/wal.rs: extract `for_each_shard` + `check_shard_len` so
  `current_position`/`persisted_read_positions`/`set_persisted_positions`/
  `advance_by_counts` stop duplicating the same per-shard iterator and
  length-check pattern. Add `current_position_for_shard` (no-allocation
  single-shard variant) and use it in `BufferedWriteLayer::insert` —
  the hot ingest path no longer builds an N-element Vec per append.
  Add `list_topic_pairs` so callers iterating topics don't reparse the
  `:`-joined convention.

- src/mem_buffer.rs: merge `TimeBucket.wal_shard_counts` and
  `wal_positions` into a single `Mutex<WalShardState>` so one append
  takes one lock instead of two, and a snapshot can't see counts
  ahead of positions. `record_wal_append` keeps both in sync under
  one lock; `snapshot_wal_shard_state` returns both vectors. Use
  `Ord::max` for the monotonicity guard.

- src/database.rs: parallelize `derive_wal_cursors_from_delta` (cap 8
  concurrent S3 history reads — startup-blocking otherwise scales
  linearly with table count). Hoist `build_watermark_commit_properties`
  out of the retry loop. Flatten `derive_wal_cursor_for_table`'s
  shard-walk into a single `map_or` and drop the redundant
  all-None early-return. Unify the per-shard `max` via `Ord::max`.

- src/config.rs: drop the dead `_current_memory_mb` parameter on
  `compute_shutdown_timeout` (formula was removed; arg was kept as
  underscore-prefixed dead weight).

Tests: all 7 buffered_write_layer tests + all 4 watermark unit tests
pass. MinIO-dependent tests in src/database.rs were failing before
this commit too (no MinIO running locally).
Schema YAML grows a `dedup_keys` list (e.g. [id, timestamp]); the buffered
write layer collapses dupes inside a bucket via Arrow RowConverter before
the Delta commit and tantivy index build, so client retries land as one
row. WAL still records every original, so crash replay is deterministic.
Cross-bucket dupes are left for a follow-up read-side row_number rewrite.

Opted in for otel_logs_and_spans on (id, timestamp).
#28)

* gitignore: ignore vendored crate target/ dirs

* pgwire: reply NoData for DML Describe so strict clients don't poison rows

datafusion-postgres synthesises a `count: UInt64` output schema on every
LogicalPlan::Dml / LogicalPlan::Copy. The default ExtendedQueryHandler
fed that schema straight back to clients as a RowDescription at
Describe-Statement time, so prepared INSERT/UPDATE/DELETE responses
were tagged TuplesOk instead of NoData.

Lenient drivers (tokio-postgres' `execute`, asyncpg) silently discard
the extra DataRow; strict prepare-validating clients (Hasql, pgjdbc,
Npgsql, psycopg3, sqlx) reject the protocol mismatch and drop the
write. monoscope's Hasql background-ingest job was logging
POISON_ROW_DROPPED with `UnexpectedResultStatementError "TuplesOk.
Expecting [CommandOk]"` for every prepared INSERT.

The prior attempt (7d053b2 "tag DML responses off LogicalPlan") only
patched the Execute response via dml_completion and never touched
Describe, which is where Hasql actually fails — before Bind/Execute
is ever sent. That's why the bug survived two rounds of integration
tests using tokio-postgres `execute`.

Fix: in Parser::get_result_schema, recognise the synthetic DML count
schema (Dml/Copy plan with one UInt64 `count` field) and return an
empty result schema so pgwire emits NoData. Guard on the exact field
shape so a future RETURNING-capable plan (wider schema) falls through
to the normal path.

Wire-level regression tests in tests/pgwire_dml_tag_test.rs cover
INSERT/UPDATE/DELETE/Variant-INSERT Describe via tokio-postgres and
sqlx (the latter independently exercises the same wire fact a strict
Rust client would observe), plus an end-to-end Execute+SELECT to keep
the Execute path honest, plus the simple-query path. Removed the
prior false-confidence vendor unit tests that hit the wrong handler.

* vendor: in-handlers unit test for DML get_result_schema → empty

Adds get_result_schema_returns_no_data_for_dml: parses INSERT/UPDATE/
DELETE/SELECT and asserts the DML cases return an empty FieldInfo list
(SELECT returns columns as the positive control). Lives next to the
code it covers, no test infra dependencies, suitable as the regression
test in an upstream PR.

* address PR review: source-pointer, over-match guard, test-server cleanups

- Comment on the get_result_schema guard now names the upstream source
  (datafusion/expr/src/logical_plan/dml.rs `make_count_schema`) so a
  future rename/widen of the synthetic DML schema points the maintainer
  straight at the right symbol to update.
- Unit test gains a `SELECT COUNT(*) AS count FROM t` case as the
  over-match guard: ensures a SELECT producing a single UInt64 `count`
  column is NOT suppressed — only Dml/Copy of that shape is. If the
  `LogicalPlan::Dml | Copy` matcher is ever dropped, this case fails.
- COPY can't be reached via `state.statement_to_plan` (rejected as
  unsupported), so the Copy arm stays defensive only — noted in test.
- tests: bind to 127.0.0.1 (was 0.0.0.0), widen port range to ~2000
  (was 100), and explain why simple_query uses interpolated SQL (no
  parameters in that wire path). Drop redundant `#[cfg(test)]` from
  the integration-test module.

No RETURNING wire test added: DataFusion still rejects RETURNING at
planning today, and the inverse (`SELECT count(*)`) covers the same
"don't over-suppress" invariant from the other side.

* fmt: nightly cargo fmt on tests/pgwire_dml_tag_test.rs

* unbreak master CI: missing dedup_keys, DeltaWriteCallback arity, clippy

Pre-existing on master, surfaced by this PR's CI run:

- src/wal.rs:562 — needless-range-loop in advance_by_counts. Rewrite per
  clippy's suggestion (counts.iter().enumerate()). Behavior unchanged;
  counts.len() == shards_per_topic is asserted by check_shard_len above.
- tests/tantivy_{search,index,storage}_test.rs, benches/tantivy_benchmarks.rs
  — TableSchema literals missing `dedup_keys: vec![]` (field added in
  8d2d18d but these call sites weren't updated).
- benches/{tantivy,core}_benchmarks.rs — insert_records_batch grew a 5th
  arg (wal watermark Option) and DeltaWriteCallback grew a 4th arg (the
  per-shard DeltaWatermark) when the zero-replay-shutdown work landed.
  Pass None / `_watermark` placeholder respectively; benches don't drive
  the watermark path.

cargo check --all-targets --all-features and cargo clippy --all-targets
--all-features -- -D warnings now both pass locally.

* address review round 2: OS-assigned port, explicit table create, timeout context

- TestServer: bind 127.0.0.1:0 to pull an OS-assigned port instead of
  random-guessing in a range. Eliminates collision with other test
  binaries / CI jobs sharing the host. The micro race window between
  drop and re-bind is harmless in practice.
- TestServer: also pre-create variant_bench so Variant INSERT failures
  in the Describe test surface deterministically (table init errors)
  rather than as confusing lazy-create errors during prepare().
- connect(): explicit 10s deadline + propagate the last connect error
  with context. Silent 10s spins are gone.
- Unit test: explicit "COPY: not tested — unreachable via prepare path"
  entry where the case would live, so a future reader doesn't add a
  COPY test and get a NotImplemented error.
- metrics.rs: replace the 13 hand-written counter registrations with a
  counter_registry! macro (single source of truth for field + id + desc),
  collapse 5 observable gauges into a layer_gauge! macro, and generate the
  no-arg record_* helpers via a simple_recorders! macro.
- Remove a duplicated #[instrument] attribute on head_cached.
- Extract config::is_insecure_auth_allowed(), shared by the pgwire and gRPC
  auth paths instead of inlining the env-var check twice.
- Extract record_query_span() in pgwire_handlers, used by both the simple
  and extended query handlers.
Adds derive_more (debug + display features) and uses it for:
- StorageConfig: #[debug("[redacted]")] on the two credential fields keeps
  them out of {:?} output without a hand-rolled fmt impl.
- DmlQueryPlanner / DmlExec: #[debug(skip)] on the non-printable plan/session
  fields, preserving the previous Debug output.
- FoyerObjectStoreCache: container-level #[display]/#[debug] over inner.

Behavior-preserving; removes ~45 lines of boilerplate fmt code.
- Route the two error-swallowing payload-collection sites (proactive and
  background _last_checkpoint refresh) through the existing collect_payload
  helper instead of re-inlining the Stream/File match. The two sites that
  propagate errors or slice ranges keep their bespoke handling.
- Extract table_path_from_uri(), shared by both invalidate_checkpoint_cache
  impls instead of duplicating the scheme-strip + trim logic.
- Extract is_parquet_file() and use it at the 7 inline .ends_with(".parquet")
  call sites.
ToCharUDF and AtTimeZoneUDF both inlined the same ~20-line block to pull a
constant UTF-8 string out of a scalar-or-length-1-array argument (StringView
or String). Factor it into extract_scalar_string(arg, label); the label
parameterizes the error messages. Preserves the && short-circuit so is_null(0)
is never evaluated on an empty array.
The as_any/name/signature trio was spelled out identically in every UDF
ScalarUDFImpl block. A scalar_udf_boilerplate!("name") macro emits all three;
return_type and invoke_with_args stay per-impl. Covers JsonToPgText, ToChar,
AtTimeZone, JsonBuildArray, ToJson, ExtractEpoch, ApproxPercentile and
JsonbPathExists.
- functions.rs: push_json_primitive! macro collapses the identical
  downcast + null-aware json!() loop for the Int64/Float64/Boolean arms.
- manifest.rs: extract mutate() for the shared load->apply->save skeleton
  behind upsert() and remove_many().
query() and query_partitioned() both matched on the optional compiled
predicate to filter a bucket snapshot. Factor that into filter_snapshot();
the text_match query variant keeps its bespoke id-set + predicate flow.
Verified by the mem_buffer unit tests (27 passed).
The DynamoDB-locking storage-option block was written twice: once in
AwsConfig::build_storage_options and again, hand-inlined, in the per-project
custom-table path in database.rs. Extract a single add_dynamodb_locking_options
method and call it from both, so the locking config has one source of truth.
@claude

claude Bot commented Jun 4, 2026

Copy link
Copy Markdown

Code Review: Conciseness Cleanups

This is a solid refactoring PR that achieves genuine reduction of repetition (-180 net lines across 12 files) without behavioral changes. The extractions are well-scoped and improve long-term maintainability. A few minor items worth noting.


Summary of Changes

Area Technique Impact
config.rs Extract is_insecure_auth_allowed() and add_dynamodb_locking_options() Removes 3x env-var duplication; DynamoDB block was in 2 places
database.rs / dml.rs derive_more::Debug with #[debug("[redacted]")] Replaces manual Debug impls
functions.rs extract_scalar_string(), scalar_udf_boilerplate!, push_json_primitive! Removes ~60 lines of repeated UDF boilerplate
metrics.rs counter_registry!, layer_gauge!, simple_recorders! Each new counter/gauge is now a single line
object_store_cache.rs table_path_from_uri(), is_parquet_file(), collect_payload() 7x .ends_with(".parquet") calls consolidated; duplicate #[instrument] removed
mem_buffer.rs / manifest.rs / pgwire_handlers.rs Small function extractions Obvious dedup

Security: Improvement Worth Highlighting

Replacing the manual Debug impl on StorageConfig with derive_more::Debug and #[debug("[redacted]")] is strictly safer. A developer adding a new credential field to a manual impl could forget to redact it; with the attribute, a credential can only leak if the annotation is explicitly absent — a much easier failure to spot in review.


Minor Issues

1. Subtle error message regression in extract_scalar_string (src/functions.rs)

The shared helper normalizes messages. For the non-scalar array path, the original AtTimeZoneUDF said "Timezone must be a scalar string value", but the new helper says "Timezone must be a scalar value" (the word "string" is dropped). Harmless in practice, but if any tests assert on exact error text they will break. Worth a quick grep for the old strings.

2. DmlQueryPlanner::Debug output may differ slightly

The original impl called f.debug_struct("DmlQueryPlanner").finish() which renders as DmlQueryPlanner. derive_more with all fields #[debug(skip)] emits DmlQueryPlanner { } (with empty braces). Cosmetic only, but worth confirming no snapshot tests match on this output.

3. is_insecure_auth_allowed() reads the env var on every call

This was the same behaviour before (the read was inlined), but extraction makes it more visible. It is only called at startup/connection time so there is no performance concern. The real risk is in tests that mutate env vars between calls — both auth paths could see different values. Caching in OnceLock would be more robust, but that is a larger change worth a separate PR. Flagging for awareness only.

4. PR description is not filled in

The "Closes #" line and checklist items are left at template defaults. Even for a cleanup PR it helps to note that no tests are required (pure refactor) and which issue, if any, prompted it.


Nits

  • src/config.rs:92 — the one-liner for is_insecure_auth_allowed() runs ~95 characters. Worth checking against the project rustfmt max_width.
  • The scalar_udf_boilerplate! macro makes IDE go-to-definition for as_any / name / signature harder inside UDF impls. A short comment on each invocation site would help future readers.

Verdict

Approved with minor suggestions. Each extraction is small enough to verify at a glance and improves the code without over-abstracting. The credential-redaction change is a net security improvement. The only thing worth confirming before merge is that no tests assert on the exact error strings that were normalized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants