@saptools/cf-hana 0.1.6 → 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 +13 -0
- package/README.md +57 -9
- package/dist/cli.js +867 -21
- package/dist/cli.js.map +1 -1
- package/dist/index.js +34 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/backup.ts","../src/errors.ts","../src/format.ts","../src/statements.ts","../src/builder.ts","../src/catalog.ts","../src/config.ts","../src/credentials.ts","../src/driver/fake.ts","../src/driver/hdb.ts","../src/driver/index.ts","../src/history.ts","../src/safety.ts","../src/connection.ts","../src/pool.ts","../src/transaction.ts","../src/client.ts","../src/api.ts"],"sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport { QueryError } from \"./errors.js\";\nimport { formatCsv } from \"./format.js\";\nimport { assertParamArity, countPlaceholders, firstKeyword } from \"./statements.js\";\nimport type { QueryResult, SqlParam } from \"./types.js\";\n\nconst SAPTOOLS_DIR_NAME = \".saptools\";\nconst CF_HANA_DIR_NAME = \"cf-hana\";\nconst BACKUPS_DIR_NAME = \"backups\";\n\nexport type WriteBackupOperation = \"update\" | \"delete\";\n\nexport interface WriteBackupPlan {\n readonly operation: WriteBackupOperation;\n readonly statementSql: string;\n readonly selectSql: string;\n readonly selectParams: readonly SqlParam[];\n}\n\nexport interface SqlBackupWriteInput {\n readonly operation: WriteBackupOperation;\n readonly statementSql: string;\n readonly result: QueryResult;\n}\n\nexport interface SqlBackupWriteOptions {\n readonly now?: Date;\n readonly saptoolsRoot?: string;\n}\n\nexport interface SqlBackupRecord {\n readonly directory: string;\n readonly statementPath: string;\n readonly backupPath: string;\n readonly rowCount: number;\n}\n\ntype TopLevelKeyword = \"UPDATE\" | \"DELETE\" | \"SET\" | \"FROM\" | \"WHERE\";\n\nfunction defaultSaptoolsRoot(): string {\n return join(homedir(), SAPTOOLS_DIR_NAME);\n}\n\nfunction trimStatementSql(sql: string): string {\n return sql.trim().replace(/;+\\s*$/, \"\").trim();\n}\n\nfunction isIdentifierChar(char: string | undefined): boolean {\n return char !== undefined && /[A-Za-z0-9_$#]/.test(char);\n}\n\nfunction skipQuotedText(sql: string, index: number): number {\n const quote = sql[index];\n let cursor = index + 1;\n while (cursor < sql.length) {\n if (sql[cursor] === quote) {\n if (sql[cursor + 1] === quote) {\n cursor += 2;\n continue;\n }\n return cursor + 1;\n }\n cursor += 1;\n }\n return cursor;\n}\n\nfunction skipLineComment(sql: string, index: number): number {\n let cursor = index + 2;\n while (cursor < sql.length && sql[cursor] !== \"\\n\") {\n cursor += 1;\n }\n return cursor;\n}\n\nfunction skipBlockComment(sql: string, index: number): number {\n let cursor = index + 2;\n while (cursor < sql.length && !(sql[cursor] === \"*\" && sql[cursor + 1] === \"/\")) {\n cursor += 1;\n }\n return Math.min(cursor + 2, sql.length);\n}\n\nfunction skipNonCode(sql: string, index: number): number | undefined {\n const char = sql[index];\n if (char === \"'\" || char === '\"') {\n return skipQuotedText(sql, index);\n }\n if (char === \"-\" && sql[index + 1] === \"-\") {\n return skipLineComment(sql, index);\n }\n if (char === \"/\" && sql[index + 1] === \"*\") {\n return skipBlockComment(sql, index);\n }\n return undefined;\n}\n\nfunction keywordMatches(sql: string, index: number, keyword: TopLevelKeyword): boolean {\n const end = index + keyword.length;\n return (\n sql.slice(index, end).toUpperCase() === keyword &&\n !isIdentifierChar(sql[index - 1]) &&\n !isIdentifierChar(sql[end])\n );\n}\n\nfunction findTopLevelKeyword(\n sql: string,\n keyword: TopLevelKeyword,\n startIndex = 0,\n): number | undefined {\n let index = startIndex;\n let depth = 0;\n while (index < sql.length) {\n const skipped = skipNonCode(sql, index);\n if (skipped !== undefined) {\n index = skipped;\n continue;\n }\n\n const char = sql[index];\n if (char === \"(\") {\n depth += 1;\n } else if (char === \")\" && depth > 0) {\n depth -= 1;\n } else if (depth === 0 && keywordMatches(sql, index, keyword)) {\n return index;\n }\n index += 1;\n }\n return undefined;\n}\n\nfunction selectParamsAfterWhere(\n statementSql: string,\n whereIndex: number,\n params: readonly SqlParam[],\n): readonly SqlParam[] {\n return params.slice(countPlaceholders(statementSql.slice(0, whereIndex)));\n}\n\nfunction buildSelectPlan(\n operation: WriteBackupOperation,\n statementSql: string,\n targetSql: string,\n whereIndex: number | undefined,\n params: readonly SqlParam[],\n): WriteBackupPlan {\n const target = targetSql.trim();\n if (target.length === 0) {\n throw new QueryError(`${operation.toUpperCase()} backup requires a target table`);\n }\n if (whereIndex === undefined) {\n return { operation, statementSql, selectSql: `SELECT * FROM ${target}`, selectParams: [] };\n }\n\n const whereSql = statementSql.slice(whereIndex + \"WHERE\".length).trim();\n if (whereSql.length === 0) {\n throw new QueryError(`${operation.toUpperCase()} backup requires a non-empty WHERE clause`);\n }\n return {\n operation,\n statementSql,\n selectSql: `SELECT * FROM ${target} WHERE ${whereSql}`,\n selectParams: selectParamsAfterWhere(statementSql, whereIndex, params),\n };\n}\n\nfunction buildUpdateBackupPlan(\n statementSql: string,\n params: readonly SqlParam[],\n): WriteBackupPlan {\n const updateIndex = findTopLevelKeyword(statementSql, \"UPDATE\");\n const setIndex =\n updateIndex === undefined\n ? undefined\n : findTopLevelKeyword(statementSql, \"SET\", updateIndex + \"UPDATE\".length);\n if (updateIndex === undefined || setIndex === undefined) {\n throw new QueryError(\"UPDATE backup requires UPDATE <target> SET syntax\");\n }\n\n const whereIndex = findTopLevelKeyword(statementSql, \"WHERE\", setIndex + \"SET\".length);\n return buildSelectPlan(\n \"update\",\n statementSql,\n statementSql.slice(updateIndex + \"UPDATE\".length, setIndex),\n whereIndex,\n params,\n );\n}\n\nfunction buildDeleteBackupPlan(\n statementSql: string,\n params: readonly SqlParam[],\n): WriteBackupPlan {\n const deleteIndex = findTopLevelKeyword(statementSql, \"DELETE\");\n const fromIndex =\n deleteIndex === undefined\n ? undefined\n : findTopLevelKeyword(statementSql, \"FROM\", deleteIndex + \"DELETE\".length);\n if (deleteIndex === undefined || fromIndex === undefined) {\n throw new QueryError(\"DELETE backup requires DELETE FROM <target> syntax\");\n }\n\n const whereIndex = findTopLevelKeyword(statementSql, \"WHERE\", fromIndex + \"FROM\".length);\n const targetEnd = whereIndex ?? statementSql.length;\n return buildSelectPlan(\n \"delete\",\n statementSql,\n statementSql.slice(fromIndex + \"FROM\".length, targetEnd),\n whereIndex,\n params,\n );\n}\n\nfunction backupTimestamp(now: Date): string {\n return now.toISOString().replace(/:/g, \"\").replace(\".\", \"\");\n}\n\nfunction backupHash(statementSql: string): string {\n return createHash(\"sha256\")\n .update(statementSql)\n .update(\"\\0\")\n .update(randomUUID())\n .digest(\"hex\")\n .slice(0, 12);\n}\n\nexport function cfHanaBackupRoot(saptoolsRoot?: string): string {\n return join(saptoolsRoot ?? defaultSaptoolsRoot(), CF_HANA_DIR_NAME, BACKUPS_DIR_NAME);\n}\n\nexport function buildWriteBackupPlan(\n sql: string,\n params: readonly SqlParam[] = [],\n): WriteBackupPlan | undefined {\n const statementSql = trimStatementSql(sql);\n const keyword = firstKeyword(statementSql);\n if (keyword !== \"UPDATE\" && keyword !== \"DELETE\") {\n return undefined;\n }\n\n assertParamArity(statementSql, params);\n if (keyword === \"UPDATE\") {\n return buildUpdateBackupPlan(statementSql, params);\n }\n return buildDeleteBackupPlan(statementSql, params);\n}\n\nexport async function writeSqlBackup(\n input: SqlBackupWriteInput,\n options: SqlBackupWriteOptions = {},\n): Promise<SqlBackupRecord> {\n const now = options.now ?? new Date();\n const directory = join(\n cfHanaBackupRoot(options.saptoolsRoot),\n `${backupTimestamp(now)}-${input.operation}-${backupHash(input.statementSql)}`,\n );\n const statementPath = join(directory, \"statement.sql\");\n const backupPath = join(directory, \"backup.csv\");\n\n await mkdir(directory, { recursive: true, mode: 0o700 });\n await Promise.all([\n writeFile(statementPath, `${input.statementSql}\\n`, { encoding: \"utf8\", mode: 0o600 }),\n writeFile(backupPath, formatCsv(input.result), { encoding: \"utf8\", mode: 0o600 }),\n ]);\n\n return {\n directory,\n statementPath,\n backupPath,\n rowCount: input.result.rowCount,\n };\n}\n","export type CfHanaErrorCode =\n | \"CONFIG\"\n | \"CREDENTIALS_NOT_FOUND\"\n | \"AMBIGUOUS_BINDING\"\n | \"CONNECTION\"\n | \"QUERY\"\n | \"READ_ONLY_VIOLATION\"\n | \"DESTRUCTIVE_BLOCKED\"\n | \"TIMEOUT\"\n | \"POOL_CLOSED\";\n\nexport interface CfHanaErrorOptions {\n readonly cause?: unknown;\n}\n\nexport interface QueryErrorOptions extends CfHanaErrorOptions {\n readonly sqlState?: string;\n}\n\n/** Base error for every failure surfaced by `@saptools/cf-hana`. */\nexport class CfHanaError extends Error {\n readonly code: CfHanaErrorCode;\n\n constructor(code: CfHanaErrorCode, message: string, options?: CfHanaErrorOptions) {\n super(message, options);\n this.name = \"CfHanaError\";\n this.code = code;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** No HANA credentials could be resolved for the requested selector. */\nexport class CredentialsNotFoundError extends CfHanaError {\n constructor(message: string, options?: CfHanaErrorOptions) {\n super(\"CREDENTIALS_NOT_FOUND\", message, options);\n this.name = \"CredentialsNotFoundError\";\n }\n}\n\n/** A SQL statement failed on the HANA server. */\nexport class QueryError extends CfHanaError {\n readonly sqlState: string | undefined;\n\n constructor(message: string, options?: QueryErrorOptions) {\n super(\"QUERY\", message, options);\n this.name = \"QueryError\";\n this.sqlState = options?.sqlState;\n }\n}\n\n/** A write/DDL statement was issued on a read-only client. */\nexport class ReadOnlyViolationError extends CfHanaError {\n constructor(message: string, options?: CfHanaErrorOptions) {\n super(\"READ_ONLY_VIOLATION\", message, options);\n this.name = \"ReadOnlyViolationError\";\n }\n}\n\n/** A destructive statement was blocked because it was not explicitly allowed. */\nexport class DestructiveStatementError extends CfHanaError {\n constructor(message: string, options?: CfHanaErrorOptions) {\n super(\"DESTRUCTIVE_BLOCKED\", message, options);\n this.name = \"DestructiveStatementError\";\n }\n}\n\n/** Narrow an unknown thrown value to a human-readable message. */\nexport function errorMessage(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n return String(error);\n}\n","import type { OutputFormat, QueryResult, SqlParam } from \"./types.js\";\n\ntype JsonCell = string | number | boolean | null;\n\nfunction cellText(value: SqlParam, nullText: string): string {\n if (value === null) {\n return nullText;\n }\n if (value instanceof Date) {\n return value.toISOString();\n }\n if (Buffer.isBuffer(value)) {\n return `0x${value.toString(\"hex\")}`;\n }\n if (typeof value === \"boolean\") {\n return value ? \"true\" : \"false\";\n }\n return typeof value === \"number\" ? value.toString() : value;\n}\n\nfunction serializeCell(value: SqlParam): JsonCell {\n if (value === null) {\n return null;\n }\n if (value instanceof Date) {\n return value.toISOString();\n }\n if (Buffer.isBuffer(value)) {\n return `0x${value.toString(\"hex\")}`;\n }\n return value;\n}\n\nfunction csvEscape(text: string): string {\n if (/[\",\\r\\n]/.test(text)) {\n return `\"${text.replace(/\"/g, '\"\"')}\"`;\n }\n return text;\n}\n\n/** Render a result as an aligned ASCII table. */\nexport function formatTable(result: QueryResult): string {\n if (result.columns.length === 0) {\n return `(${String(result.rowCount)} row(s) affected)`;\n }\n\n const headers = result.columns.map((column) => column.name);\n const rows = result.rows.map((row) =>\n result.columns.map((column) => cellText(row[column.name] ?? null, \"NULL\")),\n );\n const widths = headers.map((header, index) => {\n const widest = rows.reduce(\n (max, cells) => Math.max(max, (cells[index] ?? \"\").length),\n header.length,\n );\n return widest;\n });\n\n const renderRow = (cells: readonly string[]): string =>\n cells.map((cell, index) => cell.padEnd(widths[index] ?? 0)).join(\" | \");\n const separator = widths.map((width) => \"-\".repeat(width)).join(\"-+-\");\n const body = rows.map((cells) => renderRow(cells));\n\n return [renderRow(headers), separator, ...body].join(\"\\n\");\n}\n\n/** Render a result's rows as pretty-printed JSON. */\nexport function formatJson(result: QueryResult): string {\n const rows = result.rows.map((row) => {\n const serialized: Record<string, JsonCell> = {};\n for (const [key, value] of Object.entries(row)) {\n serialized[key] = serializeCell(value);\n }\n return serialized;\n });\n return JSON.stringify(rows, null, 2);\n}\n\n/** Render a result as RFC 4180 CSV. */\nexport function formatCsv(result: QueryResult): string {\n const headers = result.columns.map((column) => column.name);\n const lines = [headers.map((header) => csvEscape(header)).join(\",\")];\n for (const row of result.rows) {\n lines.push(\n result.columns\n .map((column) => csvEscape(cellText(row[column.name] ?? null, \"\")))\n .join(\",\"),\n );\n }\n return lines.join(\"\\r\\n\");\n}\n\n/** Render a query result in the requested output format. */\nexport function formatResult(result: QueryResult, format: OutputFormat): string {\n switch (format) {\n case \"table\":\n return formatTable(result);\n case \"json\":\n return formatJson(result);\n case \"csv\":\n return formatCsv(result);\n }\n}\n","import { QueryError } from \"./errors.js\";\nimport type { SqlParam, StatementKind } from \"./types.js\";\n\nconst LEADING_NOISE = /^(?:\\s|--[^\\n]*\\n?|\\/\\*[\\s\\S]*?\\*\\/)+/;\n\nconst SELECT_KEYWORDS = new Set([\"SELECT\", \"WITH\"]);\nconst DML_KEYWORDS = new Set([\"INSERT\", \"UPDATE\", \"DELETE\", \"MERGE\", \"UPSERT\", \"REPLACE\"]);\nconst DDL_KEYWORDS = new Set([\"CREATE\", \"DROP\", \"ALTER\", \"TRUNCATE\", \"RENAME\", \"COMMENT\"]);\n\n/** The leading SQL keyword, upper-cased, skipping comments and whitespace. */\nexport function firstKeyword(sql: string): string {\n const stripped = sql.replace(LEADING_NOISE, \"\");\n const match = /^[A-Za-z]+/.exec(stripped);\n return (match?.[0] ?? \"\").toUpperCase();\n}\n\n/** Classify a SQL statement by its leading keyword. */\nexport function classifyStatement(sql: string): StatementKind {\n const keyword = firstKeyword(sql);\n if (SELECT_KEYWORDS.has(keyword)) {\n return \"select\";\n }\n if (DML_KEYWORDS.has(keyword)) {\n return \"dml\";\n }\n if (DDL_KEYWORDS.has(keyword)) {\n return \"ddl\";\n }\n return \"unknown\";\n}\n\n/** Quote a SQL identifier (table/column name) for safe interpolation. */\nexport function quoteIdentifier(identifier: string): string {\n if (identifier.length === 0) {\n throw new QueryError(\"A SQL identifier must not be empty\");\n }\n if (identifier.includes(\"\\0\")) {\n throw new QueryError(\"A SQL identifier must not contain a NUL character\");\n }\n return `\"${identifier.replace(/\"/g, '\"\"')}\"`;\n}\n\n/** Build a `\"schema\".\"table\"` qualified, quoted name. */\nexport function qualifiedName(schema: string, table: string): string {\n return `${quoteIdentifier(schema)}.${quoteIdentifier(table)}`;\n}\n\n/** Count `?` placeholders, skipping string literals, quoted identifiers, and comments. */\nexport function countPlaceholders(sql: string): number {\n let count = 0;\n let index = 0;\n const length = sql.length;\n\n while (index < length) {\n const char = sql[index];\n\n if (char === \"'\" || char === '\"') {\n const quote = char;\n index += 1;\n while (index < length) {\n if (sql[index] === quote) {\n if (sql[index + 1] === quote) {\n index += 2;\n continue;\n }\n index += 1;\n break;\n }\n index += 1;\n }\n continue;\n }\n\n if (char === \"-\" && sql[index + 1] === \"-\") {\n index += 2;\n while (index < length && sql[index] !== \"\\n\") {\n index += 1;\n }\n continue;\n }\n\n if (char === \"/\" && sql[index + 1] === \"*\") {\n index += 2;\n while (index < length && !(sql[index] === \"*\" && sql[index + 1] === \"/\")) {\n index += 1;\n }\n index += 2;\n continue;\n }\n\n if (char === \"?\") {\n count += 1;\n }\n index += 1;\n }\n\n return count;\n}\n\n/** Throw when the number of `?` placeholders does not match the parameter count. */\nexport function assertParamArity(sql: string, params: readonly SqlParam[]): void {\n const expected = countPlaceholders(sql);\n if (expected !== params.length) {\n throw new QueryError(\n `SQL expects ${String(expected)} bound parameter(s) ` +\n `but received ${String(params.length)} value(s)`,\n );\n }\n}\n","import { QueryError } from \"./errors.js\";\nimport { qualifiedName, quoteIdentifier } from \"./statements.js\";\nimport type { BuiltStatement, SelectSpec, SqlParam } from \"./types.js\";\n\ninterface WhereClause {\n readonly clause: string;\n readonly params: readonly SqlParam[];\n}\n\nfunction buildWhere(where: Readonly<Record<string, SqlParam>> | undefined): WhereClause {\n if (where === undefined) {\n return { clause: \"\", params: [] };\n }\n const entries = Object.entries(where);\n if (entries.length === 0) {\n return { clause: \"\", params: [] };\n }\n const conditions = entries.map(([column]) => `${quoteIdentifier(column)} = ?`);\n return {\n clause: ` WHERE ${conditions.join(\" AND \")}`,\n params: entries.map(([, value]) => value),\n };\n}\n\n/** Build a parameterized `SELECT` statement from a typed spec. */\nexport function buildSelect(spec: SelectSpec): BuiltStatement {\n const columns =\n spec.columns === undefined || spec.columns.length === 0\n ? \"*\"\n : spec.columns.map((column) => quoteIdentifier(column)).join(\", \");\n const where = buildWhere(spec.where);\n const clauses: string[] = [\n `SELECT ${columns} FROM ${qualifiedName(spec.schema, spec.table)}${where.clause}`,\n ];\n\n if (spec.orderBy !== undefined && spec.orderBy.length > 0) {\n clauses.push(`ORDER BY ${spec.orderBy.map((column) => quoteIdentifier(column)).join(\", \")}`);\n }\n if (spec.limit !== undefined) {\n clauses.push(`LIMIT ${String(spec.limit)}`);\n }\n if (spec.offset !== undefined) {\n clauses.push(`OFFSET ${String(spec.offset)}`);\n }\n\n return { sql: clauses.join(\" \"), params: where.params };\n}\n\n/** Build a parameterized `SELECT COUNT(*)` statement. */\nexport function buildCount(\n spec: Pick<SelectSpec, \"schema\" | \"table\" | \"where\">,\n): BuiltStatement {\n const where = buildWhere(spec.where);\n return {\n sql: `SELECT COUNT(*) AS \"COUNT\" FROM ${qualifiedName(spec.schema, spec.table)}${where.clause}`,\n params: where.params,\n };\n}\n\n/** Build a parameterized single-row `INSERT` statement. */\nexport function buildInsert(\n schema: string,\n table: string,\n values: Readonly<Record<string, SqlParam>>,\n): BuiltStatement {\n const entries = Object.entries(values);\n if (entries.length === 0) {\n throw new QueryError(\"INSERT requires at least one column value\");\n }\n const columns = entries.map(([column]) => quoteIdentifier(column)).join(\", \");\n const placeholders = entries.map(() => \"?\").join(\", \");\n return {\n sql: `INSERT INTO ${qualifiedName(schema, table)} (${columns}) VALUES (${placeholders})`,\n params: entries.map(([, value]) => value),\n };\n}\n\n/** Build a parameterized `UPDATE` statement. A non-empty `where` is required. */\nexport function buildUpdate(\n schema: string,\n table: string,\n values: Readonly<Record<string, SqlParam>>,\n where: Readonly<Record<string, SqlParam>>,\n): BuiltStatement {\n const valueEntries = Object.entries(values);\n if (valueEntries.length === 0) {\n throw new QueryError(\"UPDATE requires at least one column value\");\n }\n const whereClause = buildWhere(where);\n if (whereClause.clause === \"\") {\n throw new QueryError(\"UPDATE requires a non-empty WHERE filter to avoid a full-table write\");\n }\n const assignments = valueEntries\n .map(([column]) => `${quoteIdentifier(column)} = ?`)\n .join(\", \");\n return {\n sql: `UPDATE ${qualifiedName(schema, table)} SET ${assignments}${whereClause.clause}`,\n params: [...valueEntries.map(([, value]) => value), ...whereClause.params],\n };\n}\n\n/** Build a parameterized `DELETE` statement. A non-empty `where` is required. */\nexport function buildDelete(\n schema: string,\n table: string,\n where: Readonly<Record<string, SqlParam>>,\n): BuiltStatement {\n const whereClause = buildWhere(where);\n if (whereClause.clause === \"\") {\n throw new QueryError(\"DELETE requires a non-empty WHERE filter to avoid a full-table delete\");\n }\n return {\n sql: `DELETE FROM ${qualifiedName(schema, table)}${whereClause.clause}`,\n params: whereClause.params,\n };\n}\n","import type { Connection } from \"./connection.js\";\nimport type { ColumnInfo, TableInfo } from \"./types.js\";\n\ninterface SchemaRow {\n readonly SCHEMA_NAME: string;\n}\n\ninterface TableRow {\n readonly SCHEMA_NAME: string;\n readonly TABLE_NAME: string;\n readonly TABLE_TYPE: string;\n}\n\ninterface ColumnRow {\n readonly COLUMN_NAME: string;\n readonly DATA_TYPE_NAME: string;\n readonly LENGTH: number | null;\n readonly SCALE: number | null;\n readonly IS_NULLABLE: string;\n readonly POSITION: number;\n}\n\nexport interface TableDescription {\n readonly table: TableInfo | undefined;\n readonly columns: readonly ColumnInfo[];\n}\n\n/** List every schema visible to the current user. */\nexport async function listSchemas(connection: Connection): Promise<readonly string[]> {\n const result = await connection.query<SchemaRow>(\n \"SELECT SCHEMA_NAME FROM SYS.SCHEMAS ORDER BY SCHEMA_NAME\",\n );\n return result.rows.map((row) => row.SCHEMA_NAME);\n}\n\n/** List the tables in a schema. */\nexport async function listTables(\n connection: Connection,\n schema: string,\n): Promise<readonly TableInfo[]> {\n const result = await connection.query<TableRow>(\n \"SELECT SCHEMA_NAME, TABLE_NAME, TABLE_TYPE FROM SYS.TABLES \" +\n \"WHERE SCHEMA_NAME = ? ORDER BY TABLE_NAME\",\n [schema],\n );\n return result.rows.map((row) => ({\n schema: row.SCHEMA_NAME,\n name: row.TABLE_NAME,\n type: row.TABLE_TYPE,\n rowCount: undefined,\n }));\n}\n\n/** List the columns of a table, ordered by position. */\nexport async function listColumns(\n connection: Connection,\n schema: string,\n table: string,\n): Promise<readonly ColumnInfo[]> {\n const result = await connection.query<ColumnRow>(\n \"SELECT COLUMN_NAME, DATA_TYPE_NAME, LENGTH, SCALE, IS_NULLABLE, POSITION \" +\n \"FROM SYS.TABLE_COLUMNS WHERE SCHEMA_NAME = ? AND TABLE_NAME = ? ORDER BY POSITION\",\n [schema, table],\n );\n return result.rows.map((row) => ({\n name: row.COLUMN_NAME,\n dataType: row.DATA_TYPE_NAME,\n length: row.LENGTH ?? undefined,\n scale: row.SCALE ?? undefined,\n nullable: row.IS_NULLABLE === \"TRUE\",\n position: row.POSITION,\n }));\n}\n\n/** Describe a table: its catalog entry plus its columns. */\nexport async function describeTable(\n connection: Connection,\n schema: string,\n table: string,\n): Promise<TableDescription> {\n const tables = await listTables(connection, schema);\n const columns = await listColumns(connection, schema, table);\n return {\n table: tables.find((info) => info.name === table),\n columns,\n };\n}\n","export const CLI_NAME = \"cf-hana\";\nexport const CLI_VERSION = \"0.1.6\";\nexport const ENV_PREFIX = \"CF_HANA\";\n\nexport const DEFAULT_QUERY_TIMEOUT_MS = 30_000;\nexport const DEFAULT_CONNECT_TIMEOUT_MS = 30_000;\nexport const DEFAULT_POOL_MAX = 4;\nexport const DEFAULT_POOL_IDLE_MS = 60_000;\nexport const DEFAULT_AUTO_LIMIT = 1_000;\nexport const HANA_CLOUD_DEFAULT_PORT = 443;\n\n/** Build a `CF_HANA_*` environment variable name from a suffix. */\nexport function envName(suffix: string): string {\n return `${ENV_PREFIX}_${suffix}`;\n}\n\n/** Read an environment variable, treating blank values as absent. */\nexport function readEnv(name: string): string | undefined {\n const value = process.env[name];\n if (value === undefined) {\n return undefined;\n }\n const trimmed = value.trim();\n return trimmed.length === 0 ? undefined : trimmed;\n}\n\nexport interface SapCredentials {\n readonly email: string;\n readonly password: string;\n}\n\nexport interface SapCredentialOverrides {\n readonly email?: string | undefined;\n readonly password?: string | undefined;\n}\n\n/**\n * Resolve SAP BTP credentials from explicit overrides, falling back to the\n * repo-wide `SAP_EMAIL` / `SAP_PASSWORD` environment variables. Returns\n * `undefined` when either half is missing.\n */\nexport function readSapCredentials(\n overrides?: SapCredentialOverrides,\n): SapCredentials | undefined {\n const email = overrides?.email ?? readEnv(\"SAP_EMAIL\");\n const password = overrides?.password ?? readEnv(\"SAP_PASSWORD\");\n if (email === undefined || password === undefined) {\n return undefined;\n }\n return { email, password };\n}\n","import { fetchAppDbBindings, readDbAppView } from \"@saptools/cf-sync\";\nimport type { AppDbBinding } from \"@saptools/cf-sync\";\n\nimport { readSapCredentials } from \"./config.js\";\nimport { CfHanaError, CredentialsNotFoundError } from \"./errors.js\";\nimport type { CredentialSource, DbUserRole } from \"./types.js\";\n\nexport interface ResolveBindingsOptions {\n readonly refresh?: boolean;\n readonly email?: string;\n readonly password?: string;\n}\n\nexport interface ResolvedBindings {\n readonly selector: string;\n readonly appName: string;\n readonly bindings: readonly AppDbBinding[];\n readonly source: CredentialSource;\n}\n\nexport interface BindingSelector {\n readonly bindingName?: string;\n readonly bindingIndex?: number;\n}\n\nexport interface SelectedConnectionTarget {\n readonly host: string;\n readonly port: number;\n readonly user: string;\n readonly password: string;\n readonly schema: string;\n readonly certificate: string;\n readonly databaseId: string;\n}\n\n/**\n * Resolve an app's HANA bindings for a `region/org/space/app` selector (or a\n * bare app name): cache-first via `cf-sync`, falling back to a live CF fetch.\n */\nexport async function resolveAppBindings(\n selector: string,\n options: ResolveBindingsOptions,\n): Promise<ResolvedBindings> {\n if (options.refresh !== true) {\n const view = await readDbAppView(selector);\n if (view !== undefined && view.entry.bindings.length > 0) {\n return {\n selector: view.entry.selector,\n appName: view.entry.appName,\n bindings: view.entry.bindings,\n source: \"cache\",\n };\n }\n }\n\n const credentials = readSapCredentials({\n email: options.email,\n password: options.password,\n });\n if (credentials === undefined) {\n throw new CredentialsNotFoundError(\n `No cached HANA credentials for \"${selector}\". ` +\n `Run \\`cf-sync db-sync ${selector}\\` first, or set SAP_EMAIL and ` +\n `SAP_PASSWORD to fetch them live.`,\n );\n }\n\n const fetched = await fetchAppDbBindings({\n selector,\n email: credentials.email,\n password: credentials.password,\n });\n if (fetched.bindings.length === 0) {\n throw new CredentialsNotFoundError(\n `App \"${fetched.selector}\" has no HANA service binding.`,\n );\n }\n return {\n selector: fetched.selector,\n appName: fetched.appName,\n bindings: fetched.bindings,\n source: \"fresh\",\n };\n}\n\n/** Pick a single HANA binding from an app's bindings. */\nexport function selectBinding(\n bindings: readonly AppDbBinding[],\n selector: BindingSelector,\n): AppDbBinding {\n if (selector.bindingName !== undefined) {\n const match = bindings.find((binding) => binding.name === selector.bindingName);\n if (match === undefined) {\n throw new CfHanaError(\n \"AMBIGUOUS_BINDING\",\n `No HANA binding named \"${selector.bindingName}\"`,\n );\n }\n return match;\n }\n\n if (selector.bindingIndex !== undefined) {\n const match = bindings[selector.bindingIndex];\n if (match === undefined) {\n throw new CfHanaError(\n \"AMBIGUOUS_BINDING\",\n `No HANA binding at index ${String(selector.bindingIndex)}`,\n );\n }\n return match;\n }\n\n const first = bindings[0];\n if (first === undefined) {\n throw new CredentialsNotFoundError(\"No HANA bindings are available for this app\");\n }\n if (bindings.length > 1) {\n const labels = bindings\n .map((binding, index) => binding.name ?? `#${String(index)}`)\n .join(\", \");\n throw new CfHanaError(\n \"AMBIGUOUS_BINDING\",\n `App has multiple HANA bindings (${labels}); ` +\n `choose one with bindingName or bindingIndex`,\n );\n }\n return first;\n}\n\n/** Map a HANA binding to concrete connection parameters for the chosen role. */\nexport function toConnectionTarget(\n binding: AppDbBinding,\n role: DbUserRole,\n): SelectedConnectionTarget {\n const credentials = binding.credentials;\n const port = Number.parseInt(credentials.port, 10);\n if (!Number.isInteger(port) || port <= 0) {\n throw new CfHanaError(\"CONFIG\", `Invalid HANA port in binding: \"${credentials.port}\"`);\n }\n return {\n host: credentials.host,\n port,\n user: role === \"hdi\" ? credentials.hdiUser : credentials.user,\n password: role === \"hdi\" ? credentials.hdiPassword : credentials.password,\n schema: credentials.schema,\n certificate: credentials.certificate,\n databaseId: credentials.databaseId,\n };\n}\n","import { appendFile } from \"node:fs/promises\";\n\nimport { envName, readEnv } from \"../config.js\";\nimport { classifyStatement } from \"../statements.js\";\nimport type { SqlParam } from \"../types.js\";\n\nimport type {\n DriverConnectParams,\n DriverConnection,\n DriverExecResult,\n HanaDriver,\n} from \"./types.js\";\n\n/**\n * Deterministic in-memory driver used by hermetic end-to-end tests and offline\n * smoke checks. Selected via `CF_HANA_DRIVER=fake`. It never opens a socket.\n */\nfunction fakeExec(sql: string): DriverExecResult {\n const kind = classifyStatement(sql);\n const forcedFailure = readEnv(envName(\"FAKE_FAIL_STATEMENT\"))?.toLowerCase();\n if (forcedFailure === kind) {\n throw new Error(`fake driver forced ${kind.toUpperCase()} failure`);\n }\n\n if (sql.toUpperCase().includes(\"DUMMY\")) {\n return {\n rows: [{ \"1\": 1 }],\n columns: [{ name: \"1\", typeName: \"INTEGER\" }],\n affectedRows: 0,\n };\n }\n\n if (kind === \"select\") {\n return {\n rows: [\n { ID: 1, NAME: \"sample-row\" },\n { ID: 2, NAME: \"second-row\" },\n ],\n columns: [\n { name: \"ID\", typeName: \"INTEGER\" },\n { name: \"NAME\", typeName: \"NVARCHAR\" },\n ],\n affectedRows: 0,\n };\n }\n if (kind === \"dml\") {\n return { rows: [], columns: [], affectedRows: 1 };\n }\n return { rows: [], columns: [], affectedRows: 0 };\n}\n\n/** Opt-in fake-driver trace; parameter values stay out of test artifacts. */\nasync function traceFakeExec(sql: string, params: readonly SqlParam[]): Promise<void> {\n const tracePath = readEnv(envName(\"FAKE_TRACE_FILE\"));\n if (tracePath === undefined) {\n return;\n }\n const entry = { sql, paramCount: params.length };\n await appendFile(tracePath, `${JSON.stringify(entry)}\\n`, {\n encoding: \"utf8\",\n mode: 0o600,\n });\n}\n\nclass FakeConnection implements DriverConnection {\n private closed = false;\n\n async exec(sql: string, params: readonly SqlParam[]): Promise<DriverExecResult> {\n await traceFakeExec(sql, params);\n return fakeExec(sql);\n }\n\n setAutoCommit(_enabled: boolean): Promise<void> {\n return Promise.resolve();\n }\n\n commit(): Promise<void> {\n return Promise.resolve();\n }\n\n rollback(): Promise<void> {\n return Promise.resolve();\n }\n\n close(): Promise<void> {\n this.closed = true;\n return Promise.resolve();\n }\n\n isClosed(): boolean {\n return this.closed;\n }\n}\n\n/** Build the deterministic test-only fake HANA driver. */\nexport function createFakeDriver(): HanaDriver {\n return {\n name: \"fake\",\n connect: (_params: DriverConnectParams): Promise<DriverConnection> =>\n Promise.resolve(new FakeConnection()),\n };\n}\n","import { createClient } from \"hdb\";\nimport type { HdbClient, HdbColumnMetadata, HdbStatement } from \"hdb\";\n\nimport { CfHanaError, QueryError } from \"../errors.js\";\nimport { quoteIdentifier } from \"../statements.js\";\nimport type { QueryResultColumn, SqlParam } from \"../types.js\";\n\nimport type {\n DriverConnectParams,\n DriverConnection,\n DriverExecResult,\n HanaDriver,\n} from \"./types.js\";\n\nconst TYPE_NAMES: Readonly<Record<number, string>> = {\n 0: \"NULL\",\n 1: \"TINYINT\",\n 2: \"SMALLINT\",\n 3: \"INTEGER\",\n 4: \"BIGINT\",\n 5: \"DECIMAL\",\n 6: \"REAL\",\n 7: \"DOUBLE\",\n 8: \"CHAR\",\n 9: \"VARCHAR\",\n 10: \"NCHAR\",\n 11: \"NVARCHAR\",\n 12: \"BINARY\",\n 13: \"VARBINARY\",\n 14: \"DATE\",\n 15: \"TIME\",\n 16: \"TIMESTAMP\",\n 25: \"CLOB\",\n 26: \"NCLOB\",\n 27: \"BLOB\",\n 28: \"BOOLEAN\",\n 29: \"STRING\",\n 30: \"NSTRING\",\n 47: \"DECIMAL\",\n 51: \"TEXT\",\n 52: \"SHORTTEXT\",\n 62: \"DECIMAL\",\n};\n\nfunction typeName(code: number): string {\n return TYPE_NAMES[code] ?? `TYPE_${String(code)}`;\n}\n\nfunction extractSqlState(error: Error): string | undefined {\n const value = (error as { readonly sqlState?: unknown }).sqlState;\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction toQueryError(error: Error): QueryError {\n const sqlState = extractSqlState(error);\n return sqlState === undefined\n ? new QueryError(error.message, { cause: error })\n : new QueryError(error.message, { cause: error, sqlState });\n}\n\nfunction toColumns(\n metadata: readonly HdbColumnMetadata[] | undefined,\n): readonly QueryResultColumn[] {\n if (metadata === undefined) {\n return [];\n }\n return metadata.map((column) => ({\n name: column.columnDisplayName ?? column.columnName ?? \"\",\n typeName: typeName(column.dataType),\n }));\n}\n\nfunction toExecResult(statement: HdbStatement, raw: unknown): DriverExecResult {\n if (typeof raw === \"number\") {\n return { rows: [], columns: [], affectedRows: raw };\n }\n if (Array.isArray(raw)) {\n return {\n rows: raw as readonly Record<string, SqlParam>[],\n columns: toColumns(statement.resultSetMetadata),\n affectedRows: 0,\n };\n }\n return { rows: [], columns: [], affectedRows: 0 };\n}\n\nclass HdbConnection implements DriverConnection {\n private closed = false;\n\n constructor(private readonly client: HdbClient) {}\n\n async exec(sql: string, params: readonly SqlParam[]): Promise<DriverExecResult> {\n const statement = await this.prepareStatement(sql);\n try {\n const raw = await this.executeStatement(statement, params);\n return toExecResult(statement, raw);\n } finally {\n dropStatementQuietly(statement);\n }\n }\n\n setAutoCommit(enabled: boolean): Promise<void> {\n this.client.setAutoCommit(enabled);\n return Promise.resolve();\n }\n\n async commit(): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n this.client.commit((error) => {\n if (error) {\n reject(toQueryError(error));\n } else {\n resolve();\n }\n });\n });\n }\n\n async rollback(): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n this.client.rollback((error) => {\n if (error) {\n reject(toQueryError(error));\n } else {\n resolve();\n }\n });\n });\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n this.closed = true;\n await new Promise<void>((resolve) => {\n this.client.disconnect(() => {\n resolve();\n });\n });\n this.client.close();\n }\n\n isClosed(): boolean {\n return (\n this.closed ||\n this.client.readyState === \"closed\" ||\n this.client.readyState === \"disconnected\"\n );\n }\n\n private async prepareStatement(sql: string): Promise<HdbStatement> {\n return await new Promise<HdbStatement>((resolve, reject) => {\n this.client.prepare(sql, (error, statement) => {\n if (error) {\n reject(toQueryError(error));\n } else {\n resolve(statement);\n }\n });\n });\n }\n\n private async executeStatement(\n statement: HdbStatement,\n params: readonly SqlParam[],\n ): Promise<unknown> {\n return await new Promise<unknown>((resolve, reject) => {\n statement.exec([...params], (error, result) => {\n if (error) {\n reject(toQueryError(error));\n } else {\n resolve(result);\n }\n });\n });\n }\n}\n\nfunction dropStatementQuietly(statement: HdbStatement): void {\n try {\n statement.drop();\n } catch {\n // Best-effort cleanup; preserve the query result or original query error.\n }\n}\n\nfunction openClient(client: HdbClient, timeoutMs: number): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n let settled = false;\n const timer = setTimeout(() => {\n if (settled) {\n return;\n }\n settled = true;\n client.close();\n reject(\n new CfHanaError(\n \"TIMEOUT\",\n `HANA connection timed out after ${String(timeoutMs)}ms`,\n ),\n );\n }, timeoutMs);\n timer.unref();\n client.connect((error) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timer);\n if (error) {\n reject(\n new CfHanaError(\"CONNECTION\", `Failed to connect to HANA: ${error.message}`, {\n cause: error,\n }),\n );\n } else {\n resolve();\n }\n });\n });\n}\n\nasync function closeClientQuietly(client: HdbClient): Promise<void> {\n try {\n await new Promise<void>((resolve) => {\n client.disconnect(() => {\n resolve();\n });\n });\n } catch {\n // Best-effort cleanup after a partially opened connection.\n }\n try {\n client.close();\n } catch {\n // Best-effort cleanup after a partially opened connection.\n }\n}\n\nfunction setCurrentSchema(client: HdbClient, schema: string): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n client.exec(`SET SCHEMA ${quoteIdentifier(schema)}`, (error) => {\n if (error) {\n reject(toQueryError(error));\n } else {\n resolve();\n }\n });\n });\n}\n\nasync function connectHdb(params: DriverConnectParams): Promise<DriverConnection> {\n const client = createClient({\n host: params.host,\n port: params.port,\n user: params.user,\n password: params.password,\n ca: params.certificate,\n useTLS: true,\n });\n await openClient(client, params.connectTimeoutMs);\n try {\n await setCurrentSchema(client, params.schema);\n } catch (error) {\n await closeClientQuietly(client);\n throw error;\n }\n return new HdbConnection(client);\n}\n\n/** Build the production HANA driver backed by the pure-JavaScript `hdb` library. */\nexport function createHdbDriver(): HanaDriver {\n return {\n name: \"hdb\",\n connect: connectHdb,\n };\n}\n","import { envName, readEnv } from \"../config.js\";\nimport { CfHanaError } from \"../errors.js\";\n\nimport { createFakeDriver } from \"./fake.js\";\nimport { createHdbDriver } from \"./hdb.js\";\nimport type { HanaDriver } from \"./types.js\";\n\nexport type {\n DriverConnectParams,\n DriverConnection,\n DriverExecResult,\n HanaDriver,\n} from \"./types.js\";\n\n/**\n * Create a HANA driver by name. Defaults to `hdb`; the `CF_HANA_DRIVER`\n * environment variable overrides the default when no name is passed.\n */\nexport function createDriver(name?: string): HanaDriver {\n const resolved = name ?? readEnv(envName(\"DRIVER\")) ?? \"hdb\";\n switch (resolved) {\n case \"hdb\":\n return createHdbDriver();\n case \"fake\":\n return createFakeDriver();\n default:\n throw new CfHanaError(\"CONFIG\", `Unknown HANA driver: ${resolved}`);\n }\n}\n","import { appendFile, mkdir, readdir, rm } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport type { DbUserRole, StatementKind } from \"./types.js\";\n\nconst SAPTOOLS_DIR_NAME = \".saptools\";\nconst CF_HANA_DIR_NAME = \"cf-hana\";\nconst HISTORIES_DIR_NAME = \"histories\";\nconst HISTORY_RETENTION_DAYS = 5;\nconst HISTORY_FILE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}\\.jsonl$/;\n\nexport type SqlHistoryOperation = \"query\" | \"execute\";\n\nexport interface SqlHistoryEntryInput {\n readonly version: string;\n readonly operation: SqlHistoryOperation;\n readonly selector: string;\n readonly appName: string;\n readonly schema: string;\n readonly role: DbUserRole;\n readonly statement: StatementKind;\n readonly sql: string;\n readonly paramCount: number;\n readonly rowCount: number;\n readonly truncated: boolean;\n readonly elapsedMs: number;\n}\n\nexport interface SqlHistoryEntry extends SqlHistoryEntryInput {\n readonly at: string;\n}\n\nexport interface SqlHistoryWriteOptions {\n readonly now?: Date;\n readonly saptoolsRoot?: string;\n}\n\nfunction defaultSaptoolsRoot(): string {\n return join(homedir(), SAPTOOLS_DIR_NAME);\n}\n\nfunction padDatePart(value: number): string {\n return value.toString().padStart(2, \"0\");\n}\n\nfunction dateKey(date: Date): string {\n return [\n date.getFullYear().toString(),\n padDatePart(date.getMonth() + 1),\n padDatePart(date.getDate()),\n ].join(\"-\");\n}\n\nfunction retentionCutoffKey(now: Date): string {\n const cutoff = new Date(now);\n cutoff.setHours(0, 0, 0, 0);\n cutoff.setDate(cutoff.getDate() - HISTORY_RETENTION_DAYS);\n return dateKey(cutoff);\n}\n\nfunction resolveSaptoolsRoot(root: string | undefined): string {\n return root ?? defaultSaptoolsRoot();\n}\n\nexport function cfHanaHistoryDirectory(saptoolsRoot?: string): string {\n return join(resolveSaptoolsRoot(saptoolsRoot), CF_HANA_DIR_NAME, HISTORIES_DIR_NAME);\n}\n\nexport function sqlHistoryFilePath(now: Date = new Date(), saptoolsRoot?: string): string {\n return join(cfHanaHistoryDirectory(saptoolsRoot), `${dateKey(now)}.jsonl`);\n}\n\nasync function pruneSqlHistory(now: Date, saptoolsRoot?: string): Promise<void> {\n const historyDir = cfHanaHistoryDirectory(saptoolsRoot);\n let files: readonly string[];\n try {\n files = await readdir(historyDir);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return;\n }\n throw error;\n }\n\n const cutoffKey = retentionCutoffKey(now);\n await Promise.all(\n files\n .filter((file) => HISTORY_FILE_PATTERN.test(file))\n .filter((file) => file.slice(0, 10) < cutoffKey)\n .map(async (file) => {\n await rm(join(historyDir, file), { force: true });\n }),\n );\n}\n\nexport async function appendSqlHistory(\n input: SqlHistoryEntryInput,\n options: SqlHistoryWriteOptions = {},\n): Promise<SqlHistoryEntry> {\n const now = options.now ?? new Date();\n const historyDir = cfHanaHistoryDirectory(options.saptoolsRoot);\n const entry: SqlHistoryEntry = {\n at: now.toISOString(),\n ...input,\n };\n\n await mkdir(historyDir, { recursive: true, mode: 0o700 });\n await appendFile(sqlHistoryFilePath(now, options.saptoolsRoot), `${JSON.stringify(entry)}\\n`, {\n encoding: \"utf8\",\n mode: 0o600,\n });\n try {\n await pruneSqlHistory(now, options.saptoolsRoot);\n } catch {\n // Retention is best-effort; the SQL history entry was already recorded.\n }\n\n return entry;\n}\n","import { classifyStatement, firstKeyword } from \"./statements.js\";\nimport type { StatementKind } from \"./types.js\";\n\nconst DESTRUCTIVE_DDL_KEYWORDS = new Set([\"DROP\", \"TRUNCATE\", \"ALTER\"]);\nconst UNSCOPED_WRITE_KEYWORDS = new Set([\"UPDATE\", \"DELETE\"]);\n\nexport type GuardViolation = \"read-only\" | \"destructive\";\n\nexport interface GuardConfig {\n readonly readOnly: boolean;\n readonly allowDestructive: boolean;\n}\n\nexport interface GuardDecision {\n readonly allowed: boolean;\n readonly destructive: boolean;\n readonly violation: GuardViolation | undefined;\n readonly reason: string | undefined;\n}\n\nexport interface StatementInspection {\n readonly kind: StatementKind;\n readonly destructive: boolean;\n}\n\nexport interface AutoLimitResult {\n readonly sql: string;\n readonly applied: boolean;\n}\n\nfunction skipQuotedText(sql: string, start: number): number {\n const quote = sql[start];\n let index = start + 1;\n while (index < sql.length) {\n if (sql[index] === quote) {\n if (sql[index + 1] === quote) {\n index += 2;\n continue;\n }\n index += 1;\n break;\n }\n index += 1;\n }\n return index;\n}\n\nfunction skipLineComment(sql: string, start: number): number {\n let index = start + 2;\n while (index < sql.length && sql[index] !== \"\\n\") {\n index += 1;\n }\n return index;\n}\n\nfunction skipBlockComment(sql: string, start: number): number {\n let index = start + 2;\n while (index < sql.length && !(sql[index] === \"*\" && sql[index + 1] === \"/\")) {\n index += 1;\n }\n return Math.min(index + 2, sql.length);\n}\n\nfunction maskIgnoredSqlText(sql: string): string {\n let masked = \"\";\n let index = 0;\n while (index < sql.length) {\n const char = sql[index];\n if (char === \"'\" || char === '\"') {\n const end = skipQuotedText(sql, index);\n masked += \" \".repeat(end - index);\n index = end;\n continue;\n }\n if (char === \"-\" && sql[index + 1] === \"-\") {\n const end = skipLineComment(sql, index);\n masked += \" \".repeat(end - index);\n index = end;\n continue;\n }\n if (char === \"/\" && sql[index + 1] === \"*\") {\n const end = skipBlockComment(sql, index);\n masked += \" \".repeat(end - index);\n index = end;\n continue;\n }\n masked += char ?? \"\";\n index += 1;\n }\n return masked;\n}\n\nfunction hasWhereClause(sql: string): boolean {\n return /\\bwhere\\b/i.test(maskIgnoredSqlText(sql));\n}\n\nfunction trailingLineCommentIndex(sql: string): number | undefined {\n let index = 0;\n while (index < sql.length) {\n const char = sql[index];\n if (char === \"'\" || char === '\"') {\n index = skipQuotedText(sql, index);\n continue;\n }\n if (char === \"/\" && sql[index + 1] === \"*\") {\n index = skipBlockComment(sql, index);\n continue;\n }\n if (char === \"-\" && sql[index + 1] === \"-\") {\n const lineEnd = sql.indexOf(\"\\n\", index + 2);\n if (lineEnd === -1 || sql.slice(lineEnd + 1).trim().length === 0) {\n return index;\n }\n index = lineEnd + 1;\n continue;\n }\n index += 1;\n }\n return undefined;\n}\n\nfunction appendLimit(sql: string, limit: number): string {\n const trimmed = sql.replace(/[\\s;]+$/, \"\");\n const commentIndex = trailingLineCommentIndex(trimmed);\n if (commentIndex === undefined) {\n return `${trimmed} LIMIT ${String(limit)}`;\n }\n const beforeComment = trimmed.slice(0, commentIndex).replace(/[\\s;]+$/, \"\");\n const comment = trimmed.slice(commentIndex);\n return `${beforeComment} LIMIT ${String(limit)} ${comment}`;\n}\n\n/** Inspect a statement's kind and whether it is destructive. */\nexport function inspectStatement(sql: string): StatementInspection {\n const kind = classifyStatement(sql);\n const keyword = firstKeyword(sql);\n if (kind === \"ddl\") {\n return { kind, destructive: DESTRUCTIVE_DDL_KEYWORDS.has(keyword) };\n }\n if (kind === \"dml\") {\n return {\n kind,\n destructive: UNSCOPED_WRITE_KEYWORDS.has(keyword) && !hasWhereClause(sql),\n };\n }\n return { kind, destructive: false };\n}\n\n/** Decide whether a statement may run under the given safety configuration. */\nexport function evaluateGuard(sql: string, config: GuardConfig): GuardDecision {\n const inspection = inspectStatement(sql);\n\n if (config.readOnly && inspection.kind !== \"select\") {\n return {\n allowed: false,\n destructive: inspection.destructive,\n violation: \"read-only\",\n reason:\n inspection.kind === \"unknown\"\n ? \"read-only mode only permits SELECT/WITH statements\"\n : `read-only mode blocks ${inspection.kind.toUpperCase()} statements`,\n };\n }\n\n if (inspection.destructive && !config.allowDestructive) {\n return {\n allowed: false,\n destructive: true,\n violation: \"destructive\",\n reason:\n \"destructive statement blocked (DROP/TRUNCATE/ALTER or unscoped UPDATE/DELETE); \" +\n \"allow it explicitly to proceed\",\n };\n }\n\n return {\n allowed: true,\n destructive: inspection.destructive,\n violation: undefined,\n reason: undefined,\n };\n}\n\n/** Append a `LIMIT` clause to a bare SELECT that has no row cap of its own. */\nexport function applyAutoLimit(sql: string, limit: number | false): AutoLimitResult {\n if (limit === false || classifyStatement(sql) !== \"select\") {\n return { sql, applied: false };\n }\n\n const stripped = maskIgnoredSqlText(sql);\n if (/\\blimit\\b/i.test(stripped) || /\\btop\\s+\\d/i.test(stripped)) {\n return { sql, applied: false };\n }\n\n return { sql: appendLimit(sql, limit), applied: true };\n}\n","import type { DriverConnection, HanaDriver } from \"./driver/types.js\";\nimport { CfHanaError, DestructiveStatementError, ReadOnlyViolationError } from \"./errors.js\";\nimport { applyAutoLimit, evaluateGuard } from \"./safety.js\";\nimport { assertParamArity, classifyStatement } from \"./statements.js\";\nimport type { QueryOptions, QueryResult, QueryRow, SqlParam } from \"./types.js\";\n\nexport interface ConnectionConfig {\n readonly host: string;\n readonly port: number;\n readonly user: string;\n readonly password: string;\n readonly schema: string;\n readonly certificate: string;\n readonly connectTimeoutMs: number;\n readonly queryTimeoutMs: number;\n readonly readOnly: boolean;\n readonly allowDestructive: boolean;\n readonly autoLimit: number | false;\n}\n\ninterface RunOptions {\n readonly bypassSafety?: boolean;\n}\n\nasync function withTimeout<T>(\n work: Promise<T>,\n timeoutMs: number,\n onTimeout: () => void,\n): Promise<T> {\n let timer: NodeJS.Timeout | undefined;\n const timeout = new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => {\n onTimeout();\n reject(\n new CfHanaError(\"TIMEOUT\", `Statement timed out after ${String(timeoutMs)}ms`),\n );\n }, timeoutMs);\n timer.unref();\n });\n\n try {\n return await Promise.race([work, timeout]);\n } finally {\n if (timer !== undefined) {\n clearTimeout(timer);\n }\n }\n}\n\n/** A single HANA connection: applies the safety guard, auto-limit, and timeouts. */\nexport class Connection {\n private constructor(\n private readonly driverConnection: DriverConnection,\n private readonly config: ConnectionConfig,\n ) {}\n\n static async open(driver: HanaDriver, config: ConnectionConfig): Promise<Connection> {\n const driverConnection = await driver.connect({\n host: config.host,\n port: config.port,\n user: config.user,\n password: config.password,\n schema: config.schema,\n certificate: config.certificate,\n connectTimeoutMs: config.connectTimeoutMs,\n });\n return new Connection(driverConnection, config);\n }\n\n async query<TRow = QueryRow>(\n sql: string,\n params?: readonly SqlParam[],\n options?: QueryOptions,\n ): Promise<QueryResult<TRow>> {\n const result = await this.run(sql, params ?? [], options ?? {});\n // The driver returns untyped rows; the caller asserts the row shape via TRow.\n return result as unknown as QueryResult<TRow>;\n }\n\n async execute(\n sql: string,\n params?: readonly SqlParam[],\n options?: QueryOptions,\n ): Promise<QueryResult> {\n return await this.run(sql, params ?? [], options ?? {});\n }\n\n assertAllowed(sql: string, options?: QueryOptions): void {\n this.evaluateSafety(sql, options ?? {});\n }\n\n async executeInternal(\n sql: string,\n params?: readonly SqlParam[],\n options?: QueryOptions,\n ): Promise<QueryResult> {\n return await this.run(sql, params ?? [], options ?? {}, { bypassSafety: true });\n }\n\n async setAutoCommit(enabled: boolean): Promise<void> {\n await this.driverConnection.setAutoCommit(enabled);\n }\n\n async commit(): Promise<void> {\n await this.driverConnection.commit();\n }\n\n async rollback(): Promise<void> {\n await this.driverConnection.rollback();\n }\n\n async close(): Promise<void> {\n await this.driverConnection.close();\n }\n\n get isClosed(): boolean {\n return this.driverConnection.isClosed();\n }\n\n private async run(\n sql: string,\n params: readonly SqlParam[],\n options: QueryOptions,\n runOptions: RunOptions = {},\n ): Promise<QueryResult> {\n assertParamArity(sql, params);\n if (runOptions.bypassSafety !== true) {\n this.evaluateSafety(sql, options);\n }\n\n const kind = classifyStatement(sql);\n const autoLimit = options.autoLimit ?? this.config.autoLimit;\n const limited =\n kind === \"select\" ? applyAutoLimit(sql, autoLimit) : { sql, applied: false };\n const timeoutMs = options.timeoutMs ?? this.config.queryTimeoutMs;\n\n const started = Date.now();\n const execResult = await withTimeout(\n this.driverConnection.exec(limited.sql, params),\n timeoutMs,\n () => {\n void this.closeQuietly();\n },\n );\n const elapsedMs = Date.now() - started;\n\n return {\n rows: execResult.rows,\n columns: execResult.columns,\n rowCount: kind === \"select\" ? execResult.rows.length : execResult.affectedRows,\n statement: kind,\n truncated: limited.applied && execResult.rows.length === autoLimit,\n elapsedMs,\n };\n }\n\n private async closeQuietly(): Promise<void> {\n try {\n await this.driverConnection.close();\n } catch {\n // Best-effort close after a timeout; the connection is already unusable.\n }\n }\n\n private evaluateSafety(sql: string, options: QueryOptions): void {\n const decision = evaluateGuard(sql, {\n readOnly: this.config.readOnly,\n allowDestructive: options.allowDestructive ?? this.config.allowDestructive,\n });\n if (decision.allowed) {\n return;\n }\n if (decision.violation === \"read-only\") {\n throw new ReadOnlyViolationError(\n decision.reason ?? \"read-only mode blocks this statement\",\n );\n }\n throw new DestructiveStatementError(\n decision.reason ?? \"destructive statement blocked\",\n );\n }\n}\n","import { DEFAULT_POOL_IDLE_MS, DEFAULT_POOL_MAX } from \"./config.js\";\nimport { Connection } from \"./connection.js\";\nimport type { ConnectionConfig } from \"./connection.js\";\nimport type { HanaDriver } from \"./driver/types.js\";\nimport { CfHanaError } from \"./errors.js\";\nimport type { PoolOptions } from \"./types.js\";\n\ninterface IdleEntry {\n readonly connection: Connection;\n readonly since: number;\n}\n\ninterface Waiter {\n readonly resolve: (connection: Connection) => void;\n readonly reject: (error: unknown) => void;\n}\n\nasync function closeQuietly(connection: Connection): Promise<void> {\n try {\n await connection.close();\n } catch {\n // Best-effort close.\n }\n}\n\n/**\n * A process-local, in-memory pool of HANA connections. It holds no shared disk\n * state, so multiple pools (in this or other processes) are fully independent.\n */\nexport class ConnectionPool {\n private readonly idle: IdleEntry[] = [];\n private readonly busy = new Set<Connection>();\n private readonly waiters: Waiter[] = [];\n private readonly maxSize: number;\n private readonly idleTimeoutMs: number;\n private created = 0;\n private draining = false;\n\n constructor(\n private readonly driver: HanaDriver,\n private readonly config: ConnectionConfig,\n options?: PoolOptions,\n ) {\n this.maxSize = Math.max(1, options?.max ?? DEFAULT_POOL_MAX);\n this.idleTimeoutMs = options?.idleTimeoutMs ?? DEFAULT_POOL_IDLE_MS;\n }\n\n /** Total connections currently owned by the pool (idle + busy). */\n get size(): number {\n return this.created;\n }\n\n /** Connections currently idle and immediately reusable. */\n get available(): number {\n return this.idle.length;\n }\n\n /** Borrow a connection, opening or queueing as needed. */\n async acquire(): Promise<Connection> {\n if (this.draining) {\n throw new CfHanaError(\"POOL_CLOSED\", \"Connection pool is draining\");\n }\n\n const reused = this.takeIdleConnection();\n if (reused !== undefined) {\n this.busy.add(reused);\n return reused;\n }\n\n if (this.created < this.maxSize) {\n return await this.openBusyConnection();\n }\n\n return await new Promise<Connection>((resolve, reject) => {\n this.waiters.push({ resolve, reject });\n });\n }\n\n /** Return a borrowed connection to the pool. */\n release(connection: Connection): void {\n if (!this.busy.delete(connection)) {\n return;\n }\n\n if (connection.isClosed) {\n this.created -= 1;\n this.servePendingWaiters();\n return;\n }\n\n const waiter = this.waiters.shift();\n if (waiter !== undefined) {\n this.busy.add(connection);\n waiter.resolve(connection);\n return;\n }\n\n if (this.draining) {\n this.created -= 1;\n void closeQuietly(connection);\n return;\n }\n\n this.idle.push({ connection, since: Date.now() });\n }\n\n /** Run `work` with a borrowed connection, releasing it afterwards. */\n async withConnection<T>(work: (connection: Connection) => Promise<T>): Promise<T> {\n const connection = await this.acquire();\n try {\n return await work(connection);\n } finally {\n this.release(connection);\n }\n }\n\n /** Reject queued waiters and close idle connections. */\n async drain(): Promise<void> {\n this.draining = true;\n\n while (this.waiters.length > 0) {\n const waiter = this.waiters.shift();\n if (waiter !== undefined) {\n waiter.reject(new CfHanaError(\"POOL_CLOSED\", \"Connection pool was drained\"));\n }\n }\n\n const idleConnections = this.idle.splice(0).map((entry) => entry.connection);\n this.created -= idleConnections.length;\n await Promise.all(idleConnections.map((connection) => closeQuietly(connection)));\n }\n\n private takeIdleConnection(): Connection | undefined {\n for (;;) {\n const entry = this.idle.pop();\n if (entry === undefined) {\n return undefined;\n }\n if (entry.connection.isClosed) {\n this.created -= 1;\n continue;\n }\n if (this.idleTimeoutMs > 0 && Date.now() - entry.since > this.idleTimeoutMs) {\n this.created -= 1;\n void closeQuietly(entry.connection);\n continue;\n }\n return entry.connection;\n }\n }\n\n private async openBusyConnection(): Promise<Connection> {\n this.created += 1;\n let connection: Connection;\n try {\n connection = await Connection.open(this.driver, this.config);\n } catch (error) {\n this.created -= 1;\n throw error;\n }\n this.busy.add(connection);\n return connection;\n }\n\n private servePendingWaiters(): void {\n while (this.waiters.length > 0 && this.created < this.maxSize) {\n const waiter = this.waiters.shift();\n if (waiter === undefined) {\n return;\n }\n this.created += 1;\n void Connection.open(this.driver, this.config).then(\n (connection) => {\n this.busy.add(connection);\n waiter.resolve(connection);\n },\n (error: unknown) => {\n this.created -= 1;\n waiter.reject(error);\n this.servePendingWaiters();\n },\n );\n }\n }\n}\n","import type { Connection } from \"./connection.js\";\nimport { CfHanaError } from \"./errors.js\";\nimport type { QueryOptions, QueryResult, QueryRow, SqlParam } from \"./types.js\";\n\n/** A HANA transaction bound to one connection with autocommit disabled. */\nexport class Transaction {\n private finished = false;\n\n constructor(private readonly connection: Connection) {}\n\n /** Whether the transaction has already been committed or rolled back. */\n get isFinished(): boolean {\n return this.finished;\n }\n\n async query<TRow = QueryRow>(\n sql: string,\n params?: readonly SqlParam[],\n options?: QueryOptions,\n ): Promise<QueryResult<TRow>> {\n this.assertActive();\n return await this.connection.query<TRow>(sql, params, options);\n }\n\n async execute(\n sql: string,\n params?: readonly SqlParam[],\n options?: QueryOptions,\n ): Promise<QueryResult> {\n this.assertActive();\n return await this.connection.execute(sql, params, options);\n }\n\n async commit(): Promise<void> {\n this.assertActive();\n this.finished = true;\n await this.connection.commit();\n }\n\n async rollback(): Promise<void> {\n this.assertActive();\n this.finished = true;\n await this.connection.rollback();\n }\n\n private assertActive(): void {\n if (this.finished) {\n throw new CfHanaError(\n \"QUERY\",\n \"This transaction has already been committed or rolled back\",\n );\n }\n }\n}\n","import { buildWriteBackupPlan, writeSqlBackup } from \"./backup.js\";\nimport type { SqlBackupRecord } from \"./backup.js\";\nimport { buildCount, buildDelete, buildInsert, buildSelect, buildUpdate } from \"./builder.js\";\nimport { listColumns, listSchemas, listTables } from \"./catalog.js\";\nimport {\n DEFAULT_AUTO_LIMIT,\n CLI_VERSION,\n DEFAULT_CONNECT_TIMEOUT_MS,\n DEFAULT_QUERY_TIMEOUT_MS,\n} from \"./config.js\";\nimport type { ConnectionConfig } from \"./connection.js\";\nimport { resolveAppBindings, selectBinding, toConnectionTarget } from \"./credentials.js\";\nimport { createDriver } from \"./driver/index.js\";\nimport { appendSqlHistory } from \"./history.js\";\nimport type { SqlHistoryOperation } from \"./history.js\";\nimport { ConnectionPool } from \"./pool.js\";\nimport { Transaction } from \"./transaction.js\";\nimport type {\n ColumnInfo,\n ConnectOptions,\n DbUserRole,\n HanaClientInfo,\n PoolOptions,\n QueryOptions,\n QueryResult,\n QueryRow,\n SelectSpec,\n SqlParam,\n TableInfo,\n} from \"./types.js\";\n\nlet explainStatementCounter = 0;\n\nfunction nextExplainStatementName(): string {\n explainStatementCounter = (explainStatementCounter + 1) % Number.MAX_SAFE_INTEGER;\n return `cf_hana_${String(process.pid)}_${String(Date.now())}_${String(explainStatementCounter)}`;\n}\n\n/**\n * The high-level entry point: a pooled, reusable client for the HANA database\n * bound to a single Cloud Foundry app.\n */\nexport class HanaClient {\n constructor(\n private readonly pool: ConnectionPool,\n readonly info: HanaClientInfo,\n ) {}\n\n /** Open a client for a `region/org/space/app` selector (or a bare app name). */\n static async connect(selector: string, options: ConnectOptions = {}): Promise<HanaClient> {\n const resolved = await resolveAppBindings(selector, options);\n const role: DbUserRole = options.role ?? \"runtime\";\n const binding = selectBinding(resolved.bindings, options);\n const target = toConnectionTarget(binding, role);\n const driver = createDriver();\n\n const config: ConnectionConfig = {\n host: target.host,\n port: target.port,\n user: target.user,\n password: target.password,\n schema: target.schema,\n certificate: target.certificate,\n connectTimeoutMs: options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS,\n queryTimeoutMs: options.queryTimeoutMs ?? DEFAULT_QUERY_TIMEOUT_MS,\n readOnly: options.readOnly ?? false,\n allowDestructive: options.allowDestructive ?? false,\n autoLimit: options.autoLimit ?? DEFAULT_AUTO_LIMIT,\n };\n const poolOptions: PoolOptions =\n options.pool === false ? { max: 1 } : (options.pool ?? {});\n\n const info: HanaClientInfo = {\n selector: resolved.selector,\n appName: resolved.appName,\n host: target.host,\n schema: target.schema,\n role,\n driver: driver.name,\n credentialSource: resolved.source,\n };\n return new HanaClient(new ConnectionPool(driver, config, poolOptions), info);\n }\n\n /** Run a SELECT (or any read) statement and return typed rows. */\n async query<TRow = QueryRow>(\n sql: string,\n params?: readonly SqlParam[],\n options?: QueryOptions,\n ): Promise<QueryResult<TRow>> {\n const resolvedParams = params ?? [];\n const result = await this.runQuery<TRow>(sql, resolvedParams, options);\n await this.recordSqlHistory(\"query\", sql, resolvedParams, result);\n return result;\n }\n\n /** Run a DML/DDL statement and return its affected-row count. */\n async execute(\n sql: string,\n params?: readonly SqlParam[],\n options?: QueryOptions,\n ): Promise<QueryResult> {\n const resolvedParams = params ?? [];\n const result = await this.runExecute(sql, resolvedParams, options);\n await this.recordSqlHistory(\"execute\", sql, resolvedParams, result);\n return result;\n }\n\n /** Back up rows matched by an UPDATE or DELETE before the caller runs it. */\n async backupWriteStatement(\n sql: string,\n params?: readonly SqlParam[],\n options?: QueryOptions,\n ): Promise<SqlBackupRecord | undefined> {\n const resolvedParams = params ?? [];\n const plan = buildWriteBackupPlan(sql, resolvedParams);\n if (plan === undefined) {\n return undefined;\n }\n\n return await this.pool.withConnection(async (connection) => {\n connection.assertAllowed(plan.statementSql, options);\n const backupQueryOptions: QueryOptions = {\n autoLimit: false,\n ...(options?.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),\n };\n const result = await connection.query(plan.selectSql, plan.selectParams, backupQueryOptions);\n return await writeSqlBackup({\n operation: plan.operation,\n statementSql: plan.statementSql,\n result,\n });\n });\n }\n\n /** Run a typed `SELECT` built from a spec. */\n async selectFrom<TRow = QueryRow>(spec: SelectSpec): Promise<QueryResult<TRow>> {\n const built = buildSelect(spec);\n return await this.runQuery<TRow>(built.sql, built.params);\n }\n\n /** Count rows in a table, optionally filtered. */\n async count(spec: Pick<SelectSpec, \"schema\" | \"table\" | \"where\">): Promise<number> {\n const built = buildCount(spec);\n const result = await this.runQuery<{ COUNT: number }>(built.sql, built.params);\n return result.rows[0]?.COUNT ?? 0;\n }\n\n /** Insert a single row. */\n async insertInto(\n schema: string,\n table: string,\n values: Readonly<Record<string, SqlParam>>,\n ): Promise<QueryResult> {\n const built = buildInsert(schema, table, values);\n return await this.runExecute(built.sql, built.params);\n }\n\n /** Update rows matching a non-empty `where` filter. */\n async update(\n schema: string,\n table: string,\n values: Readonly<Record<string, SqlParam>>,\n where: Readonly<Record<string, SqlParam>>,\n ): Promise<QueryResult> {\n const built = buildUpdate(schema, table, values, where);\n return await this.runExecute(built.sql, built.params);\n }\n\n /** Delete rows matching a non-empty `where` filter. */\n async deleteFrom(\n schema: string,\n table: string,\n where: Readonly<Record<string, SqlParam>>,\n ): Promise<QueryResult> {\n const built = buildDelete(schema, table, where);\n return await this.runExecute(built.sql, built.params);\n }\n\n /** Run `work` inside a transaction, auto-committing on success. */\n async transaction<T>(work: (tx: Transaction) => Promise<T>): Promise<T> {\n return await this.pool.withConnection(async (connection) => {\n await connection.setAutoCommit(false);\n const tx = new Transaction(connection);\n try {\n const result = await work(tx);\n if (!tx.isFinished) {\n await tx.commit();\n }\n return result;\n } catch (error) {\n if (!tx.isFinished) {\n try {\n await tx.rollback();\n } catch {\n // Preserve the original error; ignore a rollback failure.\n }\n }\n throw error;\n } finally {\n await connection.setAutoCommit(true);\n }\n });\n }\n\n /** List every schema visible to the connected user. */\n async listSchemas(): Promise<readonly string[]> {\n return await this.pool.withConnection((connection) => listSchemas(connection));\n }\n\n /** List the tables in a schema. */\n async listTables(schema: string): Promise<readonly TableInfo[]> {\n return await this.pool.withConnection((connection) => listTables(connection, schema));\n }\n\n /** List the columns of a table. */\n async listColumns(schema: string, table: string): Promise<readonly ColumnInfo[]> {\n return await this.pool.withConnection((connection) =>\n listColumns(connection, schema, table),\n );\n }\n\n /** Return the HANA execution plan for a statement. */\n async explain(sql: string, params?: readonly SqlParam[]): Promise<QueryResult> {\n return await this.pool.withConnection(async (connection) => {\n connection.assertAllowed(sql);\n const statementName = nextExplainStatementName();\n await connection.executeInternal(\n `EXPLAIN PLAN SET STATEMENT_NAME = '${statementName}' FOR ${sql}`,\n params,\n );\n const cleanup = async (): Promise<void> => {\n await connection.executeInternal(\n \"DELETE FROM EXPLAIN_PLAN_TABLE WHERE STATEMENT_NAME = ?\",\n [statementName],\n );\n };\n let queryCompleted = false;\n try {\n const result = await connection.query(\n \"SELECT OPERATOR_NAME, TABLE_NAME, TABLE_TYPE, EXECUTION_ENGINE \" +\n \"FROM EXPLAIN_PLAN_TABLE WHERE STATEMENT_NAME = ? ORDER BY OPERATOR_ID\",\n [statementName],\n );\n queryCompleted = true;\n await cleanup();\n return result;\n } catch (error) {\n if (!queryCompleted) {\n try {\n await cleanup();\n } catch {\n // Preserve the original explain-plan read error.\n }\n }\n throw error;\n }\n });\n }\n\n /** Close every pooled connection. The client must not be used afterwards. */\n async close(): Promise<void> {\n await this.pool.drain();\n }\n\n private async runQuery<TRow = QueryRow>(\n sql: string,\n params: readonly SqlParam[],\n options?: QueryOptions,\n ): Promise<QueryResult<TRow>> {\n return await this.pool.withConnection((connection) =>\n connection.query<TRow>(sql, params, options),\n );\n }\n\n private async runExecute(\n sql: string,\n params: readonly SqlParam[],\n options?: QueryOptions,\n ): Promise<QueryResult> {\n return await this.pool.withConnection((connection) =>\n connection.execute(sql, params, options),\n );\n }\n\n private async recordSqlHistory<TRow>(\n operation: SqlHistoryOperation,\n sql: string,\n params: readonly SqlParam[],\n result: QueryResult<TRow>,\n ): Promise<void> {\n try {\n await appendSqlHistory({\n version: CLI_VERSION,\n operation,\n selector: this.info.selector,\n appName: this.info.appName,\n schema: this.info.schema,\n role: this.info.role,\n statement: result.statement,\n sql,\n paramCount: params.length,\n rowCount: result.rowCount,\n truncated: result.truncated,\n elapsedMs: result.elapsedMs,\n });\n } catch {\n // History is diagnostic local state; never fail a successful SQL statement.\n }\n }\n}\n","import { HanaClient } from \"./client.js\";\nimport type {\n ConnectOptions,\n QueryOptions,\n QueryResult,\n QueryRow,\n SqlParam,\n} from \"./types.js\";\n\n/**\n * Open a reusable, pooled client for the HANA database bound to a Cloud Foundry\n * app, addressed by a `region/org/space/app` selector or a bare app name.\n */\nexport async function connect(\n selector: string,\n options?: ConnectOptions,\n): Promise<HanaClient> {\n return await HanaClient.connect(selector, options);\n}\n\n/**\n * Connect, run a single query, and close — the one-shot convenience for scripts.\n */\nexport async function query<TRow = QueryRow>(\n selector: string,\n sql: string,\n params?: readonly SqlParam[],\n options?: ConnectOptions & QueryOptions,\n): Promise<QueryResult<TRow>> {\n const client = await HanaClient.connect(selector, options);\n try {\n return await client.query<TRow>(sql, params, options);\n } finally {\n await client.close();\n }\n}\n\n/** Run `work` with a client that is closed automatically afterwards. */\nexport async function withConnection<T>(\n selector: string,\n work: (client: HanaClient) => Promise<T>,\n options?: ConnectOptions,\n): Promise<T> {\n const client = await HanaClient.connect(selector, options);\n try {\n return await work(client);\n } finally {\n await client.close();\n }\n}\n"],"mappings":"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","names":["appendFile","mkdir","homedir","join","SAPTOOLS_DIR_NAME","CF_HANA_DIR_NAME","defaultSaptoolsRoot","skipQuotedText","skipLineComment","skipBlockComment"]}
|
|
1
|
+
{"version":3,"sources":["../src/backup.ts","../src/errors.ts","../src/format.ts","../src/statements.ts","../src/builder.ts","../src/catalog.ts","../src/config.ts","../src/credentials.ts","../src/driver/fake.ts","../src/driver/hdb.ts","../src/driver/index.ts","../src/history.ts","../src/safety.ts","../src/connection.ts","../src/pool.ts","../src/transaction.ts","../src/client.ts","../src/api.ts"],"sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport { QueryError } from \"./errors.js\";\nimport { formatCsv } from \"./format.js\";\nimport { assertParamArity, countPlaceholders, firstKeyword } from \"./statements.js\";\nimport type { QueryResult, SqlParam } from \"./types.js\";\n\nconst SAPTOOLS_DIR_NAME = \".saptools\";\nconst CF_HANA_DIR_NAME = \"cf-hana\";\nconst BACKUPS_DIR_NAME = \"backups\";\n\nexport type WriteBackupOperation = \"update\" | \"delete\";\n\nexport interface WriteBackupPlan {\n readonly operation: WriteBackupOperation;\n readonly statementSql: string;\n readonly selectSql: string;\n readonly selectParams: readonly SqlParam[];\n}\n\nexport interface SqlBackupWriteInput {\n readonly operation: WriteBackupOperation;\n readonly statementSql: string;\n readonly result: QueryResult;\n}\n\nexport interface SqlBackupWriteOptions {\n readonly now?: Date;\n readonly saptoolsRoot?: string;\n}\n\nexport interface SqlBackupRecord {\n readonly directory: string;\n readonly statementPath: string;\n readonly backupPath: string;\n readonly rowCount: number;\n}\n\ntype TopLevelKeyword = \"UPDATE\" | \"DELETE\" | \"SET\" | \"FROM\" | \"WHERE\";\n\nfunction defaultSaptoolsRoot(): string {\n return join(homedir(), SAPTOOLS_DIR_NAME);\n}\n\nfunction trimStatementSql(sql: string): string {\n return sql.trim().replace(/;+\\s*$/, \"\").trim();\n}\n\nfunction isIdentifierChar(char: string | undefined): boolean {\n return char !== undefined && /[A-Za-z0-9_$#]/.test(char);\n}\n\nfunction skipQuotedText(sql: string, index: number): number {\n const quote = sql[index];\n let cursor = index + 1;\n while (cursor < sql.length) {\n if (sql[cursor] === quote) {\n if (sql[cursor + 1] === quote) {\n cursor += 2;\n continue;\n }\n return cursor + 1;\n }\n cursor += 1;\n }\n return cursor;\n}\n\nfunction skipLineComment(sql: string, index: number): number {\n let cursor = index + 2;\n while (cursor < sql.length && sql[cursor] !== \"\\n\") {\n cursor += 1;\n }\n return cursor;\n}\n\nfunction skipBlockComment(sql: string, index: number): number {\n let cursor = index + 2;\n while (cursor < sql.length && !(sql[cursor] === \"*\" && sql[cursor + 1] === \"/\")) {\n cursor += 1;\n }\n return Math.min(cursor + 2, sql.length);\n}\n\nfunction skipNonCode(sql: string, index: number): number | undefined {\n const char = sql[index];\n if (char === \"'\" || char === '\"') {\n return skipQuotedText(sql, index);\n }\n if (char === \"-\" && sql[index + 1] === \"-\") {\n return skipLineComment(sql, index);\n }\n if (char === \"/\" && sql[index + 1] === \"*\") {\n return skipBlockComment(sql, index);\n }\n return undefined;\n}\n\nfunction keywordMatches(sql: string, index: number, keyword: TopLevelKeyword): boolean {\n const end = index + keyword.length;\n return (\n sql.slice(index, end).toUpperCase() === keyword &&\n !isIdentifierChar(sql[index - 1]) &&\n !isIdentifierChar(sql[end])\n );\n}\n\nfunction findTopLevelKeyword(\n sql: string,\n keyword: TopLevelKeyword,\n startIndex = 0,\n): number | undefined {\n let index = startIndex;\n let depth = 0;\n while (index < sql.length) {\n const skipped = skipNonCode(sql, index);\n if (skipped !== undefined) {\n index = skipped;\n continue;\n }\n\n const char = sql[index];\n if (char === \"(\") {\n depth += 1;\n } else if (char === \")\" && depth > 0) {\n depth -= 1;\n } else if (depth === 0 && keywordMatches(sql, index, keyword)) {\n return index;\n }\n index += 1;\n }\n return undefined;\n}\n\nfunction selectParamsAfterWhere(\n statementSql: string,\n whereIndex: number,\n params: readonly SqlParam[],\n): readonly SqlParam[] {\n return params.slice(countPlaceholders(statementSql.slice(0, whereIndex)));\n}\n\nfunction buildSelectPlan(\n operation: WriteBackupOperation,\n statementSql: string,\n targetSql: string,\n whereIndex: number | undefined,\n params: readonly SqlParam[],\n): WriteBackupPlan {\n const target = targetSql.trim();\n if (target.length === 0) {\n throw new QueryError(`${operation.toUpperCase()} backup requires a target table`);\n }\n if (whereIndex === undefined) {\n return { operation, statementSql, selectSql: `SELECT * FROM ${target}`, selectParams: [] };\n }\n\n const whereSql = statementSql.slice(whereIndex + \"WHERE\".length).trim();\n if (whereSql.length === 0) {\n throw new QueryError(`${operation.toUpperCase()} backup requires a non-empty WHERE clause`);\n }\n return {\n operation,\n statementSql,\n selectSql: `SELECT * FROM ${target} WHERE ${whereSql}`,\n selectParams: selectParamsAfterWhere(statementSql, whereIndex, params),\n };\n}\n\nfunction buildUpdateBackupPlan(\n statementSql: string,\n params: readonly SqlParam[],\n): WriteBackupPlan {\n const updateIndex = findTopLevelKeyword(statementSql, \"UPDATE\");\n const setIndex =\n updateIndex === undefined\n ? undefined\n : findTopLevelKeyword(statementSql, \"SET\", updateIndex + \"UPDATE\".length);\n if (updateIndex === undefined || setIndex === undefined) {\n throw new QueryError(\"UPDATE backup requires UPDATE <target> SET syntax\");\n }\n\n const whereIndex = findTopLevelKeyword(statementSql, \"WHERE\", setIndex + \"SET\".length);\n return buildSelectPlan(\n \"update\",\n statementSql,\n statementSql.slice(updateIndex + \"UPDATE\".length, setIndex),\n whereIndex,\n params,\n );\n}\n\nfunction buildDeleteBackupPlan(\n statementSql: string,\n params: readonly SqlParam[],\n): WriteBackupPlan {\n const deleteIndex = findTopLevelKeyword(statementSql, \"DELETE\");\n const fromIndex =\n deleteIndex === undefined\n ? undefined\n : findTopLevelKeyword(statementSql, \"FROM\", deleteIndex + \"DELETE\".length);\n if (deleteIndex === undefined || fromIndex === undefined) {\n throw new QueryError(\"DELETE backup requires DELETE FROM <target> syntax\");\n }\n\n const whereIndex = findTopLevelKeyword(statementSql, \"WHERE\", fromIndex + \"FROM\".length);\n const targetEnd = whereIndex ?? statementSql.length;\n return buildSelectPlan(\n \"delete\",\n statementSql,\n statementSql.slice(fromIndex + \"FROM\".length, targetEnd),\n whereIndex,\n params,\n );\n}\n\nfunction backupTimestamp(now: Date): string {\n return now.toISOString().replace(/:/g, \"\").replace(\".\", \"\");\n}\n\nfunction backupHash(statementSql: string): string {\n return createHash(\"sha256\")\n .update(statementSql)\n .update(\"\\0\")\n .update(randomUUID())\n .digest(\"hex\")\n .slice(0, 12);\n}\n\nexport function cfHanaBackupRoot(saptoolsRoot?: string): string {\n return join(saptoolsRoot ?? defaultSaptoolsRoot(), CF_HANA_DIR_NAME, BACKUPS_DIR_NAME);\n}\n\nexport function buildWriteBackupPlan(\n sql: string,\n params: readonly SqlParam[] = [],\n): WriteBackupPlan | undefined {\n const statementSql = trimStatementSql(sql);\n const keyword = firstKeyword(statementSql);\n if (keyword !== \"UPDATE\" && keyword !== \"DELETE\") {\n return undefined;\n }\n\n assertParamArity(statementSql, params);\n if (keyword === \"UPDATE\") {\n return buildUpdateBackupPlan(statementSql, params);\n }\n return buildDeleteBackupPlan(statementSql, params);\n}\n\nexport async function writeSqlBackup(\n input: SqlBackupWriteInput,\n options: SqlBackupWriteOptions = {},\n): Promise<SqlBackupRecord> {\n const now = options.now ?? new Date();\n const directory = join(\n cfHanaBackupRoot(options.saptoolsRoot),\n `${backupTimestamp(now)}-${input.operation}-${backupHash(input.statementSql)}`,\n );\n const statementPath = join(directory, \"statement.sql\");\n const backupPath = join(directory, \"backup.csv\");\n\n await mkdir(directory, { recursive: true, mode: 0o700 });\n await Promise.all([\n writeFile(statementPath, `${input.statementSql}\\n`, { encoding: \"utf8\", mode: 0o600 }),\n writeFile(backupPath, formatCsv(input.result), { encoding: \"utf8\", mode: 0o600 }),\n ]);\n\n return {\n directory,\n statementPath,\n backupPath,\n rowCount: input.result.rowCount,\n };\n}\n","export type CfHanaErrorCode =\n | \"CONFIG\"\n | \"CREDENTIALS_NOT_FOUND\"\n | \"AMBIGUOUS_BINDING\"\n | \"CONNECTION\"\n | \"QUERY\"\n | \"READ_ONLY_VIOLATION\"\n | \"DESTRUCTIVE_BLOCKED\"\n | \"TIMEOUT\"\n | \"POOL_CLOSED\";\n\nexport interface CfHanaErrorOptions {\n readonly cause?: unknown;\n}\n\nexport interface QueryErrorOptions extends CfHanaErrorOptions {\n readonly sqlState?: string;\n}\n\n/** Base error for every failure surfaced by `@saptools/cf-hana`. */\nexport class CfHanaError extends Error {\n readonly code: CfHanaErrorCode;\n\n constructor(code: CfHanaErrorCode, message: string, options?: CfHanaErrorOptions) {\n super(message, options);\n this.name = \"CfHanaError\";\n this.code = code;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** No HANA credentials could be resolved for the requested selector. */\nexport class CredentialsNotFoundError extends CfHanaError {\n constructor(message: string, options?: CfHanaErrorOptions) {\n super(\"CREDENTIALS_NOT_FOUND\", message, options);\n this.name = \"CredentialsNotFoundError\";\n }\n}\n\n/** A SQL statement failed on the HANA server. */\nexport class QueryError extends CfHanaError {\n readonly sqlState: string | undefined;\n\n constructor(message: string, options?: QueryErrorOptions) {\n super(\"QUERY\", message, options);\n this.name = \"QueryError\";\n this.sqlState = options?.sqlState;\n }\n}\n\n/** A write/DDL statement was issued on a read-only client. */\nexport class ReadOnlyViolationError extends CfHanaError {\n constructor(message: string, options?: CfHanaErrorOptions) {\n super(\"READ_ONLY_VIOLATION\", message, options);\n this.name = \"ReadOnlyViolationError\";\n }\n}\n\n/** A destructive statement was blocked because it was not explicitly allowed. */\nexport class DestructiveStatementError extends CfHanaError {\n constructor(message: string, options?: CfHanaErrorOptions) {\n super(\"DESTRUCTIVE_BLOCKED\", message, options);\n this.name = \"DestructiveStatementError\";\n }\n}\n\n/** Narrow an unknown thrown value to a human-readable message. */\nexport function errorMessage(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n return String(error);\n}\n","import { previewCell } from \"./result-preview.js\";\nimport type { OutputFormat, QueryResult, SqlParam } from \"./types.js\";\n\ntype JsonCell = string | number | boolean | null;\n\nfunction cellText(value: SqlParam, nullText: string): string {\n if (value === null) {\n return nullText;\n }\n if (value instanceof Date) {\n return value.toISOString();\n }\n if (Buffer.isBuffer(value)) {\n return `0x${value.toString(\"hex\")}`;\n }\n if (typeof value === \"boolean\") {\n return value ? \"true\" : \"false\";\n }\n return typeof value === \"number\" ? value.toString() : value;\n}\n\nfunction serializeCell(value: SqlParam): JsonCell {\n if (value === null) {\n return null;\n }\n if (value instanceof Date) {\n return value.toISOString();\n }\n if (Buffer.isBuffer(value)) {\n return `0x${value.toString(\"hex\")}`;\n }\n return value;\n}\n\nfunction csvEscape(text: string): string {\n if (/[\",\\r\\n]/.test(text)) {\n return `\"${text.replace(/\"/g, '\"\"')}\"`;\n }\n return text;\n}\n\nexport interface CompactCsv {\n readonly text: string;\n readonly truncatedCells: number;\n}\n\n/** Render a result as an aligned ASCII table. */\nexport function formatTable(result: QueryResult): string {\n if (result.columns.length === 0) {\n return `(${String(result.rowCount)} row(s) affected)`;\n }\n\n const headers = result.columns.map((column) => column.name);\n const rows = result.rows.map((row) =>\n result.columns.map((column) => cellText(row[column.name] ?? null, \"NULL\")),\n );\n const widths = headers.map((header, index) => {\n const widest = rows.reduce(\n (max, cells) => Math.max(max, (cells[index] ?? \"\").length),\n header.length,\n );\n return widest;\n });\n\n const renderRow = (cells: readonly string[]): string =>\n cells.map((cell, index) => cell.padEnd(widths[index] ?? 0)).join(\" | \");\n const separator = widths.map((width) => \"-\".repeat(width)).join(\"-+-\");\n const body = rows.map((cells) => renderRow(cells));\n\n return [renderRow(headers), separator, ...body].join(\"\\n\");\n}\n\n/** Render a result's rows as pretty-printed JSON. */\nexport function formatJson(result: QueryResult): string {\n const rows = result.rows.map((row) => {\n const serialized: Record<string, JsonCell> = {};\n for (const [key, value] of Object.entries(row)) {\n serialized[key] = serializeCell(value);\n }\n return serialized;\n });\n return JSON.stringify(rows, null, 2);\n}\n\n/** Render a result as RFC 4180 CSV. */\nexport function formatCsv(result: QueryResult): string {\n const headers = result.columns.map((column) => column.name);\n const lines = [headers.map((header) => csvEscape(header)).join(\",\")];\n for (const row of result.rows) {\n lines.push(\n result.columns\n .map((column) => csvEscape(cellText(row[column.name] ?? null, \"\")))\n .join(\",\"),\n );\n }\n return lines.join(\"\\r\\n\");\n}\n\n/** Render bounded CSV for CLI SELECT output without mutating the source result. */\nexport function formatCompactCsv(result: QueryResult, cellLimit: number): CompactCsv {\n const headers = result.columns.map((column) => column.name);\n const lines = [headers.map((header) => csvEscape(header)).join(\",\")];\n let truncatedCells = 0;\n for (const row of result.rows) {\n const cells = result.columns.map((column) => {\n const preview = previewCell(row[column.name] ?? null, cellLimit);\n if (preview.truncated) {\n truncatedCells += 1;\n }\n return csvEscape(preview.text);\n });\n lines.push(cells.join(\",\"));\n }\n return { text: lines.join(\"\\r\\n\"), truncatedCells };\n}\n\n/** Render a query result in the requested output format. */\nexport function formatResult(result: QueryResult, format: OutputFormat): string {\n switch (format) {\n case \"table\":\n return formatTable(result);\n case \"json\":\n return formatJson(result);\n case \"csv\":\n return formatCsv(result);\n }\n}\n","import { QueryError } from \"./errors.js\";\nimport type { SqlParam, StatementKind } from \"./types.js\";\n\nconst LEADING_NOISE = /^(?:\\s|--[^\\n]*\\n?|\\/\\*[\\s\\S]*?\\*\\/)+/;\n\nconst SELECT_KEYWORDS = new Set([\"SELECT\", \"WITH\"]);\nconst DML_KEYWORDS = new Set([\"INSERT\", \"UPDATE\", \"DELETE\", \"MERGE\", \"UPSERT\", \"REPLACE\"]);\nconst DDL_KEYWORDS = new Set([\"CREATE\", \"DROP\", \"ALTER\", \"TRUNCATE\", \"RENAME\", \"COMMENT\"]);\n\n/** The leading SQL keyword, upper-cased, skipping comments and whitespace. */\nexport function firstKeyword(sql: string): string {\n const stripped = sql.replace(LEADING_NOISE, \"\");\n const match = /^[A-Za-z]+/.exec(stripped);\n return (match?.[0] ?? \"\").toUpperCase();\n}\n\n/** Classify a SQL statement by its leading keyword. */\nexport function classifyStatement(sql: string): StatementKind {\n const keyword = firstKeyword(sql);\n if (SELECT_KEYWORDS.has(keyword)) {\n return \"select\";\n }\n if (DML_KEYWORDS.has(keyword)) {\n return \"dml\";\n }\n if (DDL_KEYWORDS.has(keyword)) {\n return \"ddl\";\n }\n return \"unknown\";\n}\n\n/** Quote a SQL identifier (table/column name) for safe interpolation. */\nexport function quoteIdentifier(identifier: string): string {\n if (identifier.length === 0) {\n throw new QueryError(\"A SQL identifier must not be empty\");\n }\n if (identifier.includes(\"\\0\")) {\n throw new QueryError(\"A SQL identifier must not contain a NUL character\");\n }\n return `\"${identifier.replace(/\"/g, '\"\"')}\"`;\n}\n\n/** Build a `\"schema\".\"table\"` qualified, quoted name. */\nexport function qualifiedName(schema: string, table: string): string {\n return `${quoteIdentifier(schema)}.${quoteIdentifier(table)}`;\n}\n\n/** Count `?` placeholders, skipping string literals, quoted identifiers, and comments. */\nexport function countPlaceholders(sql: string): number {\n let count = 0;\n let index = 0;\n const length = sql.length;\n\n while (index < length) {\n const char = sql[index];\n\n if (char === \"'\" || char === '\"') {\n const quote = char;\n index += 1;\n while (index < length) {\n if (sql[index] === quote) {\n if (sql[index + 1] === quote) {\n index += 2;\n continue;\n }\n index += 1;\n break;\n }\n index += 1;\n }\n continue;\n }\n\n if (char === \"-\" && sql[index + 1] === \"-\") {\n index += 2;\n while (index < length && sql[index] !== \"\\n\") {\n index += 1;\n }\n continue;\n }\n\n if (char === \"/\" && sql[index + 1] === \"*\") {\n index += 2;\n while (index < length && !(sql[index] === \"*\" && sql[index + 1] === \"/\")) {\n index += 1;\n }\n index += 2;\n continue;\n }\n\n if (char === \"?\") {\n count += 1;\n }\n index += 1;\n }\n\n return count;\n}\n\n/** Throw when the number of `?` placeholders does not match the parameter count. */\nexport function assertParamArity(sql: string, params: readonly SqlParam[]): void {\n const expected = countPlaceholders(sql);\n if (expected !== params.length) {\n throw new QueryError(\n `SQL expects ${String(expected)} bound parameter(s) ` +\n `but received ${String(params.length)} value(s)`,\n );\n }\n}\n","import { QueryError } from \"./errors.js\";\nimport { qualifiedName, quoteIdentifier } from \"./statements.js\";\nimport type { BuiltStatement, SelectSpec, SqlParam } from \"./types.js\";\n\ninterface WhereClause {\n readonly clause: string;\n readonly params: readonly SqlParam[];\n}\n\nfunction buildWhere(where: Readonly<Record<string, SqlParam>> | undefined): WhereClause {\n if (where === undefined) {\n return { clause: \"\", params: [] };\n }\n const entries = Object.entries(where);\n if (entries.length === 0) {\n return { clause: \"\", params: [] };\n }\n const conditions = entries.map(([column]) => `${quoteIdentifier(column)} = ?`);\n return {\n clause: ` WHERE ${conditions.join(\" AND \")}`,\n params: entries.map(([, value]) => value),\n };\n}\n\n/** Build a parameterized `SELECT` statement from a typed spec. */\nexport function buildSelect(spec: SelectSpec): BuiltStatement {\n const columns =\n spec.columns === undefined || spec.columns.length === 0\n ? \"*\"\n : spec.columns.map((column) => quoteIdentifier(column)).join(\", \");\n const where = buildWhere(spec.where);\n const clauses: string[] = [\n `SELECT ${columns} FROM ${qualifiedName(spec.schema, spec.table)}${where.clause}`,\n ];\n\n if (spec.orderBy !== undefined && spec.orderBy.length > 0) {\n clauses.push(`ORDER BY ${spec.orderBy.map((column) => quoteIdentifier(column)).join(\", \")}`);\n }\n if (spec.limit !== undefined) {\n clauses.push(`LIMIT ${String(spec.limit)}`);\n }\n if (spec.offset !== undefined) {\n clauses.push(`OFFSET ${String(spec.offset)}`);\n }\n\n return { sql: clauses.join(\" \"), params: where.params };\n}\n\n/** Build a parameterized `SELECT COUNT(*)` statement. */\nexport function buildCount(\n spec: Pick<SelectSpec, \"schema\" | \"table\" | \"where\">,\n): BuiltStatement {\n const where = buildWhere(spec.where);\n return {\n sql: `SELECT COUNT(*) AS \"COUNT\" FROM ${qualifiedName(spec.schema, spec.table)}${where.clause}`,\n params: where.params,\n };\n}\n\n/** Build a parameterized single-row `INSERT` statement. */\nexport function buildInsert(\n schema: string,\n table: string,\n values: Readonly<Record<string, SqlParam>>,\n): BuiltStatement {\n const entries = Object.entries(values);\n if (entries.length === 0) {\n throw new QueryError(\"INSERT requires at least one column value\");\n }\n const columns = entries.map(([column]) => quoteIdentifier(column)).join(\", \");\n const placeholders = entries.map(() => \"?\").join(\", \");\n return {\n sql: `INSERT INTO ${qualifiedName(schema, table)} (${columns}) VALUES (${placeholders})`,\n params: entries.map(([, value]) => value),\n };\n}\n\n/** Build a parameterized `UPDATE` statement. A non-empty `where` is required. */\nexport function buildUpdate(\n schema: string,\n table: string,\n values: Readonly<Record<string, SqlParam>>,\n where: Readonly<Record<string, SqlParam>>,\n): BuiltStatement {\n const valueEntries = Object.entries(values);\n if (valueEntries.length === 0) {\n throw new QueryError(\"UPDATE requires at least one column value\");\n }\n const whereClause = buildWhere(where);\n if (whereClause.clause === \"\") {\n throw new QueryError(\"UPDATE requires a non-empty WHERE filter to avoid a full-table write\");\n }\n const assignments = valueEntries\n .map(([column]) => `${quoteIdentifier(column)} = ?`)\n .join(\", \");\n return {\n sql: `UPDATE ${qualifiedName(schema, table)} SET ${assignments}${whereClause.clause}`,\n params: [...valueEntries.map(([, value]) => value), ...whereClause.params],\n };\n}\n\n/** Build a parameterized `DELETE` statement. A non-empty `where` is required. */\nexport function buildDelete(\n schema: string,\n table: string,\n where: Readonly<Record<string, SqlParam>>,\n): BuiltStatement {\n const whereClause = buildWhere(where);\n if (whereClause.clause === \"\") {\n throw new QueryError(\"DELETE requires a non-empty WHERE filter to avoid a full-table delete\");\n }\n return {\n sql: `DELETE FROM ${qualifiedName(schema, table)}${whereClause.clause}`,\n params: whereClause.params,\n };\n}\n","import type { Connection } from \"./connection.js\";\nimport type { ColumnInfo, TableInfo } from \"./types.js\";\n\ninterface SchemaRow {\n readonly SCHEMA_NAME: string;\n}\n\ninterface TableRow {\n readonly SCHEMA_NAME: string;\n readonly TABLE_NAME: string;\n readonly TABLE_TYPE: string;\n}\n\ninterface ColumnRow {\n readonly COLUMN_NAME: string;\n readonly DATA_TYPE_NAME: string;\n readonly LENGTH: number | null;\n readonly SCALE: number | null;\n readonly IS_NULLABLE: string;\n readonly POSITION: number;\n}\n\nexport interface TableDescription {\n readonly table: TableInfo | undefined;\n readonly columns: readonly ColumnInfo[];\n}\n\n/** List every schema visible to the current user. */\nexport async function listSchemas(connection: Connection): Promise<readonly string[]> {\n const result = await connection.query<SchemaRow>(\n \"SELECT SCHEMA_NAME FROM SYS.SCHEMAS ORDER BY SCHEMA_NAME\",\n [],\n { autoLimit: false },\n );\n return result.rows.map((row) => row.SCHEMA_NAME);\n}\n\n/** List the tables in a schema. */\nexport async function listTables(\n connection: Connection,\n schema: string,\n): Promise<readonly TableInfo[]> {\n const result = await connection.query<TableRow>(\n \"SELECT SCHEMA_NAME, TABLE_NAME, TABLE_TYPE FROM SYS.TABLES \" +\n \"WHERE SCHEMA_NAME = ? ORDER BY TABLE_NAME\",\n [schema],\n { autoLimit: false },\n );\n return result.rows.map((row) => ({\n schema: row.SCHEMA_NAME,\n name: row.TABLE_NAME,\n type: row.TABLE_TYPE,\n rowCount: undefined,\n }));\n}\n\n/** List the columns of a table, ordered by position. */\nexport async function listColumns(\n connection: Connection,\n schema: string,\n table: string,\n): Promise<readonly ColumnInfo[]> {\n const result = await connection.query<ColumnRow>(\n \"SELECT COLUMN_NAME, DATA_TYPE_NAME, LENGTH, SCALE, IS_NULLABLE, POSITION \" +\n \"FROM SYS.TABLE_COLUMNS WHERE SCHEMA_NAME = ? AND TABLE_NAME = ? ORDER BY POSITION\",\n [schema, table],\n { autoLimit: false },\n );\n return result.rows.map((row) => ({\n name: row.COLUMN_NAME,\n dataType: row.DATA_TYPE_NAME,\n length: row.LENGTH ?? undefined,\n scale: row.SCALE ?? undefined,\n nullable: row.IS_NULLABLE === \"TRUE\",\n position: row.POSITION,\n }));\n}\n\n/** Describe a table: its catalog entry plus its columns. */\nexport async function describeTable(\n connection: Connection,\n schema: string,\n table: string,\n): Promise<TableDescription> {\n const tables = await listTables(connection, schema);\n const columns = await listColumns(connection, schema, table);\n return {\n table: tables.find((info) => info.name === table),\n columns,\n };\n}\n","export const CLI_NAME = \"cf-hana\";\nexport const CLI_VERSION = \"0.2.0\";\nexport const ENV_PREFIX = \"CF_HANA\";\n\nexport const DEFAULT_QUERY_TIMEOUT_MS = 30_000;\nexport const DEFAULT_CONNECT_TIMEOUT_MS = 30_000;\nexport const DEFAULT_POOL_MAX = 4;\nexport const DEFAULT_POOL_IDLE_MS = 60_000;\nexport const DEFAULT_AUTO_LIMIT = 100;\nexport const DEFAULT_CELL_LIMIT = 128;\nexport const MAX_CELL_LIMIT = 10_000;\nexport const DEFAULT_RESULT_TTL_MINUTES = 60;\nexport const DEFAULT_RESULT_SEARCH_LIMIT = 20;\nexport const MAX_RESULT_STORE_BYTES = 256 * 1024 * 1024;\nexport const HANA_CLOUD_DEFAULT_PORT = 443;\n\n/** Build a `CF_HANA_*` environment variable name from a suffix. */\nexport function envName(suffix: string): string {\n return `${ENV_PREFIX}_${suffix}`;\n}\n\n/** Read an environment variable, treating blank values as absent. */\nexport function readEnv(name: string): string | undefined {\n const value = process.env[name];\n if (value === undefined) {\n return undefined;\n }\n const trimmed = value.trim();\n return trimmed.length === 0 ? undefined : trimmed;\n}\n\nexport interface SapCredentials {\n readonly email: string;\n readonly password: string;\n}\n\nexport interface SapCredentialOverrides {\n readonly email?: string | undefined;\n readonly password?: string | undefined;\n}\n\n/**\n * Resolve SAP BTP credentials from explicit overrides, falling back to the\n * repo-wide `SAP_EMAIL` / `SAP_PASSWORD` environment variables. Returns\n * `undefined` when either half is missing.\n */\nexport function readSapCredentials(\n overrides?: SapCredentialOverrides,\n): SapCredentials | undefined {\n const email = overrides?.email ?? readEnv(\"SAP_EMAIL\");\n const password = overrides?.password ?? readEnv(\"SAP_PASSWORD\");\n if (email === undefined || password === undefined) {\n return undefined;\n }\n return { email, password };\n}\n","import { fetchAppDbBindings, readDbAppView } from \"@saptools/cf-sync\";\nimport type { AppDbBinding } from \"@saptools/cf-sync\";\n\nimport { readSapCredentials } from \"./config.js\";\nimport { CfHanaError, CredentialsNotFoundError } from \"./errors.js\";\nimport type { CredentialSource, DbUserRole } from \"./types.js\";\n\nexport interface ResolveBindingsOptions {\n readonly refresh?: boolean;\n readonly email?: string;\n readonly password?: string;\n}\n\nexport interface ResolvedBindings {\n readonly selector: string;\n readonly appName: string;\n readonly bindings: readonly AppDbBinding[];\n readonly source: CredentialSource;\n}\n\nexport interface BindingSelector {\n readonly bindingName?: string;\n readonly bindingIndex?: number;\n}\n\nexport interface SelectedConnectionTarget {\n readonly host: string;\n readonly port: number;\n readonly user: string;\n readonly password: string;\n readonly schema: string;\n readonly certificate: string;\n readonly databaseId: string;\n}\n\n/**\n * Resolve an app's HANA bindings for a `region/org/space/app` selector (or a\n * bare app name): cache-first via `cf-sync`, falling back to a live CF fetch.\n */\nexport async function resolveAppBindings(\n selector: string,\n options: ResolveBindingsOptions,\n): Promise<ResolvedBindings> {\n if (options.refresh !== true) {\n const view = await readDbAppView(selector);\n if (view !== undefined && view.entry.bindings.length > 0) {\n return {\n selector: view.entry.selector,\n appName: view.entry.appName,\n bindings: view.entry.bindings,\n source: \"cache\",\n };\n }\n }\n\n const credentials = readSapCredentials({\n email: options.email,\n password: options.password,\n });\n if (credentials === undefined) {\n throw new CredentialsNotFoundError(\n `No cached HANA credentials for \"${selector}\". ` +\n `Run \\`cf-sync db-sync ${selector}\\` first, or set SAP_EMAIL and ` +\n `SAP_PASSWORD to fetch them live.`,\n );\n }\n\n const fetched = await fetchAppDbBindings({\n selector,\n email: credentials.email,\n password: credentials.password,\n });\n if (fetched.bindings.length === 0) {\n throw new CredentialsNotFoundError(\n `App \"${fetched.selector}\" has no HANA service binding.`,\n );\n }\n return {\n selector: fetched.selector,\n appName: fetched.appName,\n bindings: fetched.bindings,\n source: \"fresh\",\n };\n}\n\n/** Pick a single HANA binding from an app's bindings. */\nexport function selectBinding(\n bindings: readonly AppDbBinding[],\n selector: BindingSelector,\n): AppDbBinding {\n if (selector.bindingName !== undefined) {\n const match = bindings.find((binding) => binding.name === selector.bindingName);\n if (match === undefined) {\n throw new CfHanaError(\n \"AMBIGUOUS_BINDING\",\n `No HANA binding named \"${selector.bindingName}\"`,\n );\n }\n return match;\n }\n\n if (selector.bindingIndex !== undefined) {\n const match = bindings[selector.bindingIndex];\n if (match === undefined) {\n throw new CfHanaError(\n \"AMBIGUOUS_BINDING\",\n `No HANA binding at index ${String(selector.bindingIndex)}`,\n );\n }\n return match;\n }\n\n const first = bindings[0];\n if (first === undefined) {\n throw new CredentialsNotFoundError(\"No HANA bindings are available for this app\");\n }\n if (bindings.length > 1) {\n const labels = bindings\n .map((binding, index) => binding.name ?? `#${String(index)}`)\n .join(\", \");\n throw new CfHanaError(\n \"AMBIGUOUS_BINDING\",\n `App has multiple HANA bindings (${labels}); ` +\n `choose one with bindingName or bindingIndex`,\n );\n }\n return first;\n}\n\n/** Map a HANA binding to concrete connection parameters for the chosen role. */\nexport function toConnectionTarget(\n binding: AppDbBinding,\n role: DbUserRole,\n): SelectedConnectionTarget {\n const credentials = binding.credentials;\n const port = Number.parseInt(credentials.port, 10);\n if (!Number.isInteger(port) || port <= 0) {\n throw new CfHanaError(\"CONFIG\", `Invalid HANA port in binding: \"${credentials.port}\"`);\n }\n return {\n host: credentials.host,\n port,\n user: role === \"hdi\" ? credentials.hdiUser : credentials.user,\n password: role === \"hdi\" ? credentials.hdiPassword : credentials.password,\n schema: credentials.schema,\n certificate: credentials.certificate,\n databaseId: credentials.databaseId,\n };\n}\n","import { appendFile } from \"node:fs/promises\";\n\nimport { envName, readEnv } from \"../config.js\";\nimport { classifyStatement } from \"../statements.js\";\nimport type { SqlParam } from \"../types.js\";\n\nimport type {\n DriverConnectParams,\n DriverConnection,\n DriverExecResult,\n HanaDriver,\n} from \"./types.js\";\n\n/**\n * Deterministic in-memory driver used by hermetic end-to-end tests and offline\n * smoke checks. Selected via `CF_HANA_DRIVER=fake`. It never opens a socket.\n */\nfunction fakeExec(sql: string): DriverExecResult {\n const kind = classifyStatement(sql);\n const forcedFailure = readEnv(envName(\"FAKE_FAIL_STATEMENT\"))?.toLowerCase();\n if (forcedFailure === kind) {\n throw new Error(`fake driver forced ${kind.toUpperCase()} failure`);\n }\n\n if (sql.toUpperCase().includes(\"DUMMY\")) {\n return {\n rows: [{ \"1\": 1 }],\n columns: [{ name: \"1\", typeName: \"INTEGER\" }],\n affectedRows: 0,\n };\n }\n\n if (kind === \"select\") {\n return {\n rows: [\n { ID: 1, NAME: \"sample-row\" },\n { ID: 2, NAME: \"second-row\" },\n ],\n columns: [\n { name: \"ID\", typeName: \"INTEGER\" },\n { name: \"NAME\", typeName: \"NVARCHAR\" },\n ],\n affectedRows: 0,\n };\n }\n if (kind === \"dml\") {\n return { rows: [], columns: [], affectedRows: 1 };\n }\n return { rows: [], columns: [], affectedRows: 0 };\n}\n\n/** Opt-in fake-driver trace; parameter values stay out of test artifacts. */\nasync function traceFakeExec(sql: string, params: readonly SqlParam[]): Promise<void> {\n const tracePath = readEnv(envName(\"FAKE_TRACE_FILE\"));\n if (tracePath === undefined) {\n return;\n }\n const entry = { sql, paramCount: params.length };\n await appendFile(tracePath, `${JSON.stringify(entry)}\\n`, {\n encoding: \"utf8\",\n mode: 0o600,\n });\n}\n\nclass FakeConnection implements DriverConnection {\n private closed = false;\n\n async exec(sql: string, params: readonly SqlParam[]): Promise<DriverExecResult> {\n await traceFakeExec(sql, params);\n return fakeExec(sql);\n }\n\n setAutoCommit(_enabled: boolean): Promise<void> {\n return Promise.resolve();\n }\n\n commit(): Promise<void> {\n return Promise.resolve();\n }\n\n rollback(): Promise<void> {\n return Promise.resolve();\n }\n\n close(): Promise<void> {\n this.closed = true;\n return Promise.resolve();\n }\n\n isClosed(): boolean {\n return this.closed;\n }\n}\n\n/** Build the deterministic test-only fake HANA driver. */\nexport function createFakeDriver(): HanaDriver {\n return {\n name: \"fake\",\n connect: (_params: DriverConnectParams): Promise<DriverConnection> =>\n Promise.resolve(new FakeConnection()),\n };\n}\n","import { createClient } from \"hdb\";\nimport type { HdbClient, HdbColumnMetadata, HdbStatement } from \"hdb\";\n\nimport { CfHanaError, QueryError } from \"../errors.js\";\nimport { quoteIdentifier } from \"../statements.js\";\nimport type { QueryResultColumn, SqlParam } from \"../types.js\";\n\nimport type {\n DriverConnectParams,\n DriverConnection,\n DriverExecResult,\n HanaDriver,\n} from \"./types.js\";\n\nconst TYPE_NAMES: Readonly<Record<number, string>> = {\n 0: \"NULL\",\n 1: \"TINYINT\",\n 2: \"SMALLINT\",\n 3: \"INTEGER\",\n 4: \"BIGINT\",\n 5: \"DECIMAL\",\n 6: \"REAL\",\n 7: \"DOUBLE\",\n 8: \"CHAR\",\n 9: \"VARCHAR\",\n 10: \"NCHAR\",\n 11: \"NVARCHAR\",\n 12: \"BINARY\",\n 13: \"VARBINARY\",\n 14: \"DATE\",\n 15: \"TIME\",\n 16: \"TIMESTAMP\",\n 25: \"CLOB\",\n 26: \"NCLOB\",\n 27: \"BLOB\",\n 28: \"BOOLEAN\",\n 29: \"STRING\",\n 30: \"NSTRING\",\n 47: \"DECIMAL\",\n 51: \"TEXT\",\n 52: \"SHORTTEXT\",\n 62: \"DECIMAL\",\n};\n\nfunction typeName(code: number): string {\n return TYPE_NAMES[code] ?? `TYPE_${String(code)}`;\n}\n\nfunction extractSqlState(error: Error): string | undefined {\n const value = (error as { readonly sqlState?: unknown }).sqlState;\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction toQueryError(error: Error): QueryError {\n const sqlState = extractSqlState(error);\n return sqlState === undefined\n ? new QueryError(error.message, { cause: error })\n : new QueryError(error.message, { cause: error, sqlState });\n}\n\nfunction toColumns(\n metadata: readonly HdbColumnMetadata[] | undefined,\n): readonly QueryResultColumn[] {\n if (metadata === undefined) {\n return [];\n }\n return metadata.map((column) => ({\n name: column.columnDisplayName ?? column.columnName ?? \"\",\n typeName: typeName(column.dataType),\n }));\n}\n\nfunction toExecResult(statement: HdbStatement, raw: unknown): DriverExecResult {\n if (typeof raw === \"number\") {\n return { rows: [], columns: [], affectedRows: raw };\n }\n if (Array.isArray(raw)) {\n return {\n rows: raw as readonly Record<string, SqlParam>[],\n columns: toColumns(statement.resultSetMetadata),\n affectedRows: 0,\n };\n }\n return { rows: [], columns: [], affectedRows: 0 };\n}\n\nclass HdbConnection implements DriverConnection {\n private closed = false;\n\n constructor(private readonly client: HdbClient) {}\n\n async exec(sql: string, params: readonly SqlParam[]): Promise<DriverExecResult> {\n const statement = await this.prepareStatement(sql);\n try {\n const raw = await this.executeStatement(statement, params);\n return toExecResult(statement, raw);\n } finally {\n dropStatementQuietly(statement);\n }\n }\n\n setAutoCommit(enabled: boolean): Promise<void> {\n this.client.setAutoCommit(enabled);\n return Promise.resolve();\n }\n\n async commit(): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n this.client.commit((error) => {\n if (error) {\n reject(toQueryError(error));\n } else {\n resolve();\n }\n });\n });\n }\n\n async rollback(): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n this.client.rollback((error) => {\n if (error) {\n reject(toQueryError(error));\n } else {\n resolve();\n }\n });\n });\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n this.closed = true;\n await new Promise<void>((resolve) => {\n this.client.disconnect(() => {\n resolve();\n });\n });\n this.client.close();\n }\n\n isClosed(): boolean {\n return (\n this.closed ||\n this.client.readyState === \"closed\" ||\n this.client.readyState === \"disconnected\"\n );\n }\n\n private async prepareStatement(sql: string): Promise<HdbStatement> {\n return await new Promise<HdbStatement>((resolve, reject) => {\n this.client.prepare(sql, (error, statement) => {\n if (error) {\n reject(toQueryError(error));\n } else {\n resolve(statement);\n }\n });\n });\n }\n\n private async executeStatement(\n statement: HdbStatement,\n params: readonly SqlParam[],\n ): Promise<unknown> {\n return await new Promise<unknown>((resolve, reject) => {\n statement.exec([...params], (error, result) => {\n if (error) {\n reject(toQueryError(error));\n } else {\n resolve(result);\n }\n });\n });\n }\n}\n\nfunction dropStatementQuietly(statement: HdbStatement): void {\n try {\n statement.drop();\n } catch {\n // Best-effort cleanup; preserve the query result or original query error.\n }\n}\n\nfunction openClient(client: HdbClient, timeoutMs: number): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n let settled = false;\n const timer = setTimeout(() => {\n if (settled) {\n return;\n }\n settled = true;\n client.close();\n reject(\n new CfHanaError(\n \"TIMEOUT\",\n `HANA connection timed out after ${String(timeoutMs)}ms`,\n ),\n );\n }, timeoutMs);\n timer.unref();\n client.connect((error) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timer);\n if (error) {\n reject(\n new CfHanaError(\"CONNECTION\", `Failed to connect to HANA: ${error.message}`, {\n cause: error,\n }),\n );\n } else {\n resolve();\n }\n });\n });\n}\n\nasync function closeClientQuietly(client: HdbClient): Promise<void> {\n try {\n await new Promise<void>((resolve) => {\n client.disconnect(() => {\n resolve();\n });\n });\n } catch {\n // Best-effort cleanup after a partially opened connection.\n }\n try {\n client.close();\n } catch {\n // Best-effort cleanup after a partially opened connection.\n }\n}\n\nfunction setCurrentSchema(client: HdbClient, schema: string): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n client.exec(`SET SCHEMA ${quoteIdentifier(schema)}`, (error) => {\n if (error) {\n reject(toQueryError(error));\n } else {\n resolve();\n }\n });\n });\n}\n\nasync function connectHdb(params: DriverConnectParams): Promise<DriverConnection> {\n const client = createClient({\n host: params.host,\n port: params.port,\n user: params.user,\n password: params.password,\n ca: params.certificate,\n useTLS: true,\n });\n await openClient(client, params.connectTimeoutMs);\n try {\n await setCurrentSchema(client, params.schema);\n } catch (error) {\n await closeClientQuietly(client);\n throw error;\n }\n return new HdbConnection(client);\n}\n\n/** Build the production HANA driver backed by the pure-JavaScript `hdb` library. */\nexport function createHdbDriver(): HanaDriver {\n return {\n name: \"hdb\",\n connect: connectHdb,\n };\n}\n","import { envName, readEnv } from \"../config.js\";\nimport { CfHanaError } from \"../errors.js\";\n\nimport { createFakeDriver } from \"./fake.js\";\nimport { createHdbDriver } from \"./hdb.js\";\nimport type { HanaDriver } from \"./types.js\";\n\nexport type {\n DriverConnectParams,\n DriverConnection,\n DriverExecResult,\n HanaDriver,\n} from \"./types.js\";\n\n/**\n * Create a HANA driver by name. Defaults to `hdb`; the `CF_HANA_DRIVER`\n * environment variable overrides the default when no name is passed.\n */\nexport function createDriver(name?: string): HanaDriver {\n const resolved = name ?? readEnv(envName(\"DRIVER\")) ?? \"hdb\";\n switch (resolved) {\n case \"hdb\":\n return createHdbDriver();\n case \"fake\":\n return createFakeDriver();\n default:\n throw new CfHanaError(\"CONFIG\", `Unknown HANA driver: ${resolved}`);\n }\n}\n","import { appendFile, mkdir, readdir, rm } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport type { DbUserRole, StatementKind } from \"./types.js\";\n\nconst SAPTOOLS_DIR_NAME = \".saptools\";\nconst CF_HANA_DIR_NAME = \"cf-hana\";\nconst HISTORIES_DIR_NAME = \"histories\";\nconst HISTORY_RETENTION_DAYS = 5;\nconst HISTORY_FILE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}\\.jsonl$/;\n\nexport type SqlHistoryOperation = \"query\" | \"execute\";\n\nexport interface SqlHistoryEntryInput {\n readonly version: string;\n readonly operation: SqlHistoryOperation;\n readonly selector: string;\n readonly appName: string;\n readonly schema: string;\n readonly role: DbUserRole;\n readonly statement: StatementKind;\n readonly sql: string;\n readonly paramCount: number;\n readonly rowCount: number;\n readonly truncated: boolean;\n readonly elapsedMs: number;\n}\n\nexport interface SqlHistoryEntry extends SqlHistoryEntryInput {\n readonly at: string;\n}\n\nexport interface SqlHistoryWriteOptions {\n readonly now?: Date;\n readonly saptoolsRoot?: string;\n}\n\nfunction defaultSaptoolsRoot(): string {\n return join(homedir(), SAPTOOLS_DIR_NAME);\n}\n\nfunction padDatePart(value: number): string {\n return value.toString().padStart(2, \"0\");\n}\n\nfunction dateKey(date: Date): string {\n return [\n date.getFullYear().toString(),\n padDatePart(date.getMonth() + 1),\n padDatePart(date.getDate()),\n ].join(\"-\");\n}\n\nfunction retentionCutoffKey(now: Date): string {\n const cutoff = new Date(now);\n cutoff.setHours(0, 0, 0, 0);\n cutoff.setDate(cutoff.getDate() - HISTORY_RETENTION_DAYS);\n return dateKey(cutoff);\n}\n\nfunction resolveSaptoolsRoot(root: string | undefined): string {\n return root ?? defaultSaptoolsRoot();\n}\n\nexport function cfHanaHistoryDirectory(saptoolsRoot?: string): string {\n return join(resolveSaptoolsRoot(saptoolsRoot), CF_HANA_DIR_NAME, HISTORIES_DIR_NAME);\n}\n\nexport function sqlHistoryFilePath(now: Date = new Date(), saptoolsRoot?: string): string {\n return join(cfHanaHistoryDirectory(saptoolsRoot), `${dateKey(now)}.jsonl`);\n}\n\nasync function pruneSqlHistory(now: Date, saptoolsRoot?: string): Promise<void> {\n const historyDir = cfHanaHistoryDirectory(saptoolsRoot);\n let files: readonly string[];\n try {\n files = await readdir(historyDir);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return;\n }\n throw error;\n }\n\n const cutoffKey = retentionCutoffKey(now);\n await Promise.all(\n files\n .filter((file) => HISTORY_FILE_PATTERN.test(file))\n .filter((file) => file.slice(0, 10) < cutoffKey)\n .map(async (file) => {\n await rm(join(historyDir, file), { force: true });\n }),\n );\n}\n\nexport async function appendSqlHistory(\n input: SqlHistoryEntryInput,\n options: SqlHistoryWriteOptions = {},\n): Promise<SqlHistoryEntry> {\n const now = options.now ?? new Date();\n const historyDir = cfHanaHistoryDirectory(options.saptoolsRoot);\n const entry: SqlHistoryEntry = {\n at: now.toISOString(),\n ...input,\n };\n\n await mkdir(historyDir, { recursive: true, mode: 0o700 });\n await appendFile(sqlHistoryFilePath(now, options.saptoolsRoot), `${JSON.stringify(entry)}\\n`, {\n encoding: \"utf8\",\n mode: 0o600,\n });\n try {\n await pruneSqlHistory(now, options.saptoolsRoot);\n } catch {\n // Retention is best-effort; the SQL history entry was already recorded.\n }\n\n return entry;\n}\n","import { classifyStatement, firstKeyword } from \"./statements.js\";\nimport type { StatementKind } from \"./types.js\";\n\nconst DESTRUCTIVE_DDL_KEYWORDS = new Set([\"DROP\", \"TRUNCATE\", \"ALTER\"]);\nconst UNSCOPED_WRITE_KEYWORDS = new Set([\"UPDATE\", \"DELETE\"]);\n\nexport type GuardViolation = \"read-only\" | \"destructive\";\n\nexport interface GuardConfig {\n readonly readOnly: boolean;\n readonly allowDestructive: boolean;\n}\n\nexport interface GuardDecision {\n readonly allowed: boolean;\n readonly destructive: boolean;\n readonly violation: GuardViolation | undefined;\n readonly reason: string | undefined;\n}\n\nexport interface StatementInspection {\n readonly kind: StatementKind;\n readonly destructive: boolean;\n}\n\nexport interface AutoLimitResult {\n readonly sql: string;\n readonly applied: boolean;\n readonly requestedLimit?: number;\n}\n\nfunction skipQuotedText(sql: string, start: number): number {\n const quote = sql[start];\n let index = start + 1;\n while (index < sql.length) {\n if (sql[index] === quote) {\n if (sql[index + 1] === quote) {\n index += 2;\n continue;\n }\n index += 1;\n break;\n }\n index += 1;\n }\n return index;\n}\n\nfunction skipLineComment(sql: string, start: number): number {\n let index = start + 2;\n while (index < sql.length && sql[index] !== \"\\n\") {\n index += 1;\n }\n return index;\n}\n\nfunction skipBlockComment(sql: string, start: number): number {\n let index = start + 2;\n while (index < sql.length && !(sql[index] === \"*\" && sql[index + 1] === \"/\")) {\n index += 1;\n }\n return Math.min(index + 2, sql.length);\n}\n\nfunction maskIgnoredSqlText(sql: string): string {\n let masked = \"\";\n let index = 0;\n while (index < sql.length) {\n const char = sql[index];\n if (char === \"'\" || char === '\"') {\n const end = skipQuotedText(sql, index);\n masked += \" \".repeat(end - index);\n index = end;\n continue;\n }\n if (char === \"-\" && sql[index + 1] === \"-\") {\n const end = skipLineComment(sql, index);\n masked += \" \".repeat(end - index);\n index = end;\n continue;\n }\n if (char === \"/\" && sql[index + 1] === \"*\") {\n const end = skipBlockComment(sql, index);\n masked += \" \".repeat(end - index);\n index = end;\n continue;\n }\n masked += char ?? \"\";\n index += 1;\n }\n return masked;\n}\n\nfunction hasWhereClause(sql: string): boolean {\n return /\\bwhere\\b/i.test(maskIgnoredSqlText(sql));\n}\n\nfunction trailingLineCommentIndex(sql: string): number | undefined {\n let index = 0;\n while (index < sql.length) {\n const char = sql[index];\n if (char === \"'\" || char === '\"') {\n index = skipQuotedText(sql, index);\n continue;\n }\n if (char === \"/\" && sql[index + 1] === \"*\") {\n index = skipBlockComment(sql, index);\n continue;\n }\n if (char === \"-\" && sql[index + 1] === \"-\") {\n const lineEnd = sql.indexOf(\"\\n\", index + 2);\n if (lineEnd === -1 || sql.slice(lineEnd + 1).trim().length === 0) {\n return index;\n }\n index = lineEnd + 1;\n continue;\n }\n index += 1;\n }\n return undefined;\n}\n\nfunction appendLimit(sql: string, limit: number): string {\n const trimmed = sql.replace(/[\\s;]+$/, \"\");\n const commentIndex = trailingLineCommentIndex(trimmed);\n if (commentIndex === undefined) {\n return `${trimmed} LIMIT ${String(limit)}`;\n }\n const beforeComment = trimmed.slice(0, commentIndex).replace(/[\\s;]+$/, \"\");\n const comment = trimmed.slice(commentIndex);\n return `${beforeComment} LIMIT ${String(limit)} ${comment}`;\n}\n\n/** Inspect a statement's kind and whether it is destructive. */\nexport function inspectStatement(sql: string): StatementInspection {\n const kind = classifyStatement(sql);\n const keyword = firstKeyword(sql);\n if (kind === \"ddl\") {\n return { kind, destructive: DESTRUCTIVE_DDL_KEYWORDS.has(keyword) };\n }\n if (kind === \"dml\") {\n return {\n kind,\n destructive: UNSCOPED_WRITE_KEYWORDS.has(keyword) && !hasWhereClause(sql),\n };\n }\n return { kind, destructive: false };\n}\n\n/** Decide whether a statement may run under the given safety configuration. */\nexport function evaluateGuard(sql: string, config: GuardConfig): GuardDecision {\n const inspection = inspectStatement(sql);\n\n if (config.readOnly && inspection.kind !== \"select\") {\n return {\n allowed: false,\n destructive: inspection.destructive,\n violation: \"read-only\",\n reason:\n inspection.kind === \"unknown\"\n ? \"read-only mode only permits SELECT/WITH statements\"\n : `read-only mode blocks ${inspection.kind.toUpperCase()} statements`,\n };\n }\n\n if (inspection.destructive && !config.allowDestructive) {\n return {\n allowed: false,\n destructive: true,\n violation: \"destructive\",\n reason:\n \"destructive statement blocked (DROP/TRUNCATE/ALTER or unscoped UPDATE/DELETE); \" +\n \"allow it explicitly to proceed\",\n };\n }\n\n return {\n allowed: true,\n destructive: inspection.destructive,\n violation: undefined,\n reason: undefined,\n };\n}\n\n/** Append a `LIMIT` clause to a bare SELECT that has no row cap of its own. */\nexport function applyAutoLimit(sql: string, limit: number | false): AutoLimitResult {\n if (limit === false || classifyStatement(sql) !== \"select\") {\n return { sql, applied: false };\n }\n\n const stripped = maskIgnoredSqlText(sql);\n if (/\\blimit\\b/i.test(stripped) || /\\btop\\s+\\d/i.test(stripped)) {\n return { sql, applied: false };\n }\n\n return {\n sql: appendLimit(sql, limit + 1),\n applied: true,\n requestedLimit: limit,\n };\n}\n","import type { DriverConnection, HanaDriver } from \"./driver/types.js\";\nimport { CfHanaError, DestructiveStatementError, ReadOnlyViolationError } from \"./errors.js\";\nimport { applyAutoLimit, evaluateGuard } from \"./safety.js\";\nimport { assertParamArity, classifyStatement } from \"./statements.js\";\nimport type { QueryOptions, QueryResult, QueryRow, SqlParam } from \"./types.js\";\n\nexport interface ConnectionConfig {\n readonly host: string;\n readonly port: number;\n readonly user: string;\n readonly password: string;\n readonly schema: string;\n readonly certificate: string;\n readonly connectTimeoutMs: number;\n readonly queryTimeoutMs: number;\n readonly readOnly: boolean;\n readonly allowDestructive: boolean;\n readonly autoLimit: number | false;\n}\n\ninterface RunOptions {\n readonly bypassSafety?: boolean;\n}\n\nfunction assertValidAutoLimit(value: number | false): void {\n if (\n value !== false &&\n (!Number.isSafeInteger(value) || value <= 0 || value >= Number.MAX_SAFE_INTEGER)\n ) {\n throw new CfHanaError(\"CONFIG\", \"autoLimit must be a positive safe integer\");\n }\n}\n\nfunction boundedRows(\n rows: readonly Record<string, SqlParam>[],\n requestedLimit: number | undefined,\n): {\n readonly rows: readonly Record<string, SqlParam>[];\n readonly truncated: boolean;\n} {\n if (requestedLimit === undefined) {\n return { rows, truncated: false };\n }\n return {\n rows: rows.slice(0, requestedLimit),\n truncated: rows.length > requestedLimit,\n };\n}\n\nasync function withTimeout<T>(\n work: Promise<T>,\n timeoutMs: number,\n onTimeout: () => void,\n): Promise<T> {\n let timer: NodeJS.Timeout | undefined;\n const timeout = new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => {\n onTimeout();\n reject(\n new CfHanaError(\"TIMEOUT\", `Statement timed out after ${String(timeoutMs)}ms`),\n );\n }, timeoutMs);\n timer.unref();\n });\n\n try {\n return await Promise.race([work, timeout]);\n } finally {\n if (timer !== undefined) {\n clearTimeout(timer);\n }\n }\n}\n\n/** A single HANA connection: applies the safety guard, auto-limit, and timeouts. */\nexport class Connection {\n private constructor(\n private readonly driverConnection: DriverConnection,\n private readonly config: ConnectionConfig,\n ) {}\n\n static async open(driver: HanaDriver, config: ConnectionConfig): Promise<Connection> {\n const driverConnection = await driver.connect({\n host: config.host,\n port: config.port,\n user: config.user,\n password: config.password,\n schema: config.schema,\n certificate: config.certificate,\n connectTimeoutMs: config.connectTimeoutMs,\n });\n return new Connection(driverConnection, config);\n }\n\n async query<TRow = QueryRow>(\n sql: string,\n params?: readonly SqlParam[],\n options?: QueryOptions,\n ): Promise<QueryResult<TRow>> {\n const result = await this.run(sql, params ?? [], options ?? {});\n // The driver returns untyped rows; the caller asserts the row shape via TRow.\n return result as unknown as QueryResult<TRow>;\n }\n\n async execute(\n sql: string,\n params?: readonly SqlParam[],\n options?: QueryOptions,\n ): Promise<QueryResult> {\n return await this.run(sql, params ?? [], options ?? {});\n }\n\n assertAllowed(sql: string, options?: QueryOptions): void {\n this.evaluateSafety(sql, options ?? {});\n }\n\n async executeInternal(\n sql: string,\n params?: readonly SqlParam[],\n options?: QueryOptions,\n ): Promise<QueryResult> {\n return await this.run(sql, params ?? [], options ?? {}, { bypassSafety: true });\n }\n\n async setAutoCommit(enabled: boolean): Promise<void> {\n await this.driverConnection.setAutoCommit(enabled);\n }\n\n async commit(): Promise<void> {\n await this.driverConnection.commit();\n }\n\n async rollback(): Promise<void> {\n await this.driverConnection.rollback();\n }\n\n async close(): Promise<void> {\n await this.driverConnection.close();\n }\n\n get isClosed(): boolean {\n return this.driverConnection.isClosed();\n }\n\n private async run(\n sql: string,\n params: readonly SqlParam[],\n options: QueryOptions,\n runOptions: RunOptions = {},\n ): Promise<QueryResult> {\n assertParamArity(sql, params);\n if (runOptions.bypassSafety !== true) {\n this.evaluateSafety(sql, options);\n }\n\n const kind = classifyStatement(sql);\n const autoLimit = options.autoLimit ?? this.config.autoLimit;\n assertValidAutoLimit(autoLimit);\n const limited =\n kind === \"select\" ? applyAutoLimit(sql, autoLimit) : { sql, applied: false };\n const timeoutMs = options.timeoutMs ?? this.config.queryTimeoutMs;\n\n const started = Date.now();\n const execResult = await withTimeout(\n this.driverConnection.exec(limited.sql, params),\n timeoutMs,\n () => {\n void this.closeQuietly();\n },\n );\n const elapsedMs = Date.now() - started;\n const selected = boundedRows(execResult.rows, limited.requestedLimit);\n\n return {\n rows: selected.rows,\n columns: execResult.columns,\n rowCount: kind === \"select\" ? selected.rows.length : execResult.affectedRows,\n statement: kind,\n truncated: selected.truncated,\n elapsedMs,\n };\n }\n\n private async closeQuietly(): Promise<void> {\n try {\n await this.driverConnection.close();\n } catch {\n // Best-effort close after a timeout; the connection is already unusable.\n }\n }\n\n private evaluateSafety(sql: string, options: QueryOptions): void {\n const decision = evaluateGuard(sql, {\n readOnly: this.config.readOnly,\n allowDestructive: options.allowDestructive ?? this.config.allowDestructive,\n });\n if (decision.allowed) {\n return;\n }\n if (decision.violation === \"read-only\") {\n throw new ReadOnlyViolationError(\n decision.reason ?? \"read-only mode blocks this statement\",\n );\n }\n throw new DestructiveStatementError(\n decision.reason ?? \"destructive statement blocked\",\n );\n }\n}\n","import { DEFAULT_POOL_IDLE_MS, DEFAULT_POOL_MAX } from \"./config.js\";\nimport { Connection } from \"./connection.js\";\nimport type { ConnectionConfig } from \"./connection.js\";\nimport type { HanaDriver } from \"./driver/types.js\";\nimport { CfHanaError } from \"./errors.js\";\nimport type { PoolOptions } from \"./types.js\";\n\ninterface IdleEntry {\n readonly connection: Connection;\n readonly since: number;\n}\n\ninterface Waiter {\n readonly resolve: (connection: Connection) => void;\n readonly reject: (error: unknown) => void;\n}\n\nasync function closeQuietly(connection: Connection): Promise<void> {\n try {\n await connection.close();\n } catch {\n // Best-effort close.\n }\n}\n\n/**\n * A process-local, in-memory pool of HANA connections. It holds no shared disk\n * state, so multiple pools (in this or other processes) are fully independent.\n */\nexport class ConnectionPool {\n private readonly idle: IdleEntry[] = [];\n private readonly busy = new Set<Connection>();\n private readonly waiters: Waiter[] = [];\n private readonly maxSize: number;\n private readonly idleTimeoutMs: number;\n private created = 0;\n private draining = false;\n\n constructor(\n private readonly driver: HanaDriver,\n private readonly config: ConnectionConfig,\n options?: PoolOptions,\n ) {\n this.maxSize = Math.max(1, options?.max ?? DEFAULT_POOL_MAX);\n this.idleTimeoutMs = options?.idleTimeoutMs ?? DEFAULT_POOL_IDLE_MS;\n }\n\n /** Total connections currently owned by the pool (idle + busy). */\n get size(): number {\n return this.created;\n }\n\n /** Connections currently idle and immediately reusable. */\n get available(): number {\n return this.idle.length;\n }\n\n /** Borrow a connection, opening or queueing as needed. */\n async acquire(): Promise<Connection> {\n if (this.draining) {\n throw new CfHanaError(\"POOL_CLOSED\", \"Connection pool is draining\");\n }\n\n const reused = this.takeIdleConnection();\n if (reused !== undefined) {\n this.busy.add(reused);\n return reused;\n }\n\n if (this.created < this.maxSize) {\n return await this.openBusyConnection();\n }\n\n return await new Promise<Connection>((resolve, reject) => {\n this.waiters.push({ resolve, reject });\n });\n }\n\n /** Return a borrowed connection to the pool. */\n release(connection: Connection): void {\n if (!this.busy.delete(connection)) {\n return;\n }\n\n if (connection.isClosed) {\n this.created -= 1;\n this.servePendingWaiters();\n return;\n }\n\n const waiter = this.waiters.shift();\n if (waiter !== undefined) {\n this.busy.add(connection);\n waiter.resolve(connection);\n return;\n }\n\n if (this.draining) {\n this.created -= 1;\n void closeQuietly(connection);\n return;\n }\n\n this.idle.push({ connection, since: Date.now() });\n }\n\n /** Run `work` with a borrowed connection, releasing it afterwards. */\n async withConnection<T>(work: (connection: Connection) => Promise<T>): Promise<T> {\n const connection = await this.acquire();\n try {\n return await work(connection);\n } finally {\n this.release(connection);\n }\n }\n\n /** Reject queued waiters and close idle connections. */\n async drain(): Promise<void> {\n this.draining = true;\n\n while (this.waiters.length > 0) {\n const waiter = this.waiters.shift();\n if (waiter !== undefined) {\n waiter.reject(new CfHanaError(\"POOL_CLOSED\", \"Connection pool was drained\"));\n }\n }\n\n const idleConnections = this.idle.splice(0).map((entry) => entry.connection);\n this.created -= idleConnections.length;\n await Promise.all(idleConnections.map((connection) => closeQuietly(connection)));\n }\n\n private takeIdleConnection(): Connection | undefined {\n for (;;) {\n const entry = this.idle.pop();\n if (entry === undefined) {\n return undefined;\n }\n if (entry.connection.isClosed) {\n this.created -= 1;\n continue;\n }\n if (this.idleTimeoutMs > 0 && Date.now() - entry.since > this.idleTimeoutMs) {\n this.created -= 1;\n void closeQuietly(entry.connection);\n continue;\n }\n return entry.connection;\n }\n }\n\n private async openBusyConnection(): Promise<Connection> {\n this.created += 1;\n let connection: Connection;\n try {\n connection = await Connection.open(this.driver, this.config);\n } catch (error) {\n this.created -= 1;\n throw error;\n }\n this.busy.add(connection);\n return connection;\n }\n\n private servePendingWaiters(): void {\n while (this.waiters.length > 0 && this.created < this.maxSize) {\n const waiter = this.waiters.shift();\n if (waiter === undefined) {\n return;\n }\n this.created += 1;\n void Connection.open(this.driver, this.config).then(\n (connection) => {\n this.busy.add(connection);\n waiter.resolve(connection);\n },\n (error: unknown) => {\n this.created -= 1;\n waiter.reject(error);\n this.servePendingWaiters();\n },\n );\n }\n }\n}\n","import type { Connection } from \"./connection.js\";\nimport { CfHanaError } from \"./errors.js\";\nimport type { QueryOptions, QueryResult, QueryRow, SqlParam } from \"./types.js\";\n\n/** A HANA transaction bound to one connection with autocommit disabled. */\nexport class Transaction {\n private finished = false;\n\n constructor(private readonly connection: Connection) {}\n\n /** Whether the transaction has already been committed or rolled back. */\n get isFinished(): boolean {\n return this.finished;\n }\n\n async query<TRow = QueryRow>(\n sql: string,\n params?: readonly SqlParam[],\n options?: QueryOptions,\n ): Promise<QueryResult<TRow>> {\n this.assertActive();\n return await this.connection.query<TRow>(sql, params, options);\n }\n\n async execute(\n sql: string,\n params?: readonly SqlParam[],\n options?: QueryOptions,\n ): Promise<QueryResult> {\n this.assertActive();\n return await this.connection.execute(sql, params, options);\n }\n\n async commit(): Promise<void> {\n this.assertActive();\n this.finished = true;\n await this.connection.commit();\n }\n\n async rollback(): Promise<void> {\n this.assertActive();\n this.finished = true;\n await this.connection.rollback();\n }\n\n private assertActive(): void {\n if (this.finished) {\n throw new CfHanaError(\n \"QUERY\",\n \"This transaction has already been committed or rolled back\",\n );\n }\n }\n}\n","import { buildWriteBackupPlan, writeSqlBackup } from \"./backup.js\";\nimport type { SqlBackupRecord } from \"./backup.js\";\nimport { buildCount, buildDelete, buildInsert, buildSelect, buildUpdate } from \"./builder.js\";\nimport { listColumns, listSchemas, listTables } from \"./catalog.js\";\nimport {\n DEFAULT_AUTO_LIMIT,\n CLI_VERSION,\n DEFAULT_CONNECT_TIMEOUT_MS,\n DEFAULT_QUERY_TIMEOUT_MS,\n} from \"./config.js\";\nimport type { ConnectionConfig } from \"./connection.js\";\nimport { resolveAppBindings, selectBinding, toConnectionTarget } from \"./credentials.js\";\nimport { createDriver } from \"./driver/index.js\";\nimport { appendSqlHistory } from \"./history.js\";\nimport type { SqlHistoryOperation } from \"./history.js\";\nimport { ConnectionPool } from \"./pool.js\";\nimport { Transaction } from \"./transaction.js\";\nimport type {\n ColumnInfo,\n ConnectOptions,\n DbUserRole,\n HanaClientInfo,\n PoolOptions,\n QueryOptions,\n QueryResult,\n QueryRow,\n SelectSpec,\n SqlParam,\n TableInfo,\n} from \"./types.js\";\n\nlet explainStatementCounter = 0;\n\nfunction nextExplainStatementName(): string {\n explainStatementCounter = (explainStatementCounter + 1) % Number.MAX_SAFE_INTEGER;\n return `cf_hana_${String(process.pid)}_${String(Date.now())}_${String(explainStatementCounter)}`;\n}\n\n/**\n * The high-level entry point: a pooled, reusable client for the HANA database\n * bound to a single Cloud Foundry app.\n */\nexport class HanaClient {\n constructor(\n private readonly pool: ConnectionPool,\n readonly info: HanaClientInfo,\n ) {}\n\n /** Open a client for a `region/org/space/app` selector (or a bare app name). */\n static async connect(selector: string, options: ConnectOptions = {}): Promise<HanaClient> {\n const resolved = await resolveAppBindings(selector, options);\n const role: DbUserRole = options.role ?? \"runtime\";\n const binding = selectBinding(resolved.bindings, options);\n const target = toConnectionTarget(binding, role);\n const driver = createDriver();\n\n const config: ConnectionConfig = {\n host: target.host,\n port: target.port,\n user: target.user,\n password: target.password,\n schema: target.schema,\n certificate: target.certificate,\n connectTimeoutMs: options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS,\n queryTimeoutMs: options.queryTimeoutMs ?? DEFAULT_QUERY_TIMEOUT_MS,\n readOnly: options.readOnly ?? false,\n allowDestructive: options.allowDestructive ?? false,\n autoLimit: options.autoLimit ?? DEFAULT_AUTO_LIMIT,\n };\n const poolOptions: PoolOptions =\n options.pool === false ? { max: 1 } : (options.pool ?? {});\n\n const info: HanaClientInfo = {\n selector: resolved.selector,\n appName: resolved.appName,\n host: target.host,\n schema: target.schema,\n role,\n driver: driver.name,\n credentialSource: resolved.source,\n };\n return new HanaClient(new ConnectionPool(driver, config, poolOptions), info);\n }\n\n /** Run a SELECT (or any read) statement and return typed rows. */\n async query<TRow = QueryRow>(\n sql: string,\n params?: readonly SqlParam[],\n options?: QueryOptions,\n ): Promise<QueryResult<TRow>> {\n const resolvedParams = params ?? [];\n const result = await this.runQuery<TRow>(sql, resolvedParams, options);\n await this.recordSqlHistory(\"query\", sql, resolvedParams, result);\n return result;\n }\n\n /** Run a DML/DDL statement and return its affected-row count. */\n async execute(\n sql: string,\n params?: readonly SqlParam[],\n options?: QueryOptions,\n ): Promise<QueryResult> {\n const resolvedParams = params ?? [];\n const result = await this.runExecute(sql, resolvedParams, options);\n await this.recordSqlHistory(\"execute\", sql, resolvedParams, result);\n return result;\n }\n\n /** Back up rows matched by an UPDATE or DELETE before the caller runs it. */\n async backupWriteStatement(\n sql: string,\n params?: readonly SqlParam[],\n options?: QueryOptions,\n ): Promise<SqlBackupRecord | undefined> {\n const resolvedParams = params ?? [];\n const plan = buildWriteBackupPlan(sql, resolvedParams);\n if (plan === undefined) {\n return undefined;\n }\n\n return await this.pool.withConnection(async (connection) => {\n connection.assertAllowed(plan.statementSql, options);\n const backupQueryOptions: QueryOptions = {\n autoLimit: false,\n ...(options?.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),\n };\n const result = await connection.query(plan.selectSql, plan.selectParams, backupQueryOptions);\n return await writeSqlBackup({\n operation: plan.operation,\n statementSql: plan.statementSql,\n result,\n });\n });\n }\n\n /** Run a typed `SELECT` built from a spec. */\n async selectFrom<TRow = QueryRow>(spec: SelectSpec): Promise<QueryResult<TRow>> {\n const built = buildSelect(spec);\n return await this.runQuery<TRow>(built.sql, built.params);\n }\n\n /** Count rows in a table, optionally filtered. */\n async count(spec: Pick<SelectSpec, \"schema\" | \"table\" | \"where\">): Promise<number> {\n const built = buildCount(spec);\n const result = await this.runQuery<{ COUNT: number }>(built.sql, built.params);\n return result.rows[0]?.COUNT ?? 0;\n }\n\n /** Insert a single row. */\n async insertInto(\n schema: string,\n table: string,\n values: Readonly<Record<string, SqlParam>>,\n ): Promise<QueryResult> {\n const built = buildInsert(schema, table, values);\n return await this.runExecute(built.sql, built.params);\n }\n\n /** Update rows matching a non-empty `where` filter. */\n async update(\n schema: string,\n table: string,\n values: Readonly<Record<string, SqlParam>>,\n where: Readonly<Record<string, SqlParam>>,\n ): Promise<QueryResult> {\n const built = buildUpdate(schema, table, values, where);\n return await this.runExecute(built.sql, built.params);\n }\n\n /** Delete rows matching a non-empty `where` filter. */\n async deleteFrom(\n schema: string,\n table: string,\n where: Readonly<Record<string, SqlParam>>,\n ): Promise<QueryResult> {\n const built = buildDelete(schema, table, where);\n return await this.runExecute(built.sql, built.params);\n }\n\n /** Run `work` inside a transaction, auto-committing on success. */\n async transaction<T>(work: (tx: Transaction) => Promise<T>): Promise<T> {\n return await this.pool.withConnection(async (connection) => {\n await connection.setAutoCommit(false);\n const tx = new Transaction(connection);\n try {\n const result = await work(tx);\n if (!tx.isFinished) {\n await tx.commit();\n }\n return result;\n } catch (error) {\n if (!tx.isFinished) {\n try {\n await tx.rollback();\n } catch {\n // Preserve the original error; ignore a rollback failure.\n }\n }\n throw error;\n } finally {\n await connection.setAutoCommit(true);\n }\n });\n }\n\n /** List every schema visible to the connected user. */\n async listSchemas(): Promise<readonly string[]> {\n return await this.pool.withConnection((connection) => listSchemas(connection));\n }\n\n /** List the tables in a schema. */\n async listTables(schema: string): Promise<readonly TableInfo[]> {\n return await this.pool.withConnection((connection) => listTables(connection, schema));\n }\n\n /** List the columns of a table. */\n async listColumns(schema: string, table: string): Promise<readonly ColumnInfo[]> {\n return await this.pool.withConnection((connection) =>\n listColumns(connection, schema, table),\n );\n }\n\n /** Return the HANA execution plan for a statement. */\n async explain(sql: string, params?: readonly SqlParam[]): Promise<QueryResult> {\n return await this.pool.withConnection(async (connection) => {\n connection.assertAllowed(sql);\n const statementName = nextExplainStatementName();\n await connection.executeInternal(\n `EXPLAIN PLAN SET STATEMENT_NAME = '${statementName}' FOR ${sql}`,\n params,\n );\n const cleanup = async (): Promise<void> => {\n await connection.executeInternal(\n \"DELETE FROM EXPLAIN_PLAN_TABLE WHERE STATEMENT_NAME = ?\",\n [statementName],\n );\n };\n let queryCompleted = false;\n try {\n const result = await connection.query(\n \"SELECT OPERATOR_NAME, TABLE_NAME, TABLE_TYPE, EXECUTION_ENGINE \" +\n \"FROM EXPLAIN_PLAN_TABLE WHERE STATEMENT_NAME = ? ORDER BY OPERATOR_ID\",\n [statementName],\n );\n queryCompleted = true;\n await cleanup();\n return result;\n } catch (error) {\n if (!queryCompleted) {\n try {\n await cleanup();\n } catch {\n // Preserve the original explain-plan read error.\n }\n }\n throw error;\n }\n });\n }\n\n /** Close every pooled connection. The client must not be used afterwards. */\n async close(): Promise<void> {\n await this.pool.drain();\n }\n\n private async runQuery<TRow = QueryRow>(\n sql: string,\n params: readonly SqlParam[],\n options?: QueryOptions,\n ): Promise<QueryResult<TRow>> {\n return await this.pool.withConnection((connection) =>\n connection.query<TRow>(sql, params, options),\n );\n }\n\n private async runExecute(\n sql: string,\n params: readonly SqlParam[],\n options?: QueryOptions,\n ): Promise<QueryResult> {\n return await this.pool.withConnection((connection) =>\n connection.execute(sql, params, options),\n );\n }\n\n private async recordSqlHistory<TRow>(\n operation: SqlHistoryOperation,\n sql: string,\n params: readonly SqlParam[],\n result: QueryResult<TRow>,\n ): Promise<void> {\n try {\n await appendSqlHistory({\n version: CLI_VERSION,\n operation,\n selector: this.info.selector,\n appName: this.info.appName,\n schema: this.info.schema,\n role: this.info.role,\n statement: result.statement,\n sql,\n paramCount: params.length,\n rowCount: result.rowCount,\n truncated: result.truncated,\n elapsedMs: result.elapsedMs,\n });\n } catch {\n // History is diagnostic local state; never fail a successful SQL statement.\n }\n }\n}\n","import { HanaClient } from \"./client.js\";\nimport type {\n ConnectOptions,\n QueryOptions,\n QueryResult,\n QueryRow,\n SqlParam,\n} from \"./types.js\";\n\n/**\n * Open a reusable, pooled client for the HANA database bound to a Cloud Foundry\n * app, addressed by a `region/org/space/app` selector or a bare app name.\n */\nexport async function connect(\n selector: string,\n options?: ConnectOptions,\n): Promise<HanaClient> {\n return await HanaClient.connect(selector, options);\n}\n\n/**\n * Connect, run a single query, and close — the one-shot convenience for scripts.\n */\nexport async function query<TRow = QueryRow>(\n selector: string,\n sql: string,\n params?: readonly SqlParam[],\n options?: ConnectOptions & QueryOptions,\n): Promise<QueryResult<TRow>> {\n const client = await HanaClient.connect(selector, options);\n try {\n return await client.query<TRow>(sql, params, options);\n } finally {\n await client.close();\n }\n}\n\n/** Run `work` with a client that is closed automatically afterwards. */\nexport async function withConnection<T>(\n selector: string,\n work: (client: HanaClient) => Promise<T>,\n options?: ConnectOptions,\n): Promise<T> {\n const client = await HanaClient.connect(selector, options);\n try {\n return await work(client);\n } finally {\n await client.close();\n }\n}\n"],"mappings":"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","names":["appendFile","mkdir","homedir","join","SAPTOOLS_DIR_NAME","CF_HANA_DIR_NAME","defaultSaptoolsRoot","skipQuotedText","skipLineComment","skipBlockComment"]}
|
package/package.json
CHANGED