perf: look script lineage up by key when copying drafts

The lineage check compared a hex rendering of every script hash, which
ruled out the primary key and scanned the target's script versions for
each script draft, fork creation included. It now converts the draft's
16-digit hex to the bigint column, guarded so a malformed value never
reaches the cast.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Guilhem Lemouel
2026-10-27 02:15:12 -07:00
co-authored by Claude Opus 5.5
parent 489d3b7677
commit b313a09abc
2 changed files with 14 additions and 11 deletions
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO draft (workspace_id, path, typ, value, created_at, email, base)\n SELECT $2::text, path, typ,\n to_json(\n CASE WHEN typ IN ('script', 'flow') THEN clean - 'on_behalf_of' ELSE clean END\n - CASE WHEN typ = 'flow' THEN 'version_id'\n WHEN typ IN ('app', 'raw_app') THEN 'parent_version'\n ELSE '' END\n - CASE WHEN typ = 'script' AND NOT EXISTS (\n SELECT 1 FROM script sc WHERE sc.workspace_id::text = $2::text\n AND lpad(to_hex(sc.hash), 16, '0') = clean->>'parent_hash')\n THEN 'parent_hash' ELSE '' END\n ),\n created_at, email,\n CASE WHEN typ = 'script' AND EXISTS (\n SELECT 1 FROM script sc WHERE sc.workspace_id::text = $2::text\n AND lpad(to_hex(sc.hash), 16, '0') = base)\n THEN base END\n FROM (\n SELECT d.path, d.typ, d.created_at, d.email, d.base,\n replace(replace(replace(d.value::text, chr(92) || chr(92), chr(1)), chr(92) || 'u0000', ''), chr(1), chr(92) || chr(92))::jsonb AS clean\n FROM draft d\n WHERE d.workspace_id = $1\n AND CASE WHEN $4::text[] IS NULL THEN d.email = $3 OR d.email IS NULL\n ELSE d.email = $3\n AND (d.typ::text, d.path) IN (SELECT * FROM unnest($4::text[], $5::text[]))\n END\n ) s\n RETURNING typ AS \"typ: UserDraftItemKind\", path",
"query": "INSERT INTO draft (workspace_id, path, typ, value, created_at, email, base)\n SELECT $2::text, path, typ,\n to_json(\n CASE WHEN typ IN ('script', 'flow') THEN clean - 'on_behalf_of' ELSE clean END\n - CASE WHEN typ = 'flow' THEN 'version_id'\n WHEN typ IN ('app', 'raw_app') THEN 'parent_version'\n ELSE '' END\n - CASE WHEN typ <> 'script' THEN ''\n WHEN (clean->>'parent_hash' ~ '^[0-9a-f]{16}$') IS NOT TRUE THEN 'parent_hash'\n WHEN EXISTS (SELECT 1 FROM script sc WHERE sc.workspace_id = $2::text\n AND sc.hash = ('x' || (clean->>'parent_hash'))::bit(64)::bigint)\n THEN '' ELSE 'parent_hash' END\n ),\n created_at, email,\n CASE WHEN typ = 'script' AND base ~ '^[0-9a-f]{16}$' THEN\n CASE WHEN EXISTS (SELECT 1 FROM script sc WHERE sc.workspace_id = $2::text\n AND sc.hash = ('x' || base)::bit(64)::bigint)\n THEN base END\n END\n FROM (\n SELECT d.path, d.typ, d.created_at, d.email, d.base,\n replace(replace(replace(d.value::text, chr(92) || chr(92), chr(1)), chr(92) || 'u0000', ''), chr(1), chr(92) || chr(92))::jsonb AS clean\n FROM draft d\n WHERE d.workspace_id = $1\n AND CASE WHEN $4::text[] IS NULL THEN d.email = $3 OR d.email IS NULL\n ELSE d.email = $3\n AND (d.typ::text, d.path) IN (SELECT * FROM unnest($4::text[], $5::text[]))\n END\n ) s\n RETURNING typ AS \"typ: UserDraftItemKind\", path",
"describe": {
"columns": [
{
@@ -62,5 +62,5 @@
false
]
},
"hash": "8b62d25c98a6b3db3a0b7039cacf79799e6d7c7c6c9532545844bec9ec314516"
"hash": "e9efda2d1ab6b111103cfcd626cd91e2241721e34586dedf7bdacb83b732f2b7"
}
+12 -9
View File
@@ -695,7 +695,8 @@ pub async fn copy_drafts_to_workspace(
// deploy time, which is the more accurate answer of the two.
let rows = sqlx::query!(
// A script hash is content-addressed and shared between a workspace and its forks, so
// a script draft keeps its base (and `parent_hash`) when the target has that version,
// a script draft keeps its base (and `parent_hash`, both `ScriptHash`'s 16 hex digits,
// matched on the column so the lookup uses the key) when the target has that version,
// which a fork's clone does; one deployed in the source after the fork was made is not
// there, and a deploy naming it as parent would be refused. Flow and app ids are minted
// per workspace, so those drafts arrive with no base (staleness falls back to the
@@ -713,16 +714,18 @@ pub async fn copy_drafts_to_workspace(
- CASE WHEN typ = 'flow' THEN 'version_id'
WHEN typ IN ('app', 'raw_app') THEN 'parent_version'
ELSE '' END
- CASE WHEN typ = 'script' AND NOT EXISTS (
SELECT 1 FROM script sc WHERE sc.workspace_id::text = $2::text
AND lpad(to_hex(sc.hash), 16, '0') = clean->>'parent_hash')
THEN 'parent_hash' ELSE '' END
- CASE WHEN typ <> 'script' THEN ''
WHEN (clean->>'parent_hash' ~ '^[0-9a-f]{16}$') IS NOT TRUE THEN 'parent_hash'
WHEN EXISTS (SELECT 1 FROM script sc WHERE sc.workspace_id = $2::text
AND sc.hash = ('x' || (clean->>'parent_hash'))::bit(64)::bigint)
THEN '' ELSE 'parent_hash' END
),
created_at, email,
CASE WHEN typ = 'script' AND EXISTS (
SELECT 1 FROM script sc WHERE sc.workspace_id::text = $2::text
AND lpad(to_hex(sc.hash), 16, '0') = base)
THEN base END
CASE WHEN typ = 'script' AND base ~ '^[0-9a-f]{16}$' THEN
CASE WHEN EXISTS (SELECT 1 FROM script sc WHERE sc.workspace_id = $2::text
AND sc.hash = ('x' || base)::bit(64)::bigint)
THEN base END
END
FROM (
SELECT d.path, d.typ, d.created_at, d.email, d.base,
replace(replace(replace(d.value::text, chr(92) || chr(92), chr(1)), chr(92) || 'u0000', ''), chr(1), chr(92) || chr(92))::jsonb AS clean