Skip to content
Draft
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
36 changes: 34 additions & 2 deletions src/91indexeddb.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,26 @@ IDB.createTable = async function (databaseid, tableid, ifnotexists, cb) {
throw err;
}

// Get primary key information from the table definition
const table = alasql.databases[databaseid].tables[tableid];
const pkColumns = table && table.pk && table.pk.columns;

// Build object store options
let storeOptions;
if (pkColumns && pkColumns.length === 1) {
// Single-column primary key: use keyPath
storeOptions = {keyPath: pkColumns[0]};
} else if (pkColumns && pkColumns.length > 1) {
// Composite primary key: use array keyPath
storeOptions = {keyPath: pkColumns};
} else {
// No primary key: use auto-increment
storeOptions = {autoIncrement: true};
}

const request = indexedDB.open(ixdbid, found.version + 1);
request.onupgradeneeded = function (event) {
request.result.createObjectStore(tableid, {autoIncrement: true});
request.result.createObjectStore(tableid, storeOptions);
};
request.onsuccess = function (event) {
request.result.close();
Expand Down Expand Up @@ -299,8 +316,23 @@ IDB.intoTable = function (databaseid, tableid, value, columns, cb) {
var ixdb = request.result;
var tx = ixdb.transaction([tableid], 'readwrite');
var tb = tx.objectStore(tableid);
var errorHandled = false;
for (var i = 0, ilen = value.length; i < ilen; i++) {
tb.add(value[i]);
var addRequest = tb.add(value[i]);
addRequest.onerror = function (evt) {
// Handle duplicate key errors
if (!errorHandled && evt.target.error && evt.target.error.name === 'ConstraintError') {
errorHandled = true;
evt.preventDefault();
evt.stopPropagation();
tx.abort();
ixdb.close();
var err = new Error(
'Cannot insert record, because it already exists in primary key index'
);
if (cb) cb(null, err);
}
};
}
tx.oncomplete = function () {
ixdb.close();
Expand Down
64 changes: 64 additions & 0 deletions test/test1292.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
if (typeof exports === 'object') {
var assert = require('assert');
var alasql = require('..');
}

describe('Test 1292 - Primary key enforcement prevents duplicate values', function () {
const test = '1292';

before(function () {
alasql('create database test' + test);
alasql('use test' + test);
});

after(function () {
alasql('drop database test' + test);
});

it('A) Primary key column constraint prevents duplicates', function () {
alasql('CREATE TABLE settings (setting varchar(50) PRIMARY KEY, val varchar(300))');
alasql("INSERT INTO settings (setting, val) values ('domain', 'http')");

// Inserting a duplicate primary key should throw an error
assert.throws(function () {
alasql("INSERT INTO settings (setting, val) values ('domain', 'https')");
}, /already exists in primary key/);

// Verify only one record exists
var res = alasql('SELECT * FROM settings');
assert.deepEqual(res, [{setting: 'domain', val: 'http'}]);

alasql('DROP TABLE settings');
});

it('B) Primary key table constraint prevents duplicates', function () {
alasql('CREATE TABLE settings2 (setting varchar(50), val varchar(300), PRIMARY KEY (setting))');
alasql("INSERT INTO settings2 (setting, val) values ('domain', 'http')");

// Inserting a duplicate primary key should throw an error
assert.throws(function () {
alasql("INSERT INTO settings2 (setting, val) values ('domain', 'https')");
}, /already exists in primary key/);

// Verify only one record exists
var res = alasql('SELECT * FROM settings2');
assert.deepEqual(res, [{setting: 'domain', val: 'http'}]);

alasql('DROP TABLE settings2');
});

it('C) Primary key allows different key values', function () {
alasql('CREATE TABLE settings3 (setting varchar(50) PRIMARY KEY, val varchar(300))');
alasql("INSERT INTO settings3 (setting, val) values ('domain', 'http')");
alasql("INSERT INTO settings3 (setting, val) values ('port', '8080')");

// Verify both records exist
var res = alasql('SELECT * FROM settings3 ORDER BY setting');
assert.deepEqual(res, [
{setting: 'domain', val: 'http'},
{setting: 'port', val: '8080'},
]);

alasql('DROP TABLE settings3');
});
});
Loading