142 lines
4.7 KiB
TypeScript
142 lines
4.7 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
import {
|
|
buildCountQuerySql,
|
|
buildPaginatedQuerySql,
|
|
buildQueryPaginationExecutionPlan,
|
|
} from "../src/lib/queryResultPagination.ts";
|
|
|
|
test("wraps a single select query with limit and offset", () => {
|
|
const result = buildPaginatedQuerySql("SELECT id, name FROM users;", "postgres", 100, 200);
|
|
|
|
assert.deepEqual(result, {
|
|
ok: true,
|
|
sql: 'SELECT * FROM (SELECT id, name FROM users) "dbx_page" LIMIT 100 OFFSET 200;',
|
|
});
|
|
});
|
|
|
|
test("uses MySQL style quoting for paginated query alias", () => {
|
|
const result = buildPaginatedQuerySql("SELECT id FROM users WHERE active = 1", "mysql", 50, 0);
|
|
|
|
assert.deepEqual(result, {
|
|
ok: true,
|
|
sql: "SELECT * FROM (SELECT id FROM users WHERE active = 1) `dbx_page` LIMIT 50;",
|
|
});
|
|
});
|
|
|
|
test("uses SQL Server offset fetch pagination", () => {
|
|
const result = buildPaginatedQuerySql("SELECT id FROM users", "sqlserver", 100, 300);
|
|
|
|
assert.deepEqual(result, {
|
|
ok: true,
|
|
sql: "SELECT * FROM (SELECT id FROM users) [dbx_page] ORDER BY (SELECT NULL) OFFSET 300 ROWS FETCH NEXT 100 ROWS ONLY",
|
|
});
|
|
});
|
|
|
|
test("uses fetch first pagination for Oracle first page", () => {
|
|
const result = buildPaginatedQuerySql("SELECT id FROM users", "oracle", 100, 0);
|
|
|
|
assert.deepEqual(result, {
|
|
ok: true,
|
|
sql: 'SELECT * FROM (SELECT id FROM users) "dbx_page" FETCH FIRST 100 ROWS ONLY',
|
|
});
|
|
});
|
|
|
|
test("supports CTE select queries", () => {
|
|
const result = buildPaginatedQuerySql("WITH cte AS (SELECT 1 AS id) SELECT * FROM cte", "clickhouse", 100, 0);
|
|
|
|
assert.deepEqual(result, {
|
|
ok: true,
|
|
sql: 'SELECT * FROM (WITH cte AS (SELECT 1 AS id) SELECT * FROM cte) "dbx_page" LIMIT 100;',
|
|
});
|
|
});
|
|
|
|
test("rejects multiple statements", () => {
|
|
const result = buildPaginatedQuerySql("SELECT 1; SELECT 2;", "postgres", 100, 0);
|
|
|
|
assert.deepEqual(result, { ok: false, reason: "multi" });
|
|
});
|
|
|
|
test("rejects non select statements", () => {
|
|
const result = buildPaginatedQuerySql("UPDATE users SET name = 'A'", "postgres", 100, 0);
|
|
|
|
assert.deepEqual(result, { ok: false, reason: "not_select" });
|
|
});
|
|
|
|
test("wraps a single select query for total row count", () => {
|
|
const result = buildCountQuerySql("SELECT id, name FROM users;", "postgres");
|
|
|
|
assert.deepEqual(result, {
|
|
ok: true,
|
|
sql: 'SELECT COUNT(*) AS dbx_total_rows FROM (SELECT id, name FROM users) "dbx_count";',
|
|
});
|
|
});
|
|
|
|
test("uses MySQL style quoting for count query alias", () => {
|
|
const result = buildCountQuerySql("WITH cte AS (SELECT 1 AS id) SELECT * FROM cte", "mysql");
|
|
|
|
assert.deepEqual(result, {
|
|
ok: true,
|
|
sql: "SELECT COUNT(*) AS dbx_total_rows FROM (WITH cte AS (SELECT 1 AS id) SELECT * FROM cte) `dbx_count`;",
|
|
});
|
|
});
|
|
|
|
test("rejects count query for unsupported database types", () => {
|
|
const result = buildCountQuerySql("SELECT * FROM nodes", "neo4j");
|
|
|
|
assert.deepEqual(result, { ok: false, reason: "unsupported" });
|
|
});
|
|
|
|
test("rejects count query for multiple statements", () => {
|
|
const result = buildCountQuerySql("SELECT 1; SELECT 2;", "postgres");
|
|
|
|
assert.deepEqual(result, { ok: false, reason: "multi" });
|
|
});
|
|
|
|
test("uses an agent result session for the first jdbc page", () => {
|
|
const plan = buildQueryPaginationExecutionPlan({
|
|
sql: "SELECT * FROM events",
|
|
queryBaseSql: "SELECT * FROM events",
|
|
databaseType: "oracle",
|
|
pagination: { limit: 500, offset: 0 },
|
|
useAgentCursor: true,
|
|
});
|
|
|
|
assert.equal(plan.sqlToExecute, "SELECT * FROM events");
|
|
assert.equal(plan.pageLimit, 500);
|
|
assert.equal(plan.pageOffset, 0);
|
|
assert.equal(plan.pageSql, undefined);
|
|
assert.equal(plan.useAgentResultSession, true);
|
|
});
|
|
|
|
test("keeps using an agent result session for sequential jdbc pages", () => {
|
|
const plan = buildQueryPaginationExecutionPlan({
|
|
sql: "SELECT * FROM events",
|
|
queryBaseSql: "SELECT * FROM events",
|
|
databaseType: "oracle",
|
|
pagination: { limit: 500, offset: 500, sessionId: "session-1" },
|
|
useAgentCursor: true,
|
|
});
|
|
|
|
assert.equal(plan.sqlToExecute, "SELECT * FROM events");
|
|
assert.equal(plan.pageLimit, 500);
|
|
assert.equal(plan.pageOffset, 500);
|
|
assert.equal(plan.useAgentResultSession, true);
|
|
});
|
|
|
|
test("uses SQL pagination instead of jdbc cursor for random agent page jumps", () => {
|
|
const plan = buildQueryPaginationExecutionPlan({
|
|
sql: "SELECT * FROM events",
|
|
queryBaseSql: "SELECT * FROM events",
|
|
databaseType: "oracle",
|
|
pagination: { limit: 500, offset: 1500 },
|
|
useAgentCursor: true,
|
|
});
|
|
|
|
assert.equal(plan.sqlToExecute, 'SELECT * FROM (SELECT * FROM events) "dbx_page" OFFSET 1500 ROWS FETCH FIRST 500 ROWS ONLY');
|
|
assert.equal(plan.pageSql, plan.sqlToExecute);
|
|
assert.equal(plan.pageLimit, 500);
|
|
assert.equal(plan.pageOffset, 1500);
|
|
assert.equal(plan.useAgentResultSession, false);
|
|
});
|