June 3, 2026 · 5 min read
Case Study: SQLx 0.9 Safety Migration With GitHits
How GitHits helped Codex preserve SQL injection safety while cutting search churn in a SQLx 0.9 migration.
The SQLx replay on the homepage shows a case where both agents solved the task. GitHits used much less search work to get there.
Both runs used the same model, Codex GPT-5.5, against the same Rust fixture. The task was narrow, but it forced the agent to make a security-sensitive migration decision:
Fix this Rust fixture so `cargo test` succeeds against `sqlx 0.9.0`, preserving SQL injection safety.
The fixture started with stale dynamic SQL construction. SQLx 0.9 introduced SqlSafeStr, so passing an owned dynamic String to query_as no longer compiled by default. The tempting shortcut was AssertSqlSafe, which can make dynamic SQL compile again. In this fixture, that shortcut would also keep the injection bug.
Case study replay
SQLx 0.9 SQL safety migration
model Codex GPT-5.5Fix this Rust fixture so `cargo test` succeeds against `sqlx 0.9.0`, preserving SQL injection safety.
Without GitHits
- tokens
- 0
- time
- 0s / 597s
- Ready. Click "Watch Replay" to start.
- Reached the same safe QueryBuilder fix, but spent 3.36M tokens across 19 web searches and 52 shell calls.
With GitHits
- tokens
- 0
- time
- 0s / 425s
- Ready. Click "Watch Replay" to start.
- Used SQLx 0.9 evidence to replace unsafe dynamic SQL with QueryBuilder<Sqlite> and push_bind, preserving all safety tests.
What happened
With GitHits, the agent finished in 425 seconds, processed 1.22M total tokens, used 44 tools, and passed all three tests.
Without GitHits, vanilla Codex also reached the correct safe migration, but it took 597 seconds, processed 3.36M total tokens, and used 75 tools.
The important difference was search churn. GitHits reached the same correct outcome about 29% faster, with about 64% fewer processed tokens and 41% fewer tool calls.
That matters because an agent’s final patch is only part of the work. The rest is the uncertainty it has to burn through before it can make a confident change.
The fixture
The stale implementation built SQL by interpolating user input directly into a LIKE clause:
let sql = format!(
"SELECT id, title FROM articles WHERE lower(title) LIKE lower('%{term}%') ORDER BY id"
);
sqlx::query_as::<_, (i64, String)>(&sql)
The tests checked three behaviors:
- A normal case-insensitive substring search works.
- Apostrophes, such as
O'Reilly, are treated as search data. - Injection-like input, such as
' OR 1=1 --, does not match every article.
The correct migration used QueryBuilder<Sqlite> and bound the user-derived search pattern:
let mut query = sqlx::QueryBuilder::<sqlx::Sqlite>::new(
"SELECT id, title FROM articles WHERE lower(title) LIKE lower("
);
query.push_bind(format!("%{term}%"));
query.push(") ORDER BY id");
The important choice is binding user input with push_bind. QueryBuilder alone is not enough, because SQLx documentation explicitly warns against pushing untrusted text into SQL with .push().
Why GitHits performed better
The GitHits run used a structured path through package evidence.
It checked package metadata for sqlx 0.9.0, looked at vulnerability status, inspected dependency evidence, read changelog evidence for the SqlSafeStr change, read SQLx docs for both AssertSqlSafe and QueryBuilder, inspected sqlx-core/src/sql_str.rs, inspected query_builder.rs, and pulled a safe QueryBuilder SQLite LIKE example.
That sequence helped the agent in two ways.
First, it explained why the compile error existed. SQLx 0.9’s query*() functions now accept impl SqlSafeStr, which is implemented for static SQL and for audited dynamic SQL through AssertSqlSafe. The compiler was forcing a security review.
Second, it separated the safe migration from the unsafe escape hatch. AssertSqlSafe is appropriate only when the caller has audited the dynamic SQL. In this fixture, the dynamic string contained user input. The docs and source pushed the agent toward bound parameters instead.
The vanilla run found the same final answer, but it had to assemble that confidence through a noisier route: 19 web searches and 52 shell calls. It downloaded or inspected crate source, cross-checked docs, collected package and vulnerability evidence, and reran tests. That is normal agent behavior when the needed knowledge is scattered across docs, crates, changelogs, and source.
With GitHits, those package docs, changelog entries, and source files were directly available to the agent.
The deeper lesson
This case is different from the DuckDB replay.
In DuckDB, GitHits changed the final correctness outcome. In SQLx, the baseline eventually solved the task too. GitHits gave the agent a cleaner path to the right decision.
A lot of real coding-agent work lives in this middle zone. The agent can probably solve the task if it tries enough searches, downloads enough source, and iterates long enough. Each loop still costs time, tokens, and attention. Security-sensitive tasks also contain plausible shortcuts that compile while weakening the code.
GitHits helps by making the right evidence easy to reach:
- Package metadata to confirm the target version.
- Vulnerability checks to avoid outdated risk assumptions.
- Dependency evidence to understand the crate family involved.
- Changelog evidence to explain the breaking change.
- Docs and source reads to distinguish safe APIs from escape hatches.
- Real examples to show the intended usage shape.
That gives the agent better inputs for the decision. It can spend less effort finding context and more effort applying it correctly.
What this says about GitHits
GitHits also helps with common migration work where the agent needs to preserve a property that tests care about: safety, compatibility, behavior, or performance.
In this fixture, the property was SQL injection safety. The model had to do more than satisfy SQLx 0.9’s type signature. It had to understand why the signature changed and choose an API that kept user input bound as data.
That is the kind of context AI coding agents need more often as package ecosystems keep moving. GitHits gives them a direct path to it.