Skip to content

Commit 6671e09

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 three new evals covering the updated behaviors — JSON column storage, array-column rejection, and the INACTIVE-cluster wake flow — plus the 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. 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 6671e09

11 files changed

Lines changed: 218 additions & 53 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; SHOULD store JSON in a `JSON` column
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+
- **SHOULD store JSON in a `JSON` column** - cast to `JSONB` at query time for JSONB operators
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,9 @@ 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)
59+
- SHOULD store JSON in a `JSON` column; cast to `JSONB` at query time for JSONB operators
5960
- ALWAYS include tenant_id in tables for multi-tenant isolation
6061
- SHOULD create async indexes for tenant_id and common query patterns
6162

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

125126
### Supported Data Types
126127

127-
```
128-
VARCHAR, TEXT, INTEGER, DECIMAL, BOOLEAN, TIMESTAMP, UUID
129-
```
128+
**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). `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: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ INSERT INTO distributors VALUES (nextval('order_seq'), 'nothing');
131131

132132
## Data Serialization
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+
**Pattern:** **MUST** store arrays as `TEXT` (comma-separated). **SHOULD** store JSON in a `JSON` column and cast to `JSONB` at query time for JSONB operators. Arrays and `JSONB` are [runtime-only types](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/working-with-postgresql-compatibility-supported-data-types.html#working-with-postgresql-compatibility-query-runtime).
135135

136136
```javascript
137137
function toTextArray(values) {
@@ -142,32 +142,21 @@ function fromTextArray(textValue) {
142142
return textValue ? textValue.split(',').map(v => v.trim()) : [];
143143
}
144144

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-
159145
const categoriesText = toTextArray(['backend', 'api', 'database']);
160146
await pool.query('INSERT INTO projects (project_id, categories) VALUES ($1, $2)', [projectId, categoriesText]);
161147

162-
const configText = toTextJSON({ theme: 'dark', notifications: true });
163-
await pool.query('INSERT INTO user_settings (user_id, preferences) VALUES ($1, $2)', [userId, configText]);
148+
await pool.query(
149+
'INSERT INTO user_settings (user_id, preferences) VALUES ($1, $2)',
150+
[userId, { theme: 'dark', notifications: true }],
151+
);
164152
```
165153

166154
Query-time operations:
167155

168156
```sql
169-
SELECT user_id, preferences::jsonb->>'theme' as theme
170-
FROM user_settings WHERE preferences::jsonb->>'notifications' = 'true';
157+
SELECT user_id, preferences::jsonb->>'theme' AS theme
158+
FROM user_settings
159+
WHERE preferences::jsonb->>'notifications' = 'true';
171160

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

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+
- JSON uses the `JSON` column type; cast to `JSONB` at query time
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. **Serialization:** Store arrays as TEXT (comma-separated); store JSON in a `JSON` column and cast to `JSONB` at query time
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: 12 additions & 0 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+
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:

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

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,29 @@ 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** (8 eval prompts, 30 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 | Prefers `JSON` column type, explains `::jsonb` cast, does not claim JSON must be stored as TEXT |
92+
| 7. Array storage | Type guidance | Flags `TEXT[]` / array column unsupported (runtime-only), 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 |
8094

8195
### Tier 3: Safe-Query Enforcement Evals
8296

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,40 @@
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": "Prefers the JSON column type over TEXT serialization. Explains that JSONB is runtime-only and must be cast (preferences::jsonb->>'key') at query time for JSONB operators.",
68+
"files": [],
69+
"expectations": [
70+
"Prefers or recommends the JSON column type for the preferences blob",
71+
"Mentions casting to jsonb at query time for JSONB operators",
72+
"Does NOT claim JSON must be stored as TEXT (JSON column is supported in DSQL)"
73+
]
74+
},
75+
{
76+
"id": 7,
77+
"prompt": "I want to store a tags array per project in DSQL (e.g. ['backend','database','api']). Can I use TEXT[] for that?",
78+
"expected_output": "Indicates TEXT[] / array column type is not supported (array is a runtime-only type in DSQL). Recommends storing arrays as TEXT (comma-separated) or inside a JSON column.",
79+
"files": [],
80+
"expectations": [
81+
"Indicates TEXT[] or array column type is not supported in DSQL",
82+
"Mentions arrays are runtime-only in DSQL",
83+
"Recommends storing arrays as TEXT (comma-separated) or inside a JSON column"
84+
]
85+
},
86+
{
87+
"id": 8,
88+
"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?",
89+
"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.",
90+
"files": [],
91+
"expectations": [
92+
"Identifies the cluster is in INACTIVE state and waking up",
93+
"Mentions aws dsql get-cluster for checking cluster status",
94+
"Recommends polling until the cluster reaches ACTIVE state",
95+
"Recommends retrying the connection after cluster becomes ACTIVE"
96+
]
6397
}
6498
]
6599
}

0 commit comments

Comments
 (0)