ts-postgres
Advanced tools
Comparing version
@@ -453,3 +453,3 @@ "use strict"; | ||
{ | ||
logger.warn(`Message not implemented: ${mtype} `); | ||
logger.warn(`Message not implemented: ${mtype}`); | ||
break; | ||
@@ -456,0 +456,0 @@ } |
export * from './client'; | ||
export * from './types'; | ||
export * from './query'; | ||
export { Result, ResultIterator } from './result'; | ||
export { Result, ResultIterator, ResultRow } from './result'; |
@@ -12,2 +12,3 @@ "use strict"; | ||
exports.ResultIterator = result_1.ResultIterator; | ||
exports.ResultRow = result_1.ResultRow; | ||
//# sourceMappingURL=index.js.map |
declare type ResultHandler<T> = (resolve: (error: null | string) => void) => void; | ||
declare type Callback<T> = (item: T) => void; | ||
export declare class ResultRow<T> { | ||
readonly names: string[]; | ||
readonly data: T[]; | ||
private readonly length; | ||
constructor(names: string[], data: T[]); | ||
get(name: string): T; | ||
} | ||
export declare class Result<T> { | ||
@@ -7,5 +14,5 @@ names: string[]; | ||
constructor(names: string[], rows: T[][]); | ||
[Symbol.iterator](): Iterator<Map<string, T>>; | ||
[Symbol.iterator](): Iterator<ResultRow<T>>; | ||
} | ||
export declare class ResultIterator<T> extends Promise<Result<T>> implements AsyncIterable<Map<string, T>> { | ||
export declare class ResultIterator<T> extends Promise<Result<T>> { | ||
private container; | ||
@@ -18,3 +25,3 @@ private subscribers; | ||
notify(done: boolean): void; | ||
[Symbol.asyncIterator](): AsyncIterator<Map<string, T>>; | ||
[Symbol.asyncIterator](): AsyncIterator<ResultRow<T>>; | ||
} | ||
@@ -21,0 +28,0 @@ export declare type DataHandler<T> = Callback<T | null | string>; |
@@ -11,3 +11,18 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const utils_1 = require("./utils"); | ||
class ResultRow { | ||
constructor(names, data) { | ||
this.names = names; | ||
this.data = data; | ||
this.length = names.length; | ||
} | ||
get(name) { | ||
for (let i = 0; this.length; i++) { | ||
if (this.names[i] === name) { | ||
return this.data[i]; | ||
} | ||
} | ||
throw new Error(`Key not found: ${name}`); | ||
} | ||
} | ||
exports.ResultRow = ResultRow; | ||
class Result { | ||
@@ -26,3 +41,3 @@ constructor(names, rows) { | ||
i++; | ||
return utils_1.zip(names, values); | ||
return new ResultRow(names, values); | ||
}; | ||
@@ -77,3 +92,3 @@ return { | ||
} | ||
return utils_1.zip(names, values); | ||
return new ResultRow(names, values); | ||
}; | ||
@@ -80,0 +95,0 @@ return { |
@@ -23,7 +23,7 @@ "use strict"; | ||
const query = () => client.query('select generate_series($1::int, $2::int) as i', [0, 9]); | ||
const maps = yield f(query()); | ||
expect(maps.length).toEqual(10); | ||
const rows = yield f(query()); | ||
expect(rows.length).toEqual(10); | ||
let expectation = [...Array(10).keys()]; | ||
const keys = maps.map((map) => [...map.keys()]); | ||
const values = maps.map((map) => [...map.values()]); | ||
const keys = rows.map((row) => [...row.names]); | ||
const values = rows.map((row) => [...row.data]); | ||
// Keys are column names. | ||
@@ -66,6 +66,2 @@ expect(keys).toEqual(expectation.map(() => ['i'])); | ||
expect(count).toEqual(20); | ||
// Or use the spread operator. | ||
const rows = [...yield query()]; | ||
expect(rows.length).toEqual(10); | ||
expect(rows[0].get('i')).toEqual(0); | ||
// The result is also available in the public rows attribute. | ||
@@ -89,8 +85,8 @@ expect(result.rows).toEqual(expectation.map((i) => { return [i]; })); | ||
return p.then((result) => { | ||
const maps = []; | ||
for (const map of result) { | ||
maps.push(map); | ||
const rows = []; | ||
for (const row of result) { | ||
rows.push(row); | ||
} | ||
; | ||
return maps; | ||
return rows; | ||
}); | ||
@@ -105,7 +101,7 @@ }); | ||
var e_3, _a; | ||
const maps = []; | ||
const rows = []; | ||
try { | ||
for (result_3 = __asyncValues(result); result_3_1 = yield result_3.next(), !result_3_1.done;) { | ||
const map = result_3_1.value; | ||
maps.push(map); | ||
const row = result_3_1.value; | ||
rows.push(row); | ||
} | ||
@@ -121,3 +117,3 @@ } | ||
; | ||
return maps; | ||
return rows; | ||
}); }); | ||
@@ -124,0 +120,0 @@ })); |
{ | ||
"name": "ts-postgres", | ||
"version": "1.0.0-beta2", | ||
"version": "1.0.0-beta3", | ||
"description": "PostgreSQL client in TypeScript", | ||
@@ -5,0 +5,0 @@ "declaration": true, |
@@ -24,4 +24,4 @@ # ts-postgres | ||
* Hybrid query result object | ||
* Iteration (synchronous or asynchronous, yields one map per row); | ||
* All-at-once, when promise completes; result data is available in array form | ||
* Iterable (synchronous or asynchronous; one row at a time) | ||
* Promise-based | ||
@@ -41,4 +41,3 @@ --- | ||
// The query result is an asynchronous iterator. | ||
const iterator = client.query( | ||
const stream = client.query( | ||
`SELECT 'Hello ' || $1 || '!' AS message`, | ||
@@ -48,4 +47,4 @@ ['world'] | ||
for await (const item of iterator) { | ||
console.log(item.get('message')); // 'Hello world!' | ||
for await (const row of stream) { | ||
console.log(row.get('message')); // 'Hello world!' | ||
} | ||
@@ -58,14 +57,29 @@ | ||
``` | ||
We often want to just wait for the entire result set to arrive and subsequently process the data: | ||
The example above uses the variable ``stream`` to indicate that the result set is made available as it arrives on the connection. But we'll often want to just wait for the entire result set to arrive before starting to process the data. | ||
```typescript | ||
const result = await client.query('select generate_series(1, 10)'); | ||
``` | ||
The iterator interface yields one map object per row (from column names to values). The spread operator is a convenient way to turn a result into an array of such maps: | ||
If the query fails, waiting for the result will throw an exception. | ||
### Iterator interface | ||
Whether we're operating on a stream or an already waited for result set, the iterator interface provides the most high-level row interface. This also applies when using the _spread_ operator: | ||
```typescript | ||
const items = [...result]; | ||
for (let item of items) { | ||
console.log('The number is: ' + item.get('i')); // 1, 2, 3, ... | ||
const rows = [...result]; | ||
``` | ||
Each row provides direct access to values through its ``data`` attribute, but we can also get a value by name using the ``get(name)`` method. | ||
```typescript | ||
for (let row of rows) { | ||
console.log('The number is: ' + row.get('i')); // 1, 2, 3, ... | ||
} | ||
``` | ||
Using the ``rows`` attribute is the most efficient way to work with result data. It contains the raw result data as an array of arrays. | ||
Note that values are polymorphic and need to be explicitly cast to a concrete type such as ``number`` or ``string``. | ||
### Result interface | ||
This interface is available on the already waited for result object. It makes data available in the ``rows`` attribute as an array of arrays (of values). | ||
```typescript | ||
@@ -76,3 +90,3 @@ for (let row of result.rows) { | ||
``` | ||
Column names are available as the ``names`` attribute of a result. | ||
This is the most efficient way to work with result data. Column names are available as the ``names`` attribute of a result. | ||
@@ -79,0 +93,0 @@ ### Multiple queries |
export * from './client'; | ||
export * from './types'; | ||
export * from './query'; | ||
export { Result, ResultIterator } from './result'; | ||
export { Result, ResultIterator, ResultRow } from './result'; |
@@ -1,10 +0,25 @@ | ||
import { zip } from './utils'; | ||
type ResultHandler<T> = (resolve: (error: null | string) => void) => void; | ||
type Callback<T> = (item: T) => void; | ||
export class ResultRow<T> { | ||
private readonly length: number; | ||
constructor(public readonly names: string[], public readonly data: T[]) { | ||
this.length = names.length; | ||
} | ||
get(name: string): T { | ||
for (let i = 0; this.length; i++) { | ||
if (this.names[i] === name) { | ||
return this.data[i]; | ||
} | ||
} | ||
throw new Error(`Key not found: ${name}`); | ||
} | ||
} | ||
export class Result<T> { | ||
constructor(public names: string[], public rows: T[][]) { } | ||
[Symbol.iterator](): Iterator<Map<string, T>> { | ||
[Symbol.iterator](): Iterator<ResultRow<T>> { | ||
let i = 0; | ||
@@ -19,3 +34,3 @@ | ||
i++; | ||
return zip(names, values); | ||
return new ResultRow<T>(names, values); | ||
}; | ||
@@ -32,4 +47,3 @@ | ||
export class ResultIterator<T> extends Promise<Result<T>> | ||
implements AsyncIterable<Map<string, T>> { | ||
export class ResultIterator<T> extends Promise<Result<T>> { | ||
private subscribers: ((done: boolean) => void)[] = []; | ||
@@ -60,3 +74,3 @@ private done = false; | ||
[Symbol.asyncIterator](): AsyncIterator<Map<string, T>> { | ||
[Symbol.asyncIterator](): AsyncIterator<ResultRow<T>> { | ||
let i = 0; | ||
@@ -75,3 +89,3 @@ | ||
return zip(names, values); | ||
return new ResultRow<T>(names, values); | ||
}; | ||
@@ -78,0 +92,0 @@ |
export function sum(...nums: number[]): number { | ||
return nums.reduce((a, b) => a + b, 0); | ||
}; | ||
export function zip<K, V>(keys: K[], values: V[]) { | ||
const length = Math.min(keys.length, values.length); | ||
const map: Map<K, V> = new Map(); | ||
for (let i = 0; i < length; i++) { | ||
map.set(keys[i], values[i]); | ||
} | ||
return map; | ||
} |
import { withClient } from './helper'; | ||
import { Client } from '../src/client'; | ||
import { DataType, Row, Value } from '../src/types'; | ||
import { Result, ResultIterator } from '../src/result'; | ||
import { Result, ResultIterator, ResultRow } from '../src/result'; | ||
type ResultFunction = | ||
(result: ResultIterator<Value>) => | ||
Promise<Map<string, Value>[]>; | ||
Promise<ResultRow<Value>[]>; | ||
@@ -14,9 +14,12 @@ async function testIteratorResult(client: Client, f: ResultFunction) { | ||
); | ||
const maps = await f(query()); | ||
const rows = await f(query()); | ||
expect(maps.length).toEqual(10); | ||
expect(rows.length).toEqual(10); | ||
let expectation = [...Array(10).keys()]; | ||
const keys = maps.map((map) => [...map.keys()]); | ||
const values = maps.map((map) => [...map.values()]); | ||
const keys = rows.map((row) => [...row.names]); | ||
const values = rows.map((row) => [...row.data]); | ||
// The get method returns a column using name lookup. | ||
expect(values).toEqual(rows.map((row) => [row.get('i')])); | ||
// Keys are column names. | ||
@@ -28,2 +31,3 @@ expect(keys).toEqual(expectation.map(() => ['i'])); | ||
// We could iterate multiple times over the same result. | ||
let count = 0; | ||
@@ -36,3 +40,2 @@ const result = query(); | ||
// We could iterate once again over the same set of data. | ||
for await (const row of result) { | ||
@@ -43,7 +46,2 @@ count += 1; | ||
// Or use the spread operator. | ||
const rows = [...await query()]; | ||
expect(rows.length).toEqual(10); | ||
expect(rows[0].get('i')).toEqual(0); | ||
// The result is also available in the public rows attribute. | ||
@@ -69,3 +67,2 @@ expect(result.rows).toEqual( | ||
test('Synchronous iteration', async () => { | ||
expect.assertions(8); | ||
await testIteratorResult( | ||
@@ -75,7 +72,7 @@ client, | ||
return p.then((result) => { | ||
const maps: Map<string, Value>[] = []; | ||
for (const map of result) { | ||
maps.push(map); | ||
const rows: ResultRow<Value>[] = []; | ||
for (const row of result) { | ||
rows.push(row); | ||
}; | ||
return maps; | ||
return rows; | ||
}); | ||
@@ -87,11 +84,10 @@ }); | ||
test('Asynchronous iteration', async () => { | ||
expect.assertions(8); | ||
await testIteratorResult( | ||
client, | ||
async (result) => { | ||
const maps: Map<string, Value>[] = []; | ||
for await (const map of result) { | ||
maps.push(map); | ||
const rows: ResultRow<Value>[] = []; | ||
for await (const row of result) { | ||
rows.push(row); | ||
}; | ||
return maps; | ||
return rows; | ||
}); | ||
@@ -98,0 +94,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
268118
0.43%5109
0.37%137
11.38%