Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/dsql-alter-table-drop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@aws-blocks/bb-distributed-data": patch
"@aws-blocks/blocks": patch
---

Reject `ALTER TABLE DROP COLUMN` at dev time, including the keyword-less Postgres shorthand (`ALTER TABLE t DROP col` / `DROP IF EXISTS col`). It is not in DSQL's supported `ALTER TABLE` subset ("unsupported ALTER TABLE DROP COLUMN statement", 0A000), but the PGlite-based local mock previously accepted it, so the error only surfaced on deploy. Migration and mock validation now fail locally instead. The supported forms — `ALTER COLUMN ... DROP DEFAULT` / `DROP NOT NULL` / `DROP EXPRESSION` / `DROP IDENTITY` and `DROP CONSTRAINT` — are not affected.

The `@aws-blocks/blocks` umbrella package receives a `patch` because its published `docs/` folder is assembled from sibling block READMEs at build time (`scripts/sync-block-docs.mjs`), so this `bb-distributed-data` README update changes `@aws-blocks/blocks` packaged content.
1 change: 1 addition & 0 deletions packages/bb-distributed-data/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ The core insight: PGlite supports everything DSQL doesn't. Without validation, c
| `SET TRANSACTION ISOLATION LEVEL` | Fixed Repeatable Read |
| `COLLATE` | C collation only |
| `CREATE INDEX ... ASC/DESC` | Sort direction on index keys (NULLS FIRST/LAST is allowed) |
| `ALTER TABLE ... DROP [COLUMN]` | Not in DSQL's supported ALTER TABLE subset (`DROP CONSTRAINT` and `ALTER COLUMN ... DROP DEFAULT/NOT NULL/EXPRESSION/IDENTITY` are supported) |

### Transaction Tracking

Expand Down
1 change: 1 addition & 0 deletions packages/bb-distributed-data/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ DSQL is a subset of PostgreSQL. The local mock enforces these restrictions so co
| Extensions | Not available |
| ADD COLUMN with DEFAULT | Add column without default, handle nulls in app |
| Index key sort direction (`ASC`/`DESC`) | Omit it; enforce ordering with `ORDER BY` in queries (`NULLS FIRST/LAST` is supported) |
| `ALTER TABLE DROP [COLUMN]` | Leave the column in place and stop referencing it; or rebuild the table (create new → `INSERT INTO ... SELECT` → `DROP` → `RENAME TO`, one migration file per step) |

### Transaction Constraints

Expand Down
8 changes: 8 additions & 0 deletions packages/bb-distributed-data/src/e2e-mock.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
import { describe, it, before, after } from 'node:test';
import assert from 'node:assert/strict';
import { rmSync } from 'node:fs';
import { DatabaseBase, sql, type Transaction } from '@aws-blocks/data-common';

Check warning on line 11 in packages/bb-distributed-data/src/e2e-mock.test.ts

View workflow job for this annotation

GitHub Actions / Build, Unit Tests, E2E Local

lint/correctness/noUnusedImports

Several of these imports are unused.
import { DsqlMockEngine } from './engines/dsql-mock-engine.js';
import { DistributedDatabaseErrors } from './errors.js';
import { runMigrations } from './migrations.js';
import { transactionWithRetry } from './transaction.js';
import type { TransactionOptions } from './types.js';

Check warning on line 16 in packages/bb-distributed-data/src/e2e-mock.test.ts

View workflow job for this annotation

GitHub Actions / Build, Unit Tests, E2E Local

lint/correctness/noUnusedImports

This import is unused.

const DIR = '.bb-data/__test_dsql_e2e__';

Expand Down Expand Up @@ -214,6 +214,14 @@
);
});

it('rejects ALTER TABLE DROP COLUMN', async () => {
await engine.withDdl(() => db.execute(sql`CREATE TABLE legacy (id TEXT PRIMARY KEY, obsolete TEXT)`));
await assert.rejects(
() => engine.withDdl(() => db.execute(sql`ALTER TABLE legacy DROP COLUMN obsolete`)),
/DROP COLUMN/i,
);
});

it('does not strip ASYNC outside of CREATE INDEX (column named "async")', async () => {
await engine.withDdl(() => db.execute(sql`CREATE TABLE jobs (id TEXT PRIMARY KEY, async BOOLEAN)`));
await db.execute(sql`INSERT INTO jobs (id, async) VALUES (${'j1'}, ${true})`);
Expand Down
30 changes: 30 additions & 0 deletions packages/bb-distributed-data/src/validation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,43 @@ describe('validateStatement', () => {
['ISOLATION LEVEL', 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE'],
// DSQL only supports C collation — locale-aware sorting is not available
['COLLATE', 'SELECT * FROM t ORDER BY name COLLATE "en_US"'],
// DROP COLUMN is not in DSQL's supported ALTER TABLE subset — rebuild the table instead
['DROP COLUMN', 'ALTER TABLE t DROP COLUMN x'],
// Postgres allows omitting the COLUMN keyword — same DROP COLUMN action, same rejection
['DROP COLUMN shorthand', 'ALTER TABLE t DROP x'],
['DROP COLUMN IF EXISTS shorthand', 'ALTER TABLE t DROP IF EXISTS x'],
['DROP COLUMN quoted shorthand', 'ALTER TABLE t DROP "identity"'],
] as const;

for (const [label, sql] of rejects) {
it(`rejects ${label}`, () => {
assert.throws(() => validateStatement(sql), { name: 'DsqlValidationError' });
});
}

it('allows supported ALTER TABLE forms that contain DROP', () => {
// Per the DSQL ALTER TABLE grammar, the ALTER COLUMN ... DROP actions and
// DROP CONSTRAINT are supported — must not be confused with the
// unsupported DROP [COLUMN].
// https://docs.aws.amazon.com/aurora-dsql/latest/userguide/alter-table-syntax-support.html
assert.doesNotThrow(() => validateStatement('ALTER TABLE t ALTER COLUMN c DROP IDENTITY'));
assert.doesNotThrow(() => validateStatement('ALTER TABLE t ALTER COLUMN c DROP DEFAULT'));
assert.doesNotThrow(() => validateStatement('ALTER TABLE t ALTER COLUMN c DROP NOT NULL'));
assert.doesNotThrow(() => validateStatement('ALTER TABLE t ALTER COLUMN c DROP EXPRESSION'));
assert.doesNotThrow(() => validateStatement('ALTER TABLE t DROP CONSTRAINT c'));
assert.doesNotThrow(() => validateStatement('ALTER TABLE t DROP CONSTRAINT IF EXISTS c CASCADE'));
assert.doesNotThrow(() => validateStatement('ALTER TABLE t RENAME TO t2'));
assert.doesNotThrow(() => validateStatement('DROP TABLE t'));
});

it('allows a supported ALTER TABLE followed by an unrelated statement in one batch', () => {
// The DROP COLUMN rule must not match across a statement boundary: the
// ALTER TABLE here is a supported DROP DEFAULT, and the DROP TABLE that
// follows the semicolon belongs to a different statement.
assert.doesNotThrow(() =>
validateStatement('ALTER TABLE t ALTER COLUMN c DROP DEFAULT; DROP TABLE archived'),
);
});
});

describe('classifyStatement', () => {
Expand Down
1 change: 1 addition & 0 deletions packages/bb-distributed-data/src/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ const RULES: ValidationRule[] = [
{ pattern: /\b(LISTEN|NOTIFY)\b/i, message: 'DSQL does not support LISTEN/NOTIFY.', severity: 'error' },
{ pattern: /\bCREATE\s+EXTENSION\b/i, message: 'DSQL does not support extensions.', severity: 'error' },
{ pattern: /\bALTER\s+TABLE\b[\s\S]*\bADD\s+COLUMN\b[\s\S]*\bDEFAULT\b/i, message: 'DSQL does not support ADD COLUMN with DEFAULT.', severity: 'error' },
{ pattern: /\bALTER\s+TABLE\b[^;]*\bDROP\s+(?:COLUMN\b|IF\s+EXISTS\b|(?!(?:DEFAULT|NOT|EXPRESSION|IDENTITY|CONSTRAINT)\b)[\w"])/i, message: 'DSQL does not support ALTER TABLE DROP COLUMN. Leave the column in place and stop referencing it, or rebuild the table (create a new table → INSERT INTO ... SELECT → DROP → RENAME TO). (DSQL: "unsupported ALTER TABLE DROP COLUMN statement")', severity: 'error' },
{ pattern: /\bALTER\s+DEFAULT\s+PRIVILEGES\b/i, message: 'DSQL does not support ALTER DEFAULT PRIVILEGES.', severity: 'error' },
{ pattern: /\b(CREATE\s+POLICY|ENABLE\s+ROW\s+LEVEL\s+SECURITY)\b/i, message: 'DSQL does not support Row Level Security.', severity: 'error' },
{ pattern: /\bCREATE\s+(TEMP|TEMPORARY)\s+TABLE\b/i, message: 'DSQL does not support temporary tables.', severity: 'error' },
Expand Down
Loading