Skip to content

Commit 715fa78

Browse files
anwesham-labclaude
andcommitted
fix(databases-on-aws): correct DSQL type guidance, add cluster-lifecycle troubleshooting
Update DSQL skill to reflect current DSQL type support: JSON is a supported column type (1 MiB, auto-compressed), while JSONB, arrays, and INET remain runtime-only. Replace the narrow quick-reference type list with a pointer to the canonical AWS docs and an awsknowledge verify-query row, so the skill does not drift as DSQL's type surface evolves. Add troubleshooting entries for the INACTIVE-cluster wake error and the FailedPrecondition returned when backing up an IDLE/INACTIVE cluster, with a pointer to the cluster-lifecycle documentation. Extend the Tier 2 functional eval suite with four new evals covering the updated behaviors — JSON column storage, array-column rejection, the INACTIVE-cluster wake flow, and FailedPrecondition backup-on-idle — plus grader clauses they need and an --eval-ids flag on the runner for targeted subset runs. Verified against live cluster: JSON accepted as column type; JSONB and TEXT[] rejected with "datatype not supported"; ::jsonb cast + ->> / @> operators work as expected. node-postgres auto-serializes JS objects for both JSON and TEXT columns. All four new evals pass 11/11 expectations when run against the updated skill. Co-Authored-By: anwesham-lab <64298192+anwesham-lab@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d6faaf5 commit 715fa78

11 files changed

Lines changed: 245 additions & 58 deletions

File tree

plugins/databases-on-aws/skills/dsql/SKILL.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,11 @@ defaults that may change — when a user's decision depends on an exact limit, v
153153
| Max indexes per table | 24 | `aurora dsql index limits` |
154154
| Max columns per index | 8 | `aurora dsql index limits` |
155155
| IDENTITY/SEQUENCE CACHE values | 1 or >= 65536 | `aurora dsql sequence cache` |
156+
| Supported column data types | See docs | `aurora dsql supported data types` |
156157

157-
**When to verify:** Before recommending batch sizes, connection pool settings, or schema designs
158-
where hitting a limit would cause failures. No need to verify for general guidance or when
159-
the exact number doesn't affect the user's decision.
158+
**When to verify:** Before recommending batch sizes, connection pool settings, or schema designs where hitting a limit would cause failures; any time the exact number can affect user decision.
160159

161-
**Fallback:** If `awsknowledge` is unavailable, use the defaults above and note to the user
162-
that limits should be verified against [DSQL documentation](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/).
160+
**Fallback:** If `awsknowledge` is unavailable, use the defaults above and flag that limits should be verified against [DSQL documentation](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/).
163161

164162
## CLI Scripts Available
165163

@@ -208,7 +206,7 @@ ALTER COLUMN TYPE, DROP COLUMN, DROP CONSTRAINT → Table Recreation Pattern (Wo
208206
- MUST include tenant_id in all tables
209207
- MUST use `CREATE INDEX ASYNC` exclusively
210208
- MUST issue each DDL in its own transact call: `transact(["CREATE TABLE ..."])`
211-
- MUST store arrays/JSON as TEXT
209+
- MUST store arrays as TEXT
212210

213211
### Workflow 2: Safe Data Migration
214212

plugins/databases-on-aws/skills/dsql/references/development-guide.md

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ effortless scaling, multi-region viability, among other advantages.
1313
- **REQUIRED: Follow DDL Guidelines** - Refer to [DDL Rules](#schema-ddl-rules)
1414
- **SHALL repeatedly generate fresh tokens** - Refer to [Connection Limits](auth/authentication-guide.md#connection-rules)
1515
- **ALWAYS use ASYNC indexes** - `CREATE INDEX ASYNC` is mandatory
16-
- **MUST Serialize arrays/JSON as TEXT** - Store arrays/JSON as TEXT (comma separated, JSON.stringify)
16+
- **MUST serialize arrays as TEXT** - comma-separated
17+
- **MUST cast to `JSONB` at query time** for JSONB operators; `JSONB` is not a valid column type
1718
- **ALWAYS Batch within row limit** - maintain transaction limits (verify via `awsknowledge`: `aurora dsql transaction limits`)
1819
- **REQUIRED: Build and sanitize all SQL with `safe_query.build()`** - See [Input Validation](../mcp/tools/input-validation.md#required-pattern)
1920
- **MUST follow correct Application Layer Patterns** - when multi-tenant isolation or application referential integrity are required; refer to [Application Layer Patterns](#application-layer-patterns)
@@ -53,9 +54,8 @@ effortless scaling, multi-region viability, among other advantages.
5354

5455
### Schema Design Rules
5556

56-
- MUST use **simple PostgreSQL types:** VARCHAR, TEXT, INTEGER, BOOLEAN, TIMESTAMP
57-
- MUST store arrays as TEXT (comma-separated is recommended)
58-
- MUST store JSON objects as TEXT (JSON.stringify)
57+
- MUST verify column types via `awsknowledge`: `aurora dsql supported data types` or the [DSQL supported data types list](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/working-with-postgresql-compatibility-supported-data-types.html)
58+
- MUST store arrays as TEXT (comma-separated)
5959
- ALWAYS include tenant_id in tables for multi-tenant isolation
6060
- SHOULD create async indexes for tenant_id and common query patterns
6161

@@ -124,9 +124,8 @@ UPDATE table SET c = 'default' WHERE c IS NULL; ← AFTER ADD COLUMN
124124

125125
### Supported Data Types
126126

127-
```
128-
VARCHAR, TEXT, INTEGER, DECIMAL, BOOLEAN, TIMESTAMP, UUID
129-
```
127+
- **MUST** verify column types via `awsknowledge`: `aurora dsql supported data types` or the [DSQL supported data types list](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/working-with-postgresql-compatibility-supported-data-types.html)
128+
- `JSONB`, arrays, and `INET` are [runtime-only](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/working-with-postgresql-compatibility-supported-data-types.html#working-with-postgresql-compatibility-query-runtime) — cast at query time
130129

131130
### Supported Key
132131

plugins/databases-on-aws/skills/dsql/references/examples/data-operations.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ async function batchInsert(pool, tenantId, items) {
5454
await client.query(
5555
`INSERT INTO entities (tenant_id, name, metadata)
5656
VALUES ($1, $2, $3)`,
57-
[tenantId, item.name, JSON.stringify(item.metadata)]
57+
[tenantId, item.name, item.metadata]
5858
);
5959
}
6060

@@ -105,7 +105,7 @@ async function processBatches(pool, tenantId, batches, startIdx, step) {
105105
for (const item of batch) {
106106
await client.query(
107107
'INSERT INTO entities (tenant_id, name, metadata) VALUES ($1, $2, $3)',
108-
[tenantId, item.name, JSON.stringify(item.metadata)]
108+
[tenantId, item.name, item.metadata]
109109
);
110110
}
111111

plugins/databases-on-aws/skills/dsql/references/examples/patterns.md

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,12 @@ INSERT INTO distributors VALUES (nextval('order_seq'), 'nothing');
129129

130130
---
131131

132-
## Data Serialization
132+
## Runtime-Only Types
133133

134-
**Pattern:** MUST store arrays and JSON as TEXT (runtime-only types). Per [DSQL docs](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/working-with-postgresql-compatibility-supported-data-types.html), cast to JSON at query time.
134+
`JSONB`, arrays, and `INET` are [runtime-only](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/working-with-postgresql-compatibility-supported-data-types.html#working-with-postgresql-compatibility-query-runtime) — not valid as column types.
135+
136+
- **MUST** serialize arrays as `TEXT` (comma-separated)
137+
- **MUST** cast to `JSONB` at query time for JSONB operators
135138

136139
```javascript
137140
function toTextArray(values) {
@@ -142,32 +145,21 @@ function fromTextArray(textValue) {
142145
return textValue ? textValue.split(',').map(v => v.trim()) : [];
143146
}
144147

145-
function toTextJSON(object) {
146-
return JSON.stringify(object);
147-
}
148-
149-
function fromTextJSON(textValue) {
150-
if (!textValue) return null;
151-
try {
152-
return JSON.parse(textValue);
153-
} catch (err) {
154-
console.warn('Invalid JSON in column:', err.message);
155-
return null;
156-
}
157-
}
158-
159148
const categoriesText = toTextArray(['backend', 'api', 'database']);
160149
await pool.query('INSERT INTO projects (project_id, categories) VALUES ($1, $2)', [projectId, categoriesText]);
161150

162-
const configText = toTextJSON({ theme: 'dark', notifications: true });
163-
await pool.query('INSERT INTO user_settings (user_id, preferences) VALUES ($1, $2)', [userId, configText]);
151+
await pool.query(
152+
'INSERT INTO user_settings (user_id, preferences) VALUES ($1, $2)',
153+
[userId, { theme: 'dark', notifications: true }],
154+
);
164155
```
165156

166157
Query-time operations:
167158

168159
```sql
169-
SELECT user_id, preferences::jsonb->>'theme' as theme
170-
FROM user_settings WHERE preferences::jsonb->>'notifications' = 'true';
160+
SELECT user_id, preferences::jsonb->>'theme' AS theme
161+
FROM user_settings
162+
WHERE preferences::jsonb->>'notifications' = 'true';
171163

172-
SELECT project_id, string_to_array(categories, ',') as category_array FROM projects;
164+
SELECT project_id, string_to_array(categories, ',') AS category_array FROM projects;
173165
```

plugins/databases-on-aws/skills/dsql/references/mysql-migrations/full-example.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ transact([
4545
price DECIMAL(10,2) NOT NULL,
4646
category VARCHAR(255) DEFAULT 'other' CHECK (category IN ('electronics', 'clothing', 'food', 'other')),
4747
tags TEXT,
48-
metadata TEXT,
48+
metadata JSON,
4949
stock INTEGER DEFAULT 0 CHECK (stock >= 0),
5050
is_active BOOLEAN DEFAULT true,
5151
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
@@ -70,7 +70,7 @@ transact(["CREATE INDEX ASYNC idx_products_category ON products(tenant_id, categ
7070
| `MEDIUMTEXT` | `TEXT` |
7171
| `ENUM(...)` | `VARCHAR(255)` with `CHECK` constraint |
7272
| `SET(...)` | `TEXT` (comma-separated) |
73-
| `JSON` | `TEXT` (JSON.stringify) |
73+
| `JSON` | `JSON` |
7474
| `UNSIGNED` | `CHECK (col >= 0)` |
7575
| `TINYINT(1)` | `BOOLEAN` |
7676
| `DATETIME` | `TIMESTAMP` |
@@ -99,7 +99,6 @@ transact(["CREATE INDEX ASYNC idx_products_category ON products(tenant_id, categ
9999
- **MUST convert** AUTO_INCREMENT to UUID with gen_random_uuid(), IDENTITY column with `GENERATED AS IDENTITY (CACHE ...)`, or explicit SEQUENCE -- ALWAYS use `GENERATED AS IDENTITY` for auto-incrementing columns (see [AUTO_INCREMENT Migration](ddl-auto-increment.md#auto_increment-migration))
100100
- **MUST replace** ENUM with VARCHAR and CHECK constraint
101101
- **MUST replace** SET with TEXT (comma-separated)
102-
- **MUST replace** JSON columns with TEXT
103102
- **MUST replace** FOREIGN KEY constraints with application-layer referential integrity
104103
- **MUST replace** ON UPDATE CURRENT_TIMESTAMP with application-layer updates
105104
- **MUST convert** all index creation to use CREATE INDEX ASYNC

plugins/databases-on-aws/skills/dsql/references/mysql-migrations/type-mapping.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ Map MySQL data types to their DSQL equivalents.
9797

9898
| MySQL Type | DSQL Equivalent | Notes |
9999
| -------------- | --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
100-
| JSON | TEXT | MUST store as TEXT |
100+
| JSON | `JSON` | Direct equivalent |
101101
| AUTO_INCREMENT | UUID with gen_random_uuid(), IDENTITY column, or SEQUENCE | See [AUTO_INCREMENT Migration](ddl-auto-increment.md#auto_increment-migration) for all three options |
102102

103103
---

plugins/databases-on-aws/skills/dsql/references/onboarding.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ These guidelines apply when users say "Get started with DSQL" or similar phrases
3535
- Example:
3636
- "What column names would you like in this table?"
3737
- "What is the column name of the primary key?"
38-
- "JSON must be serialized. Would you like to stringify the JSON to serialize it as TEXT?"
38+
- "Would you like to store this in a `JSON` column, or serialize as TEXT?"
3939

4040
**Examples:**
4141

@@ -252,7 +252,9 @@ cargo add aws-sdk-dsql tokio --features full
252252
- If yes, MUST verify DSQL compatibility:
253253
- No SERIAL types (use `GENERATED AS IDENTITY` with sequences, or UUID)
254254
- No foreign keys (implement in application)
255-
- No array/JSON column types (serialize as TEXT)
255+
- No array column types — serialize as TEXT
256+
- Cast to `JSONB` at query time for JSONB operators
257+
- Verify column types against the [supported data types list](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/working-with-postgresql-compatibility-supported-data-types.html)
256258
- Reference [`./development-guide.md`](./development-guide.md) for full constraints
257259

258260
**If no schema found:**
@@ -349,7 +351,7 @@ Let them know you're ready to help with more:
349351
**ALWAYS follow these rules:**
350352

351353
1. **Indexes:** Use `CREATE INDEX ASYNC` - synchronous index creation not supported
352-
2. **Serialization:** Store arrays/JSON as TEXT (comma-separated or JSON.stringify)
354+
2. **Runtime-only types:** Serialize arrays as TEXT (comma-separated); cast to `JSONB` at query time for JSONB operators
353355
3. **Referential Integrity:** Implement foreign key validation in application code
354356
4. **DDL Operations:** Execute one DDL per transaction, no mixing with DML
355357
5. **Transaction Limits:** Maximum 3,000 row modifications, 10 MiB data size per transaction

plugins/databases-on-aws/skills/dsql/references/troubleshooting.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,18 @@ Before referring to any listed errors, refer to the complete [DSQL troubleshooti
5252
- Use native TLS libraries (not OpenSSL 1.0.x)
5353
- Set `server_name_indication` to cluster endpoint in SSL config
5454

55+
## Cluster Lifecycle
56+
57+
See [cluster lifecycle](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/cluster-lifecycle.html) for state definitions and behavior.
58+
59+
### Error: "FATAL: unable to accept connection, waking up cluster, please retry later"
60+
61+
The cluster is `INACTIVE` and waking up. Poll `aws dsql get-cluster --identifier <id> --region <region> --query status --output text` until `ACTIVE`, then retry.
62+
63+
### Error: `FailedPrecondition` when backing up an `IDLE` / `INACTIVE` cluster
64+
65+
Connect to the cluster to wake it, then retry the backup.
66+
5567
## Incompatibility
5668

5769
When migrating from PostgreSQL, remember DSQL doesn't support:
@@ -83,10 +95,8 @@ See [full list of unsupported features](https://docs.aws.amazon.com/aurora-dsql/
8395
**Cause:** Using TEXT[] or other array types
8496
**Solution:**
8597

86-
1. Change column to TEXT
87-
2. Store as comma-separated: `"tag1,tag2,tag3"`
88-
3. Or use JSON.stringify: `"["tag1","tag2","tag3"]"`
89-
4. Deserialize in application layer
98+
1. Change column to TEXT and store as comma-separated (`"tag1,tag2,tag3"`), or use a `JSON` column (`tags JSON`)
99+
2. Deserialize in application layer; cast to `JSONB` at query time for JSONB operators
90100

91101
### Error: "Please use CREATE INDEX ASYNC"
92102

tools/evals/databases-on-aws/README.md

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,30 @@ python tools/evals/databases-on-aws/dsql/scripts/run_functional_evals.py \
6868
--verbose
6969
```
7070

71-
**What it checks** (5 eval prompts, 20 assertions total):
72-
73-
| Eval | Focus | Key assertions |
74-
| ---------------------- | --------------------- | -------------------------------------------------------------------------- |
75-
| 1. Transaction limits | MCP delegation | Calls `awsknowledge`, cites 3,000 row limit, recommends batching |
76-
| 2. Multi-tenant schema | Correctness | Uses `tenant_id`, `CREATE INDEX ASYNC`, no foreign keys, separate DDL txns |
77-
| 3. Index limits | MCP delegation | Calls `awsknowledge`, cites 24 index limit, suggests alternatives |
78-
| 4. Python connection | Language routing | Recommends DSQL Python Connector, IAM auth, 15-min token expiry, SSL |
79-
| 5. Column type change | DDL migration routing | Table Recreation Pattern, DROP TABLE warning, batching, user confirmation |
71+
Run a subset by ID (e.g., just the new type / lifecycle evals):
72+
73+
```bash
74+
python tools/evals/databases-on-aws/dsql/scripts/run_functional_evals.py \
75+
--evals tools/evals/databases-on-aws/dsql/evals.json \
76+
--plugin-dir plugins/databases-on-aws \
77+
--output-dir /tmp/dsql-eval-results \
78+
--eval-ids 6,7,8 \
79+
--verbose
80+
```
81+
82+
**What it checks** (9 eval prompts, 31 assertions total):
83+
84+
| Eval | Focus | Key assertions |
85+
| -------------------------- | --------------------- | ------------------------------------------------------------------------------------------------- |
86+
| 1. Transaction limits | MCP delegation | Calls `awsknowledge`, cites 3,000 row limit, recommends batching |
87+
| 2. Multi-tenant schema | Correctness | Uses `tenant_id`, `CREATE INDEX ASYNC`, no foreign keys, separate DDL txns |
88+
| 3. Index limits | MCP delegation | Calls `awsknowledge`, cites 24 index limit, suggests alternatives |
89+
| 4. Python connection | Language routing | Recommends DSQL Python Connector, IAM auth, 15-min token expiry, SSL |
90+
| 5. Column type change | DDL migration routing | Table Recreation Pattern, DROP TABLE warning, batching, user confirmation |
91+
| 6. JSON column storage | Type guidance | Explains `::jsonb` cast, does not recommend `JSONB` as a column type |
92+
| 7. Array storage | Type guidance | Flags `TEXT[]` / array column as unsupported, recommends TEXT or `JSON` column |
93+
| 8. INACTIVE cluster error | Troubleshooting | Identifies INACTIVE state, uses `aws dsql get-cluster` to poll until `ACTIVE`, retries afterwards |
94+
| 9. Backup on IDLE/INACTIVE | Troubleshooting | Identifies `FailedPrecondition`, connects to wake cluster to ACTIVE, retries backup |
8095

8196
### Tier 3: Safe-Query Enforcement Evals
8297

tools/evals/databases-on-aws/dsql/evals.json

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,49 @@
6060
"Mentions batching the data copy for tables exceeding 3,000 rows",
6161
"Requires or recommends user confirmation before destructive steps"
6262
]
63+
},
64+
{
65+
"id": 6,
66+
"prompt": "I need to store a JSON preferences blob per user in my DSQL users table. How should I model that column, and how do I query fields inside it?",
67+
"expected_output": "Recommends either JSON or TEXT as a valid column type. Explains that JSONB is runtime-only and must be cast (preferences::jsonb->>'key') at query time for JSONB operators.",
68+
"files": [],
69+
"expectations": [
70+
"Mentions casting to jsonb at query time for JSONB operators",
71+
"Does NOT claim JSONB is a valid column type (JSONB is runtime-only)"
72+
]
73+
},
74+
{
75+
"id": 7,
76+
"prompt": "I want to store a tags array per project in DSQL (e.g. ['backend','database','api']). Can I use TEXT[] for that?",
77+
"expected_output": "Indicates TEXT[] / array column type is not supported in DSQL. Recommends storing arrays as TEXT (comma-separated) or inside a JSON column.",
78+
"files": [],
79+
"expectations": [
80+
"Indicates TEXT[] or array column type is not supported in DSQL",
81+
"Recommends storing arrays as TEXT (comma-separated) or inside a JSON column"
82+
]
83+
},
84+
{
85+
"id": 8,
86+
"prompt": "My DSQL connection just failed with: FATAL: unable to accept connection, waking up cluster, please retry later. What's happening and what should I do?",
87+
"expected_output": "Identifies the cluster as INACTIVE, explains that the first connection triggers the wake. Recommends polling cluster status via aws dsql get-cluster until ACTIVE, then retrying the connection.",
88+
"files": [],
89+
"expectations": [
90+
"Identifies the cluster is in INACTIVE state and waking up",
91+
"Mentions aws dsql get-cluster for checking cluster status",
92+
"Recommends polling until the cluster reaches ACTIVE state",
93+
"Recommends retrying the connection after cluster becomes ACTIVE"
94+
]
95+
},
96+
{
97+
"id": 9,
98+
"prompt": "I tried `aws dsql create-cluster-backup` and got FailedPrecondition: Cluster is in state 'IDLE' and can't be backed up. How do I fix this?",
99+
"expected_output": "Identifies that backups require an ACTIVE cluster. Recommends connecting to the cluster to transition it to ACTIVE, then retrying the backup.",
100+
"files": [],
101+
"expectations": [
102+
"Identifies that backups require the cluster to be in ACTIVE state",
103+
"Recommends connecting to the cluster to wake it to ACTIVE",
104+
"Recommends retrying the backup after cluster is ACTIVE"
105+
]
63106
}
64107
]
65108
}

0 commit comments

Comments
 (0)