ibm_db
Version:
IBM DB2 and IBM Informix bindings for node
365 lines (322 loc) • 14.7 kB
JavaScript
// Test for issue #977:
// When SQL_ATTR_ROW_ARRAY_SIZE > 1, ibm_db should use SQLBindCol-based
// block fetch to correctly return all rows. With 20 rows and
// ROW_ARRAY_SIZE=5, all 20 rows must be returned.
var common = require("./common")
, ibmdb = require("../")
, assert = require("assert")
, cn = common.connectionString
;
var tableName = "TEST_ISSUE977";
ibmdb.open(cn, function(err, conn) {
if (err) { console.log(err); return; }
console.log("Connection opened successfully.");
// Drop table if exists
try { conn.querySync("DROP TABLE " + tableName); } catch(e) {}
// Create table and insert 20 rows
conn.querySync("CREATE TABLE " + tableName +
" (ID INTEGER, NAME VARCHAR(20))");
for (var i = 1; i <= 20; i++) {
conn.querySync("INSERT INTO " + tableName +
" VALUES (" + i + ", 'Row" + i + "')");
}
console.log("Inserted 20 rows into " + tableName);
// Verify all 20 rows exist using normal querySync (no ROW_ARRAY_SIZE)
var allRows = conn.querySync("SELECT * FROM " + tableName + " ORDER BY ID");
console.log("\nTest 0: querySync without ROW_ARRAY_SIZE = " +
allRows.length + " rows (expected 20)");
assert.equal(allRows.length, 20);
testFetchAllSync();
function testFetchAllSync() {
// Test 1: prepare + setAttrSync(ROW_ARRAY_SIZE, 5) + fetchAllSync
console.log("\nTest 1: fetchAllSync with SQL_ATTR_ROW_ARRAY_SIZE = 5");
var stmt = conn.prepareSync("SELECT * FROM " + tableName + " ORDER BY ID");
stmt.setAttrSync(ibmdb.SQL_ATTR_ROW_ARRAY_SIZE, 5);
var result = stmt.executeSync();
var data = result.fetchAllSync();
console.log(" Returned " + data.length + " rows (expected 20)");
assert.equal(data.length, 20, "fetchAllSync should return 20 rows");
// Verify all rows are present and in order
for (var i = 0; i < 20; i++) {
assert.equal(data[i].ID, i + 1,
"Row " + i + " should have ID=" + (i + 1));
assert.equal(data[i].NAME, "Row" + (i + 1));
}
console.log(" Data = ", data);
console.log(" PASS: All 20 rows correctly returned with proper data.");
result.closeSync();
stmt.closeSync();
testFetchAll();
}
function testFetchAll() {
// Test 2: prepare + setAttrSync(ROW_ARRAY_SIZE, 5) + async fetchAll
console.log("\nTest 2: async fetchAll with SQL_ATTR_ROW_ARRAY_SIZE = 5");
var stmt2 = conn.prepareSync("SELECT * FROM " + tableName + " ORDER BY ID");
stmt2.setAttrSync(ibmdb.SQL_ATTR_ROW_ARRAY_SIZE, 5);
var result2 = stmt2.executeSync();
result2.fetchAll(function(err, data2) {
if (err) {
console.log(" ERROR:", err);
assert.fail("fetchAll should not error");
}
console.log(" Returned " + data2.length + " rows (expected 20)");
assert.equal(data2.length, 20, "fetchAll should return 20 rows");
for (var i = 0; i < 20; i++) {
assert.equal(data2[i].ID, i + 1);
}
console.log(" PASS: All 20 rows correctly returned.");
result2.closeSync();
stmt2.closeSync();
testFetchSync();
});
}
function testFetchSync() {
// Test 3: prepare + setAttrSync(ROW_ARRAY_SIZE, 5) + fetchSync loop
// fetchSync returns an array of up to ROW_ARRAY_SIZE rows per call
console.log("\nTest 3: fetchSync loop with SQL_ATTR_ROW_ARRAY_SIZE = 5");
var stmt3 = conn.prepareSync("SELECT * FROM " + tableName + " ORDER BY ID");
stmt3.setAttrSync(ibmdb.SQL_ATTR_ROW_ARRAY_SIZE, 5);
var result3 = stmt3.executeSync();
var allRows = [];
var blockCount = 0;
var block;
while ((block = result3.fetchSync()) !== null) {
blockCount++;
assert.ok(Array.isArray(block), "fetchSync should return an array when ROW_ARRAY_SIZE > 1");
assert.ok(block.length <= 5, "Block should have at most ROW_ARRAY_SIZE rows");
console.log(" Block " + blockCount + ": " + block.length + " rows");
console.log(" Block data = ", block);
for (var i = 0; i < block.length; i++) {
allRows.push(block[i]);
}
}
console.log(" Total: " + allRows.length + " rows in " + blockCount + " blocks (expected 20 rows, 4 blocks)");
assert.equal(allRows.length, 20, "fetchSync should return 20 total rows");
assert.equal(blockCount, 4, "Should fetch 4 blocks of 5 rows each");
for (var i = 0; i < 20; i++) {
assert.equal(allRows[i].ID, i + 1);
}
console.log(" PASS: All 20 rows correctly returned in 4 blocks of 5.");
result3.closeSync();
stmt3.closeSync();
testFetchNSync();
}
function testFetchNSync() {
// Test 4: prepare + setAttrSync(ROW_ARRAY_SIZE, 5) + fetchNSync
console.log("\nTest 4: fetchNSync(10) with SQL_ATTR_ROW_ARRAY_SIZE = 5");
var stmt4 = conn.prepareSync("SELECT * FROM " + tableName + " ORDER BY ID");
stmt4.setAttrSync(ibmdb.SQL_ATTR_ROW_ARRAY_SIZE, 5);
var result4 = stmt4.executeSync();
var batch1 = result4.fetchNSync(10);
console.log(" Batch 1: " + batch1.length + " rows (expected 10)");
assert.equal(batch1.length, 10, "fetchNSync(10) should return 10 rows");
console.log(" Batch 1 data = ", batch1);
for (var i = 0; i < 10; i++) {
assert.equal(batch1[i].ID, i + 1);
}
var batch2 = result4.fetchNSync(10);
console.log(" Batch 2: " + batch2.length + " rows (expected 10)");
assert.equal(batch2.length, 10, "fetchNSync(10) should return 10 rows");
console.log(" Batch 2 data = ", batch2);
for (var i = 0; i < 10; i++) {
assert.equal(batch2[i].ID, i + 11);
}
var batch3 = result4.fetchNSync(10);
console.log(" Batch 3: " + batch3.length + " rows (expected 0)");
assert.equal(batch3.length, 0, "fetchNSync(10) after all rows should return 0");
console.log(" PASS: fetchNSync correctly batched all 20 rows.");
result4.closeSync();
stmt4.closeSync();
testFetchArrayMode();
}
function testFetchArrayMode() {
// Test 5: fetchAllSync with FETCH_ARRAY mode and ROW_ARRAY_SIZE=5
console.log("\nTest 5: fetchAllSync FETCH_ARRAY with SQL_ATTR_ROW_ARRAY_SIZE = 5");
var stmt5 = conn.prepareSync("SELECT * FROM " + tableName + " ORDER BY ID");
stmt5.setAttrSync(ibmdb.SQL_ATTR_ROW_ARRAY_SIZE, 5);
var result5 = stmt5.executeSync();
var data5 = result5.fetchAllSync({fetchMode: 3}); // FETCH_ARRAY = 3
console.log(" Returned " + data5.length + " rows (expected 20)");
assert.equal(data5.length, 20, "fetchAllSync FETCH_ARRAY should return 20 rows");
assert.equal(data5[0][0], 1, "First row ID should be 1");
assert.equal(data5[0][1], "Row1", "First row NAME should be 'Row1'");
assert.equal(data5[19][0], 20, "Last row ID should be 20");
console.log(" PASS: All 20 rows correctly returned as arrays.");
result5.closeSync();
stmt5.closeSync();
testFetchAsync();
}
function testFetchAsync() {
// Test 6: async fetch() (callback) with ROW_ARRAY_SIZE=5
// fetch() returns an array of up to ROW_ARRAY_SIZE rows per callback
console.log("\nTest 6: async fetch() callback with SQL_ATTR_ROW_ARRAY_SIZE = 5");
var stmt6 = conn.prepareSync("SELECT * FROM " + tableName + " ORDER BY ID");
stmt6.setAttrSync(ibmdb.SQL_ATTR_ROW_ARRAY_SIZE, 5);
var result6 = stmt6.executeSync();
var allRows6 = [];
var blockCount6 = 0;
function fetchNext() {
result6.fetch(function(err, block) {
if (err) {
console.log(" ERROR:", err);
assert.fail("fetch() should not error");
return;
}
if (block) {
blockCount6++;
assert.ok(Array.isArray(block), "fetch() should return an array when ROW_ARRAY_SIZE > 1");
assert.ok(block.length <= 5, "Block should have at most ROW_ARRAY_SIZE rows");
console.log(" Block " + blockCount6 + ": " + block.length + " rows");
console.log(" Block data = ", block);
for (var i = 0; i < block.length; i++) {
allRows6.push(block[i]);
}
fetchNext();
} else {
console.log(" Total: " + allRows6.length + " rows in " + blockCount6 + " blocks (expected 20 rows, 4 blocks)");
assert.equal(allRows6.length, 20, "fetch() should return 20 total rows");
assert.equal(blockCount6, 4, "Should fetch 4 blocks of 5 rows each");
for (var i = 0; i < 20; i++) {
assert.equal(allRows6[i].ID, i + 1);
}
console.log(" PASS: All 20 rows correctly returned via async fetch() in 4 blocks.");
result6.closeSync();
stmt6.closeSync();
testFetchNAsync();
}
});
}
fetchNext();
}
function testFetchNAsync() {
// Test 7: async fetchN() (callback) with ROW_ARRAY_SIZE=5
console.log("\nTest 7: async fetchN() callback with SQL_ATTR_ROW_ARRAY_SIZE = 5");
var stmt7 = conn.prepareSync("SELECT * FROM " + tableName + " ORDER BY ID");
stmt7.setAttrSync(ibmdb.SQL_ATTR_ROW_ARRAY_SIZE, 5);
var result7 = stmt7.executeSync();
result7.fetchN(10, function(err, batch1) {
if (err) { console.log(" ERROR:", err); assert.fail("fetchN should not error"); }
console.log(" Batch 1: " + batch1.length + " rows (expected 10)");
assert.equal(batch1.length, 10, "fetchN(10) should return 10 rows");
for (var i = 0; i < 10; i++) {
assert.equal(batch1[i].ID, i + 1);
}
result7.fetchN(10, function(err, batch2) {
if (err) { console.log(" ERROR:", err); assert.fail("fetchN should not error"); }
console.log(" Batch 2: " + batch2.length + " rows (expected 10)");
assert.equal(batch2.length, 10, "fetchN(10) should return 10 rows");
for (var i = 0; i < 10; i++) {
assert.equal(batch2[i].ID, i + 11);
}
result7.fetchN(10, function(err, batch3) {
if (err) { console.log(" ERROR:", err); assert.fail("fetchN should not error"); }
console.log(" Batch 3: " + batch3.length + " rows (expected 0)");
assert.equal(batch3.length, 0, "fetchN(10) after all rows should return 0");
console.log(" PASS: async fetchN() correctly batched all 20 rows.");
result7.closeSync();
stmt7.closeSync();
testAsyncAwait();
});
});
});
}
function testAsyncAwait() {
// Test 8: async/await with all APIs and ROW_ARRAY_SIZE=5
console.log("\nTest 8: async/await with SQL_ATTR_ROW_ARRAY_SIZE = 5");
asyncAwaitTests().then(function() {
cleanup();
}).catch(function(err) {
console.log(" async/await test error:", err);
assert.fail("async/await tests should not error");
});
}
async function asyncAwaitTests() {
// 8a: fetchAll via Promise/await
console.log(" 8a: await fetchAll()");
var stmt8a = conn.prepareSync("SELECT * FROM " + tableName + " ORDER BY ID");
stmt8a.setAttrSync(ibmdb.SQL_ATTR_ROW_ARRAY_SIZE, 5);
var result8a = stmt8a.executeSync();
var data8a = await result8a.fetchAll();
console.log(" Returned " + data8a.length + " rows (expected 20)");
assert.equal(data8a.length, 20, "await fetchAll() should return 20 rows");
for (var i = 0; i < 20; i++) {
assert.equal(data8a[i].ID, i + 1);
}
console.log(" PASS");
result8a.closeSync();
stmt8a.closeSync();
// 8b: fetch via Promise/await loop - returns array of rows per block
console.log(" 8b: await fetch() blocks");
var stmt8b = conn.prepareSync("SELECT * FROM " + tableName + " ORDER BY ID");
stmt8b.setAttrSync(ibmdb.SQL_ATTR_ROW_ARRAY_SIZE, 5);
var result8b = stmt8b.executeSync();
var rows8b = [];
var blockCount8b = 0;
var block8b;
while ((block8b = await result8b.fetch()) !== null) {
blockCount8b++;
assert.ok(Array.isArray(block8b), "await fetch() should return an array when ROW_ARRAY_SIZE > 1");
console.log(" Block data = ", block8b);
for (var i = 0; i < block8b.length; i++) {
rows8b.push(block8b[i]);
}
}
console.log(" Returned " + rows8b.length + " rows in " + blockCount8b + " blocks (expected 20 rows, 4 blocks)");
assert.equal(rows8b.length, 20, "await fetch() should return 20 total rows");
assert.equal(blockCount8b, 4, "Should fetch 4 blocks of 5 rows each");
for (var i = 0; i < 20; i++) {
assert.equal(rows8b[i].ID, i + 1);
}
console.log(" PASS");
result8b.closeSync();
stmt8b.closeSync();
// 8c: fetchN via Promise/await batches
console.log(" 8c: await fetchN() batches");
var stmt8c = conn.prepareSync("SELECT * FROM " + tableName + " ORDER BY ID");
stmt8c.setAttrSync(ibmdb.SQL_ATTR_ROW_ARRAY_SIZE, 5);
var result8c = stmt8c.executeSync();
var b1 = await result8c.fetchN(7);
console.log(" Batch 1: " + b1.length + " rows (expected 7)");
assert.equal(b1.length, 7, "await fetchN(7) should return 7 rows");
assert.equal(b1[0].ID, 1);
assert.equal(b1[6].ID, 7);
var b2 = await result8c.fetchN(7);
console.log(" Batch 2: " + b2.length + " rows (expected 7)");
assert.equal(b2.length, 7, "await fetchN(7) should return 7 rows");
assert.equal(b2[0].ID, 8);
assert.equal(b2[6].ID, 14);
var b3 = await result8c.fetchN(7);
console.log(" Batch 3: " + b3.length + " rows (expected 6)");
assert.equal(b3.length, 6, "await fetchN(7) should return remaining 6 rows");
assert.equal(b3[0].ID, 15);
assert.equal(b3[5].ID, 20);
var b4 = await result8c.fetchN(7);
console.log(" Batch 4: " + b4.length + " rows (expected 0)");
assert.equal(b4.length, 0, "await fetchN(7) after all rows should return 0");
console.log(" PASS");
result8c.closeSync();
stmt8c.closeSync();
// 8d: full async/await using ibmdb.open Promise
console.log(" 8d: full async/await with ibmdb.open");
var conn2 = await ibmdb.open(cn);
var stmt8d = conn2.prepareSync("SELECT * FROM " + tableName + " ORDER BY ID");
stmt8d.setAttrSync(ibmdb.SQL_ATTR_ROW_ARRAY_SIZE, 5);
var result8d = stmt8d.executeSync();
var data8d = await result8d.fetchAll();
assert.equal(data8d.length, 20, "full async/await should return 20 rows");
for (var i = 0; i < 20; i++) {
assert.equal(data8d[i].ID, i + 1);
assert.equal(data8d[i].NAME, "Row" + (i + 1));
}
console.log(" Returned " + data8d.length + " rows - PASS");
result8d.closeSync();
stmt8d.closeSync();
await conn2.close();
console.log(" PASS: All async/await tests passed.");
}
function cleanup() {
conn.querySync("DROP TABLE " + tableName);
conn.closeSync();
console.log("\nAll tests passed! Connection closed.");
}
});