@db2lake/driver-oracle
Version:
Oracle source driver for db2lake
138 lines • 5.7 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OracleSourceDriver = void 0;
/**
* Oracle source driver implementation for paginated data fetching.
*
* Exports the `OracleSourceDriver` class which streams rows from an Oracle database
* using an async generator (`fetch()`). The driver expects a configuration that
* includes an SQL query, optional parameters, and optional cursor settings so it
* can perform cursor-based pagination by replacing one parameter between pages.
*
* Important behavior notes:
* - Connections are obtained from a `Pool` created with `oracledb.createPool(poolAttributes)`.
* - `fetch()` yields arrays of rows (objects) until the query returns an empty result set
* or the configured `cursorField` cannot be read from the last row.
* - If `cursorField` is provided, the driver will update `params[cursorParamsIndex]`
* with the last row's `cursorField` value and continue paging.
* - Call `close()` to release the connection and close the pool when finished.
*
* Usage example:
* ```typescript
* const driver = new OracleSourceDriver(config);
* for await (const batch of driver.fetch()) {
* // batch is an array of row objects
* }
* await driver.close();
* ```
*/
const oracledb_1 = __importDefault(require("oracledb"));
/**
* OracleSourceDriver streams data from an Oracle database using cursor-based pagination.
*
* Contract:
* - Inputs: `OracleConfig` (see `src/type.ts`) describing `query`, `params`, and pool attributes.
* - Outputs: `fetch()` yields `any[]` batches of rows until no more data is available.
* - Errors: network/driver errors from `oracledb` are propagated to the caller.
*/
class OracleSourceDriver {
constructor(config) {
this.config = config;
this.pool = null;
this.connection = null;
}
/**
* Establish a pool and a connection from the pool.
*
* - If the pool already exists, it will be reused.
* - Throws an Error if `poolAttributes` are not provided in the config.
* - Propagates any errors thrown by `oracledb.createPool` or `pool.getConnection`.
*/
async connect() {
if (!this.config.poolAttributes)
throw new Error('Missing pool attributes for Oracle');
if (!this.pool) {
this.pool = await oracledb_1.default.createPool(this.config.poolAttributes);
}
if (!this.connection) {
this.connection = await this.pool.getConnection();
}
}
/**
* Async generator that yields batches of rows from the configured query.
*
* Behavior and edge cases:
* - If no connection exists, `connect()` is called automatically.
* - `params` are cloned from the config so the original array is not mutated.
* - If `cursorField` and `cursorParamsIndex` are configured, the driver will
* replace `params[cursorParamsIndex]` with the last row's `cursorField` value
* for the subsequent query. If the last row does not contain `cursorField`,
* pagination stops.
* - If `cursorField` is not provided the driver yields the first batch and stops.
* - Any error from `connection.execute` is thrown to the caller.
*
* Yields:
* - `any[]` - array of row objects (outFormat: OBJECT)
*/
async *fetch() {
if (!this.connection) {
await this.connect();
}
let cursorValue = undefined;
let hasMore = true;
let params = Array.isArray(this.config.params) ? [...this.config.params] : [];
while (hasMore) {
// If cursorField is set, update params at cursorParamsIndex
if (this.config.cursorField && typeof this.config.cursorParamsIndex === 'number' && cursorValue !== undefined) {
params[this.config.cursorParamsIndex] = cursorValue;
}
// Execute using object output format for ease of use
const result = await this.connection.execute(this.config.query, params, { outFormat: oracledb_1.default.OUT_FORMAT_OBJECT });
const rows = result.rows;
if (!rows || rows.length === 0) {
hasMore = false;
break;
}
yield rows;
// Update cursorValue for next page
if (this.config.cursorField) {
const lastRow = rows[rows.length - 1];
if (lastRow && lastRow[this.config.cursorField] !== undefined) {
cursorValue = lastRow[this.config.cursorField];
}
else {
// If the expected cursor field is missing, stop paging to avoid an infinite loop
hasMore = false;
}
}
else {
// No cursor configured - return only the first batch
hasMore = false;
}
}
}
/**
* Close and cleanup resources:
* - Closes the active connection if present.
* - Closes the pool.
*
* Errors thrown by the underlying driver are propagated, but callers should
* typically swallow or log close errors since they are often invoked during
* shutdown/cleanup.
*/
async close() {
if (this.connection) {
await this.connection.close();
this.connection = null;
}
if (this.pool) {
await this.pool.close();
this.pool = null;
}
}
}
exports.OracleSourceDriver = OracleSourceDriver;
//# sourceMappingURL=index.js.map