diff --git a/types/slonik/index.d.ts b/types/slonik/index.d.ts index 5a589e8a6d..d328f4d63b 100644 --- a/types/slonik/index.d.ts +++ b/types/slonik/index.d.ts @@ -1,9 +1,13 @@ -// Type definitions for slonik 16.10 +// Type definitions for slonik 16.16 // Project: https://github.com/gajus/slonik#readme // Definitions by: Sebastian Sebald // Misha Kaletsky // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 3.0 + +/// + +import { Readable } from 'stream'; import * as SlonikSymbol from './symbols'; // @@ -13,26 +17,30 @@ import * as SlonikSymbol from './symbols'; export type LoggerType = (...args: string[]) => never; export type MaybePromiseType = T | Promise; +export type StreamHandlerType = (stream: Readable) => void; + +export type ComparisonOperatorType = '<' | '>' | '<=' | '>=' | '=' | '<>' | '!='; +export type LogicalBooleanOperatorType = 'AND' | 'OR'; + // // EXPRESSIONS AND TOKENS // ---------------------------------------------------------------------- -export type PrimitiveValueExpressionType = string | number | boolean | null; - -export type ValueExpressionType = - | PrimitiveValueExpressionType - | IdentifierTokenType - | RawSqlTokenType - | SqlSqlTokenType - | TupleListSqlTokenType - | TupleSqlTokenType - | UnnestSqlTokenType - | ValueListSqlTokenType; export interface IdentifierTokenType { names: ReadonlyArray; type: typeof SlonikSymbol.IdentifierTokenSymbol; } +export type IdentifierListMemberType = string[] | { + alias: string + identifier: string[] +}; + +export interface IdentifierListTokenType { + identifiers: IdentifierListMemberType[]; + type: typeof SlonikSymbol.IdentifierListTokenSymbol; +} + export interface SqlSqlTokenType { sql: string; type: typeof SlonikSymbol.SqlTokenSymbol; @@ -50,6 +58,12 @@ export interface ValueListSqlTokenType { type: typeof SlonikSymbol.ValueListTokenSymbol; } +export interface ArraySqlTokenType { + memberType: string; + type: typeof SlonikSymbol.ArrayTokenSymbol; + values: PrimitiveValueExpressionType[]; +} + export interface TupleSqlTokenType { values: PrimitiveValueExpressionType[]; type: typeof SlonikSymbol.TupleTokenSymbol; @@ -66,6 +80,46 @@ export interface UnnestSqlTokenType { type: typeof SlonikSymbol.UnnestTokenSymbol; } +export interface ComparisonPredicateTokenType { + leftOperand: ValueExpressionType; + operator: ComparisonOperatorType; + rightOperand: ValueExpressionType; + type: typeof SlonikSymbol.ComparisonPredicateTokenSymbol; +} + +export interface BooleanExpressionTokenType { + members: ValueExpressionType[]; + operator: LogicalBooleanOperatorType; + type: typeof SlonikSymbol.ComparisonPredicateTokenSymbol; +} + +export interface AssignmentListTokenType { + namedAssignment: NamedAssignmentType; + type: typeof SlonikSymbol.ComparisonPredicateTokenSymbol; +} + +export type PrimitiveValueExpressionType = string | number | boolean | null; + +export type SqlTokenType = + ArraySqlTokenType | + AssignmentListTokenType | + IdentifierTokenType | + IdentifierListTokenType | + RawSqlTokenType | + SqlSqlTokenType | + TupleListSqlTokenType | + TupleSqlTokenType | + UnnestSqlTokenType | + ValueListSqlTokenType | + ComparisonPredicateTokenType | + BooleanExpressionTokenType; + +export type ValueExpressionType = + SqlTokenType | + PrimitiveValueExpressionType; + +export type NamedAssignmentType = Record; + // // DATABASE // ---------------------------------------------------------------------- @@ -84,6 +138,7 @@ export type DatabaseTransactionConnectionType = CommonQueryMethodsType & { }; export type DatabasePoolConnectionType = CommonQueryMethodsType & { + stream: (sql: TaggedTemplateLiteralInvocationType, streamHandler: StreamHandlerType) => Promise, transaction: (handler: TransactionFunctionType) => Promise; }; @@ -91,6 +146,7 @@ export type ConnectionRoutineType = (connection: DatabasePoolConnectionType) export type DatabasePoolType = CommonQueryMethodsType & { connect: (connectionRoutine: ConnectionRoutineType) => Promise; + stream: (sql: TaggedTemplateLiteralInvocationType, streamHandler: StreamHandlerType) => Promise, transaction: (handler: TransactionFunctionType) => Promise; }; @@ -165,19 +221,19 @@ export interface QueryResultType { export type QueryResultRowColumnType = string | number; export type QueryResultRowType = { - [name in ColumnName]: QueryResultRowColumnType; + [name in ColumnName]: QueryResultRowColumnType; }; // TODO: Infer column names via generic -export type QueryAnyFirstFunctionType = QueryMethodType; -export type QueryAnyFunctionType = QueryMethodType; -export type QueryFunctionType = QueryMethodType>; -export type QueryManyFirstFunctionType = QueryMethodType; -export type QueryManyFunctionType = QueryMethodType; -export type QueryMaybeOneFirstFunctionType = QueryMethodType; -export type QueryMaybeOneFunctionType = QueryMethodType; -export type QueryOneFirstFunctionType = QueryMethodType; -export type QueryOneFunctionType = QueryMethodType; +export type QueryAnyFirstFunctionType = QueryMethodType; +export type QueryAnyFunctionType = QueryMethodType; +export type QueryFunctionType = QueryMethodType>; +export type QueryManyFirstFunctionType = QueryMethodType; +export type QueryManyFunctionType = QueryMethodType; +export type QueryMaybeOneFirstFunctionType = QueryMethodType; +export type QueryMaybeOneFunctionType = QueryMethodType; +export type QueryOneFirstFunctionType = QueryMethodType; +export type QueryOneFunctionType = QueryMethodType; export interface CommonQueryMethodsType { any: QueryAnyFunctionType; @@ -242,12 +298,48 @@ export const sql: SqlTaggedTemplateType; export interface SqlTaggedTemplateType { (template: TemplateStringsArray, ...vals: ValueExpressionType[]): SqlSqlTokenType; - identifier: (names: string[]) => IdentifierTokenType; - raw: (rawSql: string, values?: PrimitiveValueExpressionType[]) => RawSqlTokenType; - tuple: (values: PrimitiveValueExpressionType[]) => TupleSqlTokenType; - tupleList: (tuples: PrimitiveValueExpressionType[][]) => TupleListSqlTokenType; - unnest: (tuples: PrimitiveValueExpressionType[][], columnTypes: string[]) => UnnestSqlTokenType; - valueList: (values: PrimitiveValueExpressionType[]) => ValueListSqlTokenType; + array: ( + values: PrimitiveValueExpressionType[], + memberType: string + ) => ArraySqlTokenType; + assignmentList: ( + namedAssignmentValueBindings: NamedAssignmentType + ) => AssignmentListTokenType; + booleanExpression: ( + members: ValueExpressionType[], + operator: LogicalBooleanOperatorType + ) => BooleanExpressionTokenType; + comparisonPredicate: ( + leftOperand: ValueExpressionType, + operator: ComparisonOperatorType, + rightOperand: ValueExpressionType + ) => ComparisonPredicateTokenType; + identifier: ( + names: string[] + ) => IdentifierTokenType; + identifierList: ( + identifiers: IdentifierListMemberType[] + ) => IdentifierListTokenType; + raw: ( + rawSql: string, + values?: PrimitiveValueExpressionType[] + ) => RawSqlTokenType; + tuple: ( + values: ValueExpressionType[] + ) => TupleSqlTokenType; + tupleList: ( + tuples: ValueExpressionType[][] + ) => TupleListSqlTokenType; + unnest: ( + // Value might be PrimitiveValueExpressionType[], + // or it can be infinitely nested array, e.g. + // https://github.com/gajus/slonik/issues/44 + tuples: any[][], + columnTypes: string[] + ) => UnnestSqlTokenType; + valueList: ( + values: ValueExpressionType[] + ) => ValueListSqlTokenType; } export interface SqlFragmentType { @@ -314,10 +406,10 @@ export interface InterceptorType { query: QueryType ) => MaybePromiseType; transformRow?: ( - queryContext: QueryContextType, - query: QueryType, - row: QueryResultRowType, - fields: FieldType[] + queryContext: QueryContextType, + query: QueryType, + row: QueryResultRowType, + fields: FieldType[] ) => QueryResultRowType; } diff --git a/types/slonik/slonik-tests.ts b/types/slonik/slonik-tests.ts index 7f9d110e5c..e60a1a8809 100644 --- a/types/slonik/slonik-tests.ts +++ b/types/slonik/slonik-tests.ts @@ -24,6 +24,11 @@ import { TypeParserType, UniqueIntegrityConstraintViolationError } from 'slonik'; +import { ArrayTokenSymbol, TupleListTokenSymbol } from 'slonik/symbols'; + +// make sure symbols are unique +// $ExpectError +const badSymbolAssignment: typeof ArrayTokenSymbol = TupleListTokenSymbol; const VALUE = 'foo'; @@ -304,3 +309,156 @@ new NotNullIntegrityConstraintViolationError(); new ForeignKeyIntegrityConstraintViolationError(); new UniqueIntegrityConstraintViolationError(); new CheckIntegrityConstraintViolationError(); + +const samplesFromDocs = async () => { + // some samples generated by parsing the readme from slonik's github page + // start samples from readme + const sample1 = async () => { + connection.query(sql` + SELECT ${sql.identifier(['foo', 'a'])} + FROM ( + VALUES ${sql.tupleList([['a1', 'b1', 'c1'], ['a2', 'b2', 'c2']])} + ) foo(a, b, c) + WHERE foo.b IN (${sql.valueList(['c1', 'a2'])}) + `); + }; + + const sample2 = async () => { + await connection.query(sql` + INSERT INTO (foo, bar, baz) + VALUES ${sql.tupleList([ + [1, 2, 3], + [4, 5, 6] + ])} + `); + }; + + const sample3 = async () => { + await connection.query(sql` + INSERT INTO (foo, bar, baz) + SELECT * + FROM ${sql.unnest( + [ + [1, 2, 3], + [4, 5, 6] + ], + [ + 'int4', + 'int4', + 'int4' + ] + )} + `); + }; + + const sample4 = async () => { + await connection.query(sql` + SELECT (${sql.valueList([1, 2, 3])}) + `); + }; + + const sample5 = async () => { + await connection.query(sql` + SELECT (${sql.array([1, 2, 3], 'int4')}) + `); + }; + + const sample6 = async () => { + sql`SELECT id FROM foo WHERE id IN (${sql.valueList([1, 2, 3])})`; + sql`SELECT id FROM foo WHERE id NOT IN (${sql.valueList([1, 2, 3])})`; + }; + + const sample7 = async () => { + sql`SELECT id FROM foo WHERE id = ANY(${sql.array([1, 2, 3], 'int4')})`; + sql`SELECT id FROM foo WHERE id != ALL(${sql.array([1, 2, 3], 'int4')})`; + }; + + const sample8 = async () => { + await connection.query(sql` + INSERT INTO (foo, bar, baz) + VALUES ${sql.tuple([1, 2, 3])} + `); + }; + + const sample9 = async () => { + await connection.query(sql` + INSERT INTO (foo, bar, baz) + VALUES ${sql.tupleList([ + [1, 2, 3], + [4, 5, 6] + ])} + `); + }; + + const sample10 = async () => { + await connection.query(sql` + SELECT bar, baz + FROM ${sql.unnest( + [ + [1, 'foo'], + [2, 'bar'] + ], + [ + 'int4', + 'text' + ] + )} AS foo(bar, baz) + `); + }; + + const sample11 = async () => { + sql` + SELECT 1 + FROM ${sql.identifier(['bar', 'baz'])} + `; + }; + + const sample12 = async () => { + sql` + SELECT 1 + FROM ${sql.identifierList([ + ['bar', 'baz'], + ['qux', 'quux'] + ])} + `; + }; + + const sample13 = async () => { + sql` + SELECT 1 + FROM ${sql.identifierList([ + { + alias: 'qux', + identifier: ['bar', 'baz'] + }, + { + alias: 'corge', + identifier: ['quux', 'quuz'] + } + ])} + `; + }; + + const sample14 = async () => { + sql` + SELECT ${sql.booleanExpression([3, 4], 'AND')} + `; + }; + + const sample15 = async () => { + sql` + SELECT ${sql.comparisonPredicate(3, '=', 4)} + `; + }; + + const sample16 = async () => { + await connection.query(sql` + UPDATE foo + SET ${sql.assignmentList({ + bar: 'baz', + qux: 'quux' + })} + `); + }; + // end samples from readme +}; diff --git a/types/slonik/symbols.d.ts b/types/slonik/symbols.d.ts index 028adcb021..b50d07564a 100644 --- a/types/slonik/symbols.d.ts +++ b/types/slonik/symbols.d.ts @@ -1,7 +1,12 @@ -export const SqlTokenSymbol: symbol; -export const RawSqlTokenSymbol: symbol; -export const IdentifierTokenSymbol: symbol; -export const ValueListTokenSymbol: symbol; -export const TupleTokenSymbol: symbol; -export const TupleListTokenSymbol: symbol; -export const UnnestTokenSymbol: symbol; +export const ArrayTokenSymbol: unique symbol; +export const AssignmentListTokenSymbol: unique symbol; +export const BooleanExpressionTokenSymbol: unique symbol; +export const ComparisonPredicateTokenSymbol: unique symbol; +export const IdentifierListTokenSymbol: unique symbol; +export const IdentifierTokenSymbol: unique symbol; +export const RawSqlTokenSymbol: unique symbol; +export const SqlTokenSymbol: unique symbol; +export const ValueListTokenSymbol: unique symbol; +export const TupleTokenSymbol: unique symbol; +export const TupleListTokenSymbol: unique symbol; +export const UnnestTokenSymbol: unique symbol;