Skip to content

Commit

Permalink
Merge pull request #61 from libsql/json-str
Browse files Browse the repository at this point in the history
Add `ResultSet.toJSON()`
  • Loading branch information
honzasp authored Jul 13, 2023
2 parents 9213518 + ac71b63 commit 3de17d1
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 12 deletions.
44 changes: 44 additions & 0 deletions src/__tests__/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,50 @@ describe("values", () => {
}));
});

describe("ResultSet.toJSON()", () => {
test("simple result set", withClient(async (c) => {
const rs = await c.execute("SELECT 1 AS a");
const json = rs.toJSON();
expect(json["lastInsertRowid"] === null || json["lastInsertRowid"] === "0").toBe(true);
expect(json["columns"]).toStrictEqual(["a"]);
expect(json["rows"]).toStrictEqual([[1]]);
expect(json["rowsAffected"]).toStrictEqual(0);

const str = JSON.stringify(rs);
expect(
str === '{"columns":["a"],"rows":[[1]],"rowsAffected":0,"lastInsertRowid":null}' ||
str === '{"columns":["a"],"rows":[[1]],"rowsAffected":0,"lastInsertRowid":"0"}'
).toBe(true);
}));

test("lastInsertRowid", withClient(async (c) => {
await c.execute("DROP TABLE IF EXISTS t");
await c.execute("CREATE TABLE t (id INTEGER PRIMARY KEY NOT NULL)");
const rs = await c.execute("INSERT INTO t VALUES (12345)");
expect(rs.toJSON()).toStrictEqual({
"columns": [],
"rows": [],
"rowsAffected": 1,
"lastInsertRowid": "12345",
});
}));

test("row values", withClient(async (c) => {
const rs = await c.execute(
"SELECT 42 AS integer, 0.5 AS float, NULL AS \"null\", 'foo' AS text, X'626172' AS blob",
);
const json = rs.toJSON();
expect(json["columns"]).toStrictEqual(["integer", "float", "null", "text", "blob"]);
expect(json["rows"]).toStrictEqual([[42, 0.5, null, "foo", "YmFy"]]);
}));

test("bigint row value", withClient(async (c) => {
const rs = await c.execute("SELECT 42");
const json = rs.toJSON();
expect(json["rows"]).toStrictEqual([["42"]]);
}, {intMode: "bigint"}));
});

describe("arguments", () => {
test("? arguments", withClient(async (c) => {
const rs = await c.execute({
Expand Down
6 changes: 6 additions & 0 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,12 @@ export interface ResultSet {
* table.
*/
lastInsertRowid: bigint | undefined;

/** Converts the result set to JSON.
*
* This is used automatically by `JSON.stringify()`, but you can also call it explicitly.
*/
toJSON(): any;
}

/** Row returned from an SQL statement.
Expand Down
15 changes: 7 additions & 8 deletions src/hrana.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as hrana from "@libsql/hrana-client";
import type { InStatement, ResultSet, Transaction, TransactionMode } from "./api.js";
import { LibsqlError } from "./api.js";
import type { SqlCache } from "./sql_cache.js";
import { transactionModeToBegin } from "./util.js";
import { transactionModeToBegin, ResultSetImpl } from "./util.js";

export abstract class HranaTransaction implements Transaction {
#mode: TransactionMode;
Expand Down Expand Up @@ -316,13 +316,12 @@ export function stmtToHrana(stmt: InStatement): hrana.Stmt {
}

export function resultSetFromHrana(hranaRows: hrana.RowsResult): ResultSet {
return {
columns: hranaRows.columnNames.map(c => c ?? ""),
rows: hranaRows.rows,
rowsAffected: hranaRows.affectedRowCount,
lastInsertRowid: hranaRows.lastInsertRowid !== undefined
? BigInt(hranaRows.lastInsertRowid) : undefined,
};
const columns = hranaRows.columnNames.map(c => c ?? "");
const rows = hranaRows.rows;
const rowsAffected = hranaRows.affectedRowCount;
const lastInsertRowid = hranaRows.lastInsertRowid !== undefined
? BigInt(hranaRows.lastInsertRowid) : undefined;
return new ResultSetImpl(columns, rows, rowsAffected, lastInsertRowid);
}

export function mapHranaError(e: unknown): unknown {
Expand Down
6 changes: 3 additions & 3 deletions src/sqlite3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type {
import { LibsqlError } from "./api.js";
import type { ExpandedConfig } from "./config.js";
import { expandConfig } from "./config.js";
import { supportedUrlLink, transactionModeToBegin } from "./util.js";
import { supportedUrlLink, transactionModeToBegin, ResultSetImpl } from "./util.js";

export * from "./api.js";

Expand Down Expand Up @@ -226,12 +226,12 @@ function executeStmt(db: Database.Database, stmt: InStatement, intMode: IntMode)
// TODO: can we get this info from better-sqlite3?
const rowsAffected = 0;
const lastInsertRowid = undefined;
return { columns, rows, rowsAffected, lastInsertRowid };
return new ResultSetImpl(columns, rows, rowsAffected, lastInsertRowid);
} else {
const info = sqlStmt.run(args);
const rowsAffected = info.changes;
const lastInsertRowid = BigInt(info.lastInsertRowid);
return { columns: [], rows: [], rowsAffected, lastInsertRowid };
return new ResultSetImpl([], [], rowsAffected, lastInsertRowid);
}
} catch (e) {
throw mapSqliteError(e);
Expand Down
45 changes: 44 additions & 1 deletion src/util.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { TransactionMode, InStatement, LibsqlError } from "./api.js";
import { Base64 } from "js-base64";
import { ResultSet, Row, Value, TransactionMode, InStatement, LibsqlError } from "./api.js";

export const supportedUrlLink = "https://github.com/libsql/libsql-client-ts#supported-urls";

Expand All @@ -13,3 +14,45 @@ export function transactionModeToBegin(mode: TransactionMode): string {
throw RangeError('Unknown transaction mode, supported values are "write", "read" and "deferred"');
}
}

export class ResultSetImpl implements ResultSet {
columns: Array<string>;
rows: Array<Row>;
rowsAffected: number;
lastInsertRowid: bigint | undefined;

constructor(
columns: Array<string>,
rows: Array<Row>,
rowsAffected: number,
lastInsertRowid: bigint | undefined,
) {
this.columns = columns;
this.rows = rows;
this.rowsAffected = rowsAffected;
this.lastInsertRowid = lastInsertRowid;
}

toJSON(): any {
return {
"columns": this.columns,
"rows": this.rows.map(rowToJson),
"rowsAffected": this.rowsAffected,
"lastInsertRowid": this.lastInsertRowid !== undefined ? ""+this.lastInsertRowid : null,
};
}
}

function rowToJson(row: Row): unknown {
return Array.prototype.map.call(row, valueToJson);
}

function valueToJson(value: Value): unknown {
if (typeof value === "bigint") {
return ""+value;
} else if (value instanceof ArrayBuffer) {
return Base64.fromUint8Array(new Uint8Array(value));
} else {
return value;
}
}

0 comments on commit 3de17d1

Please sign in to comment.