Skip to main content

swifql-sql-conversion

Convert raw SQL or legacy SwifQL call sites into canonical SQL DSL representations using the installed package's actual APIs, dialect, preparation, and bind behavior.

Quellinformationen

Repository
swiftstream/SQL
Letzte Quellaktivität
6. Oktober 2026 um 21:21
Erkannte Sprache von SKILL.md
Englisch
Sterne
302
Forks
19

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
2 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
swifql-sql-conversion
description
Convert raw SQL or legacy SwifQL call sites into canonical SQL DSL representations using the installed package's actual APIs, dialect, preparation, and bind behavior.
license
LICENSE.txt
# Convert SQL into the SQL DSL Use this workflow when downstream Swift code starts from raw SQL text or an older SwifQL call site and needs a canonical representation for the installed `SQL` package. This skill is a conversion procedure. It is not a SQL parser, AST, renderer, database executor, or source-library modification workflow. ## When to use Use this skill for tasks such as: - convert a raw SQL statement into `SQL { ... }`; - convert a raw SQL statement into direct fluent `SQL.<fluent>`; - provide both declarative and fluent forms for comparison; - migrate a legacy SwifQL call site to the canonical `SQL` module/API; - explain which part of an uncommon statement has typed/declarative coverage and which part must remain an explicit raw structural boundary. For ordinary query construction that does not begin from raw SQL or legacy SwifQL, use the general query-building procedure instead. ## 1. Resolve the actual package before translating Determine the SQL package version and source the consumer really uses. Inspect, as appropriate: - `Package.swift`; - `Package.resolved`; - the resolved checkout; - installed source and tests; - the migration guide supplied by that version. Do not assume the repository's current HEAD APIs exist in an older resolved package. The current SQL 2.1+ canonical shape is: ```swift import SQL let fluent = SQL .select(\User.$id, \User.$email) .from(User.table) let declarative = SQL { Select(\User.$id, \User.$email) From(User.table) } ``` For resolved SQL 2.0.x packages, verify the exact declarative overloads from that version before using 2.1 concise clause forms. `SQLContent` is the concrete fragment/result carrier when an explicit concrete type is required. It is not the fluent root spelling. Do not target `SQL.root`. ## 2. Choose the target dialect Identify the intended preparation dialect before choosing syntax: - `.psql` for PostgreSQL; - `.mysql` for MySQL; - `.duck` for DuckDB; when those dialects exist in the resolved package version. If the caller does not specify a dialect and the SQL contains vendor-specific syntax, state the dialect assumption before converting. Do not create a false portability facade. Preserve vendor-specific semantics instead of silently translating them to a different construct. ## 3. Preserve the SQL structure first Before writing Swift, identify: - statement family; - clause order; - projections; - sources and aliases; - joins and join predicates; - filters; - grouping/having; - windows/qualification when present; - ordering; - pagination; - nesting/subqueries; - set operations; - DML/DDL structure; - dialect-specific syntax. Preserve the input's semantic structure. Do not "simplify" a vendor-specific form into something merely similar. ## 4. Separate structure from data Treat these as SQL structure: - table/schema/column identifiers; - SQL clauses and keywords; - operators; - ordering directions; - static dialect syntax; - deliberately modeled structural fragments. Treat these as data: - user input; - runtime scalar values; - runtime dates/times; - runtime list elements used as values; - other untrusted or dynamic values. Dynamic or untrusted data must stay on an explicit SQL value/bind path. Prefer the typed/declarative/fluent value path. If the resolved package exposes bind-aware `SQLRaw` and the surrounding SQL must remain an explicit raw structural fragment, runtime **value** data may use labeled `\(bind:)` interpolation. Never use ordinary Swift/String interpolation to splice runtime data into raw SQL text, and never bind identifiers/operators/clauses as data. A literal embedded in the source SQL may legitimately become a Swift value and therefore a bind. Preserve semantics, not necessarily the original quoting bytes. ### Exact operators and SQL/JSON When the installed version exposes the exact grouped binary-operator primitive, map a fixed vendor operator directly instead of reconstructing the whole expression as raw SQL. For example, PostgreSQL JSON-path match syntax can be represented as: ```swift let expression = lhs.op(.custom("@?"), rhs) ``` The operator token is fixed SQL structure. Runtime values used by either operand continue through normal SQLable preparation and binding. For SQL/JSON, choose the owner and options from the target dialect's verified capability rather than translating by name resemblance: - PostgreSQL supports `JSONExists`, `JSONValue`, and `JSONQuery`, including PostgreSQL-owned option grammar such as `Passing(...)`; - DuckDB supports `JSONExists` and `JSONValue` with bound paths, but not PostgreSQL SQL/JSON options or `JSONQuery`; - MySQL supports `JSONValue` / `JSON_VALUE` in the verified SQL 2.2 surface. On MySQL 8.4, a runtime Swift `String` path remains a bind value and is rejected by the native parser in that path position. Do not "fix" the conversion by interpolating runtime path data into raw SQL. If the original SQL contains a genuinely fixed trusted path, it may remain explicit structure: ```swift let value = JSONValue(document, SQL.raw("'$.name'")) ``` This example is a static structural boundary. A bind-aware raw API, when available, does not make a placeholder valid in a SQL grammar position that the target database rejects; for example, it does not turn MySQL JSON_VALUE path positions into bindable positions. SQL/JSON DEFAULT behavior is owner- and dialect-specific and must not be generalized into arbitrary runtime literalization. For PostgreSQL range/multirange SQL, prefer the installed exact PostgreSQL helpers such as `Fn.isEmpty(...)`, `Fn.rangeMerge(...)`, `Fn.rangeAgg { ... }`, and grouped `.op(.custom(...), ...)` forms when source/tests verify the requested construct. Do not project those APIs as portable MySQL or DuckDB range support. ## 5. Choose declarative, fluent, or raw structural output Prefer the declarative form when the installed package exposes the verified construct. For SQL 2.1+, fixed clauses should normally use the concise spelling: ```swift SQL { Select(\User.$id, \User.$email) From(User.table) Where(\User.$active == true) } ``` Use the clause result-builder spelling when multiple children, optionals, conditions, or loops make it clearer: ```swift SQL { Select(\User.$id, \User.$email) From(User.table) Where { \User.$active == true if let email { \User.$email == email } } } ``` Concise and builder forms are one DSL and must preserve the same SQL/preparation/bind semantics. Use direct fluent composition when: - the caller asks for fluent output; - the construct is verified in the fluent/core API but not in the declarative layer; - a concise fluent representation is clearer. The fluent root is the direct global `SQL` value: ```swift SQL .select(...) .from(...) .where(...) ``` For uncommon or version-sensitive syntax, inspect installed source/tests before choosing a helper. If no verified declarative helper exists but the fluent/core API supports the construct, return the fluent/core form and say that declarative coverage is unavailable in that resolved version. If no typed/core representation exists, keep only the unsupported **SQL structure** in the raw structural escape hatch and clearly label that boundary. For fixed structure use the existing String route such as `SQL.raw("...")` or `.raw("...")`. In resolved versions that expose bind-aware `SQLRaw`, legal runtime value positions inside that raw structure may use explicit labeled `\(bind:)`; ordinary Swift interpolation remains caller-owned text. For resolved versions that expose aggregate argument builders, convert aggregate-local DISTINCT and ORDER BY structurally instead of treating the whole aggregate call as raw SQL. For example: ```sql array_agg(DISTINCT value ORDER BY value DESC) ``` maps to: ```swift Fn.arrayAgg { Distinct { DB.Column("value") } OrderBy(DB.Column("value"), .desc) } ``` Keep aggregate-local `OrderBy` outside the `Distinct { ... }` body. Ordinary arguments remain normal `SQLable` inputs, so runtime values keep their normal binding behavior. Existing concise aggregate overloads remain valid; use the builder form when the SQL grammar actually needs multiple aggregate arguments, DISTINCT ownership, local ordering, conditionals, loops, or other builder composition. Verify availability in the resolved package before choosing this representation. Do not invent a DSL symbol. ### What to return For a conversion request, return only the representations the caller actually needs. When useful, include: - target dialect or explicit assumption; - canonical declarative form; - canonical direct fluent/raw form; - runtime value/bind mapping; - preparation/verification snippet; - an explicit unsupported/deferred boundary. If the caller asks for only declarative or only fluent output, do not force both forms. ## 6. Identifier policy If the consumer already has verified typed model/table metadata, use it. If no model metadata is available, do not invent types such as `User.table` or `User.$id`. Use explicit structural identifiers from the installed API. Current `master` uses the canonical `DB.*` namespace shown below; if the resolved consumer is still on SQL 2.x, follow that installed version's historical `Path.*` surface instead of backporting newer names. ```swift let users = DB.Table("users") let id = DB.Column("id") let email = DB.Column("email") ``` Use table-qualified paths when the SQL requires qualification. ## 7. Worked conversion example Raw SQL: ```sql SELECT id, email AS email_address FROM users WHERE email = $1 ORDER BY created_at DESC LIMIT 100 ``` Assume PostgreSQL and a runtime value: ```swift let email = inputEmail let users = DB.Table("users") ``` Canonical declarative form: ```swift import SQL let query = SQL { Select { DB.Column("id") DB.Column("email").as("email_address") } From { users } Where { DB.Column("email") == email } OrderBy(DB.Column("created_at"), .desc) Limit(100) } ``` Canonical direct fluent form: ```swift import SQL let query = SQL .select( DB.Column("id"), DB.Column("email").as("email_address") ) .from(users) .where(DB.Column("email") == email) .orderBy(.desc(DB.Column("created_at"))) .limit(100) ``` The runtime `email` stays a value input. Prefer the typed predicate shown above. If unsupported surrounding syntax genuinely requires raw SQL and the resolved version exposes `SQLRaw`, keep the SQL structure trusted/static and use only `\(bind: email)` for the runtime value; do not use ordinary String interpolation merely to reproduce the original text. Prepare and inspect using the target dialect: ```swift let prepared = query.prepare(.psql) let inspectionSQL = prepared.plain let driverSQL = prepared.splitted.query let bindValues = prepared.splitted.values ``` Verify both SQL shape and bind ordering. ## 8. Unsupported or version-sensitive syntax For a construct you do not recognize confidently: 1. identify the resolved package version; 2. search that version's source and tests for the SQL family/operator/clause; 3. prefer the existing typed/declarative API when it exists; 4. otherwise prefer existing fluent/core composition; 5. otherwise use a clearly marked raw structural boundary; 6. keep dynamic value data on an explicit bind path — ordinarily through typed SQLable values, or through labeled `\(bind:)` inside raw structure when the resolved version supports `SQLRaw` and the database grammar permits that parameter position; 7. prepare the result for the target dialect and inspect `.plain` plus `.splitted`. Do not infer an API from another SQL library, from a newer release, or from the database documentation alone. Database documentation tells you which SQL exists. Installed SQL package source/tests tell you which Swift representation actually exists. ## 9. Legacy SwifQL migration For old call sites, migrate toward the installed canonical SQL surface rather than blindly renaming text. Typical current migration shape: ```swift // Legacy spelling: let oldQuery = SwifQL .select(DB.Column("id")) .from(DB.Table("users")) // Canonical spelling: let query = SQL .select(DB.Column("id")) .from(DB.Table("users")) ``` Canonical module import is: ```swift import SQL ``` Do not migrate to `SQL.root`. When an explicit concrete result type is necessary, use `SQLContent` in versions where that is the current carrier. Some historical SwifQL-prefixed declarations may remain as deprecated in-module compatibility in particular versions. Consult that version's `MIGRATION.md`, source, and tests for symbol-specific renames. Do not blindly replace every `SwifQL` prefix: some historical names may map to differently named canonical declarations, may be intentionally retained, or may not exist in the resolved version. ## 10. Reusable SQLQuery output When the converted statement belongs in a reusable parameterized query component and the resolved version supports the current `SQLQuery` API, prefer its protocol-local `Query` shorthand rather than exposing the concrete carrier unnecessarily. Current canonical shape: ```swift struct ActiveUserQuery: SQLQuery { let email: String var query: Query { Select { DB.Column("id") DB.Column("email") } From { DB.Table("users") } Where { DB.Column("email") == email } } } ``` Do not repeat `@SQLBuilder` on the ordinary conformer witness merely because the protocol requirement carries the builder attribute. ## 11. Verification checklist Before presenting the conversion, verify: - the target dialect or assumption is explicit; - every Swift symbol exists in the resolved package; - statement structure and vendor semantics are preserved; - runtime/untrusted values are never spliced through ordinary String interpolation; if raw structure is unavoidable and bind-aware `SQLRaw` exists, only legal value positions use labeled `\(bind:)`; - declarative output uses only verified builders; - fluent output starts from direct `SQL`, not `SQL.root`; - `SQLContent` is used only when concrete typing is needed; - `.plain` renders the expected statement shape; - `.splitted.query` has the expected placeholders; - `.splitted.values` preserves the expected bind order; - unsupported syntax is labeled rather than hidden behind an invented helper. ## 12. Stop conditions Stop and inspect source/tests instead of guessing when: - the requested SQL syntax is uncommon or vendor-specific; - the consumer is pinned to an older version; - a symbol remembered from another version is not found; - declarative and fluent APIs differ materially; - raw fallback would need runtime/untrusted data but the resolved package lacks bind-aware `SQLRaw`, or the target grammar does not permit a parameter in that position; - exact bind order is unclear; - conversion would require modifying the SQL package itself. This skill stops at construction and preparation. Database execution belongs to a driver or integration layer. ## Maintenance rule This skill intentionally does not claim complete SQL grammar coverage. Every later accepted query-surface wave must perform a conversion-skill impact check. Update this skill in that wave's docs/skills closure when canonical constructs or spellings change. If the existing procedure remains correct, record `NO_CHANGE` rather than editing it gratuitously.
Auf GitHub ansehen