Return row value direct in .first() and .first_or(x)
This commit is contained in:
parent
00002525e4
commit
a4c0055c79
@ -14,9 +14,9 @@ The glue for TypeScript to PostgreSQL.
|
||||
## Installation
|
||||
|
||||
```ts
|
||||
import pglue from "https://git.lua.re/luaneko/pglue/raw/tag/v0.2.0/mod.ts";
|
||||
import pglue from "https://git.lua.re/luaneko/pglue/raw/tag/v0.3.0/mod.ts";
|
||||
// ...or from github:
|
||||
import pglue from "https://raw.githubusercontent.com/luaneko/pglue/refs/tags/v0.2.0/mod.ts";
|
||||
import pglue from "https://raw.githubusercontent.com/luaneko/pglue/refs/tags/v0.3.0/mod.ts";
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@luaneko/pglue",
|
||||
"version": "0.2.0",
|
||||
"version": "0.3.0",
|
||||
"exports": "./mod.ts"
|
||||
}
|
||||
|
7
mod.ts
7
mod.ts
@ -27,11 +27,10 @@ export {
|
||||
sql,
|
||||
is_sql,
|
||||
Query,
|
||||
type Row,
|
||||
type CommandResult,
|
||||
type Result,
|
||||
type Results,
|
||||
type ResultStream,
|
||||
type Row,
|
||||
type Rows,
|
||||
type RowStream,
|
||||
} from "./query.ts";
|
||||
|
||||
export type Options = {
|
||||
|
41
query.ts
41
query.ts
@ -323,22 +323,15 @@ export const sql_types: SqlTypeMap = {
|
||||
|
||||
sql.types = sql_types;
|
||||
|
||||
type ReadonlyTuple<T extends readonly unknown[]> = readonly [...T];
|
||||
|
||||
export interface CommandResult {
|
||||
export interface Result {
|
||||
readonly tag: string;
|
||||
}
|
||||
|
||||
export interface Result<T> extends CommandResult, ReadonlyTuple<[T]> {
|
||||
readonly row: T;
|
||||
}
|
||||
|
||||
export interface Results<T> extends CommandResult, ReadonlyArray<T> {
|
||||
export interface Rows<T> extends Result, ReadonlyArray<T> {
|
||||
readonly rows: ReadonlyArray<T>;
|
||||
}
|
||||
|
||||
export interface ResultStream<T>
|
||||
extends AsyncIterable<T[], CommandResult, void> {}
|
||||
export interface RowStream<T> extends AsyncIterable<T[], Result, void> {}
|
||||
|
||||
export interface Row extends Iterable<unknown, void, void> {
|
||||
[column: string]: unknown;
|
||||
@ -351,12 +344,10 @@ export interface QueryOptions {
|
||||
readonly stdout: WritableStream<Uint8Array> | null;
|
||||
}
|
||||
|
||||
export class Query<T = Row>
|
||||
implements PromiseLike<Results<T>>, ResultStream<T>
|
||||
{
|
||||
export class Query<T = Row> implements PromiseLike<Rows<T>>, RowStream<T> {
|
||||
readonly #f;
|
||||
|
||||
constructor(f: (options: Partial<QueryOptions>) => ResultStream<T>) {
|
||||
constructor(f: (options: Partial<QueryOptions>) => RowStream<T>) {
|
||||
this.#f = f;
|
||||
}
|
||||
|
||||
@ -431,20 +422,18 @@ export class Query<T = Row>
|
||||
return this.#f(options);
|
||||
}
|
||||
|
||||
async first(): Promise<Result<T>> {
|
||||
const { rows, tag } = await this.collect(1);
|
||||
if (!rows.length) throw new TypeError(`expected one row, got none instead`);
|
||||
const row = rows[0];
|
||||
return Object.assign([row] as const, { row: rows[0], tag });
|
||||
async first(): Promise<T> {
|
||||
const rows = await this.collect(1);
|
||||
if (rows.length !== 0) return rows[0];
|
||||
else throw new TypeError(`expected one row, got none instead`);
|
||||
}
|
||||
|
||||
async first_or<S>(value: S): Promise<Result<T | S>> {
|
||||
const { rows, tag } = await this.collect(1);
|
||||
const row = rows.length ? rows[0] : value;
|
||||
return Object.assign([row] as const, { row: rows[0], tag });
|
||||
async first_or<S>(value: S): Promise<T | S> {
|
||||
const rows = await this.collect(1);
|
||||
return rows.length !== 0 ? rows[0] : value;
|
||||
}
|
||||
|
||||
async collect(count = Number.POSITIVE_INFINITY): Promise<Results<T>> {
|
||||
async collect(count = Number.POSITIVE_INFINITY): Promise<Rows<T>> {
|
||||
const iter = this[Symbol.asyncIterator]();
|
||||
let next;
|
||||
const rows = [];
|
||||
@ -470,8 +459,8 @@ export class Query<T = Row>
|
||||
return n;
|
||||
}
|
||||
|
||||
then<S = Results<T>, U = never>(
|
||||
f?: ((rows: Results<T>) => S | PromiseLike<S>) | null,
|
||||
then<S = Rows<T>, U = never>(
|
||||
f?: ((rows: Rows<T>) => S | PromiseLike<S>) | null,
|
||||
g?: ((reason?: unknown) => U | PromiseLike<U>) | null
|
||||
) {
|
||||
return this.collect().then(f, g);
|
||||
|
12
test.ts
12
test.ts
@ -16,7 +16,7 @@ Deno.test(`integers`, async () => {
|
||||
await using pg = await connect();
|
||||
await using _tx = await pg.begin();
|
||||
|
||||
const [{ a, b, c }] = await pg.query`
|
||||
const { a, b, c } = await pg.query`
|
||||
select
|
||||
${"0x100"}::int2 as a,
|
||||
${777}::int4 as b,
|
||||
@ -32,7 +32,7 @@ Deno.test(`integers`, async () => {
|
||||
expect(b).toBe(777);
|
||||
expect(c).toBe(1234);
|
||||
|
||||
const [{ large }] =
|
||||
const { large } =
|
||||
await pg.query`select ${"10000000000000000"}::int8 as large`.first();
|
||||
|
||||
expect(large).toBe(10000000000000000n);
|
||||
@ -47,7 +47,7 @@ Deno.test(`boolean`, async () => {
|
||||
await using pg = await connect();
|
||||
await using _tx = await pg.begin();
|
||||
|
||||
const [{ a, b, c }] = await pg.query`
|
||||
const { a, b, c } = await pg.query`
|
||||
select
|
||||
${true}::bool as a,
|
||||
${"n"}::bool as b,
|
||||
@ -63,7 +63,7 @@ Deno.test(`bytea`, async () => {
|
||||
await using pg = await connect();
|
||||
await using _tx = await pg.begin();
|
||||
|
||||
const [{ string, array, buffer }] = await pg.query`
|
||||
const { string, array, buffer } = await pg.query`
|
||||
select
|
||||
${"hello, world"}::bytea as string,
|
||||
${[1, 2, 3, 4, 5]}::bytea as array,
|
||||
@ -93,7 +93,7 @@ Deno.test(`row`, async () => {
|
||||
).tag
|
||||
).toBe(`COPY 1`);
|
||||
|
||||
const [row] = await pg.query`select * from my_table`.first();
|
||||
const row = await pg.query`select * from my_table`.first();
|
||||
{
|
||||
// columns by name
|
||||
const { a, b, c } = row;
|
||||
@ -132,7 +132,7 @@ Deno.test(`sql injection`, async () => {
|
||||
`INSERT 0 1`
|
||||
);
|
||||
|
||||
const [{ name }] = await pg.query<{ name: string }>`
|
||||
const { name } = await pg.query<{ name: string }>`
|
||||
select name from users
|
||||
`.first();
|
||||
|
||||
|
39
wire.ts
39
wire.ts
@ -35,11 +35,11 @@ import {
|
||||
type EncoderType,
|
||||
} from "./ser.ts";
|
||||
import {
|
||||
type CommandResult,
|
||||
format,
|
||||
is_sql,
|
||||
Query,
|
||||
type ResultStream,
|
||||
type Result,
|
||||
type RowStream,
|
||||
type Row,
|
||||
sql,
|
||||
type SqlFragment,
|
||||
@ -460,22 +460,22 @@ export type WireEvents = {
|
||||
close(reason?: unknown): void;
|
||||
};
|
||||
|
||||
export interface Transaction extends CommandResult, AsyncDisposable {
|
||||
export interface Transaction extends Result, AsyncDisposable {
|
||||
readonly open: boolean;
|
||||
commit(): Promise<CommandResult>;
|
||||
rollback(): Promise<CommandResult>;
|
||||
commit(): Promise<Result>;
|
||||
rollback(): Promise<Result>;
|
||||
}
|
||||
|
||||
export type ChannelEvents = { notify: NotificationHandler };
|
||||
export type NotificationHandler = (payload: string, process_id: number) => void;
|
||||
export interface Channel
|
||||
extends TypedEmitter<ChannelEvents>,
|
||||
CommandResult,
|
||||
Result,
|
||||
AsyncDisposable {
|
||||
readonly name: string;
|
||||
readonly open: boolean;
|
||||
notify(payload: string): Promise<CommandResult>;
|
||||
unlisten(): Promise<CommandResult>;
|
||||
notify(payload: string): Promise<Result>;
|
||||
unlisten(): Promise<Result>;
|
||||
}
|
||||
|
||||
export async function wire_connect(options: WireOptions) {
|
||||
@ -546,16 +546,15 @@ export class Wire<V extends WireEvents = WireEvents>
|
||||
}
|
||||
|
||||
async get(param: string) {
|
||||
return (
|
||||
await this.query`select current_setting(${param}, true)`
|
||||
.map(([s]) => String(s))
|
||||
.first_or(null)
|
||||
)[0];
|
||||
return await this.query`select current_setting(${param}, true)`
|
||||
.map(([s]) => String(s))
|
||||
.first_or(null);
|
||||
}
|
||||
|
||||
async set(param: string, value: string, local = false) {
|
||||
return await this
|
||||
.query`select set_config(${param}, ${value}, ${local})`.execute();
|
||||
return await this.query`select set_config(${param}, ${value}, ${local})`
|
||||
.map(([s]) => String(s))
|
||||
.first();
|
||||
}
|
||||
|
||||
close(reason?: unknown) {
|
||||
@ -1143,7 +1142,7 @@ function wire_impl(
|
||||
query: string,
|
||||
stdin: ReadableStream<Uint8Array> | null,
|
||||
stdout: WritableStream<Uint8Array> | null
|
||||
): ResultStream<Row> {
|
||||
): RowStream<Row> {
|
||||
yield* await pipeline(
|
||||
() => {
|
||||
log("debug", { query }, `executing simple query`);
|
||||
@ -1189,7 +1188,7 @@ function wire_impl(
|
||||
params: unknown[],
|
||||
stdin: ReadableStream<Uint8Array> | null,
|
||||
stdout: WritableStream<Uint8Array> | null
|
||||
): ResultStream<Row> {
|
||||
): RowStream<Row> {
|
||||
const { query, name: statement } = st;
|
||||
const { ser_params, Row } = await st.parse();
|
||||
const param_values = ser_params(params);
|
||||
@ -1238,7 +1237,7 @@ function wire_impl(
|
||||
chunk_size: number,
|
||||
stdin: ReadableStream<Uint8Array> | null,
|
||||
stdout: WritableStream<Uint8Array> | null
|
||||
): ResultStream<Row> {
|
||||
): RowStream<Row> {
|
||||
const { query, name: statement } = st;
|
||||
const { ser_params, Row } = await st.parse();
|
||||
const param_values = ser_params(params);
|
||||
@ -1326,7 +1325,7 @@ function wire_impl(
|
||||
return tx_stack.indexOf(this) !== -1;
|
||||
}
|
||||
|
||||
constructor(begin: CommandResult) {
|
||||
constructor(begin: Result) {
|
||||
Object.assign(this, begin);
|
||||
}
|
||||
|
||||
@ -1384,7 +1383,7 @@ function wire_impl(
|
||||
return channels.get(this.#name) === this;
|
||||
}
|
||||
|
||||
constructor(name: string, listen: CommandResult) {
|
||||
constructor(name: string, listen: Result) {
|
||||
super();
|
||||
Object.assign(this, listen);
|
||||
this.#name = name;
|
||||
|
Loading…
Reference in New Issue
Block a user