docs+feat(#52): injection-safety doc and router-module collision guard#60
Merged
Conversation
Consolidate the injection-safety reasoning that lives as site comments in QueryExecutor.bind/3, Rpc.call_arg/2 and the Bier.Mutation SQL builders into docs/injection_safety.md: what is bound as a $n parameter vs. emitted as a single-quote-escaped literal, why Postgres unknown-typed literal contexts (ranges, arrays, typed comparisons, RPC args) force the latter, and how QueryExecutor.quote_type/1 constrains the templated casts. Notes the doc as an anchor for a future property/fuzz test over filter values. Ships with the docs via the ex_doc extras list; the README request-flow section gains a one-line pointer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bier.RouterBuilder names the generated router Module.concat(conf.name,
Router), so two live instances whose distinct names concat to the same
module (e.g. A.B and :"A.B") silently redefine each other's router on
every rebuild. Bier.start_link/1 now checks the new
Bier.Registry.instance_names/0 (bare-atom keys, i.e. live instance
supervisors) for another instance owning the same router module and
raises a clear ArgumentError naming both instances and the module.
Keying on live registrations — not on the module being defined — keeps a
stop/restart of the same named instance booting, and a same-name double
start still surfaces as {:error, {:already_started, pid}} as before.
Covered by an integration test that boots real instances against the
test DB.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Two remaining small checklist items from #52 (references, does not close — other items remain).
What
1. Injection-safety doc (
docs/injection_safety.md)Consolidates the typed-literal binding strategy that was previously spread across site comments:
$nparameter vs. emitted as a single-quote-escaped'<escaped>'::typeliteral, and why (Postgres coerces text into ranges/arrays/typed args only from an unknown-typed literal, not from atext-typed parameter);QueryExecutor.quote_type/1constrains every templated cast (conservative charset, 400 on violation), including which cast site is introspection-only (Rpc.call_arg/2);QueryExecutor.bind/3,Rpc.call_arg/2, and theBier.Mutationbuilders (insert_sql/upsert_sql/set_clause/where_clause,extract_expr), plus identifier quoting and parameterized GUCs;Added to the ex_doc
extrasinmix.exsand pointed to from the README request-flow section (2 lines).2. Router-module uniqueness at startup
Bier.RouterBuildernames the generated routerModule.concat(conf.name, Router), so two live instances with distinct names that concat to the same module (e.g.A.Band:"A.B") silently redefined each other's router.Bier.start_link/1now checks the newBier.Registry.instance_names/0(live instance supervisors only) and raises anArgumentErrornaming both the new instance, the colliding module, and the owning instance. Keying on live registrations (notCode.ensure_loaded?) keeps a stop/restart of the same named instance booting, and a same-name double start still returns{:error, {:already_started, pid}}.Verification
mix format --check-formatted,mix compile --warnings-as-errors,mix credo --strict,mix docs --warnings-as-errors,MIX_ENV=test mix compile --warnings-as-errorsall pass locally.mix run: collision raises the expected message; same-name start passes the guard and hitsalready_startedas before.test/bier/router_module_collision_test.exsboots real instances (collision raises; stop/restart of the same name boots while the router module still exists). CI runs the full suite.🤖 Generated with Claude Code