Last active
September 28, 2020 18:51
-
-
Save Buildstarted/dda77163dc84caaef7339ca2217cd794 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* ``` | |
* ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄ | |
* ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ | |
* ▀▀▀▀█░█▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀█░▌▐░▌ | |
* ▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ | |
* ▐░▌ ▐░█▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌ ▐░▌▐░▌ | |
* ▐░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░▌ | |
* ▐░▌ ▀▀▀▀▀▀▀▀▀█░▌ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀█░▌▐░█▄▄▄▄▄▄▄█░▌▐░▌ | |
* ▐░▌ ▐░▌ ▐░▌▐░░░░░░░░░░░▌▐░▌ | |
* ▐░▌ ▄▄▄▄▄▄▄▄▄█░▌ ▄▄▄▄▄▄▄▄▄█░▌ ▀▀▀▀▀▀█░█▀▀ ▐░█▄▄▄▄▄▄▄▄▄ | |
* ▐░▌ ▐░░░░░░░░░░░▌ ▐░░░░░░░░░░░▌ ▐░▌ ▐░░░░░░░░░░░▌ | |
* ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀ | |
* ``` | |
* | |
* This is a SQL database implemented purely in TypeScript type annotations. | |
* This means that it operates solely on types - you define a "database", | |
* (just a type annotation) and then query it using some more type annotations. | |
* It supports a subset of SQL, including SELECT (with conditions & joins), | |
* INSERT, UPDATE and DELETE statements. | |
* | |
* This project lives at https://github.com/codemix/ts-sql | |
* it was written by Charles Pick ( [email protected], https://twitter.com/c_pick ). | |
* | |
* You can install ts-sql in your own project with `npm install @codemix/ts-sql` or | |
* `yarn install @codemix/ts-sql` (TypeScript 4.1 is required). | |
* | |
* (Tip: hover over the type aliases below to see the results of your queries) | |
* | |
*/ | |
// # Usage | |
// First we define the data that we're going to be querying against. | |
const simpsons = { | |
people: [ | |
{ id: 1, firstName: "Bart", lastName: "Simpson", isChild: true }, | |
{ id: 2, firstName: "Lisa", lastName: "Simpson", isChild: true }, | |
{ id: 3, firstName: "Maggie", lastName: "Simpson", isChild: true }, | |
{ id: 4, firstName: "Marge", lastName: "Simpson", isChild: false }, | |
{ id: 5, firstName: "Homer", lastName: "Simpson", isChild: false }, | |
{ id: 6, firstName: "Montgomery", lastName: "Burns", isChild: false }, | |
{ id: 7, firstName: "Whelan", lastName: "Smithers", isChild: false }, | |
{ id: 8, firstName: "Nillhouse", lastName: "Van Houten", isChild: true }, | |
{ id: 9, firstName: "Nelson", lastName: "Muntz", isChild: true }, | |
{ id: 10, firstName: "Principal", lastName: "Skinner", isChild: false }, | |
], | |
places: [ | |
{ name: "Springfield Elementary", ownerId: 10, isActive: true }, | |
{ name: "Nuclear Power Plant", ownerId: 6, isActive: true }, | |
{ name: "Evergreen Terrace", ownerId: 4, isActive: true }, | |
{ name: "Monorail", ownerId: 8, isActive: false }, | |
{ name: "Treehouse of Horror", ownerId: 1, isActive: true }, | |
{ name: "Playground", ownerId: 9, isActive: true }, | |
], | |
} as const; | |
// This is the initial version of our database. | |
type DBv1 = typeof simpsons; | |
// Let's find the names of people with the last name "Simpson". | |
// We use the `Query` type to execute queries, it takes a SQL | |
// query as first type parameter, and a database as the second. | |
// Note that all SQL keywords are UPPERCASE. | |
type EX1 = Query< | |
"SELECT firstName AS name FROM people WHERE lastName = 'Simpson'", | |
DBv1 | |
>; | |
// Let's find the first and last names of all the children in the simpsons | |
type EX2 = Query< | |
"SELECT firstName, lastName FROM people WHERE isChild = true", | |
DBv1 | |
>; | |
// Let's find the adults whose names begin with M | |
type EX3 = Query< | |
"SELECT * FROM people WHERE isChild = false AND firstName LIKE 'M%'", | |
DBv1 | |
>; | |
// Let's correct the typo in Milhouse's name. Since all types are immutable in TypeScript our | |
// database is immutable too, so this query returns a new database. | |
type DBv2 = Query< | |
"UPDATE people SET firstName = 'Milhouse' WHERE id = 8", | |
DBv1 | |
>; | |
// Let's check that our update worked | |
type EX4 = Query<"SELECT * FROM people WHERE id = 8", DBv2>; | |
// Let's add a new character, again this returns a new database. | |
type DBv3 = Query< | |
"INSERT INTO people SET id = 11, firstName = 'Troy', lastName = 'McClure', isChild = false", | |
DBv2 | |
>; | |
type EX5 = Query<"SELECT * FROM people WHERE firstName = 'Troy'", DBv3>; | |
// Unfortunately, we need to delete Troy McClure | |
type DBv4 = Query< | |
"DELETE FROM people WHERE firstName = 'Troy' AND lastName = 'McClure'", | |
DBv3 | |
>; | |
type EX6 = Query< | |
"SELECT firstName FROM people WHERE isChild = false", | |
DBv4 | |
>; | |
// We can add Moe tho | |
type DBv5 = Query< | |
"INSERT INTO people SET id = 11, firstName = 'Moe', lastName = 'Szyslak', isChild = false", | |
DBv4 | |
>; | |
type EX7 = Query< | |
"SELECT id, firstName AS name FROM people WHERE isChild = false AND firstName LIKE 'M%'", | |
DBv5 | |
>; | |
// Let's add Moe's Tavern | |
type DBv6 = Query< | |
'INSERT INTO places SET name = "Moe\'s Tavern", ownerId = 11, isActive = true', | |
DBv5 | |
>; | |
// Joins! Let's get all the places along with the names of their owners. | |
type EX9 = Query< | |
"SELECT name, person.firstName, person.lastName FROM places INNER JOIN people AS person ON places.ownerId = person.id", | |
DBv1 | |
>; | |
// Bonus feature: Pretty printer. Turns AST nodes back into SQL fragments. | |
// I gave up on this because TS and VSCode both (sensibly) escape newlines in | |
// strings, so the output wasn't particularly pretty. | |
type PrinterEX0 = Print< | |
Parse< | |
'SELECT name,r.name AS roleName FROM users AS u INNER JOIN roles r ON u.roleId = r.id WHERE name = "Bob" AND awesome = true LIMIT 2 OFFSET 3' | |
> | |
>; | |
/** | |
* ======================================================================================== | |
* | |
* | |
* END OF EXAMPLES, START OF IMPLEMENTATION | |
* | |
* | |
* ======================================================================================== | |
*/ | |
type Identifier<Name extends string = string> = { | |
type: "Identifier"; | |
name: Name; | |
}; | |
type MemberExpression< | |
Object extends string = string, | |
Property extends string = string | |
> = { | |
type: "MemberExpression"; | |
object: Object; | |
property: Property; | |
}; | |
type NumericLiteral<Value extends number = number> = { | |
type: "NumericLiteral"; | |
value: Value; | |
}; | |
type StringLiteral<Value extends string = string> = { | |
type: "StringLiteral"; | |
value: Value; | |
}; | |
type BooleanLiteral<Value extends boolean = boolean> = { | |
type: "BooleanLiteral"; | |
value: Value; | |
}; | |
type NullLiteral = { type: "NullLiteral"; value: null }; | |
type BinaryOperator = "=" | "!=" | "LIKE"; | |
type BinaryExpression< | |
Left extends Expression = Expression, | |
Operator extends BinaryOperator = BinaryOperator, | |
Right extends Expression = Expression | |
> = { | |
type: "BinaryExpression"; | |
left: Left; | |
operator: Operator; | |
right: Right; | |
}; | |
type LogicalOperator = "AND" | "OR" | "&&" | "||"; | |
type LogicalExpression< | |
Left extends Expression = Expression, | |
Operator extends LogicalOperator = LogicalOperator, | |
Right extends Expression = Expression | |
> = { | |
type: "LogicalExpression"; | |
left: Left; | |
operator: Operator; | |
right: Right; | |
}; | |
type FieldSpecifier< | |
Source extends Identifier | MemberExpression = Identifier, | |
Alias extends Identifier = Identifier | |
> = { | |
type: "FieldSpecifier"; | |
source: Source; | |
alias: Alias; | |
}; | |
type TableSpecifier< | |
Source extends Identifier = Identifier, | |
Alias extends Identifier = Source | |
> = { | |
type: "TableSpecifier"; | |
source: Source; | |
alias: Alias; | |
}; | |
type AssignmentExpression< | |
Key extends Identifier = Identifier, | |
Value extends Expression = Expression | |
> = { | |
type: "AssignmentExpression"; | |
key: Key; | |
value: Value; | |
}; | |
type Expression = | |
| LogicalExpression<any, LogicalOperator, any> | |
| BinaryExpression<any, BinaryOperator, any> | |
| MemberExpression | |
| Identifier | |
| StringLiteral | |
| NumericLiteral | |
| BooleanLiteral | |
| NullLiteral; | |
type InnerJoinSpecifier< | |
From extends TableSpecifier<any> = TableSpecifier<any>, | |
Where extends Expression = Expression | |
> = { | |
type: "InnerJoinSpecifier"; | |
from: From; | |
where: Where; | |
}; | |
type JoinSpecifier = InnerJoinSpecifier; | |
type SelectStatement< | |
Fields extends FieldSpecifier<any>[] = FieldSpecifier<any>[], | |
From extends TableSpecifier<any> = TableSpecifier<any>, | |
Joins extends JoinSpecifier[] = JoinSpecifier[], | |
Where extends Expression = Expression, | |
Offset extends number = number, | |
Limit extends number = number | |
> = { | |
type: "SelectStatement"; | |
fields: Fields; | |
from: From; | |
joins: Joins | []; | |
where: Where; | |
offset: Offset; | |
limit: Limit; | |
}; | |
type InsertStatement< | |
TableName extends string = any, | |
Values extends readonly AssignmentExpression<any, any>[] = any | |
> = { | |
type: "InsertStatement"; | |
tableName: TableName; | |
values: Values; | |
}; | |
type UpdateStatement< | |
TableName extends string = string, | |
Values extends AssignmentExpression[] = AssignmentExpression[], | |
Where extends Expression = Expression | |
> = { | |
type: "UpdateStatement"; | |
tableName: TableName; | |
values: Values; | |
where: Where; | |
}; | |
type DeleteStatement< | |
TableName extends string = string, | |
Where extends Expression = Expression | |
> = { | |
type: "DeleteStatement"; | |
tableName: TableName; | |
where: Where; | |
}; | |
type Statement = | |
| SelectStatement | |
| InsertStatement | |
| UpdateStatement | |
| DeleteStatement; | |
type EvaluateStatement< | |
DB extends Database<any>, | |
Node extends Statement | |
> = Node extends SelectStatement | |
? EvaluateSelectStatement<DB, Node> | |
: Node extends InsertStatement | |
? EvaluateInsertStatement<DB, Node> | |
: Node extends UpdateStatement | |
? EvaluateUpdateStatement<DB, Node> | |
: Node extends DeleteStatement | |
? EvaluateDeleteStatement<DB, Node> | |
: never; | |
type EvaluateInsertStatement< | |
DB extends Database<any>, | |
Node extends InsertStatement | |
> = Node extends InsertStatement<infer TableName, infer Assignments> | |
? { | |
[K in keyof DB]: K extends TableName | |
? InsertRow< | |
TableName extends keyof DB ? DB[TableName] : never, | |
Assignments | |
> | |
: DB[K]; | |
} | |
: never; | |
type InsertRow< | |
Table extends readonly any[], | |
Assignments extends readonly AssignmentExpression[] | |
> = [ | |
Merge< | |
UnionToIntersection< | |
AllReadonly<AssembleEntries<ApplyAssignments<Assignments>>> | |
> | |
>, | |
...Table | |
]; | |
type AllReadonly<T> = { readonly [K in keyof T]: T[K] }; | |
type EvaluateUpdateStatement< | |
DB extends Database<any>, | |
Node extends UpdateStatement | |
> = Node extends UpdateStatement< | |
infer TableName, | |
infer Assignments, | |
infer Where | |
> | |
? { | |
[K in keyof DB]: K extends TableName | |
? UpdateRows< | |
TableName extends keyof DB ? DB[TableName] : never, | |
Assignments, | |
Where | |
> | |
: DB[K]; | |
} | |
: never; | |
type UpdateRows< | |
Table, | |
Assignments extends readonly AssignmentExpression[], | |
Where | |
> = { | |
[Index in keyof Table]: EvaluateExpression<Table[Index], Where> extends true | |
? UpdateRow<Table[Index], Assignments> | |
: Table[Index]; | |
}; | |
type UpdateRow< | |
Row, | |
Assignments extends readonly AssignmentExpression[] | |
> = MergeValues<Row, AssembleEntries<ApplyAssignments<Assignments>>>; | |
type MergeValues<T, U> = Merge< | |
{ [K in keyof T]: K extends keyof U ? U[K] : T[K] } | |
>; | |
type ApplyAssignments<Assignments extends readonly AssignmentExpression[]> = { | |
[K in keyof Assignments]: Assignments[K] extends AssignmentExpression< | |
Identifier<infer Key>, | |
infer Value | |
> | |
? [ | |
Key, | |
NullLiteral extends Value ? null : Exclude<ExtractValue<Value>, null> | |
] | |
: never; | |
}; | |
type ExtractValue<T> = T extends NullLiteral | |
? null | |
: T extends BooleanLiteral<infer Value> | |
? Value | |
: T extends NumericLiteral<infer Value> | |
? Value | |
: T extends StringLiteral<infer Value> | |
? Value | |
: never; | |
type EvaluateDeleteStatement< | |
DB extends Database<any>, | |
Node extends DeleteStatement | |
> = Node extends DeleteStatement<infer TableName, infer Where> | |
? { | |
[K in keyof DB]: K extends TableName | |
? DeleteRows<TableName extends keyof DB ? DB[TableName] : never, Where> | |
: DB[K]; | |
} | |
: never; | |
type DeleteRows<Table, Where> = FilterUndefined< | |
{ | |
[Index in keyof Table]: EvaluateExpression<Table[Index], Where> extends true | |
? undefined | |
: Table[Index]; | |
} | |
>; | |
type EvaluateSelectStatement< | |
DB extends Database<any>, | |
Node extends SelectStatement | |
> = Node extends SelectStatement< | |
infer Fields, | |
infer From, | |
infer Joins, | |
infer Where, | |
infer Offset, | |
infer Limit | |
> | |
? From extends TableSpecifier< | |
Identifier<infer Source>, | |
Identifier<infer Alias> | |
> | |
? Source extends keyof DB | |
? Fields extends [FieldSpecifier<Identifier<"*">, Identifier<"*">>] | |
? FilterWhere<CollectInputRows<DB, From, Joins>, Where> | |
: AssembleRows< | |
Fields, | |
ExtractFields< | |
FilterWhere<CollectInputRows<DB, From, Joins>, Where>, | |
Fields, | |
Merge< | |
UnionToIntersection< | |
AssembleEntries<[[Alias & string, DB[Source]]]> | |
> | |
> | |
> | |
> | |
: never | |
: never | |
: never; | |
type CollectInputRows< | |
DB extends Database<any>, | |
From, | |
Joins | |
> = From extends TableSpecifier< | |
Identifier<infer Source>, | |
Identifier<infer Alias> | |
> | |
? Source extends keyof DB | |
? Joins extends JoinSpecifier[] | |
? CollectJoins<DB, DB[Source], Alias, Joins> | |
: DB[Source] | |
: never | |
: never; | |
type CollectJoins< | |
DB extends Database<any>, | |
Table extends readonly any[], | |
Alias extends string, | |
Joins extends JoinSpecifier[] | |
> = | |
{[K in keyof Table]: CollectRowJoins<DB, Table[K], Alias, Joins>}; | |
type CollectRowJoins<DB extends Database<any>, Row, Alias extends string, Joins extends JoinSpecifier[]> = | |
Merge<Row & { | |
readonly [K in keyof Joins as ExtractJoinAlias<Joins[K]>]: | |
Joins[K] extends InnerJoinSpecifier<TableSpecifier<Identifier<infer JoinSource>, Identifier<infer JoinAlias>>, infer Where> | |
? JoinSource extends keyof DB | |
? FirstElementOrNull< | |
ExtractFieldByName< | |
FilterWhere< | |
CollectJoinedArrayForRow< | |
DB[JoinSource], | |
ExtractJoinAlias<Joins[K]>, | |
Row, | |
Alias | |
>, | |
Where | |
>, | |
JoinAlias | |
> | |
> | |
: never | |
: never | |
}> | |
type CollectJoinedArrayForRow<JoinTable extends readonly any[], JoinAlias extends string, TableRow, TableAlias extends string> = | |
{[P in keyof JoinTable]: | |
Merge< | |
TableRow | |
& {[JA in JoinAlias]: JoinTable[P]} | |
& {[TA in TableAlias]: TableRow} | |
> | |
}; | |
type ExtractFieldByName<Table, FieldName> = | |
{[K in keyof Table]: FieldName extends keyof Table[K] ? Table[K][FieldName] : never }; | |
type FirstElementOrNull<Table> = | |
Table extends readonly any[] | |
? Table['length'] extends 0 | |
? null | |
: Table[0] | |
: null; | |
type ExtractJoinAlias<Join> = Join extends InnerJoinSpecifier<TableSpecifier<Identifier<any>, Identifier<infer Alias>>> ? Alias : never; | |
type FilterUndefined<T> = T extends Readonly<[infer Head, ...infer Tail]> | |
? Head extends undefined | |
? FilterUndefined<Tail> | |
: [Head, ...FilterUndefined<Tail>] | |
: []; | |
type FilterWhere<Table, Exp> = FilterUndefined< | |
{ | |
[Index in keyof Table]: EvaluateExpression<Table[Index], Exp> extends true | |
? Table[Index] | |
: undefined; | |
} | |
>; | |
type ExtractFields<Table, Fields, Aliases> = { | |
[Index in keyof Table]: { | |
[K in keyof Fields]: Fields[K] extends FieldSpecifier<infer Source> | |
? ReadRowField<Table[Index], Source, Aliases> | |
: never; | |
}; | |
}; | |
type GetValueByKey<Row, Key, Aliases> = | |
Row extends null ? GetValueByKey<Exclude<Row, null>, Key, Aliases> : | |
Key extends keyof Row | |
? Row[Key] | |
: Key extends keyof Aliases | |
? Aliases[Key] | |
: never; | |
type ReadRowField<Row, Field, Aliases> = Field extends MemberExpression< | |
infer O, | |
infer P | |
> | |
? ReadRowField<GetValueByKey<Row, O, Aliases>, Identifier<P>, Aliases> | |
: Field extends Identifier<infer P> | |
? GetValueByKey<Row, P, Aliases> | |
: never; | |
type AssembleRows<Fields, Data> = Fields extends FieldSpecifier<any>[] | |
? { [Index in keyof Data]: AssembleRow<Fields, Data[Index]> } | |
: never; | |
type AssembleRow<Fields extends FieldSpecifier<any>[], Data> = Merge< | |
UnionToIntersection< | |
AssembleEntries< | |
{ | |
[Index in keyof Fields]: [ | |
Fields[Index] extends FieldSpecifier<any, Identifier<infer Alias>> | |
? Alias | |
: never, | |
Index extends keyof Data ? Data[Index] : never | |
]; | |
} | |
> | |
> | |
>; | |
type EvaluateExpression<Row, Exp> = | |
| EvaluateLogicalExpression<Row, Exp> | |
| EvaluateBinaryExpression<Row, Exp> | |
| EvaluateMemberExpression<Row, Exp> | |
| EvaluateIdentifier<Row, Exp> | |
| EvaluateNullLiteral<Row, Exp> | |
| EvaluateBooleanLiteral<Row, Exp> | |
| EvaluateNumericLiteral<Row, Exp> | |
| EvaluateStringLiteral<Row, Exp>; | |
type EvaluateBinaryExpression<Row, Exp> = Exp extends BinaryExpression< | |
infer Left, | |
infer Op, | |
infer Right | |
> | |
? Op extends "=" | |
? EvaluateBinaryEquals< | |
EvaluateExpression<Row, Left>, | |
EvaluateExpression<Row, Right> | |
> | |
: Op extends "!=" | |
? EvaluateBinaryNotEquals< | |
EvaluateExpression<Row, Left>, | |
EvaluateExpression<Row, Right> | |
> | |
: Op extends "LIKE" | |
? EvaluateBinaryLike< | |
EvaluateExpression<Row, Left>, | |
EvaluateExpression<Row, Right> | |
> | |
: never | |
: never; | |
type EvaluateBinaryEquals<Left, Right> = Left extends Right | |
? Right extends Left | |
? true | |
: false | |
: false; | |
type EvaluateBinaryNotEquals<Left, Right> = EvaluateBinaryEquals< | |
Left, | |
Right | |
> extends true | |
? false | |
: true; | |
type EvaluateBinaryLike<Left, Right> = MatchStringLike<Left, Right>; | |
type EvaluateLogicalExpression<Row, Exp> = Exp extends LogicalExpression< | |
infer Left, | |
infer Op, | |
infer Right | |
> | |
? Op extends "AND" | |
? EvaluateLogicalAND< | |
EvaluateExpression<Row, Left>, | |
EvaluateExpression<Row, Right> | |
> | |
: Op extends "OR" | |
? EvaluateLogicalOR< | |
EvaluateExpression<Row, Left>, | |
EvaluateExpression<Row, Right> | |
> | |
: never | |
: never; | |
type EvaluateLogicalAND<Left, Right> = Left extends true | |
? Right extends true | |
? true | |
: false | |
: false; | |
type EvaluateLogicalOR<Left, Right> = Left extends true | |
? true | |
: Right extends true | |
? true | |
: false; | |
type EvaluateMemberExpression<Row, Exp> = Exp extends MemberExpression< | |
infer O, | |
infer P | |
> | |
? O extends keyof Row | |
? EvaluateIdentifier<Row[O], Identifier<P>> | |
: never | |
: never; | |
type EvaluateIdentifier<Row, Exp> = Exp extends Identifier<infer Name> | |
? Name extends keyof Row | |
? Row[Name] | |
: never | |
: never; | |
type EvaluateNullLiteral<Row, Exp> = Exp extends NullLiteral ? null : never; | |
type EvaluateBooleanLiteral<Row, Exp> = Exp extends BooleanLiteral<infer Value> | |
? Value | |
: never; | |
type EvaluateStringLiteral<Row, Exp> = Exp extends StringLiteral<infer Value> | |
? Value | |
: never; | |
type EvaluateNumericLiteral<Row, Exp> = Exp extends NumericLiteral<infer Value> | |
? Value | |
: never; | |
type Evaluate< | |
DB extends Database<any>, | |
S extends Statement | |
> = EvaluateStatement<DB, S>; | |
type PairToObject<P extends readonly [PropertyKey, any]> = P extends any | |
? { | |
[k in P[0]]: P[1]; | |
} | |
: never; | |
type ToUnaryFunctionUnion<U> = U extends any ? (arg: U) => void : never; | |
type UnionToIntersection<U> = ToUnaryFunctionUnion<U> extends ( | |
arg: infer I | |
) => void | |
? I | |
: never; | |
type AssembleEntries< | |
Entries extends Iterable<readonly [PropertyKey, any]> | |
> = Entries extends Iterable<infer P> | |
? P extends readonly [PropertyKey, any] | |
? Merge<UnionToIntersection<PairToObject<P>>> | |
: never | |
: never; | |
type Parse<T> = | |
ParseStatement<T> extends [infer Statement, infer Rest] ? | |
Trim<Rest> extends ';' ? Statement : | |
Trim<Rest> extends '' ? Statement : never : | |
never; | |
type ParseStatement<T> = | |
ParseSelectStatement<T> | ParseInsertStatement<T> | ParseUpdateStatement<T> | ParseDeleteStatement<T>; | |
type ParseSelectStatement<T> = | |
ParseSelectClause<T> extends Partial<SelectStatement<infer Fields, infer From, infer Joins, infer Where, infer Offset, infer Limit>> | |
? [SelectStatement<Fields, From, Joins, Where, Offset, Limit>, ''] | |
: never | |
type ParseTableSpecifier<T> = | |
T extends `${infer Source} AS ${infer Alias}` ? TableSpecifier<Identifier<Source>, Identifier<Alias>> : | |
T extends `${infer Source} ${infer Alias}` ? TableSpecifier<Identifier<Source>, Identifier<Alias>> : | |
T extends string ? TableSpecifier<Identifier<Trim<T>>> : | |
never; | |
type ParseSelectClause<T> | |
= T extends `SELECT ${infer FieldNames} FROM ${infer R0}` ? | |
Merge<{fields: ParseFieldSpecifierList<FieldNames>} & ParseFromClause<Trim<R0>>> | |
: never; | |
type ParseFromClause<T> = | |
Tokenize<T> extends [infer Source, infer R0] ? | |
Tokenize<R0> extends ['AS', infer R1] | |
? Tokenize<R1> extends [infer Alias, infer R2] | |
? {from: TableSpecifier<Identifier<Source & string>, Identifier<Alias & string>>} & ParseJoinClause<R2> | |
: never | |
: {from: TableSpecifier<Identifier<Source & string>>} & ParseJoinClause<R0> | |
: never; | |
type ParseJoinClause<T> = | |
Trim<T> extends `INNER JOIN ${infer TableName} ON ${infer R0}` | |
? ParseExpression<R0> extends [infer Exp, infer R1] | |
? Exp extends Expression | |
? {joins: [InnerJoinSpecifier<ParseTableSpecifier<TableName>, Exp>]} & ParseWhereClause<Trim<R1>> | |
: never | |
: never | |
: ParseWhereClause<Trim<T>> & {joins: []} | |
type ParseWhereClause<T> = | |
Trim<T> extends '' | |
? { where: BooleanLiteral<true> } | |
: Trim<T> extends `WHERE ${infer Where}` | |
? ParseExpression<Where> extends [infer Exp, infer R0] | |
? Exp extends Expression | |
? {where: Merge<Exp>} & ParseLimitClause<R0> | |
: never | |
: never | |
: {where: BooleanLiteral<true>} & ParseLimitClause<Trim<T>> | |
type ParseLimitClause<T> = | |
Trim<T> extends `LIMIT ${infer R0}` | |
? Tokenize<R0> extends [infer Limit, infer R1] | |
? Limit extends keyof IntegerStrings | |
? {limit: IntegerStrings[Limit]} & ParseOffsetClause<R1> | |
: never | |
: never | |
: {limit: -1} & ParseOffsetClause<T>; | |
type ParseOffsetClause<T> = | |
Trim<T> extends `OFFSET ${infer R0}` | |
? Tokenize<R0> extends [infer Offset, infer R1] | |
? Offset extends keyof IntegerStrings | |
? {offset: IntegerStrings[Offset]} & ParseStatementTerminator<R1> | |
: never | |
: never | |
: {offset: 0} & ParseStatementTerminator<T>; | |
type ParseStatementTerminator<T> = | |
Trim<T> extends '' | |
? {} | |
: Trim<T> extends ';' | |
? {} | |
: never; | |
type ParseInsertStatement<T> = | |
T extends `INSERT INTO ${infer TableName} SET ${infer Fields}` ? | |
[InsertStatement<TableName, ParseAssignmentExpressionList<Fields>>, ''] | |
: never; | |
type ParseUpdateStatement<T> = | |
T extends `UPDATE ${infer TableName} SET ${infer Fields} WHERE ${infer Where}` ? | |
ParseExpression<Where> extends [infer Exp, string] ? | |
Exp extends Expression ? | |
[UpdateStatement<TableName, ParseAssignmentExpressionList<Fields>, Exp>, ''] : | |
never : | |
never : | |
T extends `UPDATE ${infer TableName} SET ${infer Fields}` ? | |
[UpdateStatement<TableName, ParseAssignmentExpressionList<Fields>, BooleanLiteral<true>>, ''] | |
: never; | |
type ParseDeleteStatement<T> = | |
T extends `DELETE FROM ${infer TableName} WHERE ${infer Where}` ? | |
ParseExpression<Where> extends [infer Exp, string] ? | |
Exp extends Expression ? | |
[DeleteStatement<TableName, Exp>, ''] : | |
never : | |
never : | |
T extends `DELETE FROM ${infer TableName}` ? | |
[DeleteStatement<TableName, BooleanLiteral<true>>, ''] | |
: never; | |
type ParseIdentifier<T> = | |
T extends '' ? never : | |
Tokenize<T> extends [infer Head, infer Tail] ? | |
Head extends '' ? never : | |
Head extends 'null' ? [NullLiteral, Tail] : | |
Head extends 'true' ? [BooleanLiteral<true>, Tail] : | |
Head extends 'false' ? [BooleanLiteral<false>, Tail] : | |
Head extends keyof IntegerStrings ? [NumericLiteral<IntegerStrings[Head] & number>, Tail] : | |
[Identifier<Head & string>, Tail] : | |
[Identifier<T & string>, '']; | |
type ParseMemberExpression<T> = | |
Tokenize<T> extends [`${infer O}.${infer P}`, infer Tail] ? | |
[MemberExpression<O, P>, Tail] | |
: ParseIdentifier<T>; | |
type ParseStringLiteral<T> = | |
T extends `"${infer Value}"${infer Rest}` ? [StringLiteral<Value>, Rest] : | |
T extends `'${infer Value}'${infer Rest}` ? [StringLiteral<Value>, Rest] : | |
ParseMemberExpression<T>; | |
type ParseCallExpression<T> = | |
Trim<T> extends '' ? never : | |
ParseStringLiteral<Trim<T>> | ParseParenthesizedExpression<T>; | |
type ParseBinaryExpression<T> = | |
ParseCallExpression<T> extends [infer Left, infer R1] ? | |
Left extends Expression ? | |
Tokenize<R1> extends [infer Op, infer R2] ? | |
Op extends BinaryOperator ? | |
ParseCallExpression<R2> extends [infer Right, infer R3] ? | |
Right extends Expression ? | |
[BinaryExpression<Left, Op, Right>, R3] : | |
never : | |
never : | |
[Left, R1] : | |
[Left, R1] : | |
never : | |
never; | |
type ParseLogicalExpression<T> = | |
ParseBinaryExpression<T> extends [infer Left, infer R1] ? | |
Tokenize<R1> extends [infer Op, infer R2] ? | |
Op extends LogicalOperator ? | |
ParseExpression<R2> extends [infer Right, infer R3] ? | |
Left extends Expression ? | |
Right extends Expression ? | |
[LogicalExpression<Left, Op, Right>, R3] : | |
never : | |
never : | |
never : | |
[Left, R1] : | |
[Left, R1] : | |
never; | |
type ParseExpression<T> = | |
Trim<T> extends '' ? never : | |
ParseLogicalExpression<Trim<T>> | ParseParenthesizedExpression<T>; | |
type ParseParenthesizedExpression<T> = T extends `(${infer Content})${infer Rest}` ? [ParseExpression<Content>, Rest] : never; | |
type ParseFieldSpecifierList<T> = | |
T extends `${infer Head},${infer Tail}` ? [ParseFieldSpecifier<Trim<Head>>, ...ParseFieldSpecifierList<Trim<Tail>>] : | |
T extends `${infer Head} AS ${infer Alias} ${infer Tail}` ? [FieldSpecifier<Trim<ParseMemberExpression<Head>[0]>, Trim<ParseIdentifier<Alias>[0]>>, Tail] : | |
T extends `${infer Head} AS ${infer Alias}` ? [FieldSpecifier<Trim<ParseMemberExpression<Head>[0]>, Trim<ParseIdentifier<Alias>[0]>>] : | |
T extends `${infer Head} ${infer Tail}` ? [ParseFieldSpecifier<Trim<Head>>, Tail] : | |
[ParseFieldSpecifier<Trim<T>>]; | |
type ParseFieldSpecifier<T> = | |
T extends `${infer Field} AS ${infer Alias}` ? FieldSpecifier<ParseMemberExpression<Trim<Field>>[0], ParseIdentifier<Trim<Alias>>[0]> : | |
ParseMemberExpression<T> extends [infer M, ''] ? | |
M extends MemberExpression<infer O, infer P> ? FieldSpecifier<M, Identifier<P>> : M extends Identifier ? FieldSpecifier<M, M> : | |
T extends string ? FieldSpecifier<Identifier<T>, Identifier<T>> : never : | |
never; | |
type ParseAssignmentExpressionList<T> = | |
T extends `${infer Head},${infer Tail}` ? [ParseAssignmentExpression<Trim<Head>>, ...ParseAssignmentExpressionList<Trim<Tail>>] : | |
T extends `${infer Head} = ${infer Value} ${infer Tail}` ? [AssignmentExpression<Identifier<Trim<Head>>, ParseExpression<Trim<Value>>[0] & Expression>, Tail] : | |
T extends `${infer Head} = ${infer Value}` ? [AssignmentExpression<Identifier<Trim<Head>>, ParseExpression<Trim<Value>>[0] & Expression>] : | |
T extends `${infer Head} ${infer Tail}` ? [ParseAssignmentExpression<Trim<Head>>, Tail] : | |
[ParseAssignmentExpression<Trim<T>>]; | |
type ParseAssignmentExpression<T> = | |
T extends `${infer Key} = ${infer Value}` ? AssignmentExpression<Identifier<Key>, ParseExpression<Value>[0] & Expression> : | |
never; | |
type Tokenize<T> = | |
Trim<T> extends `${infer Head} ${infer Tail}` ? [Head, Tail] : | |
Trim<T> extends `${infer Head},${infer Tail}` ? [Head, Tail] : | |
Trim<T> extends `${infer Head}(${infer Tail}` ? [Head, Tail] : | |
Trim<T> extends `${infer Head})${infer Tail}` ? [Head, Tail] : | |
Trim<T> extends `${infer Head};${infer Tail}` ? [Head, Tail] : | |
Trim<T> extends `${infer Head})` ? [Head, ')'] : | |
Trim<T> extends `${infer Head};` ? [Head, ';'] : | |
[Trim<T>, ''] | |
type Print<T> = | |
T extends Identifier<infer N> ? N : | |
T extends MemberExpression<infer O, infer P> ? `${O}.${P}` : | |
T extends NumericLiteral<infer V> ? `${V}` : | |
T extends StringLiteral<infer V> ? QuoteString<V> : | |
T extends BooleanLiteral<true> ? 'true' : | |
T extends BooleanLiteral<false> ? 'false' : | |
T extends NullLiteral ? 'null' : | |
// @ts-ignore due to excessive depth | |
T extends BinaryExpression<infer L, infer O, infer R> ? `${Print<L>} ${O} ${Print<R>}` : | |
T extends LogicalExpression<infer L, infer O, infer R> ? `${Print<L>} ${O} ${Print<R>}` : | |
T extends FieldSpecifier<infer S, Identifier<infer A>> ? | |
S extends Identifier<A> | |
? `${A}` | |
: `${Print<S>} AS ${A}` : | |
T extends TableSpecifier<Identifier<infer S>, Identifier<infer A>> ? | |
S extends A ? `${S}` : `${S} AS ${A}` : | |
T extends InnerJoinSpecifier<TableSpecifier<infer S, infer A>, infer Where> ? | |
JoinStrings<['INNER JOIN', Print<TableSpecifier<S, A>>, 'ON', Print<Where>], ' '> : | |
T extends SelectStatement< | |
infer Fields & any[], | |
TableSpecifier<infer TableName, infer TableAlias>, | |
infer Joins & any[], | |
infer Where, | |
infer Offset, | |
infer Limit | |
> ? | |
JoinStrings< | |
[ | |
'SELECT', | |
Fields extends readonly any[] ? JoinStrings<PrintArray<Fields>, ', '> : '', | |
'FROM', | |
Print<TableSpecifier<TableName, TableAlias>>, | |
JoinStrings<PrintArray<Joins>, ' '>, | |
Where extends BooleanLiteral<true> | |
? '' | |
: `WHERE ${Print<Where>}`, | |
Limit extends -1 ? '' : `LIMIT ${Limit}`, | |
Offset extends 0 ? '' : `OFFSET ${Offset}` | |
], | |
' ' | |
> : | |
T extends AssignmentExpression<Identifier<infer Key>, infer Value> ? | |
JoinStrings<[Key, '=', Print<Value>], ' '> : | |
`[CANNOT PRINT THIS AST NODE`; | |
type PrintArray<T> = T extends any[] ? {[K in keyof T]: Print<T[K]>} : never; | |
type QuoteString<T extends string> = | |
StringContains<T, "'"> extends true ? `"${T}"` : `'${T}'`; | |
type Query<SQL, DB extends Database<any>> = Evaluate<DB, Parse<SQL>>; | |
type Table<T> = ReadonlyArray<T>; | |
type Database<Schema> = { | |
[TableName in keyof Schema]: Table<Schema[TableName]>; | |
}; | |
type Integers = [ | |
0, | |
1, | |
2, | |
3, | |
4, | |
5, | |
6, | |
7, | |
8, | |
9, | |
10, | |
11, | |
12, | |
13, | |
14, | |
15, | |
16, | |
17, | |
18, | |
19, | |
20, | |
21, | |
22, | |
23, | |
24, | |
25, | |
26, | |
27, | |
28, | |
29, | |
30, | |
31, | |
32, | |
33, | |
34, | |
35, | |
36, | |
37, | |
38, | |
39, | |
40, | |
41, | |
42, | |
43, | |
44, | |
45, | |
46, | |
47, | |
48, | |
49, | |
50, | |
51, | |
52, | |
53, | |
54, | |
55, | |
56, | |
57, | |
58, | |
59, | |
60, | |
61, | |
62, | |
63, | |
64, | |
65, | |
66, | |
67, | |
68, | |
69, | |
70, | |
71, | |
72, | |
73, | |
74, | |
75, | |
76, | |
77, | |
78, | |
79, | |
80, | |
81, | |
82, | |
83, | |
84, | |
85, | |
86, | |
87, | |
88, | |
89, | |
90, | |
91, | |
92, | |
93, | |
94, | |
95, | |
96, | |
97, | |
98, | |
99, | |
100, | |
101, | |
102, | |
103, | |
104, | |
105, | |
106, | |
107, | |
108, | |
109, | |
110, | |
111, | |
112, | |
113, | |
114, | |
115, | |
116, | |
117, | |
118, | |
119, | |
120, | |
121, | |
122, | |
123, | |
124, | |
125, | |
126, | |
127, | |
128, | |
129, | |
130, | |
131, | |
132, | |
133, | |
134, | |
135, | |
136, | |
137, | |
138, | |
139, | |
140, | |
141, | |
142, | |
143, | |
144, | |
145, | |
146, | |
147, | |
148, | |
149, | |
150, | |
151, | |
152, | |
153, | |
154, | |
155, | |
156, | |
157, | |
158, | |
159, | |
160, | |
161, | |
162, | |
163, | |
164, | |
165, | |
166, | |
167, | |
168, | |
169, | |
170, | |
171, | |
172, | |
173, | |
174, | |
175, | |
176, | |
177, | |
178, | |
179, | |
180, | |
181, | |
182, | |
183, | |
184, | |
185, | |
186, | |
187, | |
188, | |
189, | |
190, | |
191, | |
192, | |
193, | |
194, | |
195, | |
196, | |
197, | |
198, | |
199, | |
200, | |
201, | |
202, | |
203, | |
204, | |
205, | |
206, | |
207, | |
208, | |
209, | |
210, | |
211, | |
212, | |
213, | |
214, | |
215, | |
216, | |
217, | |
218, | |
219, | |
220, | |
221, | |
222, | |
223, | |
224, | |
225, | |
226, | |
227, | |
228, | |
229, | |
230, | |
231, | |
232, | |
233, | |
234, | |
235, | |
236, | |
237, | |
238, | |
239, | |
240, | |
241, | |
242, | |
243, | |
244, | |
245, | |
246, | |
247, | |
248, | |
249, | |
250, | |
251, | |
252, | |
253, | |
254, | |
255, | |
256 | |
]; | |
type NextIntegers = Integers extends [number, ...infer Next] ? Next : never; | |
type Inc<T> = T extends keyof NextIntegers ? NextIntegers[T] : never; | |
type IntegerStrings = {[K in keyof Integers & string as `${K}`]: Integers[K]}; | |
type StringToNumber<T extends keyof IntegerStrings> = IntegerStrings[T]; | |
type _<T> = T; | |
type Merge<T> = _<{ [k in keyof T]: T[k] }>; | |
type MatchStringLike<Left, Right> = | |
Right extends `%${infer Content}%` ? MatchString<Left, Content> : | |
Right extends `%${infer Content}` ? MatchStringEnd<Left, Content> : | |
Right extends `${infer Content}%` ? MatchStringStart<Left, Content> : | |
false; | |
type EatFirstChar<T> = T extends `${infer _}${infer B}` ? B : ''; | |
type MatchStringStart<Candidate, Pattern extends string> = | |
Candidate extends Pattern ? true : | |
Candidate extends `${Pattern}${infer _}` ? true : | |
false; | |
type MatchStringEnd<Candidate, Pattern extends string> = | |
Candidate extends Pattern ? true : | |
Candidate extends `${infer _}${Pattern}` ? true : | |
false; | |
type MatchString<Candidate, Pattern extends string> = | |
Candidate extends '' ? false : | |
MatchStringStart<Candidate, Pattern> extends true ? true : | |
MatchStringEnd<Candidate, Pattern> extends true ? true : | |
MatchString<EatFirstChar<Candidate>, Pattern> extends true ? true : | |
false; | |
type Trim<T> = T extends ` ${infer Rest}` ? Trim<Rest> : T; | |
type Indents = { | |
0: '', | |
1: ' ', | |
2: ' ', | |
3: ' ', | |
4: ' ', | |
5: ' ', | |
6: ' ', | |
7: ' ', | |
}; | |
type Indent<T, Level = 1> = | |
Level extends keyof Indents | |
? `${Indents[Level]}${T & string}` | |
: `${Indents[7]}${T & string}`; | |
type I0 = Indent<'hello world', 3> | |
type JoinStrings<T, Sep = ', '> = | |
T extends [infer Head, ...infer Tail] | |
? Tail extends readonly [] | |
? Head | |
: Tail extends readonly string[] | |
? Head extends '' | |
? JoinStrings<Tail, Sep> | |
: JoinStrings<Tail, Sep> extends '' | |
? `${Head & string}` | |
: `${Head & string}${Sep & string}${JoinStrings<Tail, Sep>}` | |
: `${Head & string}` | |
: T extends [infer Head] | |
? `${Head & string}` | |
: ''; | |
type J0 = JoinStrings<['a', 'b', 'ccc', 'dd']>; | |
type J1 = JoinStrings<[], '|'>; | |
type J2 = JoinStrings<['aaa'], '|'>; | |
type J3 = JoinStrings<['aaa', '', 'bb', ''], '|'>; | |
type StringContains<Input extends string, Term extends string> = | |
Input extends Term ? true : | |
Input extends `${Term}${infer _}` ? true : | |
Input extends `${infer _0}${Term}${infer _1}` ? true : | |
Input extends `${infer _}${Term}` ? true : | |
false; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment