Skip to content

Commit 222be16

Browse files
authored
Merge pull request #590 from cipherstash/use-cipherstash-eql
chore(stack): source EQL v3 bundle from @cipherstash/eql
2 parents 2decfb0 + 70e9f19 commit 222be16

8 files changed

Lines changed: 71 additions & 43369 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@cipherstash/stack': patch
3+
---
4+
5+
Source the EQL v3 install bundle from `@cipherstash/eql@3.0.0-alpha.3` instead of a hand-vendored 43k-line SQL fixture committed to the test tree. The package publishes its SQL and its TypeScript wire types from the same `eql-bindings` commit, so the bundle is now pinned to a released EQL version rather than tracked by convention.
6+
7+
Test-and-tooling only — `@cipherstash/eql` is a `devDependency` and no public API changes.
8+
9+
The staleness check in the v3 install helper now compares `eql_v3.version()` against the pinned release instead of probing for a hand-picked sentinel domain. The previous sentinel (`public.timestamp`) exists in both the old and new bundles, so it would have reported a stale install as current and left the suite silently running the wrong SQL.

docs/eql-v3-ord-term-ordering-defect.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,14 @@ ORE-correct — and they are correct regardless of superuser.
3333

3434
## Why this happens on Supabase
3535

36-
File: `packages/stack/__tests__/fixtures/eql-v3/cipherstash-encrypt-v3.sql`
37-
(the vendored bundle — do not hand-edit; it is regenerated on re-vendor).
36+
File: the EQL v3 install bundle shipped by `@cipherstash/eql`, at
37+
`node_modules/@cipherstash/eql/dist/sql/cipherstash-encrypt.sql` (or via
38+
`installSqlPath()` from `@cipherstash/eql/sql`). Generated upstream — never
39+
hand-edit; bump the pinned package version instead.
40+
41+
Line numbers below were recorded against the bundle vendored at the time of
42+
writing and are indicative only; the released bundle orders its source files
43+
differently. Locate the definitions by name.
3844

3945
1. `eql_v3.ord_term(a public.text_search)` (and every `*_ord` domain, e.g.
4046
`public.integer_ord` at line 18117) returns the **composite** type

packages/stack/__tests__/fixtures/eql-v3/cipherstash-encrypt-v3.sql

Lines changed: 0 additions & 43339 deletions
This file was deleted.

packages/stack/__tests__/helpers/eql-v3.ts

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,40 @@
1-
import { readFile } from 'node:fs/promises'
2-
import { dirname, resolve } from 'node:path'
3-
import { fileURLToPath } from 'node:url'
1+
import { readInstallSql, releaseManifest } from '@cipherstash/eql/sql'
42
import type postgres from 'postgres'
53

64
const EQL_V3_ADVISORY_LOCK_ID = 3_733_003
75

8-
const helperDir = dirname(fileURLToPath(import.meta.url))
9-
const eqlV3SqlPath = resolve(
10-
helperDir,
11-
'../fixtures/eql-v3/cipherstash-encrypt-v3.sql',
12-
)
13-
14-
// The sentinel must be a type that exists ONLY in the currently vendored
15-
// bundle, so a database still carrying an older EQL v3 install is detected as
16-
// stale and reinstalled (the bundle's leading DROP SCHEMA … CASCADE replaces
17-
// the old install wholesale). public.timestamp is new in the current bundle
18-
// (the timestamptz → timestamp rename, encrypt-query-language@2e64ca73); the
19-
// previous sentinel, public.text_search, exists in both generations and would
20-
// leave a stale install in place.
21-
async function hasCurrentEqlV3(sql: postgres.Sql): Promise<boolean> {
22-
const [row] = await sql<{ installed: boolean }[]>`
23-
SELECT to_regtype('public.timestamp') IS NOT NULL AS installed
6+
// Staleness is decided by asking the database which EQL release it is running
7+
// and comparing that to the release the pinned @cipherstash/eql ships. Earlier
8+
// revisions probed for a sentinel type (public.text_search, then
9+
// public.timestamp) that was hand-picked to exist only in the newest bundle.
10+
// Every such sentinel decays: the next bundle keeps the type, the check starts
11+
// reporting "current" against a stale install, and the suite silently exercises
12+
// the wrong SQL. eql_v3.version() carries the release identity itself, so this
13+
// needs no maintenance when the pin moves.
14+
//
15+
// The probe has to be its own statement. Postgres resolves function references
16+
// while parsing a statement, before any branch of it runs, so guarding the call
17+
// with CASE WHEN to_regprocedure(...) IS NULL in the same statement still raises
18+
// "schema eql_v3 does not exist" against a database with no EQL v3 — the guard
19+
// only ever succeeds when it is not needed. to_regprocedure() takes the name as
20+
// a string, so on its own it yields NULL rather than raising, and a database
21+
// with no EQL v3 — or with a bundle predating eql_v3.version() — reads as stale
22+
// and gets installed.
23+
async function readEqlV3Version(sql: postgres.Sql): Promise<string | null> {
24+
const [probe] = await sql<{ present: boolean }[]>`
25+
SELECT to_regprocedure('eql_v3.version()') IS NOT NULL AS present
2426
`
25-
return row?.installed ?? false
27+
if (!probe?.present) return null
28+
29+
const [row] = await sql<
30+
{ version: string }[]
31+
>`SELECT eql_v3.version() AS version`
32+
return row?.version ?? null
2633
}
2734

2835
/**
29-
* Install the generated EQL v3 SQL bundle only when the target database does
30-
* not already expose the current bundle's sentinel type (see hasCurrentEqlV3).
36+
* Install the EQL v3 SQL bundle shipped by @cipherstash/eql only when the
37+
* target database is not already running that exact release.
3138
*
3239
* The bundle starts with DROP SCHEMA IF EXISTS eql_v3 CASCADE, so callers must
3340
* never run it unconditionally against a shared test database.
@@ -43,13 +50,18 @@ export async function installEqlV3IfNeeded(sql: postgres.Sql): Promise<void> {
4350
await reserved`SELECT pg_advisory_lock(${EQL_V3_ADVISORY_LOCK_ID})`
4451

4552
try {
46-
if (await hasCurrentEqlV3(reserved)) return
53+
if ((await readEqlV3Version(reserved)) === releaseManifest.eqlVersion)
54+
return
4755

48-
const eqlV3Sql = await readFile(eqlV3SqlPath, 'utf8')
49-
await reserved.unsafe(eqlV3Sql)
56+
// Sent as one multi-statement string: the bundle is ~43k lines and a
57+
// statement-at-a-time client would pay a round-trip per CREATE FUNCTION.
58+
await reserved.unsafe(readInstallSql())
5059

51-
if (!(await hasCurrentEqlV3(reserved))) {
52-
throw new Error('EQL v3 installation did not create public.timestamp')
60+
const installed = await readEqlV3Version(reserved)
61+
if (installed !== releaseManifest.eqlVersion) {
62+
throw new Error(
63+
`EQL v3 installation did not yield the expected release: wanted ${releaseManifest.eqlVersion}, got ${installed ?? 'no eql_v3.version()'}`,
64+
)
5365
}
5466
} finally {
5567
await reserved`SELECT pg_advisory_unlock(${EQL_V3_ADVISORY_LOCK_ID})`

packages/stack/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@
237237
"release": "tsup"
238238
},
239239
"devDependencies": {
240+
"@cipherstash/eql": "3.0.0-alpha.3",
240241
"@clack/prompts": "^1.4.0",
241242
"@supabase/supabase-js": "^2.105.4",
242243
"@types/uuid": "^11.0.0",

packages/stack/scripts/install-eql-v3.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ config({ path: '.env.development.local', quiet: true })
1010
config({ path: '.env.development', quiet: true })
1111
config({ path: '.env', quiet: true })
1212

13+
import { releaseManifest } from '@cipherstash/eql/sql'
1314
import postgres from 'postgres'
1415
import { installEqlV3IfNeeded } from '../__tests__/helpers/eql-v3'
1516

@@ -21,7 +22,7 @@ const sql = postgres(process.env.DATABASE_URL, { prepare: false })
2122

2223
try {
2324
await installEqlV3IfNeeded(sql)
24-
console.log('eql_v3 (current bundle) is installed')
25+
console.log(`eql_v3 ${releaseManifest.eqlVersion} is installed`)
2526
} finally {
2627
await sql.end()
2728
}

pnpm-lock.yaml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,13 @@ blockExoticSubdeps: true
4343
# - @cipherstash/auth* CipherStash-published auth strategies (NAPI +
4444
# WASM-inline variant); also tracked in lockstep
4545
# with protect-ffi for the WASM path.
46+
# - @cipherstash/eql CipherStash-published EQL SQL bundle + wire types,
47+
# generated from the same eql-bindings commit as the
48+
# payload format protect-ffi emits; bumped in lockstep.
4649
minimumReleaseAgeExclude:
4750
- '@prisma-next/*'
4851
- '@cipherstash/protect-ffi'
4952
- '@cipherstash/protect-ffi-*'
5053
- '@cipherstash/auth'
5154
- '@cipherstash/auth-*'
55+
- '@cipherstash/eql'

0 commit comments

Comments
 (0)