Dsql
ClineOfficialPublished by Cline
- Skill
- Tool integration
- Cloud and DevOps
- Apache-2.0
- Low risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
The dsql skill provides a structured framework for working with Amazon Aurora DSQL, a serverless, distributed SQL database. It covers the full lifecycle of database operations, including schema management, multi-tenant design, IAM-based authentication, and migration strategies from MySQL or PostgreSQL. The skill integrates with MCP tools to facilitate direct query execution, SQL linting, and documentation access, while enforcing best practices for transaction limits, OCC retry patterns, and bulk data loading.
What it does
- Provides MCP tools for direct query execution, schema inspection, and SQL compatibility linting.
- Offers step-by-step workflows for migrating existing MySQL and PostgreSQL databases to DSQL.
- Guides users through implementing multi-tenant isolation and application-layer referential integrity.
- Supplies patterns for handling OCC retries and batching DDL/DML operations within transaction limits.
- Facilitates bulk data loading from CSV, TSV, or Parquet files using specialized loader utilities.
- Includes diagnostic procedures for query plan explainability and performance optimization.
When to use it
- When starting a new project or migrating an existing application to Aurora DSQL.
- When performing schema migrations or complex DDL operations that require validation.
- When troubleshooting performance issues or unexpected query plans.
- When implementing secure, IAM-based access control and scoped database roles.
- When bulk loading large datasets into a DSQL cluster.
Advantages
- Ensures adherence to DSQL-specific constraints and best practices through linting and structured workflows.
- Simplifies complex migrations with pre-defined type mapping and schema conversion patterns.
- Promotes secure development by enforcing IAM authentication and least-privilege access control.
- Reduces operational risk by providing clear procedures for transaction management and error recovery.
- Standardizes diagnostic reporting for query performance analysis.
Usage examples
- “How do I migrate my existing PostgreSQL schema to Aurora DSQL?”
- The agent will guide you through using dsql_lint for compatibility checks, applying specific type mappings from the pg-migrations reference, and handling unfixable patterns manually.
- “I am getting an OCC serialization error, how do I fix it?”
- The agent will provide the standard OCC retry pattern and suggest checking for hot-key contention using the troubleshooting reference.
- “How do I securely connect my application to DSQL using IAM?”
- The agent will explain how to map IAM roles to scoped database roles and provide the command to generate a temporary DB connect token.
- “Analyze this slow query plan for my DSQL cluster.”
- The agent will initiate the query plan explainability workflow, requiring you to provide the plan while it guides you through diagnostic steps and produces a structured report.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/cline/dsql/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip dsql.zip -d .agents/skills
Documentation by Cline
Amazon Aurora DSQL Skill
Aurora DSQL is a serverless, PostgreSQL-compatible distributed SQL database. This skill covers direct query execution via MCP tools, schema management, migrations, multi-tenant isolation, IAM auth, and bulk data loading via aurora-dsql-loader.
Reference Files
Load these files as needed for detailed guidance:
Core:
| Reference | When to Load | Contains |
|---|---|---|
| development-guide.md | ALWAYS before schema changes or DB operations | Best practices, DDL rules, transaction limits, app-layer referential integrity |
| language.md | MUST load for language-specific choices | Driver selection, DSQL Connectors, connection code |
| access-control.md | MUST load for roles, grants, or sensitive data | Scoped role setup, IAM-to-database role mapping |
| troubleshooting.md | SHOULD load for errors or unexpected behavior | OCC errors, connection failures, cluster state errors, token expiry, DDL rejection causes |
| dsql-examples.md | Load for implementation examples | Multi-tenant schema examples, batch operations, FK validation patterns, connection pooling |
| onboarding.md | User requests "Get started with DSQL" | Interactive step-by-step guide |
| occ-retry-patterns.md | MUST load for OCC retry code or conflict mitigation | DSQL Connectors, manual retry pattern, idempotent design |
MCP:
| Reference | When to Load | Contains |
|---|---|---|
| mcp-setup.md | Always for MCP server guidance | Setup instructions, 2 configuration options |
| mcp-tools.md | For MCP tool syntax and examples | Tool parameters, input validation |
| dsql-lint.md | MUST load before running dsql_lint or processing external SQL | Tool reference, fix statuses, unfixable error resolution |
DDL Migrations:
| Reference | When to Load | Contains |
|---|---|---|
| ddl-migrations/overview.md | MUST load for DROP COLUMN, ALTER TYPE, DROP CONSTRAINT | Table recreation pattern, verify & swap |
| ddl-migrations/column-operations.md | DROP COLUMN, ALTER TYPE, SET/DROP NOT NULL/DEFAULT | Column-level migration patterns |
| ddl-migrations/constraint-operations.md | ADD/DROP CONSTRAINT, MODIFY PRIMARY KEY | Constraint and structural changes |
| ddl-migrations/batched-migration.md | Tables exceeding 3,000 rows | Batching patterns, progress tracking |
MySQL Migrations:
| Reference | When to Load | Contains |
|---|---|---|
| mysql-migrations/type-mapping.md | MUST load for MySQL → DSQL migration | Data type mappings, feature alternatives |
| mysql-migrations/ddl-operations.md | Translating MySQL DDL to DSQL | AUTO_INCREMENT, ENUM, SET, FK patterns |
| mysql-migrations/full-example.md | Complete MySQL table migration | End-to-end example with decision summary |
PostgreSQL Migrations:
| Reference | When to Load | Contains |
|---|---|---|
| pg-migrations/type-mapping.md | MUST load for PG → DSQL type questions | C collation rules, NUMERIC precision, JSON/JSONB |
| pg-migrations/fk-replacement.md | MUST load for FK validation code generation | Tenant-scoped validate_fk_*() template, cascade |
| pg-migrations/index-conversion.md | MUST load for unfixable index diagnostics | GIN/GiST/BRIN → btree, partial, expression indexes |
| pg-migrations/schema-objects.md | MUST load for ENUM, materialized views, extensions, multi-schema | ENUM → CHECK, views, role/IAM mapping |
| pg-migrations/multi-region.md | Multi-region, active-active, or HA questions | Architecture, geographic partitioning |
ORM Guides:
| Reference | When to Load | Contains |
|---|---|---|
| orm-guides/overview.md | Migrating any ORM to DSQL | Adapter names, key gotchas for Django/Hibernate/Rails/SQLAlchemy |
Data Loading:
| Reference | When to Load | Contains |
|---|---|---|
| data-loading.md | Planning or running bulk loads with aurora-dsql-loader | Fresh-vs-warm partitions, resume/retry, --on-conflict semantics, throughput diagnostics |
Query Plan Explainability:
| Reference | When to Load | Contains |
|---|---|---|
| query-plan/plan-interpretation.md | MUST load at Workflow 9 Phase 0 | DSQL node types, Node Duration math, estimation-error bands |
| query-plan/catalog-queries.md | MUST load at Workflow 9 Phase 0 | pg_class/pg_stats/pg_indexes SQL, correlated-predicate verification |
| query-plan/guc-experiments.md | MUST load at Workflow 9 Phase 0 | GUC experiment procedures, 30-second skip protocol |
| query-plan/report-format.md | MUST load at Workflow 9 Phase 0 | Required report structure, element checklist, support request template |
MCP Tools Available
The aurora-dsql MCP server provides these tools:
Database Operations:
readonly_query - Execute SELECT queries (returns list of dicts)
transact - Execute DDL/DML statements in transaction (takes list of SQL statements)
get_schema - Get table structure for a specific table
SQL Validation:
dsql_lint - Validate SQL for DSQL compatibility and optionally auto-fix issues. Use before executing externally-sourced SQL.
Documentation & Knowledge:
dsql_search_documentation - Search Aurora DSQL documentation
dsql_read_documentation - Read specific documentation pages
dsql_recommend - Get DSQL best practice recommendations
Note: There is no list_tables tool. Use readonly_query with information_schema.
See mcp-setup.md for detailed setup instructions. See mcp-tools.md for detailed usage and examples.
AWS Knowledge MCP (awsknowledge)
Consult for verifying DSQL service limits before advising users. The numeric limits below are defaults that may change — when a user's decision depends on an exact limit, verify it first:
| Limit | Default | Verify query |
|---|---|---|
| Max rows per transaction | 3,000 | aurora dsql transaction limits |
| Max data size per transaction | 10 MiB | aurora dsql transaction limits |
| Max transaction duration | 5 minutes | aurora dsql transaction limits |
| Max connections per cluster | 10,000 | aurora dsql connection limits |
| Auth token expiry | 15 minutes | aurora dsql authentication token |
| Max connection duration | 60 minutes | aurora dsql connection limits |
| Max indexes per table | 24 | aurora dsql index limits |
| Max columns per index | 8 | aurora dsql index limits |
| IDENTITY/SEQUENCE CACHE values | 1 or >= 65536 | aurora dsql sequence cache |
| Supported column data types | See docs | aurora dsql supported data types |
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.
Fallback: If awsknowledge is unavailable, use the defaults above and flag that limits should be verified against DSQL documentation.
CLI Tools
Use the AWS CLI, psql, and aurora-dsql-loader directly for cluster management, SQL connections, and bulk data loading from local or S3 CSV/TSV/Parquet files. Load data-loading.md for loader guidance.
Quick Start
Explore: Use
readonly_querywithinformation_schemato list tables. Useget_schemafor table structure.Query: Use
readonly_queryfor SELECT queries. MUST includetenant_idin WHERE for multi-tenant apps. MUST build SQL withsafe_query.build().Schema changes: Use
transactwith one DDL per transaction. MUST batch DML under 3,000 rows. MUST useCREATE INDEX ASYNCin a separate call. Usedsql_lintto validate first.Bulk load data: Use
aurora-dsql-loaderfor CSV/TSV/Parquet. Load data-loading.md for details. Use--dry-runfirst.
Common Workflows
Workflow 1: Create Multi-Tenant Schema
Create main table with tenant_id column using transact
Create async index on tenant_id in separate transact call
Create composite indexes for common query patterns (separate transact calls)
Verify schema with get_schema
MUST include tenant_id in all tables
MUST use
CREATE INDEX ASYNCexclusivelyMUST issue each DDL in its own transact call:
transact(["CREATE TABLE ..."])MUST serialize arrays as JSONB; expand at query time with
jsonb_array_elements_text(data)
Workflow 2: Safe Data Migration
MUST validate every DDL with dsql_lint(fix=true) before executing. DML does not require linting.
Validate DDL with
dsql_lint(sql=..., fix=true)— handle diagnostics per dsql-lint.mdAdd column:
transact(["ALTER TABLE ... ADD COLUMN ..."])Populate existing rows with UPDATE (batched under 3,000 rows)
Verify with readonly_query COUNT
Create index if needed: validate then
transact(["CREATE INDEX ASYNC ..."])
MUST issue each
ALTER TABLEin its owntransactcall — DSQL rejects multi-DDL transactions withmultiple ddl statements not supported in a transactionMUST add column with only name and type; apply DEFAULT via separate UPDATE
MUST batch updates under 3,000 rows in separate transact calls
Recovery: Resume failed batches by filtering WHERE new_column IS NULL.
Workflow 3: Bulk Data Loading
Use aurora-dsql-loader for CSV, TSV, or Parquet loads. MUST load data-loading.md before advising on throughput or diagnosing slow loads.
Validate with
--dry-runfirstRun with
--manifest-diron persistent storage (not/tmp— tmpfs on AL2023, lost on crash) and--headerif file has a header rowOn failure: resume with
--resume-job-id; for duplicates use--on-conflict do-nothingFor large tables: create secondary indexes after load using
CREATE INDEX ASYNC
Workflow 4: Application-Layer Referential Integrity
INSERT: MUST validate parent exists with readonly_query → throw error if not found → insert child with transact.
DELETE: MUST check dependents with readonly_query COUNT → return error if dependents exist → delete with transact if safe.
Workflow 5: Query with Tenant Isolation
MUST authorize the caller against the tenant — format validation does not establish authorization
MUST build SQL with
safe_query.build()— useallow()/regex()for values (emits'v'),ident()for table/column names (emits"v"). See input-validation.mdMUST include
tenant_idin the WHERE clause; reject cross-tenant access at the application layer
Workflow 6: Set Up Scoped Database Roles
MUST load access-control.md for role setup, IAM mapping, and schema permissions.
Workflow 7: Table Recreation DDL Migration
Use the Table Recreation Pattern for ALTER COLUMN TYPE, DROP COLUMN, DROP CONSTRAINT, or MODIFY PRIMARY KEY. This is a destructive workflow that requires user confirmation at each step. Every generated DDL in the pattern (CREATE new, INSERT ... SELECT, DROP old, RENAME) MUST be validated with dsql_lint(sql=..., fix=true) before execution.
MUST load ddl-migrations/overview.md before attempting any of these operations.
Workflow 8: Validate and Migrate to DSQL
MUST load dsql-lint.md before running dsql_lint. Run dsql_lint(sql=source_sql, fix=true) to validate and auto-convert. For MySQL-origin SQL, MUST cross-check against mysql-migrations/type-mapping.md even when lint returns clean. On parse_error, fall back to manual conversion then re-lint.
Workflow 9: Query Plan Explainability
Explains why the DSQL optimizer chose a particular plan. Triggered by slow queries, high DPU, unexpected Full Scans, or plans the user doesn't understand. REQUIRES a structured Markdown diagnostic report as the deliverable.
MUST load all four reference files at Phase 0: query-plan/plan-interpretation.md, query-plan/catalog-queries.md, query-plan/guc-experiments.md, query-plan/report-format.md. The phase procedures (capture plan, gather evidence, experiment, produce report) are defined in those files.
Safety. Plan capture uses readonly_query exclusively. Rewrite DML to SELECT for plan capture. MUST NOT use transact --allow-writes for plan capture.
Workflow 10: Full PostgreSQL → DSQL Schema Migration
MUST load pg-migrations/type-mapping.md and pg-migrations/schema-objects.md. Run dsql_lint(fix=true) first for mechanical fixes, then apply semantic conversions from the pg-migrations references for unfixable diagnostics and patterns the linter cannot handle. Re-lint the final output before deploying.
Workflow 11: ORM Migration (Django/Hibernate/Rails)
Load orm-guides/overview.md for adapter names and framework-specific gotchas.
Error Scenarios
awsknowledgereturns no results: Use the default limits in the table above and note that limits should be verified against DSQL documentation.dsql_lintunavailable or timing out: See the Error Handling section of dsql-lint.md. Do not silently skip validation — inform the user and require explicit confirmation before proceeding with manual rules from development-guide.md.OCC serialization error: Retry the transaction. If persistent, check for hot-key contention — see troubleshooting.md.
Transaction exceeds limits: Split into batches under 3,000 rows — see batched-migration.md.
Token expiration mid-operation: Generate a fresh IAM token — see authentication-guide.md. See troubleshooting.md for other issues.
Additional Resources
Files
- ATTRIBUTION.mdAdded by AutoGineer795 B
- LICENSELicence file added by AutoGineer10.5 KiB
- SKILL.md20.0 KiB
- mcp/mcp-setup.md3.6 KiB
- mcp/mcp-tools.md2.0 KiB
- mcp/tools/database-tools.md4.6 KiB
- mcp/tools/documentation-tools.md1.3 KiB
- mcp/tools/input-validation.md3.3 KiB
- mcp/tools/safe_query.pyScript10.4 KiB
- mcp/tools/workflow-patterns.md2.9 KiB
- references/access-control.md5.0 KiB
- references/auth/authentication-guide.md4.4 KiB
- references/auth/connectivity-tools.md6.1 KiB
- references/auth/scaling-guide.md3.9 KiB
- references/data-loading.md6.0 KiB
- references/ddl-migrations/batched-migration.md2.5 KiB
- references/ddl-migrations/column-operations.md5.0 KiB
- references/ddl-migrations/constraint-operations.md4.5 KiB
- references/ddl-migrations/overview.md7.2 KiB
- references/development-guide.md7.0 KiB
- references/dsql-examples.md1.5 KiB
- references/dsql-lint.md9.5 KiB
- references/examples/connection.md2.5 KiB
- references/examples/data-operations.md2.9 KiB
- references/examples/migrations.md1.6 KiB
- references/examples/patterns.md4.7 KiB
- references/examples/schema.md1.5 KiB
- references/language.md8.0 KiB
- references/mysql-migrations/ddl-auto-increment.md3.9 KiB
- references/mysql-migrations/ddl-batching.md1.0 KiB
- references/mysql-migrations/ddl-column-changes.md1.1 KiB
- references/mysql-migrations/ddl-constraints.md2.6 KiB
- references/mysql-migrations/ddl-operations.md2.9 KiB
- references/mysql-migrations/ddl-structural.md3.7 KiB
- references/mysql-migrations/ddl-type-alternatives.md3.1 KiB
- references/mysql-migrations/full-example.md6.8 KiB
- references/mysql-migrations/type-mapping.md13.5 KiB
- references/occ-retry-patterns.md5.7 KiB
- references/onboarding.md13.1 KiB
- references/orm-guides/overview.md4.4 KiB
- references/pg-migrations/fk-replacement.md2.9 KiB
- references/pg-migrations/index-conversion.md10.3 KiB
- references/pg-migrations/multi-region.md2.9 KiB
- references/pg-migrations/schema-objects.md13.2 KiB
- references/pg-migrations/type-mapping.md4.8 KiB
- references/query-plan/catalog-queries.md4.3 KiB
- references/query-plan/guc-experiments.md7.5 KiB
- references/query-plan/plan-interpretation.md7.8 KiB
- references/query-plan/report-format.md10.9 KiB
- references/troubleshooting.md4.4 KiB
mcp/tools/safe_query.py
"""Build SQL for the Aurora DSQL MCP tools without parameter binding.
The `readonly_query` and `transact` tools do not accept bound parameters. This
module is the required substitute: every interpolated value MUST pass through a
validator, and `build()` rejects raw strings by construction.
Usage:
from safe_query import build, allow, regex, ident, keyword, integer, literal
from safe_query import TENANT_SLUG, UUID
sql = build(
"SELECT * FROM {tbl} WHERE tenant_id = {tid} AND entity_id = {eid}",
tbl=ident("entities"),
tid=regex(user_tenant, TENANT_SLUG),
eid=regex(user_eid, UUID),
)
readonly_query(sql)
sql = build(
"INSERT INTO entities (entity_id, tenant_id, name) "
"VALUES ({eid}, {tid}, {name})",
eid=regex(new_id, UUID),
tid=regex(tenant, TENANT_SLUG),
name=literal(user_supplied_name), # free text — dollar-quoted
)
transact([sql])
Design rules:
- Raw strings passed to build() raise UnsafeSQLError. That is the point.
- Format validation does NOT prove authorization; authorize separately.
- Server-side filters (readonly mode) catch textbook injection only, and
they are disabled entirely in --allow-writes mode. Validation here is
the primary defense, not a backup.
"""
import re
import secrets
import string
from typing import AbstractSet, Any
TENANT_SLUG: re.Pattern[str] = re.compile(r"[a-z0-9-]{1,64}")
UUID: re.Pattern[str] = re.compile(
r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}",
re.IGNORECASE,
)
INT: re.Pattern[str] = re.compile(r"-?[0-9]{1,19}")
_IDENT: re.Pattern[str] = re.compile(r"[a-z_][a-z0-9_]{0,62}", re.IGNORECASE)
class UnsafeSQLError(ValueError):
"""A value failed validation. Never catch and fall back — fix the caller."""
class Safe:
"""A value that has passed validation and is safe to interpolate.
`build()` accepts only Safe instances. This is how the module prevents
`build("... {x} ...", x=user_input)` from ever working.
"""
__slots__ = ("_sql",)
def __init__(self, sql: str) -> None:
self._sql = sql
def __str__(self) -> str:
return self._sql
def allow(value: Any, allowed: AbstractSet[str], *, label: str = "value") -> Safe:
"""Allowlist-validate and emit as a single-quoted string literal."""
if value not in allowed:
raise UnsafeSQLError(f"{label} not in allowlist: {value!r}")
# Allowlisted values originate from developer-controlled sets; the escape
# is belt-and-braces in case someone puts a quote in the set.
return Safe("'" + str(value).replace("'", "''") + "'")
def keyword(value: str, allowed: AbstractSet[str], *, label: str = "keyword") -> Safe:
"""Allowlist-validate a SQL keyword and emit it unquoted.
Use for ASC/DESC, AND/OR, or other places where a string literal would be
syntactically wrong.
"""
if value not in allowed:
raise UnsafeSQLError(f"{label} not in allowlist: {value!r}")
return Safe(value)
def regex(value: Any, pattern: re.Pattern[str], *, label: str = "value") -> Safe:
"""Regex-validate with re.fullmatch and emit as a single-quoted literal.
Rejects values containing a single quote, backslash, or null byte.
`regex()` is for strict-format values (UUIDs, slugs, dates) that never
legitimately need embedded quotes or backslashes; free text belongs in
`literal()`, which dollar-quotes and sidesteps escaping entirely.
"""
if not isinstance(value, str) or not pattern.fullmatch(value):
raise UnsafeSQLError(f"{label} failed pattern {pattern.pattern!r}: {value!r}")
if "'" in value:
raise UnsafeSQLError(
f"{label} contains a single quote; use literal() for free text: {value!r}"
)
if "\\" in value:
raise UnsafeSQLError(
f"{label} contains a backslash; use literal() for values "
f"needing special characters: {value!r}"
)
if "\x00" in value:
raise UnsafeSQLError(
f"{label} contains a null byte: {value!r}"
)
return Safe("'" + value + "'")
def ident(name: str) -> Safe:
"""Validate a SQL identifier (table or column) and emit it double-quoted."""
if not isinstance(name, str) or not _IDENT.fullmatch(name):
raise UnsafeSQLError(f"invalid identifier: {name!r}")
return Safe('"' + name.replace('"', '""') + '"')
def integer(value: Any) -> Safe:
"""Validate an integer. Accepts int or numeric string; rejects bool."""
if isinstance(value, bool):
raise UnsafeSQLError(f"expected int, got bool: {value!r}")
if isinstance(value, int):
return Safe(str(value))
if isinstance(value, str) and INT.fullmatch(value):
return Safe(value)
raise UnsafeSQLError(f"invalid integer: {value!r}")
def literal(value: str) -> Safe:
"""Emit free text as a PostgreSQL dollar-quoted literal.
Picks a random tag until it does not appear inside `value`, which sidesteps
quote-escaping entirely. Use for descriptions, names, comments — values
without a strict format.
"""
if not isinstance(value, str):
raise UnsafeSQLError(f"expected str, got {type(value).__name__}")
for _ in range(8):
tag = "dq_" + secrets.token_hex(4)
boundary = f"${tag}$"
if boundary not in value:
return Safe(f"{boundary}{value}{boundary}")
# Eight 32-bit-random tag collisions implies adversarial input.
raise UnsafeSQLError("could not generate a unique dollar-quote tag")
def build(template: str, **parts: Safe) -> str:
"""Substitute validated parts into a SQL template.
Template uses `{name}` placeholders (str.format syntax). Every placeholder
MUST map to a Safe value; raw strings raise UnsafeSQLError so the
`build("... {t} ...", t=user_input)` anti-pattern fails loudly.
Also rejects template/kwargs mismatch: a missing key would otherwise raise
`KeyError` (invisible to callers catching `UnsafeSQLError`), and an extra
key would be silently ignored — dropping, for example, a tenant filter
from the query.
"""
if not isinstance(template, str):
raise UnsafeSQLError(f"template must be a str, got {type(template).__name__}")
for key, value in parts.items():
if not isinstance(value, Safe):
raise UnsafeSQLError(
f"{key!r} must be a Safe value from allow/regex/ident/"
f"keyword/integer/literal; got {type(value).__name__}"
)
expected: set[str] = set()
for _, fname, fspec, conv in string.Formatter().parse(template):
if fname is None:
continue
if fname == "" or fname.isdigit():
raise UnsafeSQLError(
f"template contains a positional placeholder {{{fname or ''}}}; "
f"use named placeholders like {{name}}"
)
if conv:
raise UnsafeSQLError(
f"placeholder {{{fname}!{conv}}} uses a conversion flag; "
f"Safe values must be interpolated without conversion"
)
if fspec:
raise UnsafeSQLError(
f"placeholder {{{fname}:{fspec}}} uses a format spec; "
f"Safe values must be interpolated without formatting"
)
expected.add(fname)
provided = set(parts.keys())
if expected != provided:
missing = expected - provided
extra = provided - expected
raise UnsafeSQLError(
f"template/kwargs mismatch: missing {sorted(missing)}, "
f"extra {sorted(extra)}"
)
try:
return template.format(**{k: str(v) for k, v in parts.items()})
except (KeyError, IndexError) as exc:
raise UnsafeSQLError(
f"template references a key not in kwargs "
f"(possibly in a format spec): {exc}"
) from exc
def _selftest() -> None:
"""Smoke-test every validator and build(). Run with: python safe_query.py"""
def _check(condition: bool, msg: str) -> None:
if not condition:
raise RuntimeError(msg)
def _expect_unsafe(fn: str, *args: Any, **kwargs: Any) -> None:
"""Call a validator/build by name and verify it raises UnsafeSQLError."""
target = {"allow": allow, "keyword": keyword, "regex": regex, "ident": ident,
"integer": integer, "literal": literal, "build": build}[fn]
try:
target(*args, **kwargs)
except UnsafeSQLError:
return
raise RuntimeError(f"expected UnsafeSQLError from {fn}({args!r}, {kwargs!r})")
# Happy paths
_check(str(allow("tenant-1", {"tenant-1"})) == "'tenant-1'", "allow")
_check(str(keyword("ASC", {"ASC", "DESC"})) == "ASC", "keyword")
_check(str(regex("a-1", TENANT_SLUG)) == "'a-1'", "regex")
_check(str(ident("entities")) == '"entities"', "ident")
_check(str(integer(42)) == "42", "integer")
_check(str(integer("-7")) == "-7", "integer neg")
lit = str(literal("o'reilly"))
_check(lit.startswith("$dq_") and "o'reilly" in lit, "literal")
sql = build(
"SELECT * FROM {t} WHERE tenant_id = {tid}",
t=ident("entities"),
tid=regex("acme", TENANT_SLUG),
)
_check(sql == 'SELECT * FROM "entities" WHERE tenant_id = \'acme\'', "build")
_check(str(regex("abc", TENANT_SLUG, label="tenant")) == "'abc'", "regex label")
# Rejections
_permissive = re.compile(r".+")
_expect_unsafe("allow", "evil", {"tenant-1"})
_expect_unsafe("keyword", "DROP", {"ASC", "DESC"})
_expect_unsafe("regex", "'; DROP TABLE t; --", TENANT_SLUG)
_expect_unsafe("ident", 'x" OR 1=1 --')
_expect_unsafe("integer", "1; DROP")
_expect_unsafe("integer", True)
_expect_unsafe("literal", 123)
_expect_unsafe("build", "SELECT {x}", x="raw string")
_expect_unsafe("regex", "x' OR 1=1 --", _permissive)
_expect_unsafe("regex", "it's", _permissive)
_expect_unsafe("regex", "'", _permissive)
_expect_unsafe("regex", "abc\\", _permissive)
_expect_unsafe("build", "SELECT {x}", x=ident("col"), y=ident("extra"))
_expect_unsafe("build", "SELECT {x} FROM {y}", x=ident("col"))
_expect_unsafe("build", "SELECT {x!r}", x=ident("col"))
_expect_unsafe("build", "SELECT {x:>30}", x=ident("col"))
_expect_unsafe("build", "SELECT {}", x=ident("col"))
_expect_unsafe("build", "SELECT {0}", x=ident("col"))
_expect_unsafe("build", None)
_expect_unsafe("build", 123)
print("safe_query self-test passed")
if __name__ == "__main__":
_selftest()
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs an MCP server, Needs Python, Needs Node.js
Security
What our scanner noticed· Low risk
- Instructions that use the network
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- Cline
- Publisher
- Cline (Official)
- Source
- cline/skills · skills/dsql · commit 2637846
- Licence
- Apache License 2.0 (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Cline.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download dsql.zip