Slonik 16.16 (#35023)

* [slonik] add new helpers

specifically sql.array and sql.identifierList

* [slonik] add more new types

* [slonik] generated sample tests

* [slonik] lint
This commit is contained in:
mmkal
2019-04-26 16:26:23 -04:00
committed by Pranav Senthilnathan
parent caaf192478
commit 3034a2763c
3 changed files with 294 additions and 39 deletions

View File

@@ -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 <https://github.com/sebald>
// Misha Kaletsky <https://github.com/mmkal>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 3.0
/// <reference types="node" />
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> = T | Promise<T>;
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<string>;
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<string, ValueExpressionType>;
//
// DATABASE
// ----------------------------------------------------------------------
@@ -84,6 +138,7 @@ export type DatabaseTransactionConnectionType = CommonQueryMethodsType & {
};
export type DatabasePoolConnectionType = CommonQueryMethodsType & {
stream: (sql: TaggedTemplateLiteralInvocationType, streamHandler: StreamHandlerType) => Promise<null>,
transaction: <T>(handler: TransactionFunctionType<T>) => Promise<T>;
};
@@ -91,6 +146,7 @@ export type ConnectionRoutineType<T> = (connection: DatabasePoolConnectionType)
export type DatabasePoolType = CommonQueryMethodsType & {
connect: <T>(connectionRoutine: ConnectionRoutineType<T>) => Promise<T>;
stream: (sql: TaggedTemplateLiteralInvocationType, streamHandler: StreamHandlerType) => Promise<null>,
transaction: <T>(handler: TransactionFunctionType<T>) => Promise<T>;
};
@@ -165,19 +221,19 @@ export interface QueryResultType<T> {
export type QueryResultRowColumnType = string | number;
export type QueryResultRowType<ColumnName extends string = string> = {
[name in ColumnName]: QueryResultRowColumnType;
[name in ColumnName]: QueryResultRowColumnType;
};
// TODO: Infer column names via generic
export type QueryAnyFirstFunctionType = QueryMethodType<QueryResultRowColumnType[]>;
export type QueryAnyFunctionType = QueryMethodType<QueryResultRowType[]>;
export type QueryFunctionType = QueryMethodType<QueryResultType<QueryResultRowType>>;
export type QueryManyFirstFunctionType = QueryMethodType<QueryResultRowColumnType[]>;
export type QueryManyFunctionType = QueryMethodType<QueryResultRowType[]>;
export type QueryMaybeOneFirstFunctionType = QueryMethodType<QueryResultRowColumnType>;
export type QueryMaybeOneFunctionType = QueryMethodType<QueryResultRowType | null>;
export type QueryOneFirstFunctionType = QueryMethodType<QueryResultRowColumnType>;
export type QueryOneFunctionType = QueryMethodType<QueryResultRowType>;
export type QueryAnyFirstFunctionType = QueryMethodType<QueryResultRowColumnType[]>;
export type QueryAnyFunctionType = QueryMethodType<QueryResultRowType[]>;
export type QueryFunctionType = QueryMethodType<QueryResultType<QueryResultRowType>>;
export type QueryManyFirstFunctionType = QueryMethodType<QueryResultRowColumnType[]>;
export type QueryManyFunctionType = QueryMethodType<QueryResultRowType[]>;
export type QueryMaybeOneFirstFunctionType = QueryMethodType<QueryResultRowColumnType>;
export type QueryMaybeOneFunctionType = QueryMethodType<QueryResultRowType | null>;
export type QueryOneFirstFunctionType = QueryMethodType<QueryResultRowColumnType>;
export type QueryOneFunctionType = QueryMethodType<QueryResultRowType>;
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<QueryType>;
transformRow?: (
queryContext: QueryContextType,
query: QueryType,
row: QueryResultRowType,
fields: FieldType[]
queryContext: QueryContextType,
query: QueryType,
row: QueryResultRowType,
fields: FieldType[]
) => QueryResultRowType;
}

View File

@@ -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
};

View File

@@ -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;