@saptools/cf-hana 0.1.5 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +57 -9
- package/dist/cli.js +890 -26
- package/dist/cli.js.map +1 -1
- package/dist/index.js +57 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -478,14 +478,17 @@ function buildDelete(schema, table, where) {
|
|
|
478
478
|
// src/catalog.ts
|
|
479
479
|
async function listSchemas(connection) {
|
|
480
480
|
const result = await connection.query(
|
|
481
|
-
"SELECT SCHEMA_NAME FROM SYS.SCHEMAS ORDER BY SCHEMA_NAME"
|
|
481
|
+
"SELECT SCHEMA_NAME FROM SYS.SCHEMAS ORDER BY SCHEMA_NAME",
|
|
482
|
+
[],
|
|
483
|
+
{ autoLimit: false }
|
|
482
484
|
);
|
|
483
485
|
return result.rows.map((row) => row.SCHEMA_NAME);
|
|
484
486
|
}
|
|
485
487
|
async function listTables(connection, schema) {
|
|
486
488
|
const result = await connection.query(
|
|
487
489
|
"SELECT SCHEMA_NAME, TABLE_NAME, TABLE_TYPE FROM SYS.TABLES WHERE SCHEMA_NAME = ? ORDER BY TABLE_NAME",
|
|
488
|
-
[schema]
|
|
490
|
+
[schema],
|
|
491
|
+
{ autoLimit: false }
|
|
489
492
|
);
|
|
490
493
|
return result.rows.map((row) => ({
|
|
491
494
|
schema: row.SCHEMA_NAME,
|
|
@@ -497,7 +500,8 @@ async function listTables(connection, schema) {
|
|
|
497
500
|
async function listColumns(connection, schema, table) {
|
|
498
501
|
const result = await connection.query(
|
|
499
502
|
"SELECT COLUMN_NAME, DATA_TYPE_NAME, LENGTH, SCALE, IS_NULLABLE, POSITION FROM SYS.TABLE_COLUMNS WHERE SCHEMA_NAME = ? AND TABLE_NAME = ? ORDER BY POSITION",
|
|
500
|
-
[schema, table]
|
|
503
|
+
[schema, table],
|
|
504
|
+
{ autoLimit: false }
|
|
501
505
|
);
|
|
502
506
|
return result.rows.map((row) => ({
|
|
503
507
|
name: row.COLUMN_NAME,
|
|
@@ -510,13 +514,14 @@ async function listColumns(connection, schema, table) {
|
|
|
510
514
|
}
|
|
511
515
|
|
|
512
516
|
// src/config.ts
|
|
513
|
-
var CLI_VERSION = "0.
|
|
517
|
+
var CLI_VERSION = "0.2.0";
|
|
514
518
|
var ENV_PREFIX = "CF_HANA";
|
|
515
519
|
var DEFAULT_QUERY_TIMEOUT_MS = 3e4;
|
|
516
520
|
var DEFAULT_CONNECT_TIMEOUT_MS = 3e4;
|
|
517
521
|
var DEFAULT_POOL_MAX = 4;
|
|
518
522
|
var DEFAULT_POOL_IDLE_MS = 6e4;
|
|
519
|
-
var DEFAULT_AUTO_LIMIT =
|
|
523
|
+
var DEFAULT_AUTO_LIMIT = 100;
|
|
524
|
+
var MAX_RESULT_STORE_BYTES = 256 * 1024 * 1024;
|
|
520
525
|
function envName(suffix) {
|
|
521
526
|
return `${ENV_PREFIX}_${suffix}`;
|
|
522
527
|
}
|
|
@@ -629,7 +634,13 @@ function toConnectionTarget(binding, role) {
|
|
|
629
634
|
}
|
|
630
635
|
|
|
631
636
|
// src/driver/fake.ts
|
|
637
|
+
import { appendFile } from "fs/promises";
|
|
632
638
|
function fakeExec(sql) {
|
|
639
|
+
const kind = classifyStatement(sql);
|
|
640
|
+
const forcedFailure = readEnv(envName("FAKE_FAIL_STATEMENT"))?.toLowerCase();
|
|
641
|
+
if (forcedFailure === kind) {
|
|
642
|
+
throw new Error(`fake driver forced ${kind.toUpperCase()} failure`);
|
|
643
|
+
}
|
|
633
644
|
if (sql.toUpperCase().includes("DUMMY")) {
|
|
634
645
|
return {
|
|
635
646
|
rows: [{ "1": 1 }],
|
|
@@ -637,7 +648,6 @@ function fakeExec(sql) {
|
|
|
637
648
|
affectedRows: 0
|
|
638
649
|
};
|
|
639
650
|
}
|
|
640
|
-
const kind = classifyStatement(sql);
|
|
641
651
|
if (kind === "select") {
|
|
642
652
|
return {
|
|
643
653
|
rows: [
|
|
@@ -656,10 +666,23 @@ function fakeExec(sql) {
|
|
|
656
666
|
}
|
|
657
667
|
return { rows: [], columns: [], affectedRows: 0 };
|
|
658
668
|
}
|
|
669
|
+
async function traceFakeExec(sql, params) {
|
|
670
|
+
const tracePath = readEnv(envName("FAKE_TRACE_FILE"));
|
|
671
|
+
if (tracePath === void 0) {
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
const entry = { sql, paramCount: params.length };
|
|
675
|
+
await appendFile(tracePath, `${JSON.stringify(entry)}
|
|
676
|
+
`, {
|
|
677
|
+
encoding: "utf8",
|
|
678
|
+
mode: 384
|
|
679
|
+
});
|
|
680
|
+
}
|
|
659
681
|
var FakeConnection = class {
|
|
660
682
|
closed = false;
|
|
661
|
-
exec(sql,
|
|
662
|
-
|
|
683
|
+
async exec(sql, params) {
|
|
684
|
+
await traceFakeExec(sql, params);
|
|
685
|
+
return fakeExec(sql);
|
|
663
686
|
}
|
|
664
687
|
setAutoCommit(_enabled) {
|
|
665
688
|
return Promise.resolve();
|
|
@@ -933,7 +956,7 @@ function createDriver(name) {
|
|
|
933
956
|
}
|
|
934
957
|
|
|
935
958
|
// src/history.ts
|
|
936
|
-
import { appendFile, mkdir as mkdir2, readdir, rm } from "fs/promises";
|
|
959
|
+
import { appendFile as appendFile2, mkdir as mkdir2, readdir, rm } from "fs/promises";
|
|
937
960
|
import { homedir as homedir2 } from "os";
|
|
938
961
|
import { join as join2 } from "path";
|
|
939
962
|
var SAPTOOLS_DIR_NAME2 = ".saptools";
|
|
@@ -995,7 +1018,7 @@ async function appendSqlHistory(input, options = {}) {
|
|
|
995
1018
|
...input
|
|
996
1019
|
};
|
|
997
1020
|
await mkdir2(historyDir, { recursive: true, mode: 448 });
|
|
998
|
-
await
|
|
1021
|
+
await appendFile2(sqlHistoryFilePath(now, options.saptoolsRoot), `${JSON.stringify(entry)}
|
|
999
1022
|
`, {
|
|
1000
1023
|
encoding: "utf8",
|
|
1001
1024
|
mode: 384
|
|
@@ -1152,10 +1175,28 @@ function applyAutoLimit(sql, limit) {
|
|
|
1152
1175
|
if (/\blimit\b/i.test(stripped) || /\btop\s+\d/i.test(stripped)) {
|
|
1153
1176
|
return { sql, applied: false };
|
|
1154
1177
|
}
|
|
1155
|
-
return {
|
|
1178
|
+
return {
|
|
1179
|
+
sql: appendLimit(sql, limit + 1),
|
|
1180
|
+
applied: true,
|
|
1181
|
+
requestedLimit: limit
|
|
1182
|
+
};
|
|
1156
1183
|
}
|
|
1157
1184
|
|
|
1158
1185
|
// src/connection.ts
|
|
1186
|
+
function assertValidAutoLimit(value) {
|
|
1187
|
+
if (value !== false && (!Number.isSafeInteger(value) || value <= 0 || value >= Number.MAX_SAFE_INTEGER)) {
|
|
1188
|
+
throw new CfHanaError("CONFIG", "autoLimit must be a positive safe integer");
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
function boundedRows(rows, requestedLimit) {
|
|
1192
|
+
if (requestedLimit === void 0) {
|
|
1193
|
+
return { rows, truncated: false };
|
|
1194
|
+
}
|
|
1195
|
+
return {
|
|
1196
|
+
rows: rows.slice(0, requestedLimit),
|
|
1197
|
+
truncated: rows.length > requestedLimit
|
|
1198
|
+
};
|
|
1199
|
+
}
|
|
1159
1200
|
async function withTimeout(work, timeoutMs, onTimeout) {
|
|
1160
1201
|
let timer;
|
|
1161
1202
|
const timeout = new Promise((_resolve, reject) => {
|
|
@@ -1229,6 +1270,7 @@ var Connection = class _Connection {
|
|
|
1229
1270
|
}
|
|
1230
1271
|
const kind = classifyStatement(sql);
|
|
1231
1272
|
const autoLimit = options.autoLimit ?? this.config.autoLimit;
|
|
1273
|
+
assertValidAutoLimit(autoLimit);
|
|
1232
1274
|
const limited = kind === "select" ? applyAutoLimit(sql, autoLimit) : { sql, applied: false };
|
|
1233
1275
|
const timeoutMs = options.timeoutMs ?? this.config.queryTimeoutMs;
|
|
1234
1276
|
const started = Date.now();
|
|
@@ -1240,12 +1282,13 @@ var Connection = class _Connection {
|
|
|
1240
1282
|
}
|
|
1241
1283
|
);
|
|
1242
1284
|
const elapsedMs = Date.now() - started;
|
|
1285
|
+
const selected = boundedRows(execResult.rows, limited.requestedLimit);
|
|
1243
1286
|
return {
|
|
1244
|
-
rows:
|
|
1287
|
+
rows: selected.rows,
|
|
1245
1288
|
columns: execResult.columns,
|
|
1246
|
-
rowCount: kind === "select" ?
|
|
1289
|
+
rowCount: kind === "select" ? selected.rows.length : execResult.affectedRows,
|
|
1247
1290
|
statement: kind,
|
|
1248
|
-
truncated:
|
|
1291
|
+
truncated: selected.truncated,
|
|
1249
1292
|
elapsedMs
|
|
1250
1293
|
};
|
|
1251
1294
|
}
|