-
Notifications
You must be signed in to change notification settings - Fork 515
feat(cli): upgrade pg-delta next to alpha.46 #6300
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
f387bcc
feat(cli): upgrade pg-delta next to alpha.46
avallete 59285c6
fix(cli): restore image pgjwt only when it was installed
avallete d57990e
fix(cli): harden declarative shadow-prep SQL and cancel
avallete 6af0469
fix(cli): skip quoted idents when scanning CREATE EXTENSION
avallete ec9946d
refactor(cli): drop the CREATE EXTENSION lexer
avallete 1387b70
refactor(cli): drop the declarative shadow-prep checkout adapter
avallete 1ac2318
Merge branch 'develop' into feat/upgrade-pg-delta-alpha.46
avallete File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
152 changes: 152 additions & 0 deletions
152
apps/cli/src/legacy/commands/db/shared/legacy-pgdelta-declarative-shadow-prep.ts
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,152 @@ | ||
| import { Effect } from "effect"; | ||
|
|
||
| import { LegacyPgDeltaEngineError } from "./legacy-pgdelta-engine.service.ts"; | ||
|
|
||
| export type LegacyDeclarativeShadowClient = { | ||
| readonly query: (sql: string) => Promise<{ readonly rows: ReadonlyArray<unknown> }>; | ||
| }; | ||
|
|
||
| export interface LegacyDeclarativeShadowPrepResult { | ||
| /** True only when prep dropped an installed image pgjwt to recreate pgcrypto. */ | ||
| readonly restorePgjwt: boolean; | ||
| } | ||
|
|
||
| /** Image-default extensions the user may still declare; omit means keep the install. */ | ||
| const IMAGE_DEFAULT_EXTENSIONS = ["pgjwt", "pgcrypto", "uuid-ossp"] as const; | ||
|
|
||
| const IMAGE_DEFAULT_EXTENSION_SET = new Set<string>(IMAGE_DEFAULT_EXTENSIONS); | ||
|
|
||
| const DROP_IMAGE_DEFAULT_EXTENSION: Record<(typeof IMAGE_DEFAULT_EXTENSIONS)[number], string> = { | ||
| pgjwt: "DROP EXTENSION IF EXISTS pgjwt", | ||
| pgcrypto: "DROP EXTENSION IF EXISTS pgcrypto", | ||
| "uuid-ossp": 'DROP EXTENSION IF EXISTS "uuid-ossp"', | ||
| }; | ||
|
|
||
| const CREATE_EXTENSION_RE = | ||
| /\bCREATE\s+EXTENSION\s+(?:IF\s+NOT\s+EXISTS\s+)?(?:"([^"]+)"|([a-zA-Z_][\w$-]*))/gi; | ||
|
|
||
| /** Blank comments and simple strings; keep offsets for locateSignature line mapping. */ | ||
| export const legacyMaskSqlComments = (sql: string): string => | ||
| sql.replaceAll(/--[^\r\n]*|\/\*[\s\S]*?\*\/|'(?:[^']|'')*'/g, (matched) => | ||
| matched.replaceAll(/[^\r\n]/g, " "), | ||
| ); | ||
|
|
||
| export const legacyDeclaredSqlExtensions = ( | ||
| files: ReadonlyArray<{ readonly name: string; readonly sql: string }>, | ||
| ): ReadonlySet<string> => { | ||
| const declared = new Set<string>(); | ||
| for (const file of files) { | ||
| for (const match of legacyMaskSqlComments(file.sql).matchAll(CREATE_EXTENSION_RE)) { | ||
| const name = (match[1] ?? match[2] ?? "").toLowerCase(); | ||
| if (name !== "") declared.add(name); | ||
| } | ||
| } | ||
| return declared; | ||
| }; | ||
|
|
||
| const declaredImageExtensions = ( | ||
| files: ReadonlyArray<{ readonly name: string; readonly sql: string }>, | ||
| ): ReadonlySet<string> => { | ||
| const declared = new Set<string>(); | ||
| for (const name of legacyDeclaredSqlExtensions(files)) { | ||
| if (IMAGE_DEFAULT_EXTENSION_SET.has(name)) declared.add(name); | ||
| } | ||
| return declared; | ||
| }; | ||
|
|
||
| const legacyParsePostgresMajorVersion = (serverVersion: string): number => { | ||
| const major = Number.parseInt(serverVersion, 10); | ||
| return Number.isInteger(major) ? major : 0; | ||
| }; | ||
|
|
||
| const legacyDeclarativeBaselinePrepStatements = ( | ||
| majorVersion: number, | ||
| declared: ReadonlySet<string>, | ||
| ): ReadonlyArray<string> => { | ||
| const dropPgcrypto = declared.has("pgcrypto"); | ||
| // Image pgjwt depends on pgcrypto; drop it first so pgcrypto can drop. | ||
| const dropPgjwt = declared.has("pgjwt") || dropPgcrypto; | ||
| const dropUuidOssp = declared.has("uuid-ossp"); | ||
| const statements: string[] = []; | ||
| if (majorVersion === 14 && dropUuidOssp) { | ||
| statements.push("ALTER TABLE storage.objects ALTER COLUMN id DROP DEFAULT"); | ||
| } | ||
| if (dropPgjwt) statements.push(DROP_IMAGE_DEFAULT_EXTENSION.pgjwt); | ||
| if (dropPgcrypto) statements.push(DROP_IMAGE_DEFAULT_EXTENSION.pgcrypto); | ||
| if (dropUuidOssp) statements.push(DROP_IMAGE_DEFAULT_EXTENSION["uuid-ossp"]); | ||
| return statements; | ||
| }; | ||
|
|
||
| /** Recreate image pgjwt after a pgcrypto-only drop so omit still means keep. */ | ||
| export const legacyFilesForDeclarativeShadowLoad = ( | ||
| files: ReadonlyArray<{ readonly name: string; readonly sql: string }>, | ||
| restorePgjwt: boolean, | ||
| ): ReadonlyArray<{ readonly name: string; readonly sql: string }> => { | ||
| if (!restorePgjwt) return files; | ||
| return [ | ||
| ...files, | ||
| { | ||
| name: "_cli/restore-pgjwt.sql", | ||
| sql: "CREATE EXTENSION IF NOT EXISTS pgjwt WITH SCHEMA extensions;\n", | ||
| }, | ||
| ]; | ||
| }; | ||
|
|
||
| /** User cannot edit this SQL; a persistent miss is a CLI bug. */ | ||
| const DECLARATIVE_SHADOW_PREP_FAILURE_SUGGESTION = | ||
| "This statement is CLI-owned shadow prep, not a project migration or schema file. If it persists, report it with supabase issue bug."; | ||
|
|
||
| const queryError = (sql: string, cause: unknown) => | ||
| new LegacyPgDeltaEngineError({ | ||
| message: `Failed to prepare the isolated declaration shadow (${sql}): ${ | ||
| cause instanceof Error ? cause.message : String(cause) | ||
| }`, | ||
| cause, | ||
| suggestion: DECLARATIVE_SHADOW_PREP_FAILURE_SUGGESTION, | ||
| }); | ||
|
|
||
| const readServerVersion = (rows: ReadonlyArray<unknown>): string => { | ||
| const row = rows[0]; | ||
| if (row === undefined || typeof row !== "object" || row === null) return ""; | ||
| const value = Reflect.get(row, "server_version"); | ||
| return typeof value === "string" ? value : ""; | ||
| }; | ||
|
|
||
| const rowHasPgjwt = (rows: ReadonlyArray<unknown>): boolean => | ||
| rows.some((row) => { | ||
| if (typeof row !== "object" || row === null) return false; | ||
| const name = Reflect.get(row, "extname"); | ||
| return name === "pgjwt"; | ||
| }); | ||
|
|
||
| const INSTALLED_PGJWT_SQL = "SELECT extname FROM pg_extension WHERE extname = 'pgjwt'"; | ||
|
|
||
| const queryShadow = (client: LegacyDeclarativeShadowClient, sql: string) => | ||
| Effect.tryPromise({ | ||
| try: () => client.query(sql), | ||
| catch: (cause) => queryError(sql, cause), | ||
| }); | ||
|
|
||
| export const legacyPrepareDeclarativeShadow = ( | ||
| client: LegacyDeclarativeShadowClient, | ||
| files: ReadonlyArray<{ readonly name: string; readonly sql: string }>, | ||
| ) => | ||
| Effect.gen(function* () { | ||
| const declared = declaredImageExtensions(files); | ||
| if (declared.size === 0) | ||
| return { restorePgjwt: false } satisfies LegacyDeclarativeShadowPrepResult; | ||
| let restorePgjwt = false; | ||
| if (declared.has("pgcrypto") && !declared.has("pgjwt")) { | ||
| const installed = yield* queryShadow(client, INSTALLED_PGJWT_SQL); | ||
| restorePgjwt = rowHasPgjwt(installed.rows); | ||
|
avallete marked this conversation as resolved.
|
||
| } | ||
| const versionRows = yield* queryShadow(client, "SHOW server_version"); | ||
| const statements = legacyDeclarativeBaselinePrepStatements( | ||
| legacyParsePostgresMajorVersion(readServerVersion(versionRows.rows)), | ||
| declared, | ||
| ); | ||
| for (const sql of statements) { | ||
| yield* queryShadow(client, sql); | ||
| } | ||
| return { restorePgjwt } satisfies LegacyDeclarativeShadowPrepResult; | ||
| }); | ||
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.