Comparing version 0.1.1 to 0.1.2
import { RawBuilder } from './raw-builder/raw-builder'; | ||
import { TableArg, FromQueryBuilder } from './query-builder/methods/from-method'; | ||
import { DriverConfig } from './driver/driver-config'; | ||
import { Dialect } from './dialect/dialect'; | ||
/** | ||
@@ -38,2 +40,4 @@ * The main Kysely class. | ||
export declare class Kysely<DB> { | ||
#private; | ||
constructor(config: KyselyConfig); | ||
/** | ||
@@ -265,3 +269,3 @@ * Creates a query builder against the given table/tables. | ||
* ```sql | ||
* select * from "person" where now() - birth_date > interval $0 year | ||
* select * from "person" where now() - birth_date > interval $1 year | ||
* ``` | ||
@@ -286,2 +290,44 @@ * | ||
raw<T = unknown>(sql: string, params?: any[]): RawBuilder<T>; | ||
/** | ||
* Returns true if called inside a Kysely.transaction() callback. | ||
*/ | ||
isTransactionRunning(): boolean; | ||
/** | ||
* Begins a transaction for the async chain started inside the callback. | ||
* | ||
* Any `kysely` query started inside the callback or any method called | ||
* by the callback will automatically use the save transaction. No need to | ||
* pass around a transaction object. This is possible through node's async | ||
* hooks and specifically `AsyncLocalStorage`. | ||
* | ||
* @example | ||
* ```ts | ||
* async function main() { | ||
* await db.transaction(async () => { | ||
* await doStuff() | ||
* }); | ||
* } | ||
* | ||
* async function doStuff() { | ||
* // This automatically uses the correct transasction because `doStuff` was | ||
* // called inside the transaction method that registers the transaction | ||
* // for a shared `AsyncLocalStorage` inside `Kysely`. | ||
* await db.query('person').insert({ first_name: 'Jennifer' }).execute() | ||
* | ||
* return doMoreStuff(); | ||
* } | ||
* | ||
* function doMoreStuff(): Promise<void> { | ||
* // Even this is automatically uses the correct transaction even though | ||
* // we didn't `await` on the method. Node's async hooks work with all | ||
* // possible kinds of async events. | ||
* return db.query('pet').insert({ name: 'Fluffy' }).execute() | ||
* } | ||
* ``` | ||
*/ | ||
transaction<T>(callback: () => Promise<T>): Promise<T>; | ||
destroy(): Promise<void>; | ||
} | ||
export interface KyselyConfig extends DriverConfig { | ||
dialect: 'postgres' | Dialect; | ||
} |
@@ -8,2 +8,5 @@ "use strict"; | ||
const from_method_1 = require("./query-builder/methods/from-method"); | ||
const postgres_dialect_1 = require("./dialect/postgres/postgres-dialect"); | ||
const transactional_connection_provider_1 = require("./driver/transactional-connection-provider"); | ||
const async_hooks_1 = require("async_hooks"); | ||
/** | ||
@@ -44,5 +47,19 @@ * The main Kysely class. | ||
class Kysely { | ||
constructor(config) { | ||
this.#transactions = new async_hooks_1.AsyncLocalStorage(); | ||
const dialect = createDialect(config); | ||
this.#driver = dialect.createDriver(config); | ||
this.#compiler = dialect.createQueryCompiler(); | ||
} | ||
#driver; | ||
#compiler; | ||
#transactions; | ||
query(from) { | ||
const query = new query_builder_1.QueryBuilder(); | ||
return new query_builder_1.QueryBuilder(query_node_1.cloneQueryNodeWithFroms(query.toOperationNode(), from_method_1.parseFromArgs(query, from))); | ||
const query = new query_builder_1.QueryBuilder({ queryNode: query_node_1.createQueryNode() }); | ||
const connectionProvider = new transactional_connection_provider_1.TransactionalConnectionProvider(this.#driver, this.#transactions); | ||
return new query_builder_1.QueryBuilder({ | ||
compiler: this.#compiler, | ||
connectionProvider, | ||
queryNode: query_node_1.cloneQueryNodeWithFroms(query.toOperationNode(), from_method_1.parseFromArgs(query, from)), | ||
}); | ||
} | ||
@@ -148,3 +165,3 @@ /** | ||
* ```sql | ||
* select * from "person" where now() - birth_date > interval $0 year | ||
* select * from "person" where now() - birth_date > interval $1 year | ||
* ``` | ||
@@ -171,3 +188,82 @@ * | ||
} | ||
/** | ||
* Returns true if called inside a Kysely.transaction() callback. | ||
*/ | ||
isTransactionRunning() { | ||
return !!this.#transactions.getStore(); | ||
} | ||
/** | ||
* Begins a transaction for the async chain started inside the callback. | ||
* | ||
* Any `kysely` query started inside the callback or any method called | ||
* by the callback will automatically use the save transaction. No need to | ||
* pass around a transaction object. This is possible through node's async | ||
* hooks and specifically `AsyncLocalStorage`. | ||
* | ||
* @example | ||
* ```ts | ||
* async function main() { | ||
* await db.transaction(async () => { | ||
* await doStuff() | ||
* }); | ||
* } | ||
* | ||
* async function doStuff() { | ||
* // This automatically uses the correct transasction because `doStuff` was | ||
* // called inside the transaction method that registers the transaction | ||
* // for a shared `AsyncLocalStorage` inside `Kysely`. | ||
* await db.query('person').insert({ first_name: 'Jennifer' }).execute() | ||
* | ||
* return doMoreStuff(); | ||
* } | ||
* | ||
* function doMoreStuff(): Promise<void> { | ||
* // Even this is automatically uses the correct transaction even though | ||
* // we didn't `await` on the method. Node's async hooks work with all | ||
* // possible kinds of async events. | ||
* return db.query('pet').insert({ name: 'Fluffy' }).execute() | ||
* } | ||
* ``` | ||
*/ | ||
async transaction(callback) { | ||
let connection = null; | ||
if (this.isTransactionRunning()) { | ||
throw new Error('You attempted to call Kysely.transaction() inside an existing transaction. Nested transactions are not yet supported. See the Kysely.isTransactionRunning() method.'); | ||
} | ||
try { | ||
await this.#driver.ensureInit(); | ||
connection = await this.#driver.acquireConnection(); | ||
await connection.execute({ sql: 'BEGIN', bindings: [] }); | ||
return await this.#transactions.run(connection, () => { | ||
return callback(); | ||
}); | ||
} | ||
catch (error) { | ||
if (connection) { | ||
await connection.execute({ sql: 'ROLLBACK', bindings: [] }); | ||
} | ||
throw error; | ||
} | ||
finally { | ||
if (connection) { | ||
await this.#driver.releaseConnection(connection); | ||
} | ||
} | ||
} | ||
async destroy() { | ||
await this.#driver.ensureDestroy(); | ||
this.#transactions.disable(); | ||
} | ||
} | ||
exports.Kysely = Kysely; | ||
function createDialect(config) { | ||
if (typeof config.dialect !== 'string') { | ||
return config.dialect; | ||
} | ||
else if (config.dialect === 'postgres') { | ||
return new postgres_dialect_1.PostgresDialect(); | ||
} | ||
else { | ||
throw new Error(`unknown dialect ${config.dialect}`); | ||
} | ||
} |
@@ -22,4 +22,8 @@ "use strict"; | ||
subQuery(table) { | ||
const query = new query_builder_1.QueryBuilder(); | ||
return new query_builder_1.QueryBuilder(query_node_1.cloneQueryNodeWithFroms(query.toOperationNode(), from_method_1.parseFromArgs(query, table))); | ||
const query = new query_builder_1.QueryBuilder({ | ||
queryNode: query_node_1.createQueryNode(), | ||
}); | ||
return new query_builder_1.QueryBuilder({ | ||
queryNode: query_node_1.cloneQueryNodeWithFroms(query.toOperationNode(), from_method_1.parseFromArgs(query, table)), | ||
}); | ||
} | ||
@@ -30,3 +34,5 @@ /** | ||
on(lhs, op, rhs) { | ||
const query = new query_builder_1.QueryBuilder(); | ||
const query = new query_builder_1.QueryBuilder({ | ||
queryNode: query_node_1.createQueryNode(), | ||
}); | ||
return new JoinBuilder(join_node_1.cloneJoinNodeWithOn(this.#joinNode, 'and', filter_method_1.parseFilterReferenceArgs(query, lhs, op, rhs))); | ||
@@ -33,0 +39,0 @@ } |
@@ -15,2 +15,3 @@ "use strict"; | ||
const parens_node_1 = require("../../operation-node/parens-node"); | ||
const query_node_1 = require("../../operation-node/query-node"); | ||
const OPERATOR_WHITELIST = [ | ||
@@ -133,3 +134,6 @@ '=', | ||
} | ||
const query = grouper(new query_builder_1.QueryBuilder()); | ||
const inputQuery = new query_builder_1.QueryBuilder({ | ||
queryNode: query_node_1.createQueryNode(), | ||
}); | ||
const query = grouper(inputQuery); | ||
const where = query.toOperationNode().where; | ||
@@ -136,0 +140,0 @@ if (!where) { |
import { JoinNode, JoinType } from '../../operation-node/join-node'; | ||
import { AnyColumn, AnyColumnWithTable, AnyQueryBuilder } from '../type-utils'; | ||
import { FromArgDatabaseType, ExtractAliasesFromFromArg } from './from-method'; | ||
import { JoinBuilder } from '../join-builder'; | ||
export declare type JoinReferenceArg<DB, TB extends keyof DB, F> = AnyColumn<FromArgDatabaseType<DB, F>, TB | ExtractAliasesFromFromArg<DB, F>> | AnyColumnWithTable<FromArgDatabaseType<DB, F>, TB | ExtractAliasesFromFromArg<DB, F>>; | ||
export declare type JoinCallbackArg<DB, TB extends keyof DB, F> = (join: JoinBuilder<FromArgDatabaseType<DB, F>, TB | ExtractAliasesFromFromArg<DB, F>>) => JoinBuilder<any, any>; | ||
export declare function parseJoinArgs(query: AnyQueryBuilder, joinType: JoinType, args: any[]): JoinNode; |
@@ -7,4 +7,8 @@ "use strict"; | ||
const filter_method_1 = require("./filter-method"); | ||
const join_builder_1 = require("../join-builder"); | ||
function parseJoinArgs(query, joinType, args) { | ||
if (args.length === 3) { | ||
if (args.length === 2) { | ||
return parseCallbackJoin(query, joinType, args[0], args[1]); | ||
} | ||
else if (args.length === 3) { | ||
return parseSingleOnJoin(query, joinType, args[0], args[1], args[2]); | ||
@@ -17,2 +21,7 @@ } | ||
exports.parseJoinArgs = parseJoinArgs; | ||
function parseCallbackJoin(query, joinType, from, callback) { | ||
const tableNode = from_method_1.parseFromArg(query, from); | ||
const joinBuilder = callback(new join_builder_1.JoinBuilder(join_node_1.createJoinNode(joinType, tableNode))); | ||
return joinBuilder.toOperationNode(); | ||
} | ||
function parseSingleOnJoin(query, joinType, from, lhsColumn, rhsColumn) { | ||
@@ -19,0 +28,0 @@ const tableNode = from_method_1.parseFromArg(query, from); |
@@ -5,3 +5,3 @@ import { AliasNode } from '../operation-node/alias-node'; | ||
import { QueryCompiler } from '../query-compiler/query-compiler'; | ||
import { JoinReferenceArg } from './methods/join-method'; | ||
import { JoinCallbackArg, JoinReferenceArg } from './methods/join-method'; | ||
import { QueryNode } from '../operation-node/query-node'; | ||
@@ -11,2 +11,3 @@ import { TableArg, FromQueryBuilder } from './methods/from-method'; | ||
import { FilterReferenceArg, FilterValueArg, ExistsFilterArg, FilterOperatorArg } from './methods/filter-method'; | ||
import { ConnectionProvider } from '../driver/connection-provider'; | ||
/** | ||
@@ -29,3 +30,3 @@ * The main query builder class. | ||
#private; | ||
constructor(queryNode?: QueryNode); | ||
constructor({ queryNode, compiler, connectionProvider }: QueryBuilderArgs); | ||
/** | ||
@@ -76,2 +77,120 @@ * Creates a subquery. | ||
* Adds a `where` clause to the query. | ||
* | ||
* Also see {@link QueryBuilder.whereExists | whereExists} and {@link QueryBuilder.whereRef | whereRef} | ||
* | ||
* @example | ||
* Find a row by column value: | ||
* | ||
* ```ts | ||
* db.query('person') | ||
* .where('id', '=', 100) | ||
* .selectAll() | ||
* ``` | ||
* | ||
* The generated SQL (postgresql): | ||
* | ||
* ```sql | ||
* select * from "person" where "id" = $1 | ||
* ``` | ||
* | ||
* @example | ||
* Operator can be any supported operator or if the typings don't support it | ||
* you can always use `db.raw('your operator')`. | ||
* | ||
* ```ts | ||
* db.query('person') | ||
* .where('id', '>', 100) | ||
* .selectAll() | ||
* ``` | ||
* | ||
* The generated SQL (postgresql): | ||
* | ||
* ```sql | ||
* select * from "person" where "id" > $1 | ||
* ``` | ||
* | ||
* @example | ||
* A `where in` query. The first argument can contain | ||
* the table name, but it's not mandatory. | ||
* | ||
* ```ts | ||
* db.query('person') | ||
* .where('person.id', 'in', [100, 200, 300]) | ||
* .selectAll() | ||
* ``` | ||
* | ||
* The generated SQL (postgresql): | ||
* | ||
* ```sql | ||
* select * from "person" where "id" in ($1, $2, $3) | ||
* ``` | ||
* | ||
* @example | ||
* Both the first and third argument can also be a subquery. | ||
* A subquery is defined by passing a function: | ||
* | ||
* ```ts | ||
* db.query('person') | ||
* .where( | ||
* (qb) => qb.subQuery('pet') | ||
* .select('pet.id') | ||
* .whereRef('pet.owner_id', '=', 'person.id'), | ||
* 'in', | ||
* [100, 200, 300] | ||
* ) | ||
* .selectAll() | ||
* ``` | ||
* | ||
* The generated SQL (postgresql): | ||
* | ||
* ```sql | ||
* select * | ||
* from "person" | ||
* where ( | ||
* select "pet"."id" | ||
* from "pet" | ||
* where "pet"."owner_id" = "person"."id" | ||
* ) in ($1, $2, $3) | ||
* ``` | ||
* | ||
* @example | ||
* If everything else fails, you can always pass {@link Kysely.raw | raw} | ||
* as any of the arguments, including the operator: | ||
* | ||
* ```ts | ||
* db.query('person') | ||
* .where( | ||
* db.raw('coalesce(first_name, last_name)'), | ||
* 'like', | ||
* '%' + name + '%', | ||
* ) | ||
* .selectAll() | ||
* ``` | ||
* | ||
* The generated SQL (postgresql): | ||
* | ||
* ```sql | ||
* select * | ||
* from "person" | ||
* where coalesce(first_name, last_name) like $1 | ||
* ``` | ||
* | ||
* @example | ||
* If you only pass one function argument to this method, it can be | ||
* used to create parentheses around other where clauses: | ||
* | ||
* ```ts | ||
* db.query('person') | ||
* .selectAll() | ||
* .where((qb) => qb | ||
* .where('id', '=', 1) | ||
* .orWhere('id', '=', 2) | ||
* ) | ||
* ``` | ||
* | ||
* The generated SQL (postgresql): | ||
* | ||
* ```sql | ||
* select * from "person" (where "id" = 1 or "id" = 2) | ||
* ``` | ||
*/ | ||
@@ -89,3 +208,3 @@ where(lhs: FilterReferenceArg<DB, TB, O>, op: FilterOperatorArg, rhs: FilterValueArg<DB, TB, O>): QueryBuilder<DB, TB, O>; | ||
* It's often necessary to wrap `or where` clauses in parentheses to control | ||
* the precendence. You can use the one argument version of the `where` method | ||
* precendence. You can use the one argument version of the `where` method | ||
* for that. See the examples. | ||
@@ -226,2 +345,3 @@ * | ||
innerJoin<F extends TableArg<DB, TB, O>, K1 extends JoinReferenceArg<DB, TB, F>, K2 extends JoinReferenceArg<DB, TB, F>>(table: F, k1: K1, k2: K2): FromQueryBuilder<DB, TB, O, F>; | ||
innerJoin<F extends TableArg<DB, TB, O>, FN extends JoinCallbackArg<DB, TB, F>>(table: F, callback: FN): FromQueryBuilder<DB, TB, O, F>; | ||
/** | ||
@@ -232,6 +352,11 @@ * | ||
toOperationNode(): QueryNode; | ||
compile(compiler: QueryCompiler): CompiledQuery; | ||
castTo<T>(): QueryBuilder<DB, TB, T>; | ||
compile(): CompiledQuery; | ||
execute(): Promise<O[]>; | ||
} | ||
export interface QueryBuilderArgs { | ||
queryNode: QueryNode; | ||
compiler?: QueryCompiler; | ||
connectionProvider?: ConnectionProvider; | ||
} | ||
/** | ||
@@ -238,0 +363,0 @@ * {@link QueryBuilder} with an alias. The result of calling {@link QueryBuilder.as}. |
@@ -26,11 +26,21 @@ "use strict"; | ||
class QueryBuilder { | ||
constructor(queryNode = query_node_1.createQueryNode()) { | ||
constructor({ queryNode, compiler, connectionProvider }) { | ||
this.#queryNode = queryNode; | ||
this.#compiler = compiler; | ||
this.#connectionProvider = connectionProvider; | ||
} | ||
#queryNode; | ||
#compiler; | ||
#connectionProvider; | ||
subQuery(table) { | ||
return new QueryBuilder(query_node_1.cloneQueryNodeWithFroms(query_node_1.createQueryNode(), from_method_1.parseFromArgs(this, table))); | ||
return new QueryBuilder({ | ||
queryNode: query_node_1.cloneQueryNodeWithFroms(query_node_1.createQueryNode(), from_method_1.parseFromArgs(this, table)), | ||
}); | ||
} | ||
where(...args) { | ||
return new QueryBuilder(query_node_1.cloneQueryNodeWithWhere(this.#queryNode, 'and', filter_method_1.parseFilterArgs(this, args))); | ||
return new QueryBuilder({ | ||
compiler: this.#compiler, | ||
connectionProvider: this.#connectionProvider, | ||
queryNode: query_node_1.cloneQueryNodeWithWhere(this.#queryNode, 'and', filter_method_1.parseFilterArgs(this, args)), | ||
}); | ||
} | ||
@@ -41,6 +51,14 @@ /** | ||
whereRef(lhs, op, rhs) { | ||
return new QueryBuilder(query_node_1.cloneQueryNodeWithWhere(this.#queryNode, 'and', filter_method_1.parseFilterReferenceArgs(this, lhs, op, rhs))); | ||
return new QueryBuilder({ | ||
compiler: this.#compiler, | ||
connectionProvider: this.#connectionProvider, | ||
queryNode: query_node_1.cloneQueryNodeWithWhere(this.#queryNode, 'and', filter_method_1.parseFilterReferenceArgs(this, lhs, op, rhs)), | ||
}); | ||
} | ||
orWhere(...args) { | ||
return new QueryBuilder(query_node_1.cloneQueryNodeWithWhere(this.#queryNode, 'or', filter_method_1.parseFilterArgs(this, args))); | ||
return new QueryBuilder({ | ||
compiler: this.#compiler, | ||
connectionProvider: this.#connectionProvider, | ||
queryNode: query_node_1.cloneQueryNodeWithWhere(this.#queryNode, 'or', filter_method_1.parseFilterArgs(this, args)), | ||
}); | ||
} | ||
@@ -51,3 +69,7 @@ /** | ||
orWhereRef(lhs, op, rhs) { | ||
return new QueryBuilder(query_node_1.cloneQueryNodeWithWhere(this.#queryNode, 'or', filter_method_1.parseFilterReferenceArgs(this, lhs, op, rhs))); | ||
return new QueryBuilder({ | ||
compiler: this.#compiler, | ||
connectionProvider: this.#connectionProvider, | ||
queryNode: query_node_1.cloneQueryNodeWithWhere(this.#queryNode, 'or', filter_method_1.parseFilterReferenceArgs(this, lhs, op, rhs)), | ||
}); | ||
} | ||
@@ -58,3 +80,7 @@ /** | ||
whereExists(arg) { | ||
return new QueryBuilder(query_node_1.cloneQueryNodeWithWhere(this.#queryNode, 'and', filter_method_1.parseExistsFilterArgs(this, 'exists', arg))); | ||
return new QueryBuilder({ | ||
compiler: this.#compiler, | ||
connectionProvider: this.#connectionProvider, | ||
queryNode: query_node_1.cloneQueryNodeWithWhere(this.#queryNode, 'and', filter_method_1.parseExistsFilterArgs(this, 'exists', arg)), | ||
}); | ||
} | ||
@@ -65,3 +91,7 @@ /** | ||
whereNotExists(arg) { | ||
return new QueryBuilder(query_node_1.cloneQueryNodeWithWhere(this.#queryNode, 'and', filter_method_1.parseExistsFilterArgs(this, 'not exists', arg))); | ||
return new QueryBuilder({ | ||
compiler: this.#compiler, | ||
connectionProvider: this.#connectionProvider, | ||
queryNode: query_node_1.cloneQueryNodeWithWhere(this.#queryNode, 'and', filter_method_1.parseExistsFilterArgs(this, 'not exists', arg)), | ||
}); | ||
} | ||
@@ -72,3 +102,7 @@ /** | ||
orWhereExists(arg) { | ||
return new QueryBuilder(query_node_1.cloneQueryNodeWithWhere(this.#queryNode, 'or', filter_method_1.parseExistsFilterArgs(this, 'exists', arg))); | ||
return new QueryBuilder({ | ||
compiler: this.#compiler, | ||
connectionProvider: this.#connectionProvider, | ||
queryNode: query_node_1.cloneQueryNodeWithWhere(this.#queryNode, 'or', filter_method_1.parseExistsFilterArgs(this, 'exists', arg)), | ||
}); | ||
} | ||
@@ -79,9 +113,21 @@ /** | ||
orWhereNotExists(arg) { | ||
return new QueryBuilder(query_node_1.cloneQueryNodeWithWhere(this.#queryNode, 'or', filter_method_1.parseExistsFilterArgs(this, 'not exists', arg))); | ||
return new QueryBuilder({ | ||
compiler: this.#compiler, | ||
connectionProvider: this.#connectionProvider, | ||
queryNode: query_node_1.cloneQueryNodeWithWhere(this.#queryNode, 'or', filter_method_1.parseExistsFilterArgs(this, 'not exists', arg)), | ||
}); | ||
} | ||
select(selection) { | ||
return new QueryBuilder(query_node_1.cloneQueryNodeWithSelections(this.#queryNode, select_method_1.parseSelectArgs(this, selection))); | ||
return new QueryBuilder({ | ||
compiler: this.#compiler, | ||
connectionProvider: this.#connectionProvider, | ||
queryNode: query_node_1.cloneQueryNodeWithSelections(this.#queryNode, select_method_1.parseSelectArgs(this, selection)), | ||
}); | ||
} | ||
distinctOn(selection) { | ||
return new QueryBuilder(query_node_1.cloneQueryNodeWithDistinctOnSelections(this.#queryNode, select_method_1.parseSelectArgs(this, selection))); | ||
return new QueryBuilder({ | ||
compiler: this.#compiler, | ||
connectionProvider: this.#connectionProvider, | ||
queryNode: query_node_1.cloneQueryNodeWithDistinctOnSelections(this.#queryNode, select_method_1.parseSelectArgs(this, selection)), | ||
}); | ||
} | ||
@@ -92,3 +138,7 @@ /** | ||
distinct() { | ||
return new QueryBuilder(query_node_1.cloneQueryNodeWithSelectModifier(this.#queryNode, 'Distinct')); | ||
return new QueryBuilder({ | ||
compiler: this.#compiler, | ||
connectionProvider: this.#connectionProvider, | ||
queryNode: query_node_1.cloneQueryNodeWithSelectModifier(this.#queryNode, 'Distinct'), | ||
}); | ||
} | ||
@@ -99,3 +149,7 @@ /** | ||
forUpdate() { | ||
return new QueryBuilder(query_node_1.cloneQueryNodeWithModifier(this.#queryNode, 'ForUpdate')); | ||
return new QueryBuilder({ | ||
compiler: this.#compiler, | ||
connectionProvider: this.#connectionProvider, | ||
queryNode: query_node_1.cloneQueryNodeWithModifier(this.#queryNode, 'ForUpdate'), | ||
}); | ||
} | ||
@@ -106,3 +160,7 @@ /** | ||
forShare() { | ||
return new QueryBuilder(query_node_1.cloneQueryNodeWithModifier(this.#queryNode, 'ForShare')); | ||
return new QueryBuilder({ | ||
compiler: this.#compiler, | ||
connectionProvider: this.#connectionProvider, | ||
queryNode: query_node_1.cloneQueryNodeWithModifier(this.#queryNode, 'ForShare'), | ||
}); | ||
} | ||
@@ -113,3 +171,7 @@ /** | ||
forKeyShare() { | ||
return new QueryBuilder(query_node_1.cloneQueryNodeWithModifier(this.#queryNode, 'ForKeyShare')); | ||
return new QueryBuilder({ | ||
compiler: this.#compiler, | ||
connectionProvider: this.#connectionProvider, | ||
queryNode: query_node_1.cloneQueryNodeWithModifier(this.#queryNode, 'ForKeyShare'), | ||
}); | ||
} | ||
@@ -120,3 +182,7 @@ /** | ||
forNoKeyUpdate() { | ||
return new QueryBuilder(query_node_1.cloneQueryNodeWithModifier(this.#queryNode, 'ForNoKeyUpdate')); | ||
return new QueryBuilder({ | ||
compiler: this.#compiler, | ||
connectionProvider: this.#connectionProvider, | ||
queryNode: query_node_1.cloneQueryNodeWithModifier(this.#queryNode, 'ForNoKeyUpdate'), | ||
}); | ||
} | ||
@@ -127,3 +193,7 @@ /** | ||
skipLocked() { | ||
return new QueryBuilder(query_node_1.cloneQueryNodeWithModifier(this.#queryNode, 'SkipLocked')); | ||
return new QueryBuilder({ | ||
compiler: this.#compiler, | ||
connectionProvider: this.#connectionProvider, | ||
queryNode: query_node_1.cloneQueryNodeWithModifier(this.#queryNode, 'SkipLocked'), | ||
}); | ||
} | ||
@@ -134,9 +204,21 @@ /** | ||
noWait() { | ||
return new QueryBuilder(query_node_1.cloneQueryNodeWithModifier(this.#queryNode, 'NoWait')); | ||
return new QueryBuilder({ | ||
compiler: this.#compiler, | ||
connectionProvider: this.#connectionProvider, | ||
queryNode: query_node_1.cloneQueryNodeWithModifier(this.#queryNode, 'NoWait'), | ||
}); | ||
} | ||
selectAll(table) { | ||
return new QueryBuilder(query_node_1.cloneQueryNodeWithSelections(this.#queryNode, select_method_1.parseSelectAllArgs(table))); | ||
return new QueryBuilder({ | ||
compiler: this.#compiler, | ||
connectionProvider: this.#connectionProvider, | ||
queryNode: query_node_1.cloneQueryNodeWithSelections(this.#queryNode, select_method_1.parseSelectAllArgs(table)), | ||
}); | ||
} | ||
innerJoin(...args) { | ||
return new QueryBuilder(query_node_1.cloneQueryNodeWithJoin(this.#queryNode, join_method_1.parseJoinArgs(this, 'InnerJoin', args))); | ||
return new QueryBuilder({ | ||
compiler: this.#compiler, | ||
connectionProvider: this.#connectionProvider, | ||
queryNode: query_node_1.cloneQueryNodeWithJoin(this.#queryNode, join_method_1.parseJoinArgs(this, 'InnerJoin', args)), | ||
}); | ||
} | ||
@@ -152,10 +234,29 @@ /** | ||
} | ||
compile(compiler) { | ||
return compiler.compile(this.#queryNode); | ||
} | ||
castTo() { | ||
return new QueryBuilder(this.#queryNode); | ||
return new QueryBuilder({ | ||
compiler: this.#compiler, | ||
connectionProvider: this.#connectionProvider, | ||
queryNode: this.#queryNode, | ||
}); | ||
} | ||
compile() { | ||
if (!this.#compiler) { | ||
throw new Error(`this query cannot be compiled to SQL`); | ||
} | ||
return this.#compiler.compile(this.#queryNode); | ||
} | ||
async execute() { | ||
return [{}]; | ||
if (!this.#connectionProvider) { | ||
throw new Error(`this query cannot be executed`); | ||
} | ||
let connection; | ||
try { | ||
connection = await this.#connectionProvider.acquireConnection(); | ||
return await connection.execute(this.compile()); | ||
} | ||
finally { | ||
if (connection) { | ||
await this.#connectionProvider.releaseConnection(connection); | ||
} | ||
} | ||
} | ||
@@ -162,0 +263,0 @@ } |
@@ -40,3 +40,3 @@ import { AliasNode } from '../operation-node/alias-node'; | ||
protected visitIdentifier(node: IdentifierNode): void; | ||
protected visitUnwrappedIdentifier(node: IdentifierNode): void; | ||
protected compileUnwrappedIdentifier(node: IdentifierNode): void; | ||
protected visitFilter(node: FilterNode): void; | ||
@@ -53,5 +53,6 @@ protected visitAnd(node: AndNode): void; | ||
protected visitTable(node: TableNode): void; | ||
protected appendIdentifierWrapper(): void; | ||
protected appendLeftIdentifierWrapper(): void; | ||
protected appendRightIdentifierWrapper(): void; | ||
protected append(str: string): void; | ||
protected appendValue(value: any): void; | ||
} |
@@ -113,7 +113,7 @@ "use strict"; | ||
visitIdentifier(node) { | ||
this.appendIdentifierWrapper(); | ||
this.visitUnwrappedIdentifier(node); | ||
this.appendIdentifierWrapper(); | ||
this.appendLeftIdentifierWrapper(); | ||
this.compileUnwrappedIdentifier(node); | ||
this.appendRightIdentifierWrapper(); | ||
} | ||
visitUnwrappedIdentifier(node) { | ||
compileUnwrappedIdentifier(node) { | ||
this.append(node.identifier); | ||
@@ -191,5 +191,8 @@ } | ||
} | ||
appendIdentifierWrapper() { | ||
appendLeftIdentifierWrapper() { | ||
this.append('"'); | ||
} | ||
appendRightIdentifierWrapper() { | ||
this.append('"'); | ||
} | ||
append(str) { | ||
@@ -196,0 +199,0 @@ this.#sqlFragments.push(str); |
{ | ||
"name": "kysely", | ||
"version": "0.1.1", | ||
"version": "0.1.2", | ||
"description": "Type safe SQL query builder", | ||
@@ -19,4 +19,6 @@ "main": "lib/index.js", | ||
"@types/node": "^14.14.31", | ||
"@types/pg": "^7.14.11", | ||
"chai": "^4.3.0", | ||
"mocha": "^8.3.0", | ||
"pg": "^8.5.1", | ||
"prettier": "^2.2.1", | ||
@@ -23,0 +25,0 @@ "ts-node": "^9.1.1", |
# Kysely | ||
A type safe typescript SQL query builder for node.js. | ||
A type-safe and autocompletion-friendly typescript SQL query builder for node.js. Heavily inspired by | ||
[knex](http://knexjs.org/) but not intended to be a clone. | ||
![](https://github.com/koskimas/kysely/blob/master/assets/demo.gif) | ||
Kysely's typescript typings only allow you to join tables that are available in the database and refer to | ||
columns of the tables that are joined to the query. The result type also always only contains the selected | ||
columns with correct types and aliases. This allows tools like vscode autocomplete to make your life so | ||
much easier. | ||
Kysely's typings only allow you to use tables that are available in the database and refer to | ||
columns of the tables that are joined to the query. The result type always only contains the selected | ||
columns with correct types and aliases. This allows tools like vscode autocompletion to make your life | ||
so much easier. | ||
As you can see in the gif above, through the pure magic of modern typescript, Kysely is even able to parse | ||
the alias given to `pet.name` and add a column `pet_name` to the result row type. Kysely is also able to | ||
retain and parse columns and types from selected subqueries, joined subqueries, with statements and | ||
pretty much anything you can think of. Typescript is always there for you to immediately tell what | ||
kind of query you can build and offer completions. | ||
infer colum names and types from selected subqueries, joined subqueries, `with` statements and pretty much | ||
anything you can think of. Typescript is always there for you to tell what kind of query you can build and | ||
offer completions. | ||
@@ -33,3 +34,2 @@ Of course there are cases where things cannot be typed at compile time, and Kysely offers escape | ||
last_name: string | ||
age: number | ||
gender: 'male' | 'female' | 'other' | ||
@@ -58,3 +58,7 @@ } | ||
// You'd create one of these when you start your app. | ||
const db = new Kysely<Database>() | ||
const db = new Kysely<Database>({ | ||
dialect: 'postgres', | ||
host: 'localhost', | ||
database: 'kysely_test', | ||
}) | ||
@@ -65,3 +69,4 @@ async function demo() { | ||
.innerJoin('pet', 'pet.owner_id', 'person.id') | ||
.select(['person.first_name', 'pet.name as pet_name']) | ||
.select(['first_name', 'pet.name as pet_name']) | ||
.where('person.id', '=', 1) | ||
.execute() | ||
@@ -71,3 +76,2 @@ | ||
} | ||
``` | ||
@@ -77,3 +81,15 @@ | ||
This whole library is still just a proof of concept, and you can't yet start using it for anything | ||
serious, but the concept is pretty much proven! Typescript is amazing! | ||
This whole library is still just a proof of concept and you can't yet start using it for anything | ||
serious. Only a small subset of postgres dialect is implemented. | ||
However, I'd say the concept is pretty much proven! Typescript is amazing! Let me know if this is something | ||
you'd use and I'll continue working on this. | ||
# Why not just contribute to knex | ||
Kysely is very similar to knex, but it also attempts to fix things that I personally find not-so-good | ||
in knex. Bringing the type system and the changes to knex would mean very significantly breaking the | ||
backwards compatibility. That's not possible at this point of the project. Knex was also originally | ||
written for javascript and the typescript typings were added afterwards. That always leads to | ||
compromises in the types. Designing a library for typescript from the ground up produces much | ||
better and simpler types. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
132955
92
3431
91
0
11
2