Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,66 @@ odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => {

---

### `.cancel(callback?)`

Cancels all operations currently running on the connection (queries and procedure calls) by calling `SQLCancel` on their statement handles. The cancelled operations return with SQLSTATE HY008 ("Operation canceled"), so their promises reject (or their callbacks are called with an error). If no operations are running, `.cancel` is a no-op.

**Note:** `SQLCancel` only _requests_ cancellation — when the operation actually aborts depends on the driver and on the database engine reaching a cancellation checkpoint. Row-producing operations (scans, fetches) usually abort promptly, but some operations never check for cancellation and only fail with HY008 once they finish on their own. Known examples: Impala's `sleep()` function completes its full wait before honoring the cancel, and calls to stored procedures on Db2 for IBM i are not cancelable at all.

#### Parameters:
* **callback?**: The function called when `.cancel` has finished execution. If no callback function is given, `.cancel` will return a native JavaScript `Promise`. Callback signature is:
* error: The error that occured in execution, or `null` if no error

#### Examples:

**Promises**

```javascript
const odbc = require('odbc');

// can only use await keyword in an async function
async function cancelExample() {
const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`);

// cancel the query if it is still running after 10 seconds
const timer = setTimeout(() => {
connection.cancel();
}, 10000);

try {
const result = await connection.query('SELECT * FROM HUGE_TABLE');
console.log(result);
} catch (error) {
// if cancelled, error contains an odbcError with state 'HY008'
} finally {
clearTimeout(timer);
}
}

cancelExample();
```

**Callbacks**

```javascript
const odbc = require('odbc');

odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => {
const timer = setTimeout(() => {
connection.cancel((cancelError) => {
if (cancelError) { return; } // handle
});
}, 10000);

connection.query('SELECT * FROM HUGE_TABLE', (queryError, result) => {
clearTimeout(timer);
// if cancelled, queryError contains an odbcError with state 'HY008'
});
});
```

---

### `.close(callback?)`

Closes an open connection. Any transactions on the connection that have not been ended will be rolledback.
Expand Down Expand Up @@ -1027,6 +1087,74 @@ odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => {

---

### `.cancel(callback?)`

Cancels any operation currently running on the Statement (e.g. a long `.execute()`) by calling `SQLCancel` on its handle. The cancelled operation returns with SQLSTATE HY008 ("Operation canceled").

**Note:** the cancellation-checkpoint caveat described in [connection.cancel()](#cancelcallback) applies here as well.

#### Parameters:
* **callback?**: The function called when `.cancel` has finished execution. If no callback function is given, `.cancel` will return a native JavaScript `Promise`. Callback signature is:
* error: The error that occured in execution, or `null` if no error

#### Examples:

**Promises**

```javascript
const odbc = require('odbc');

// can only use await keyword in an async function
async function cancelExample() {
const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`);
const statement = await connection.createStatement();
await statement.prepare('SELECT * FROM HUGE_TABLE WHERE FIELD_1 = ?');
await statement.bind([1]);

// cancel the execution if it is still running after 10 seconds
const timer = setTimeout(() => {
statement.cancel();
}, 10000);

try {
const result = await statement.execute();
console.log(result);
} catch (error) {
// if cancelled, error contains an odbcError with state 'HY008'
} finally {
clearTimeout(timer);
}
}

cancelExample();
```

**Callbacks**

```javascript
const odbc = require('odbc');

odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => {
connection.createStatement((error1, statement) => {
if (error1) { return; } // handle
statement.prepare('SELECT * FROM HUGE_TABLE', (error2) => {
if (error2) { return; } // handle
const timer = setTimeout(() => {
statement.cancel((cancelError) => {
if (cancelError) { return; } // handle
});
}, 10000);
statement.execute((error3, result) => {
clearTimeout(timer);
// if cancelled, error3 contains an odbcError with state 'HY008'
});
});
});
});
```

---

### `.close(callback?)`

Closes the Statement, freeing the statement handle. Running functions on the statement after closing will result in an error.
Expand Down
38 changes: 38 additions & 0 deletions lib/Connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,44 @@ class Connection {
});
}

/**
* Cancels all operations currently running on this connection (queries and
* procedure calls) by calling SQLCancel on their statement handles. The
* cancelled operations return with SQLSTATE HY008 ("Operation canceled").
* Asynchronous, can be used either with a callback function or a Promise.
* @param {function} [callback] - Callback function. If not passed, a Promise will be returned.
*/
cancel(callback = undefined) {
// type-checking
if (typeof callback !== 'function' && typeof callback !== 'undefined') {
throw new TypeError('[node-odbc]: Incorrect function signature for call to connection.cancel({function}[optional]).');
}

// promise...
if (callback === undefined) {
if (!this.odbcConnection)
{
throw new Error(Connection.CONNECTION_CLOSED_ERROR);
}
return new Promise((resolve, reject) => {
this.odbcConnection.cancel((error) => {
if (error) {
reject(error);
} else {
resolve();
}
});
});
}

// ...or callback
if (!this.odbcConnection)
{
return callback(new Error(Connection.CONNECTION_CLOSED_ERROR));
}
return this.odbcConnection.cancel((error) => callback(error));
}

// TODO: Documentation
primaryKeys(catalog, schema, table, callback = undefined) {
// promise...
Expand Down
35 changes: 34 additions & 1 deletion lib/Statement.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,43 @@ class Statement {
}
}

/**
* Cancels any operation currently running on this statement (e.g. a long
* execute()) by calling SQLCancel on its handle. The cancelled operation
* returns with SQLSTATE HY008 ("Operation canceled").
* @param {function} [callback] - The callback function that returns the result. If omitted, uses a Promise.
*/
cancel(callback = undefined) {
if (typeof callback !== 'function' && typeof callback !== 'undefined') {
throw new TypeError('[node-odbc]: Incorrect function signature for call to statement.cancel({function}[optional]).');
}

if (typeof callback === 'undefined') {
if (!this.odbcStatement) {
throw new Error(Statement.STATEMENT_CLOSED_ERROR);
}
return new Promise((resolve, reject) => {
this.odbcStatement.cancel((error) => {
if (error) {
reject(error);
} else {
resolve();
}
});
});
}

// ...or callback
if (!this.odbcStatement) {
return callback(new Error(Statement.STATEMENT_CLOSED_ERROR));
}
return this.odbcStatement.cancel((error) => callback(error));
}

/**
* Closes the statement, deleting the prepared statement and freeing the handle, making further
* calls on the object invalid.
* @param {function} [callback] - The callback function that returns the result. If omitted, uses a Promise.
* @param {function} [callback] - The callback function that returns the result. If omitted, uses a Promise.
*/
close(callback = undefined) {
if (typeof callback !== 'function' && typeof callback !== 'undefined') {
Expand Down
8 changes: 8 additions & 0 deletions lib/odbc.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ declare namespace odbc {

execute<T>(callback: (error: NodeOdbcError, result: Result<T>) => undefined): undefined;

cancel(callback: (error: NodeOdbcError) => undefined): undefined;

close(callback: (error: NodeOdbcError) => undefined): undefined;

////////////////////////////////////////////////////////////////////////////
Expand All @@ -51,6 +53,8 @@ declare namespace odbc {

execute<T>(): Promise<Result<T>>;

cancel(): Promise<void>;

close(): Promise<void>;
}

Expand Down Expand Up @@ -112,6 +116,8 @@ declare namespace odbc {

rollback(callback: (error: NodeOdbcError) => undefined): undefined;

cancel(callback: (error: NodeOdbcError) => undefined): undefined;

close(callback: (error: NodeOdbcError) => undefined): undefined;

////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -142,6 +148,8 @@ declare namespace odbc {

rollback(): Promise<void>;

cancel(): Promise<void>;

close(): Promise<void>;

connected(): boolean;
Expand Down
2 changes: 1 addition & 1 deletion src/odbc.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ typedef struct StatementData {

SQLHENV henv;
SQLHDBC hdbc;
SQLHSTMT hstmt;
SQLHSTMT hstmt = SQL_NULL_HANDLE;

QueryOptions query_options;

Expand Down
Loading