@kuindji/sql-type-parser
Version:
Type-level SQL parser for TypeScript
88 lines • 4.78 kB
TypeScript
/**
* Query Type Router
*
* This module provides the main entry point for parsing SQL queries.
* It detects the query type (SELECT, INSERT, UPDATE, DELETE) and
* routes to the appropriate parser.
*
* Design Philosophy:
* -----------------
* Each query type has its own execution tree in the type system.
* This prevents TypeScript performance issues by avoiding deep
* conditional type evaluation across all query types simultaneously.
*
* When you add support for a new query type:
* 1. Create a new directory (e.g., src/insert/)
* 2. Implement ast.ts, parser.ts, matcher.ts in that directory
* 3. Add the query type to QueryType in common/ast.ts
* 4. Add a case to DetectQueryType and ParseSQL in this file
* 5. Export from index.ts
*/
import type { NormalizeSQL, NextToken } from "./common/tokenizer.js";
import type { ParseError, Increment, Decrement, IsStringLiteral, DynamicQuery } from "./common/utils.js";
import type { ParseSelectSQL, SQLSelectQuery } from "./select/index.js";
import type { ParseInsertSQL, SQLInsertQuery } from "./insert/index.js";
import type { ParseUpdateSQL, SQLUpdateQuery } from "./update/index.js";
import type { ParseDeleteSQL, SQLDeleteQuery } from "./delete/index.js";
/**
* Detect the type of SQL query from the first keyword
*
* For WITH (CTE) queries, looks ahead to find the actual query type
*/
export type DetectQueryType<T extends string> = NextToken<NormalizeSQL<T>> extends [infer First extends string, infer Rest extends string] ? First extends "WITH" ? DetectQueryTypeAfterWith<Rest> : First extends "SELECT" ? "SELECT" : First extends "INSERT" ? "INSERT" : First extends "UPDATE" ? "UPDATE" : First extends "DELETE" ? "DELETE" : "UNKNOWN" : "UNKNOWN";
/**
* Detect query type after WITH clause by finding the main query keyword
*/
type DetectQueryTypeAfterWith<T extends string> = FindMainQueryKeyword<T> extends infer Keyword ? Keyword extends "SELECT" ? "SELECT" : Keyword extends "INSERT" ? "INSERT" : Keyword extends "UPDATE" ? "UPDATE" : Keyword extends "DELETE" ? "DELETE" : "SELECT" : "SELECT";
/**
* Find the main query keyword after CTEs by scanning for SELECT/INSERT/UPDATE/DELETE
* that is not inside parentheses
*/
type FindMainQueryKeyword<T extends string, Depth extends number = 0> = T extends "" ? "UNKNOWN" : T extends `(${infer Rest}` ? FindMainQueryKeyword<Rest, Increment<Depth>> : T extends `)${infer Rest}` ? FindMainQueryKeyword<Rest, Decrement<Depth>> : Depth extends 0 ? NextToken<T> extends [infer Token extends string, infer Rest extends string] ? Token extends "SELECT" | "INSERT" | "UPDATE" | "DELETE" ? Token : FindMainQueryKeyword<Rest, Depth> : "UNKNOWN" : NextToken<T> extends [infer _Token, infer Rest extends string] ? FindMainQueryKeyword<Rest, Depth> : "UNKNOWN";
/**
* Union of all SQL query AST types
*/
export type AnySQLQuery = SQLSelectQuery | SQLInsertQuery | SQLUpdateQuery | SQLDeleteQuery;
/**
* Parse any SQL query string into an AST
*
* This is the main entry point for parsing. It detects the query type
* and routes to the appropriate parser.
*
* For dynamic queries (where the string type is not a literal), returns
* DynamicQuery marker to indicate the query cannot be parsed at compile time.
*
* @example
* ```typescript
* type SelectAST = ParseSQL<"SELECT id, name FROM users">
* // Returns SQLSelectQuery<SelectClause<...>>
*
* // Dynamic queries pass through without validation
* declare const dynamic: string
* type DynamicAST = ParseSQL<`SELECT * FROM users ${typeof dynamic}`>
* // Returns DynamicQuery (passes through without errors)
* ```
*/
export type ParseSQL<T extends string> = IsStringLiteral<T> extends false ? DynamicQuery : DetectQueryType<T> extends infer QType ? QType extends "SELECT" ? ParseSelectSQL<T> : QType extends "INSERT" ? ParseInsertSQL<T> : QType extends "UPDATE" ? ParseUpdateSQL<T> : QType extends "DELETE" ? ParseDeleteSQL<T> : ParseError<"Unknown query type"> : never;
/**
* Check if a parsed query is a SELECT query
*/
export type IsSelectQuery<T> = T extends SQLSelectQuery ? true : false;
/**
* Check if a parsed query is an INSERT query
*/
export type IsInsertQuery<T> = T extends SQLInsertQuery ? true : false;
/**
* Check if a parsed query is an UPDATE query
*/
export type IsUpdateQuery<T> = T extends SQLUpdateQuery ? true : false;
/**
* Check if a parsed query is a DELETE query
*/
export type IsDeleteQuery<T> = T extends SQLDeleteQuery ? true : false;
export type { ParseSelectSQL } from "./select/index.js";
export type { ParseInsertSQL } from "./insert/index.js";
export type { ParseUpdateSQL } from "./update/index.js";
export type { ParseDeleteSQL } from "./delete/index.js";
export type { DynamicQuery } from "./common/utils.js";
//# sourceMappingURL=router.d.ts.map