ts-postgres
Advanced tools
Comparing version
@@ -96,4 +96,14 @@ /// <reference types="node" /> | ||
private startup; | ||
private listen; | ||
private receive; | ||
/** Connect to the database. | ||
* | ||
* @remarks | ||
* Don't forget to close the connection using {@link end} before exiting. | ||
* | ||
* @returns The connection encryption status. | ||
*/ | ||
connect(): Promise<boolean>; | ||
/** End the database connection. | ||
* | ||
*/ | ||
end(): Promise<void>; | ||
@@ -106,6 +116,20 @@ on(event: 'connect', callback: Callback<Connect>): void; | ||
on(event: 'notice', callback: Callback<ClientNotice>): void; | ||
prepare<T = ResultRecord>(text: string, name?: string, types?: DataType[]): Promise<PreparedStatement<T>>; | ||
query<T = ResultRecord>(text: string, values?: any[], types?: DataType[], format?: DataFormat | DataFormat[], streams?: Record<string, Writable>): ResultIterator<T>; | ||
/** Prepare a statement for later execution. | ||
* | ||
* @returns A prepared statement object. | ||
*/ | ||
prepare<T = ResultRecord>(text: Query | string): Promise<PreparedStatement<T>>; | ||
/** | ||
* Send a query to the database. | ||
* | ||
* The query string is given as the first argument, or pass a {@link Query} | ||
* object which provides more control. | ||
* | ||
* @param text - The query string, or pass a {@link Query} | ||
* object which provides more control (including streaming values into a socket). | ||
* @param values - The query parameters, corresponding to $1, $2, etc. | ||
* @returns A promise for the query results. | ||
*/ | ||
query<T = ResultRecord>(text: Query | string, values?: any[]): ResultIterator<T>; | ||
private bindAndExecute; | ||
execute<T = ResultRecord>(query: Query): ResultIterator<T>; | ||
private handleError; | ||
@@ -115,3 +139,3 @@ private send; | ||
private parseError; | ||
private receive; | ||
private handle; | ||
} |
@@ -11,3 +11,2 @@ "use strict"; | ||
const queue_1 = require("./queue"); | ||
const query_1 = require("./query"); | ||
const tls_1 = require("tls"); | ||
@@ -137,3 +136,3 @@ const result_1 = require("./result"); | ||
writer.startup(settings); | ||
this.listen(); | ||
this.receive(); | ||
this.sendUsing(writer); | ||
@@ -175,7 +174,7 @@ }; | ||
writer.startup(settings); | ||
this.listen(); | ||
this.receive(); | ||
} | ||
this.sendUsing(writer); | ||
} | ||
listen() { | ||
receive() { | ||
let buffer = null; | ||
@@ -204,3 +203,3 @@ let offset = 0; | ||
try { | ||
const read = this.receive(buffer, offset, size); | ||
const read = this.handle(buffer, offset, size); | ||
offset += read; | ||
@@ -233,2 +232,9 @@ remaining = size - read; | ||
} | ||
/** Connect to the database. | ||
* | ||
* @remarks | ||
* Don't forget to close the connection using {@link end} before exiting. | ||
* | ||
* @returns The connection encryption status. | ||
*/ | ||
connect() { | ||
@@ -267,2 +273,5 @@ if (this.connecting) { | ||
} | ||
/** End the database connection. | ||
* | ||
*/ | ||
end() { | ||
@@ -318,4 +327,9 @@ if (this.ending) { | ||
} | ||
prepare(text, name, types) { | ||
const providedNameOrGenerated = name || ((this.config.preparedStatementPrefix || | ||
/** Prepare a statement for later execution. | ||
* | ||
* @returns A prepared statement object. | ||
*/ | ||
prepare(text) { | ||
const query = typeof text === 'string' ? { text } : text; | ||
const providedNameOrGenerated = query.name || ((this.config.preparedStatementPrefix || | ||
defaults.preparedStatementPrefix) + (this.nextPreparedStatementId++)); | ||
@@ -325,3 +339,3 @@ return new Promise((resolve, reject) => { | ||
this.errorHandlerQueue.push(errorHandler); | ||
this.writer.parse(providedNameOrGenerated, text, types || []); | ||
this.writer.parse(providedNameOrGenerated, query.text, query.types || []); | ||
this.writer.describe(providedNameOrGenerated, 'S'); | ||
@@ -343,3 +357,3 @@ this.preFlightQueue.push({ | ||
execute: (values, portal, format, streams) => { | ||
const result = (0, result_1.makeResult)(); | ||
const result = (0, result_1.makeResult)(query === null || query === void 0 ? void 0 : query.transform); | ||
result.nameHandler(description.names); | ||
@@ -355,7 +369,7 @@ const info = { | ||
name: providedNameOrGenerated, | ||
portal: portal || '', | ||
format: format || types_1.DataFormat.Binary, | ||
portal: portal || query.portal || '', | ||
format: format || query.format || types_1.DataFormat.Binary, | ||
values: values || [], | ||
close: false | ||
}, types); | ||
}, types || query.types); | ||
return result.iterator; | ||
@@ -374,45 +388,23 @@ } | ||
} | ||
query(text, values, types, format, streams) { | ||
const query = (typeof text === 'string') ? | ||
new query_1.Query(text, values, { | ||
types: types, | ||
format: format, | ||
streams: streams, | ||
}) : | ||
text; | ||
return this.execute(query); | ||
} | ||
bindAndExecute(info, bind, types) { | ||
try { | ||
this.writer.bind(bind.name, bind.portal, bind.format, bind.values, types); | ||
} | ||
catch (error) { | ||
info.handler.callback(error); | ||
return; | ||
} | ||
this.bindQueue.push(info); | ||
this.writer.execute(bind.portal); | ||
this.cleanupQueue.push(0 /* Cleanup.Bind */); | ||
if (bind.close) { | ||
this.writer.close(bind.name, 'S'); | ||
this.closeHandlerQueue.push(null); | ||
this.cleanupQueue.push(1 /* Cleanup.Close */); | ||
} | ||
this.writer.sync(); | ||
this.errorHandlerQueue.push((error) => { info.handler.callback(error); }); | ||
this.cleanupQueue.push(2 /* Cleanup.ErrorHandler */); | ||
this.send(); | ||
} | ||
execute(query) { | ||
/** | ||
* Send a query to the database. | ||
* | ||
* The query string is given as the first argument, or pass a {@link Query} | ||
* object which provides more control. | ||
* | ||
* @param text - The query string, or pass a {@link Query} | ||
* object which provides more control (including streaming values into a socket). | ||
* @param values - The query parameters, corresponding to $1, $2, etc. | ||
* @returns A promise for the query results. | ||
*/ | ||
query(text, values) { | ||
const query = typeof text === 'string' ? { text } : text; | ||
if (this.closed && !this.connecting) { | ||
throw new Error('Connection is closed.'); | ||
} | ||
const text = query.text; | ||
const values = query.values || []; | ||
const options = query.options; | ||
const format = options ? options.format : undefined; | ||
const types = options ? options.types : undefined; | ||
const streams = options ? options.streams : undefined; | ||
const portal = (options ? options.portal : undefined) || ''; | ||
const result = (0, result_1.makeResult)(); | ||
const format = query === null || query === void 0 ? void 0 : query.format; | ||
const types = query === null || query === void 0 ? void 0 : query.types; | ||
const streams = query === null || query === void 0 ? void 0 : query.streams; | ||
const portal = (query === null || query === void 0 ? void 0 : query.portal) || ''; | ||
const result = (0, result_1.makeResult)(query === null || query === void 0 ? void 0 : query.transform); | ||
const descriptionHandler = (description) => { | ||
@@ -426,5 +418,5 @@ result.nameHandler(description.names); | ||
if (values && values.length) { | ||
const name = (options ? options.name : undefined) || ((this.config.preparedStatementPrefix || | ||
const name = (query === null || query === void 0 ? void 0 : query.name) || ((this.config.preparedStatementPrefix || | ||
defaults.preparedStatementPrefix) + (this.nextPreparedStatementId++)); | ||
this.writer.parse(name, text, types || []); | ||
this.writer.parse(name, query.text, types || []); | ||
this.writer.describe(name, 'S'); | ||
@@ -445,4 +437,4 @@ this.preFlightQueue.push({ | ||
else { | ||
const name = (options ? options.name : undefined) || ''; | ||
this.writer.parse(name, text); | ||
const name = query.name || ''; | ||
this.writer.parse(name, query.text); | ||
this.writer.bind(name, portal); | ||
@@ -474,2 +466,23 @@ this.bindQueue.push(null); | ||
} | ||
bindAndExecute(info, bind, types) { | ||
try { | ||
this.writer.bind(bind.name, bind.portal, bind.format, bind.values, types); | ||
} | ||
catch (error) { | ||
info.handler.callback(error); | ||
return; | ||
} | ||
this.bindQueue.push(info); | ||
this.writer.execute(bind.portal); | ||
this.cleanupQueue.push(0 /* Cleanup.Bind */); | ||
if (bind.close) { | ||
this.writer.close(bind.name, 'S'); | ||
this.closeHandlerQueue.push(null); | ||
this.cleanupQueue.push(1 /* Cleanup.Close */); | ||
} | ||
this.writer.sync(); | ||
this.errorHandlerQueue.push((error) => { info.handler.callback(error); }); | ||
this.cleanupQueue.push(2 /* Cleanup.ErrorHandler */); | ||
this.send(); | ||
} | ||
handleError(error) { | ||
@@ -569,3 +582,3 @@ while (true) { | ||
} | ||
receive(buffer, offset, size) { | ||
handle(buffer, offset, size) { | ||
const types = this.config.types || null; | ||
@@ -572,0 +585,0 @@ let read = 0; |
@@ -1,2 +0,2 @@ | ||
export * from './client'; | ||
export { Client, PreparedStatement } from './client'; | ||
export { DataFormat, DataType, Point, ValueTypeReader, } from './types'; | ||
@@ -3,0 +3,0 @@ export * from './query'; |
@@ -17,4 +17,5 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ErrorLevel = exports.DatabaseError = exports.ResultIterator = exports.Result = exports.DataType = exports.DataFormat = void 0; | ||
__exportStar(require("./client"), exports); | ||
exports.ErrorLevel = exports.DatabaseError = exports.ResultIterator = exports.Result = exports.DataType = exports.DataFormat = exports.Client = void 0; | ||
var client_1 = require("./client"); | ||
Object.defineProperty(exports, "Client", { enumerable: true, get: function () { return client_1.Client; } }); | ||
var types_1 = require("./types"); | ||
@@ -21,0 +22,0 @@ Object.defineProperty(exports, "DataFormat", { enumerable: true, get: function () { return types_1.DataFormat; } }); |
@@ -5,13 +5,22 @@ /// <reference types="node" /> | ||
export interface QueryOptions { | ||
/** The query name. */ | ||
readonly name: string; | ||
/** Whether to use the default portal (i.e. unnamed) or provide a name. */ | ||
readonly portal: string; | ||
/** Allows making the database native type explicit for some or all columns. */ | ||
readonly types: DataType[]; | ||
/** Whether column data should be transferred using text or binary mode. */ | ||
readonly format: DataFormat | DataFormat[]; | ||
/** A mapping from column name to a socket, e.g. an open file. */ | ||
readonly streams: Record<string, Writable>; | ||
/** Allows the transformation of column names as returned by the database. */ | ||
readonly transform: (name: string) => string; | ||
} | ||
export declare class Query { | ||
readonly text: string; | ||
readonly values?: any[] | undefined; | ||
readonly options?: Partial<QueryOptions> | undefined; | ||
constructor(text: string, values?: any[] | undefined, options?: Partial<QueryOptions> | undefined); | ||
} | ||
/** | ||
* A query parameter can be used in place of a query text as the first argument | ||
* to the {@link Client.query} method. | ||
* @interface | ||
*/ | ||
export type Query = Partial<QueryOptions> & { | ||
text: string; | ||
}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Query = void 0; | ||
class Query { | ||
constructor(text, values, options) { | ||
this.text = text; | ||
this.values = values; | ||
this.options = options; | ||
} | ||
} | ||
exports.Query = Query; | ||
//# sourceMappingURL=query.js.map |
@@ -6,3 +6,3 @@ import { DatabaseError } from './protocol'; | ||
/** The default result type, used if no generic type parameter is specified. */ | ||
export type ResultRecord = Record<string, any>; | ||
export type ResultRecord<T = any> = Record<string, T>; | ||
declare class ResultRowImpl<T> extends Array<any> { | ||
@@ -67,3 +67,3 @@ #private; | ||
export type NameHandler = Callback<string[]>; | ||
export declare function makeResult<T>(): { | ||
export declare function makeResult<T>(transform?: (name: string) => string): { | ||
iterator: ResultIterator<T>; | ||
@@ -70,0 +70,0 @@ dataHandler: DataHandler; |
@@ -238,3 +238,3 @@ "use strict"; | ||
ResultIterator.prototype.constructor = Promise; | ||
function makeResult() { | ||
function makeResult(transform) { | ||
let dataHandler = null; | ||
@@ -261,2 +261,5 @@ const names = []; | ||
names.length = 0; | ||
if (transform) { | ||
ns = ns.map(transform); | ||
} | ||
names.push(...ns); | ||
@@ -263,0 +266,0 @@ }; |
@@ -37,3 +37,3 @@ "use strict"; | ||
} | ||
return new index_1.Query(text); | ||
return { text }; | ||
} | ||
@@ -46,11 +46,14 @@ function testSelect(testQuery, batchSize, doReplaceArgs) { | ||
name: 'Array', | ||
query: new index_1.Query( | ||
// tslint:disable-next-line | ||
'select (select array_agg(i) from generate_series(1, 100) as s(i)) from generate_series(1, 100)') | ||
query: { | ||
// tslint:disable-next-line | ||
text: 'select (select array_agg(i) from generate_series(1, 100) as s(i)) from generate_series(1, 100)' | ||
} | ||
}; | ||
case 0 /* TestQuery.PgType */: return { | ||
name: 'PgType', | ||
query: new index_1.Query( | ||
// tslint:disable-next-line | ||
'select typname, typnamespace, typowner, typlen, typbyval, typcategory, typispreferred, typisdefined, typdelim, typrelid, typelem, typarray from pg_type where typtypmod = $1 and typisdefined = $2', [-1, true]) | ||
query: { | ||
// tslint:disable-next-line | ||
text: 'select typname, typnamespace, typowner, typlen, typbyval, typcategory, typispreferred, typisdefined, typdelim, typrelid, typelem, typarray from pg_type where typtypmod = $1 and typisdefined = $2', | ||
values: [-1, true] | ||
} | ||
}; | ||
@@ -78,3 +81,3 @@ } | ||
while (i--) { | ||
const p = client.execute(query).then((result) => { | ||
const p = client.query(query.text, query.values).then((result) => { | ||
acknowledged += 1; | ||
@@ -169,4 +172,3 @@ results += result.rows.length; | ||
globals_1.expect.assertions(1); | ||
const query = new index_1.Query('select 1'); | ||
const result = yield client.execute(query); | ||
const result = yield client.query('select 1'); | ||
(0, globals_1.expect)(result.rows.length).toEqual(1); | ||
@@ -176,4 +178,3 @@ })); | ||
globals_1.expect.assertions(1); | ||
const query = new index_1.Query('select $1::int', [1]); | ||
const result = yield client.execute(query); | ||
const result = yield client.query('select $1::int', [1]); | ||
(0, globals_1.expect)(result.rows.length).toEqual(1); | ||
@@ -183,4 +184,3 @@ })); | ||
globals_1.expect.assertions(1); | ||
const query = new index_1.Query('select $1::int', [1]); | ||
const result = yield client.execute(query); | ||
const result = yield client.query('select $1::int', [1]); | ||
(0, globals_1.expect)(result.rows.length).toEqual(1); | ||
@@ -203,12 +203,7 @@ })); | ||
})); | ||
(0, helper_1.testWithClient)('Prepared statement', (client) => __awaiter(void 0, void 0, void 0, function* () { | ||
const count = 5; | ||
globals_1.expect.assertions(count * 2); | ||
yield client.query('prepare test (int) as select $1'); | ||
for (let i = 0; i < count; i++) { | ||
const result = yield client.query('execute test(1)'); | ||
const rows = result.rows; | ||
(0, globals_1.expect)(rows.length).toEqual(1); | ||
(0, globals_1.expect)(rows[0]).toEqual([1]); | ||
} | ||
(0, helper_1.testWithClient)('Name transform', (client) => __awaiter(void 0, void 0, void 0, function* () { | ||
globals_1.expect.assertions(1); | ||
const query = { text: 'select 1 as foo', transform: (s) => s.toUpperCase() }; | ||
const result = yield client.query(query); | ||
(0, globals_1.expect)(result.names).toEqual(['FOO']); | ||
})); | ||
@@ -250,4 +245,6 @@ (0, helper_1.testWithClient)('Listen/notify', (client) => __awaiter(void 0, void 0, void 0, function* () { | ||
socket.connect(address.port); | ||
const query = new index_1.Query('select upper($1)::bytea as col', [Buffer.from(s)], { streams: { col: socket } }); | ||
yield client.execute(query); | ||
yield client.query({ | ||
text: 'select upper($1)::bytea as col', | ||
streams: { col: socket } | ||
}, [Buffer.from(s)]); | ||
// At this point we're done really done streaming, and how can | ||
@@ -398,2 +395,10 @@ // we know when that happens? | ||
})); | ||
(0, helper_1.testWithClient)('Prepare and execute (SELECT)', (client) => __awaiter(void 0, void 0, void 0, function* () { | ||
const query = { text: 'select $1::int as i', transform: (s) => s.toUpperCase() }; | ||
const stmt = yield client.prepare(query); | ||
yield (0, globals_1.expect)(stmt.execute([1])).resolves.toEqual({ names: ['I'], rows: [[1]], status: 'SELECT 1' }); | ||
const result = yield stmt.execute([2]); | ||
(0, globals_1.expect)(result.rows).toEqual([[2]]); | ||
yield stmt.close(); | ||
})); | ||
(0, helper_1.testWithClient)('Prepare and execute (INSERT)', (client) => __awaiter(void 0, void 0, void 0, function* () { | ||
@@ -400,0 +405,0 @@ yield client.query('create temporary table foo (bar int)'); |
@@ -36,3 +36,7 @@ "use strict"; | ||
: 'select $1 is null'; | ||
yield client.query((expected !== null) ? query + ' where $1 is not null' : query, [expected], [dataType], format) | ||
yield client.query({ | ||
text: expected !== null ? query + ' where $1 is not null' : query, | ||
types: [dataType], | ||
format | ||
}, [expected]) | ||
.then((result) => { | ||
@@ -50,3 +54,3 @@ const rows = result.rows; | ||
const query = 'select ' + expression; | ||
yield client.query(query, [], [], format).then((result) => { | ||
yield client.query({ text: query, format }, []).then((result) => { | ||
const rows = result.rows; | ||
@@ -53,0 +57,0 @@ (0, globals_1.expect)(rows.length).toEqual(1); |
{ | ||
"name": "ts-postgres", | ||
"version": "1.6.0", | ||
"version": "1.7.0", | ||
"description": "PostgreSQL client in TypeScript", | ||
@@ -5,0 +5,0 @@ "declaration": true, |
@@ -263,2 +263,12 @@  | ||
2. _How do I convert column names to camelcase?_ Use the `transform` option: | ||
```typescript | ||
const camelcase = (s: string) => s.replace(/(_\w)/g, k => k[1].toUpperCase()); | ||
const result = client.query({text: ..., transform: camelcase}) | ||
``` | ||
3. _How do I use LISTEN/NOTIFY?_ Send `LISTEN` as a regular query, then subscribe to notifications | ||
using `on(event: 'notification', callback: Callback<Notification>)`. | ||
## Benchmarking | ||
@@ -265,0 +275,0 @@ |
@@ -292,3 +292,3 @@ import { randomBytes } from 'crypto'; | ||
writer.startup(settings); | ||
this.listen(); | ||
this.receive(); | ||
this.sendUsing(writer); | ||
@@ -348,3 +348,3 @@ } | ||
writer.startup(settings); | ||
this.listen(); | ||
this.receive(); | ||
} | ||
@@ -355,3 +355,3 @@ | ||
private listen() { | ||
private receive() { | ||
let buffer: Buffer | null = null; | ||
@@ -382,3 +382,3 @@ let offset = 0; | ||
try { | ||
const read = this.receive(buffer, offset, size); | ||
const read = this.handle(buffer, offset, size); | ||
offset += read; | ||
@@ -410,2 +410,9 @@ remaining = size - read; | ||
/** Connect to the database. | ||
* | ||
* @remarks | ||
* Don't forget to close the connection using {@link end} before exiting. | ||
* | ||
* @returns The connection encryption status. | ||
*/ | ||
connect(): Promise<boolean> { | ||
@@ -454,2 +461,5 @@ if (this.connecting) { | ||
/** End the database connection. | ||
* | ||
*/ | ||
end() { | ||
@@ -523,8 +533,9 @@ if (this.ending) { | ||
prepare<T = ResultRecord>( | ||
text: string, | ||
name?: string, | ||
types?: DataType[]): Promise<PreparedStatement<T>> { | ||
const providedNameOrGenerated = name || ( | ||
/** Prepare a statement for later execution. | ||
* | ||
* @returns A prepared statement object. | ||
*/ | ||
prepare<T = ResultRecord>(text: Query | string): Promise<PreparedStatement<T>> { | ||
const query = typeof text === 'string' ? {text} : text; | ||
const providedNameOrGenerated = query.name || ( | ||
(this.config.preparedStatementPrefix || | ||
@@ -539,3 +550,3 @@ defaults.preparedStatementPrefix) + ( | ||
this.errorHandlerQueue.push(errorHandler); | ||
this.writer.parse(providedNameOrGenerated, text, types || []); | ||
this.writer.parse(providedNameOrGenerated, query.text, query.types || []); | ||
this.writer.describe(providedNameOrGenerated, 'S'); | ||
@@ -568,3 +579,3 @@ this.preFlightQueue.push({ | ||
) => { | ||
const result = makeResult<T>(); | ||
const result = makeResult<T>(query?.transform); | ||
result.nameHandler(description.names); | ||
@@ -580,7 +591,7 @@ const info = { | ||
name: providedNameOrGenerated, | ||
portal: portal || '', | ||
format: format || DataFormat.Binary, | ||
portal: portal || query.portal || '', | ||
format: format || query.format || DataFormat.Binary, | ||
values: values || [], | ||
close: false | ||
}, types); | ||
}, types || query.types); | ||
@@ -601,58 +612,16 @@ return result.iterator | ||
query<T = ResultRecord>( | ||
text: string, | ||
values?: any[], | ||
types?: DataType[], | ||
format?: DataFormat | DataFormat[], | ||
streams?: Record<string, Writable>): | ||
ResultIterator<T> { | ||
const query = | ||
(typeof text === 'string') ? | ||
new Query( | ||
text, | ||
values, { | ||
types: types, | ||
format: format, | ||
streams: streams, | ||
}) : | ||
text; | ||
return this.execute<T>(query); | ||
} | ||
/** | ||
* Send a query to the database. | ||
* | ||
* The query string is given as the first argument, or pass a {@link Query} | ||
* object which provides more control. | ||
* | ||
* @param text - The query string, or pass a {@link Query} | ||
* object which provides more control (including streaming values into a socket). | ||
* @param values - The query parameters, corresponding to $1, $2, etc. | ||
* @returns A promise for the query results. | ||
*/ | ||
query<T = ResultRecord>(text: Query | string, values?: any[]): ResultIterator<T> { | ||
const query = typeof text === 'string' ? {text} : text; | ||
private bindAndExecute( | ||
info: RowDataHandlerInfo, | ||
bind: Bind, | ||
types: DataType[]) { | ||
try { | ||
this.writer.bind( | ||
bind.name, | ||
bind.portal, | ||
bind.format, | ||
bind.values, | ||
types | ||
); | ||
} catch (error) { | ||
info.handler.callback(error as Error); | ||
return; | ||
} | ||
this.bindQueue.push(info); | ||
this.writer.execute(bind.portal); | ||
this.cleanupQueue.push(Cleanup.Bind); | ||
if (bind.close) { | ||
this.writer.close(bind.name, 'S'); | ||
this.closeHandlerQueue.push(null); | ||
this.cleanupQueue.push(Cleanup.Close); | ||
} | ||
this.writer.sync(); | ||
this.errorHandlerQueue.push( | ||
(error) => { info.handler.callback(error); } | ||
); | ||
this.cleanupQueue.push(Cleanup.ErrorHandler); | ||
this.send(); | ||
} | ||
execute<T = ResultRecord>(query: Query): ResultIterator<T> { | ||
if (this.closed && !this.connecting) { | ||
@@ -662,10 +631,7 @@ throw new Error('Connection is closed.'); | ||
const text = query.text; | ||
const values = query.values || []; | ||
const options = query.options; | ||
const format = options ? options.format : undefined; | ||
const types = options ? options.types : undefined; | ||
const streams = options ? options.streams : undefined; | ||
const portal = (options ? options.portal : undefined) || ''; | ||
const result = makeResult<T>(); | ||
const format = query?.format; | ||
const types = query?.types; | ||
const streams =query?.streams; | ||
const portal = query?.portal || ''; | ||
const result = makeResult<T>(query?.transform); | ||
@@ -682,3 +648,3 @@ const descriptionHandler = (description: RowDescription) => { | ||
if (values && values.length) { | ||
const name = (options ? options.name : undefined) || ( | ||
const name = (query?.name) || ( | ||
(this.config.preparedStatementPrefix || | ||
@@ -689,3 +655,3 @@ defaults.preparedStatementPrefix) + ( | ||
this.writer.parse(name, text, types || []); | ||
this.writer.parse(name, query.text, types || []); | ||
this.writer.describe(name, 'S'); | ||
@@ -705,4 +671,4 @@ this.preFlightQueue.push({ | ||
} else { | ||
const name = (options ? options.name : undefined) || ''; | ||
this.writer.parse(name, text); | ||
const name = query.name || ''; | ||
this.writer.parse(name, query.text); | ||
this.writer.bind(name, portal); | ||
@@ -743,2 +709,37 @@ this.bindQueue.push(null); | ||
private bindAndExecute( | ||
info: RowDataHandlerInfo, | ||
bind: Bind, | ||
types: DataType[]) { | ||
try { | ||
this.writer.bind( | ||
bind.name, | ||
bind.portal, | ||
bind.format, | ||
bind.values, | ||
types | ||
); | ||
} catch (error) { | ||
info.handler.callback(error as Error); | ||
return; | ||
} | ||
this.bindQueue.push(info); | ||
this.writer.execute(bind.portal); | ||
this.cleanupQueue.push(Cleanup.Bind); | ||
if (bind.close) { | ||
this.writer.close(bind.name, 'S'); | ||
this.closeHandlerQueue.push(null); | ||
this.cleanupQueue.push(Cleanup.Close); | ||
} | ||
this.writer.sync(); | ||
this.errorHandlerQueue.push( | ||
(error) => { info.handler.callback(error); } | ||
); | ||
this.cleanupQueue.push(Cleanup.ErrorHandler); | ||
this.send(); | ||
} | ||
private handleError(error: Error): boolean { | ||
@@ -844,3 +845,3 @@ while (true) { | ||
private receive(buffer: Buffer, offset: number, size: number): number { | ||
private handle(buffer: Buffer, offset: number, size: number): number { | ||
const types = this.config.types || null; | ||
@@ -847,0 +848,0 @@ let read = 0; |
@@ -1,2 +0,2 @@ | ||
export * from './client'; | ||
export { Client, PreparedStatement } from './client'; | ||
export { | ||
@@ -3,0 +3,0 @@ DataFormat, |
@@ -8,15 +8,21 @@ import { Writable } from 'stream'; | ||
export interface QueryOptions { | ||
/** The query name. */ | ||
readonly name: string; | ||
/** Whether to use the default portal (i.e. unnamed) or provide a name. */ | ||
readonly portal: string; | ||
/** Allows making the database native type explicit for some or all columns. */ | ||
readonly types: DataType[]; | ||
/** Whether column data should be transferred using text or binary mode. */ | ||
readonly format: DataFormat | DataFormat[]; | ||
/** A mapping from column name to a socket, e.g. an open file. */ | ||
readonly streams: Record<string, Writable>; | ||
/** Allows the transformation of column names as returned by the database. */ | ||
readonly transform: (name: string) => string; | ||
} | ||
export class Query { | ||
constructor( | ||
public readonly text: string, | ||
public readonly values?: any[], | ||
public readonly options?: Partial<QueryOptions> | ||
) { } | ||
} | ||
/** | ||
* A query parameter can be used in place of a query text as the first argument | ||
* to the {@link Client.query} method. | ||
* @interface | ||
*/ | ||
export type Query = Partial<QueryOptions> & { text: string }; |
@@ -10,3 +10,3 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ | ||
/** The default result type, used if no generic type parameter is specified. */ | ||
export type ResultRecord = Record<string, any> | ||
export type ResultRecord<T = any> = Record<string, T>; | ||
@@ -213,3 +213,3 @@ | ||
export function makeResult<T>() { | ||
export function makeResult<T>(transform?: (name: string) => string) { | ||
let dataHandler: DataHandler | null = null; | ||
@@ -237,2 +237,5 @@ | ||
names.length = 0; | ||
if (transform) { | ||
ns = ns.map(transform); | ||
} | ||
names.push(...ns); | ||
@@ -239,0 +242,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
419706
0.87%7265
0.85%306
3.38%