Skip to content

Commit

Permalink
Merge branch 'master' of github.com:gajus/slonik
Browse files Browse the repository at this point in the history
  • Loading branch information
gajus committed Dec 25, 2020
2 parents a14c511 + f855bf4 commit 31fafe4
Show file tree
Hide file tree
Showing 15 changed files with 72 additions and 180 deletions.
1 change: 1 addition & 0 deletions src/binders/bindPool.ts
Expand Up @@ -48,6 +48,7 @@ export const bindPool = (
return {
any: mapConnection('any'),
anyFirst: mapConnection('anyFirst'),
configuration: clientConfiguration,
connect: (connectionHandler) => {
return createConnection(
parentLog,
Expand Down
37 changes: 21 additions & 16 deletions src/factories/createSqlTag.ts
Expand Up @@ -25,11 +25,11 @@ import type {
SqlSqlTokenType,
SqlTaggedTemplateType,
SqlTokenType,
TypeNameIdentifierType,
UnnestSqlTokenType,
ValueExpressionType,
} from '../types';
import {
deepFreeze,
isPrimitiveValueExpression,
isSqlToken,
} from '../utilities';
Expand Down Expand Up @@ -75,7 +75,6 @@ export const createSqlTag = <T = QueryResultRowType>() => {

parameterValues.push(token);
} else if (isSqlToken(token)) {
// @ts-expect-error
const sqlFragment = createSqlTokenSqlFragment(token, parameterValues.length);

rawSql += sqlFragment.sql;
Expand All @@ -91,75 +90,81 @@ export const createSqlTag = <T = QueryResultRowType>() => {
}
}

const query = deepFreeze({
const query: SqlTokenType = {
sql: rawSql,
type: SqlToken,
values: parameterValues,
};

Object.defineProperty(query, 'sql', {
configurable: false,
enumerable: true,
writable: false,
});

return query;
};

sql.array = (
values: ReadonlyArray<PrimitiveValueExpressionType>,
memberType: string | SqlTokenType,
memberType: TypeNameIdentifierType | string | SqlTokenType,
): ArraySqlTokenType => {
return deepFreeze({
return {
memberType,
type: ArrayToken,
values,
});
};
};

sql.binary = (
data: Buffer,
): BinarySqlTokenType => {
return deepFreeze({
return {
data,
type: BinaryToken,
});
};
};

sql.identifier = (
names: ReadonlyArray<string>,
): IdentifierSqlTokenType => {
// @todo Replace `type` with a symbol once Flow adds symbol support
// @see https://github.com/facebook/flow/issues/810
return deepFreeze({
return {
names,
type: IdentifierToken,
});
};
};

sql.json = (
value: SerializableValueType,
): JsonSqlTokenType => {
return deepFreeze({
return {
type: JsonToken,
value,
});
};
};

sql.join = (
members: ReadonlyArray<ValueExpressionType>,
glue: SqlTokenType,
): ListSqlTokenType => {
return deepFreeze({
return {
glue,
members,
type: ListToken,
});
};
};

sql.unnest = (
tuples: ReadonlyArray<ReadonlyArray<PrimitiveValueExpressionType>>,
columnTypes: ReadonlyArray<string>,
): UnnestSqlTokenType => {
return deepFreeze({
return {
columnTypes,
tuples,
type: UnnestToken,
});
};
};

return sql;
Expand Down
45 changes: 16 additions & 29 deletions src/types.ts
Expand Up @@ -79,47 +79,33 @@ export type InternalDatabasePoolType = any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type InternalDatabaseConnectionType = any;

/**
* @property captureStackTrace Dictates whether to capture stack trace before executing query. Middlewares access stack trace through query execution context. (Default: true)
* @property connectionRetryLimit Number of times to retry establishing a new connection. (Default: 3)
* @property connectionTimeout Timeout (in milliseconds) after which an error is raised if connection cannot cannot be established. (Default: 5000)
* @property idleInTransactionSessionTimeout Timeout (in milliseconds) after which idle clients are closed. Use 'DISABLE_TIMEOUT' constant to disable the timeout. (Default: 60000)
* @property idleTimeout Timeout (in milliseconds) after which idle clients are closed. Use 'DISABLE_TIMEOUT' constant to disable the timeout. (Default: 5000)
* @property interceptors An array of [Slonik interceptors](https://github.com/gajus/slonik#slonik-interceptors).
* @property maximumPoolSize Do not allow more than this many connections. Use 'DISABLE_TIMEOUT' constant to disable the timeout. (Default: 10)
* @property preferNativeBindings Uses libpq bindings when `pg-native` module is installed. (Default: true)
* @property statementTimeout Timeout (in milliseconds) after which database is instructed to abort the query. Use 'DISABLE_TIMEOUT' constant to disable the timeout. (Default: 60000)
* @property transactionRetryLimit Number of times a transaction failing with Transaction Rollback class error is retried. (Default: 5)
* @property typeParsers An array of [Slonik type parsers](https://github.com/gajus/slonik#slonik-type-parsers).
*/
export type ClientConfigurationInputType = {
readonly captureStackTrace?: boolean;
readonly connectionRetryLimit?: number;
readonly connectionTimeout?: number | 'DISABLE_TIMEOUT';
readonly idleInTransactionSessionTimeout?: number | 'DISABLE_TIMEOUT';
readonly idleTimeout?: number | 'DISABLE_TIMEOUT';
readonly interceptors?: ReadonlyArray<InterceptorType>;
readonly maximumPoolSize?: number;
readonly preferNativeBindings?: boolean;
readonly statementTimeout?: number | 'DISABLE_TIMEOUT';
readonly transactionRetryLimit?: number;
readonly typeParsers?: ReadonlyArray<TypeParserType>;
};

export type ClientConfigurationType = {
/** Dictates whether to capture stack trace before executing query. Middlewares access stack trace through query execution context. (Default: true) */
readonly captureStackTrace: boolean;
/** Number of times to retry establishing a new connection. (Default: 3) */
readonly connectionRetryLimit: number;
/** Timeout (in milliseconds) after which an error is raised if connection cannot cannot be established. (Default: 5000) */
readonly connectionTimeout: number | 'DISABLE_TIMEOUT';
/** Timeout (in milliseconds) after which idle clients are closed. Use 'DISABLE_TIMEOUT' constant to disable the timeout. (Default: 60000) */
readonly idleInTransactionSessionTimeout: number | 'DISABLE_TIMEOUT';
/** Timeout (in milliseconds) after which idle clients are closed. Use 'DISABLE_TIMEOUT' constant to disable the timeout. (Default: 5000) */
readonly idleTimeout: number | 'DISABLE_TIMEOUT';
/** An array of [Slonik interceptors](https://github.com/gajus/slonik#slonik-interceptors). */
readonly interceptors: ReadonlyArray<InterceptorType>;
/** Do not allow more than this many connections. Use 'DISABLE_TIMEOUT' constant to disable the timeout. (Default: 10) */
readonly maximumPoolSize: number;
/** Uses libpq bindings when `pg-native` module is installed. (Default: true) */
readonly preferNativeBindings: boolean;
/** Timeout (in milliseconds) after which database is instructed to abort the query. Use 'DISABLE_TIMEOUT' constant to disable the timeout. (Default: 60000) */
readonly statementTimeout: number | 'DISABLE_TIMEOUT';
/** Number of times a transaction failing with Transaction Rollback class error is retried. (Default: 5) */
readonly transactionRetryLimit: number;
/** An array of [Slonik type parsers](https://github.com/gajus/slonik#slonik-type-parsers). */
readonly typeParsers: ReadonlyArray<TypeParserType>;
};

export type ClientConfigurationInputType = Partial<ClientConfigurationType>;

export type StreamFunctionType = (
sql: TaggedTemplateLiteralInvocationType,
streamHandler: StreamHandlerType,
Expand Down Expand Up @@ -173,6 +159,7 @@ export type DatabasePoolType = CommonQueryMethodsType & {
readonly getPoolState: () => PoolStateType;
readonly stream: StreamFunctionType;
readonly transaction: <T>(handler: TransactionFunctionType<T>) => Promise<T>;
readonly configuration: ClientConfigurationType;
};

/**
Expand Down Expand Up @@ -255,7 +242,7 @@ export type QueryContextType = {
};

export type ArraySqlTokenType = {
readonly memberType: TypeNameIdentifierType | SqlTokenType;
readonly memberType: TypeNameIdentifierType | string | SqlTokenType;
readonly type: typeof tokens.ArrayToken;
readonly values: ReadonlyArray<ValueExpressionType>;
};
Expand All @@ -272,7 +259,7 @@ export type IdentifierSqlTokenType = {

export type ListSqlTokenType = {
readonly glue: SqlTokenType;
readonly members: ReadonlyArray<SqlTokenType>;
readonly members: ReadonlyArray<ValueExpressionType>;
readonly type: typeof tokens.ListToken;
};

Expand Down
30 changes: 0 additions & 30 deletions src/utilities/deepFreeze.ts

This file was deleted.

3 changes: 0 additions & 3 deletions src/utilities/index.ts
Expand Up @@ -7,9 +7,6 @@ export {
export {
createUlid,
} from './createUlid';
export {
deepFreeze,
} from './deepFreeze';
export {
encodeTupleList,
} from './encodeTupleList';
Expand Down
5 changes: 4 additions & 1 deletion src/utilities/isSqlToken.ts
Expand Up @@ -8,6 +8,9 @@ import {
SqlToken,
UnnestToken,
} from '../tokens';
import {
SqlTokenType,
} from '../types';

const Tokens = [
ArrayToken,
Expand All @@ -21,7 +24,7 @@ const Tokens = [
] as const;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const isSqlToken = (subject: any): subject is typeof Tokens[number] => {
export const isSqlToken = (subject: any): subject is SqlTokenType => {
if (typeof subject !== 'object' || subject === null) {
return false;
}
Expand Down
7 changes: 7 additions & 0 deletions test/dts.ts
Expand Up @@ -30,6 +30,8 @@ import {
createTypeParserPreset,
sql,
QueryResultType,
ClientConfigurationType,
ClientConfigurationInputType,
} from '../src';

const VALUE = 'foo';
Expand All @@ -39,6 +41,11 @@ const connection = createPool('postgres://');
const poolTypes = () => {
const pool = createPool('postgres://localhost');

expectTypeOf<ClientConfigurationType>().toMatchTypeOf<ClientConfigurationInputType>();
expectTypeOf<Partial<ClientConfigurationType>>().toEqualTypeOf<ClientConfigurationInputType>();

expectTypeOf(pool).toHaveProperty('configuration').toEqualTypeOf<ClientConfigurationType>();

const promise = pool.connect(async (conn) => {
const result = await conn.query(sql`SELECT 1`);

Expand Down
22 changes: 22 additions & 0 deletions test/slonik/factories/createPool.ts
@@ -0,0 +1,22 @@
import test from 'ava';
import {
createPool,
} from '../../../src/factories/createPool';

test('pools can be extended', (t) => {
const prodPool = createPool('', {
idleInTransactionSessionTimeout: 'DISABLE_TIMEOUT',
idleTimeout: 5000,
});

const testPool = createPool('', {
...prodPool.configuration,
idleTimeout: 1000,
});

t.is(prodPool.configuration.idleInTransactionSessionTimeout, 'DISABLE_TIMEOUT');
t.is(testPool.configuration.idleInTransactionSessionTimeout, 'DISABLE_TIMEOUT');

t.is(prodPool.configuration.idleTimeout, 5000);
t.is(testPool.configuration.idleTimeout, 1000);
});
9 changes: 0 additions & 9 deletions test/slonik/templateTags/sql/array.ts
Expand Up @@ -86,12 +86,3 @@ test('throws if memberType is not a string or SqlToken of different type than "S

t.is(error.message, 'Unsupported `memberType`. `memberType` must be a string or SqlToken of "SLONIK_TOKEN_SQL" type.');
});

test('the resulting object is immutable', (t) => {
const token = sql.array([1, 2, 3], 'int4');

t.throws(() => {
// @ts-expect-error
token.foo = 'bar';
});
});
9 changes: 0 additions & 9 deletions test/slonik/templateTags/sql/identifier.ts
Expand Up @@ -42,12 +42,3 @@ test('throws if an identifier name array member type is not a string', (t) => {

t.is(error.message, 'Identifier name array member type must be a string.');
});

test('the resulting object is immutable', (t) => {
const token = sql.identifier(['bar', 'baz']);

t.throws(() => {
// @ts-expect-error
token.foo = 'bar';
});
});
9 changes: 0 additions & 9 deletions test/slonik/templateTags/sql/join.ts
Expand Up @@ -111,12 +111,3 @@ test('throws is member is not a SQL token or a primitive value expression', (t)

t.is(error.message, 'Invalid list member type. Must be a SQL token or a primitive value expression.');
});

test('the resulting object is immutable', (t) => {
const token = sql.join([1, 2, 3], sql`, `);

t.throws(() => {
// @ts-expect-error
token.foo = 'bar';
});
});
11 changes: 0 additions & 11 deletions test/slonik/templateTags/sql/json.ts
Expand Up @@ -112,17 +112,6 @@ test('throws if payload cannot be stringified (circular reference)', (t) => {
t.is(error.message, 'JSON payload cannot be stringified.');
});

test('the resulting object is immutable', (t) => {
const token = sql.json({
foo: 'bar',
});

t.throws(() => {
// @ts-expect-error
token.foo = 'bar';
});
});

test('Object types with optional properties are allowed', (t) => {
type TypeWithOptionalProperty = { foo: string; opt?: string };
const testValue: TypeWithOptionalProperty = {foo: 'bar'};
Expand Down
2 changes: 1 addition & 1 deletion test/slonik/templateTags/sql/sql.ts
Expand Up @@ -66,7 +66,7 @@ test('throws if bound an undefined value', (t) => {
t.is(error.message, 'SQL tag cannot be bound an undefined value.');
});

test('the resulting object is immutable', (t) => {
test('the sql property is immutable', (t) => {
const query = sql`SELECT 1`;

t.throws(() => {
Expand Down
9 changes: 0 additions & 9 deletions test/slonik/templateTags/sql/unnest.ts
Expand Up @@ -118,12 +118,3 @@ test('throws if tuple member length does not match column types length', (t) =>

t.is(error.message, 'Column types length must match tuple member length.');
});

test('the resulting object is immutable', (t) => {
const token = sql.unnest([[1, 2, 3], [4, 5, 6]], ['int4', 'int4', 'int4']);

t.throws(() => {
// @ts-expect-error
token.foo = 'bar';
});
});

0 comments on commit 31fafe4

Please sign in to comment.