Skip to content

Commit 08fadd2

Browse files
committed
feat: add support for foreignKeys() API to get the list of foreign key columns
1 parent 3cd231a commit 08fadd2

8 files changed

Lines changed: 678 additions & 150 deletions

APIDocumentation.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@
5151
* [closeSync([closeOption])](#-19-odbcstatement-closesynccloseoption)
5252
* [.primaryKeys(catalog, schema, table [, callback])](#-20-odbcstatement-primarykeyscatalog-schema-table--callback)
5353
* [.primaryKeysSync(catalog, schema, table)](#-21-odbcstatement-primarykeyssyncatalog-schema-table)
54+
* [.foreignKeys(pkCatalog, pkSchema, pkTable, fkCatalog, fkSchema, fkTable [, callback])](#-22-odbcstatement-foreignkeyspkcatalog-pkschema-pktable-fkcatalog-fkschema-fktable--callback)
55+
* [.foreignKeysSync(pkCatalog, pkSchema, pkTable, fkCatalog, fkSchema, fkTable)](#-23-odbcstatement-foreignkeysyncpkcatalog-pkschema-pktable-fkcatalog-fkschema-fktable)
5456

5557
**ODBCResult APIs**
5658
* [.fetch([option] [, callback])](#-20-odbcresult-fetchoption--callback)
@@ -855,6 +857,107 @@ ibmdb.open(cn, function(err, db) {
855857
});
856858
```
857859

860+
### <a name="foreignKeysApi"></a> 22) (ODBCStatement) .foreignKeys(pkCatalog, pkSchema, pkTable, fkCatalog, fkSchema, fkTable [, callback])
861+
862+
Returns foreign key relationships between two tables by calling `SQLForeignKeys()` via the ODBC CLI.
863+
The result set describes which columns in one table reference primary key columns in another.
864+
865+
* **pkCatalog** - String or `null`. Catalog qualifier of the primary-key table.
866+
* **pkSchema** - String or `null`. Schema of the primary-key table.
867+
* **pkTable** - String or `null`. Name of the primary-key table (exact match).
868+
* **fkCatalog** - String or `null`. Catalog qualifier of the foreign-key table.
869+
* **fkSchema** - String or `null`. Schema of the foreign-key table.
870+
* **fkTable** - String or `null`. Name of the foreign-key table (exact match).
871+
* **callback** - _OPTIONAL_ - `callback (err, rows)`. If omitted a Promise is returned.
872+
873+
At least one of `pkTable` or `fkTable` must be non-null (ODBC requirement).
874+
875+
Behavior when only one table is specified:
876+
- Only `pkTable` given: returns all foreign keys in other tables that reference it.
877+
- Only `fkTable` given: returns all foreign keys in that table and the primary keys they reference.
878+
- Both given: returns the specific foreign key relationship between the two tables.
879+
880+
Can also be called directly on a `Database` object as a convenience:
881+
`db.foreignKeys(pkCatalog, pkSchema, pkTable, fkCatalog, fkSchema, fkTable [, callback])`
882+
883+
Each row in the result set contains the columns returned by `SQLForeignKeys`, including:
884+
`PKTABLE_SCHEM`, `PKTABLE_NAME`, `PKCOLUMN_NAME`, `FKTABLE_SCHEM`, `FKTABLE_NAME`,
885+
`FKCOLUMN_NAME`, `KEY_SEQ`, `UPDATE_RULE`, `DELETE_RULE`, `FK_NAME`, `PK_NAME`.
886+
887+
> **Note:** After all rows are fetched, `foreignKeys()` calls `SQLFreeStmt(SQL_CLOSE)` internally to
888+
> close the result cursor. The statement handle itself remains valid — call `stmt.closeSync()` when
889+
> you are done with the statement to release it.
890+
891+
```javascript
892+
const ibmdb = require("ibm_db");
893+
const cn = "DATABASE=dbname;HOSTNAME=hostname;PORT=port;PROTOCOL=TCPIP;UID=dbuser;PWD=xxx";
894+
895+
// Async callback form via Database convenience method
896+
ibmdb.open(cn, function(err, db) {
897+
// Get the FK in ORDERS that references CUSTOMERS
898+
db.foreignKeys(null, "MYSCHEMA", "CUSTOMERS", null, "MYSCHEMA", "ORDERS", function(err, keys) {
899+
if (err) console.log(err);
900+
else console.log(keys);
901+
// e.g. [ { PKTABLE_SCHEM: 'MYSCHEMA', PKTABLE_NAME: 'CUSTOMERS',
902+
// PKCOLUMN_NAME: 'CUST_ID', FKTABLE_NAME: 'ORDERS',
903+
// FKCOLUMN_NAME: 'CUST_REF', KEY_SEQ: 1, FK_NAME: 'FK_ORDERS_CUST' } ]
904+
db.closeSync();
905+
});
906+
});
907+
908+
// Promise form via Database convenience method
909+
async function run() {
910+
const db = await ibmdb.open(cn);
911+
const keys = await db.foreignKeys(null, "MYSCHEMA", "CUSTOMERS", null, "MYSCHEMA", "ORDERS");
912+
console.log(keys);
913+
db.closeSync();
914+
}
915+
916+
// Direct use on a bare statement handle obtained via db.conn.createStatement()
917+
ibmdb.open(cn, function(err, db) {
918+
db.conn.createStatement(function(err, stmt) {
919+
stmt.foreignKeys(null, "MYSCHEMA", "CUSTOMERS", null, "MYSCHEMA", "ORDERS", function(err, keys) {
920+
console.log(keys);
921+
stmt.closeSync(); // free the statement handle when done
922+
db.closeSync();
923+
});
924+
});
925+
});
926+
```
927+
928+
### <a name="foreignKeysSyncApi"></a> 23) (ODBCStatement) .foreignKeysSync(pkCatalog, pkSchema, pkTable, fkCatalog, fkSchema, fkTable)
929+
930+
Synchronously returns foreign key relationships between two tables.
931+
Parameters and return value are the same as `foreignKeys()`.
932+
933+
Can also be called directly on a `Database` object as a convenience:
934+
`db.foreignKeysSync(pkCatalog, pkSchema, pkTable, fkCatalog, fkSchema, fkTable)`
935+
936+
> **Note:** After all rows are fetched, `foreignKeysSync()` calls `SQLFreeStmt(SQL_CLOSE)` internally
937+
> to close the result cursor. The statement handle itself remains valid — call `stmt.closeSync()` when
938+
> you are done with the statement to release it.
939+
940+
```javascript
941+
const ibmdb = require("ibm_db");
942+
const cn = "DATABASE=dbname;HOSTNAME=hostname;PORT=port;PROTOCOL=TCPIP;UID=dbuser;PWD=xxx";
943+
944+
// Sync form via Database convenience method
945+
ibmdb.open(cn, function(err, db) {
946+
const keys = db.foreignKeysSync(null, "MYSCHEMA", "CUSTOMERS", null, "MYSCHEMA", "ORDERS");
947+
console.log(keys);
948+
db.closeSync();
949+
});
950+
951+
// Direct use on a bare statement handle obtained via db.conn.createStatementSync()
952+
ibmdb.open(cn, function(err, db) {
953+
const stmt = db.conn.createStatementSync();
954+
const keys = stmt.foreignKeysSync(null, "MYSCHEMA", "CUSTOMERS", null, "MYSCHEMA", "ORDERS");
955+
console.log(keys);
956+
stmt.closeSync(); // free the statement handle when done
957+
db.closeSync();
958+
});
959+
```
960+
858961
### <a name="fetchApi"></a> 20) (ODBCResult) .fetch([option] [, callback])
859962

860963
Fetch a row of data from an ODBCResult object asynchronously.

lib/odbc.js

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1445,6 +1445,78 @@ Database.prototype.primaryKeysSync = function(catalog, schema, table)
14451445
}
14461446
};
14471447

1448+
Database.prototype.foreignKeys = function(pkCatalog, pkSchema, pkTable, fkCatalog, fkSchema, fkTable, callback)
1449+
{
1450+
var self = this, deferred;
1451+
var error = {
1452+
error : "[node-ibm_db] Missing Arguments",
1453+
message : "The object you passed must contain six arguments: " +
1454+
"pkCatalog, pkSchema, pkTable, fkCatalog, fkSchema and fkTable. " +
1455+
"This is required for the foreignKeys method to work."
1456+
};
1457+
if (!self.queue) self.queue = [];
1458+
1459+
callback = callback || arguments[arguments.length - 1];
1460+
if (typeof(callback) !== 'function') {
1461+
callback = null;
1462+
deferred = createDeferred();
1463+
if (arguments.length < 6) {
1464+
deferred.reject(error);
1465+
}
1466+
}
1467+
else if (arguments.length < 7) {
1468+
callback(error, [], false);
1469+
}
1470+
1471+
self.queue.push(function (next)
1472+
{
1473+
self.conn.createStatement(function (err, stmt)
1474+
{
1475+
if (err) {
1476+
self.checkConnectionError(err);
1477+
next();
1478+
return deferred ? deferred.reject(err) : callback(err, [], false);
1479+
}
1480+
1481+
stmt._foreignKeys(pkCatalog, pkSchema, pkTable, fkCatalog, fkSchema, fkTable, function (err, rows)
1482+
{
1483+
stmt.closeSync();
1484+
self.checkConnectionError(err);
1485+
if (err) {
1486+
next();
1487+
return deferred ? deferred.reject(err) : callback(err, [], false);
1488+
}
1489+
deferred ? deferred.resolve(rows) : callback(null, rows);
1490+
return next();
1491+
});
1492+
});
1493+
});
1494+
if (deferred) return deferred.promise;
1495+
};
1496+
1497+
Database.prototype.foreignKeysSync = function(pkCatalog, pkSchema, pkTable, fkCatalog, fkSchema, fkTable)
1498+
{
1499+
var self = this;
1500+
1501+
var stmt;
1502+
try {
1503+
stmt = self.conn.createStatementSync();
1504+
} catch (e) {
1505+
self.checkConnectionError(e);
1506+
throw e;
1507+
}
1508+
1509+
try {
1510+
var rows = stmt._foreignKeysSync(pkCatalog, pkSchema, pkTable, fkCatalog, fkSchema, fkTable);
1511+
stmt.closeSync();
1512+
return rows;
1513+
} catch (e) {
1514+
try { stmt.closeSync(); } catch (_) {}
1515+
self.checkConnectionError(e);
1516+
throw e;
1517+
}
1518+
};
1519+
14481520
Database.prototype.describe = async function(obj, callback)
14491521
{
14501522
var self = this, deferred;
@@ -1951,6 +2023,8 @@ if( !odbc.ODBCStatement.prototype._execute ) { //issue #514
19512023
odbc.ODBCStatement.prototype._close = odbc.ODBCStatement.prototype.close;
19522024
odbc.ODBCStatement.prototype._primaryKeys = odbc.ODBCStatement.prototype.primaryKeys;
19532025
odbc.ODBCStatement.prototype._primaryKeysSync = odbc.ODBCStatement.prototype.primaryKeysSync;
2026+
odbc.ODBCStatement.prototype._foreignKeys = odbc.ODBCStatement.prototype.foreignKeys;
2027+
odbc.ODBCStatement.prototype._foreignKeysSync = odbc.ODBCStatement.prototype.foreignKeysSync;
19542028

19552029
//Proxy all of the ODBCResult functions so that they are queued
19562030
odbc.ODBCResult.prototype._fetch = odbc.ODBCResult.prototype.fetch;
@@ -2664,6 +2738,38 @@ odbc.ODBCStatement.prototype.primaryKeysSync = function (catalog, schema, table)
26642738
return self._primaryKeysSync(catalog, schema, table);
26652739
};
26662740

2741+
// Async function to get foreign key columns for two tables.
2742+
// stmt.foreignKeys(pkCatalog, pkSchema, pkTable, fkCatalog, fkSchema, fkTable, callback) or
2743+
// stmt.foreignKeys(pkCatalog, pkSchema, pkTable, fkCatalog, fkSchema, fkTable) => returns Promise
2744+
odbc.ODBCStatement.prototype.foreignKeys = function (pkCatalog, pkSchema, pkTable, fkCatalog, fkSchema, fkTable, cb)
2745+
{
2746+
var self = this, deferred;
2747+
2748+
if (!cb) {
2749+
deferred = createDeferred();
2750+
}
2751+
self.queue = self.queue || new SimpleQueue();
2752+
2753+
self.queue.push(function (next) {
2754+
self._foreignKeys(pkCatalog, pkSchema, pkTable, fkCatalog, fkSchema, fkTable, function (err, rows) {
2755+
if (err) {
2756+
deferred ? deferred.reject(err) : cb(err, []);
2757+
} else {
2758+
deferred ? deferred.resolve(rows) : cb(null, rows);
2759+
}
2760+
return next();
2761+
});
2762+
});
2763+
return deferred ? deferred.promise : null;
2764+
};
2765+
2766+
// Sync function to get foreign key columns for two tables.
2767+
odbc.ODBCStatement.prototype.foreignKeysSync = function (pkCatalog, pkSchema, pkTable, fkCatalog, fkSchema, fkTable)
2768+
{
2769+
var self = this;
2770+
return self._foreignKeysSync(pkCatalog, pkSchema, pkTable, fkCatalog, fkSchema, fkTable);
2771+
};
2772+
26672773
// Async funcion to close a statement object to free statement handle
26682774
// If no callback function, then return Promise.
26692775
odbc.ODBCStatement.prototype.close = function (closeOption, cb)

package-lock.json

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
"author": "IBM",
6262
"license": "MIT",
6363
"devDependencies": {
64-
"@types/node": "^24.12.2",
64+
"@types/node": "^24.12.4",
6565
"async": "^3.2.4",
6666
"bluebird": "^3.7.2",
6767
"moment": "^2.29.4",

0 commit comments

Comments
 (0)