Skip to content

Commit e01badd

Browse files
Copilotmathiasrw
andauthored
Add support for all SQL operations on anonymous data tables to close #2348 (#2349)
Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: mathiasrw <[email protected]>
1 parent c2b51c4 commit e01badd

File tree

7 files changed

+817
-625
lines changed

7 files changed

+817
-625
lines changed

src/28yy.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,47 @@ var yy = {
2626
// Option for case sensitive
2727
casesensitive: alasql.options.casesensitive,
2828
Base,
29+
30+
// Helper for ParamValue handling in UPDATE/DELETE/INSERT
31+
compileParamValue: function (paramIndex, operation, needsSync, databaseid, self, refProp) {
32+
return function (params, cb) {
33+
var data = params[paramIndex];
34+
if (!Array.isArray(data)) {
35+
var err = new Error(operation + ' requires an array for parameter ' + paramIndex);
36+
if (cb) return cb(null, err);
37+
throw err;
38+
}
39+
40+
// Create temp table, execute, sync, cleanup
41+
var tmpid = '__p' + paramIndex + '_' + Date.now();
42+
var db = alasql.databases[databaseid || 'alasql'];
43+
db.tables[tmpid] = new alasql.Table({tableid: tmpid});
44+
db.tables[tmpid].data = data;
45+
46+
try {
47+
var origRef = self[refProp];
48+
self[refProp] = new yy.Table({tableid: tmpid, databaseid: db.databaseid});
49+
var stmt = self.compile(databaseid);
50+
self[refProp] = origRef;
51+
52+
var res = stmt(params, cb);
53+
54+
// Sync back changes for operations that replace the array
55+
if (needsSync) {
56+
var newData = db.tables[tmpid].data;
57+
data.length = 0;
58+
Array.prototype.push.apply(data, newData);
59+
}
60+
61+
return res;
62+
} catch (err) {
63+
if (cb) return cb(null, err);
64+
throw err;
65+
} finally {
66+
delete db.tables[tmpid];
67+
}
68+
};
69+
},
2970
};
3071

3172
alasqlparser.yy = alasql.yy = yy;

src/70insert.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ yy.Insert.prototype.toJS = function (context, tableid, defcols) {
5959

6060
yy.Insert.prototype.compile = function (databaseid) {
6161
var self = this;
62+
63+
// Handle ParamValue (anonymous data table) - wrap execution
64+
if (self.into instanceof yy.ParamValue) {
65+
return yy.compileParamValue(self.into.param, 'INSERT', true, databaseid, self, 'into');
66+
}
67+
6268
databaseid = self.into.databaseid || databaseid;
6369
var db = alasql.databases[databaseid];
6470
// console.log(self);

src/72delete.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ yy.Delete.prototype.toString = function () {
2929

3030
yy.Delete.prototype.compile = function (databaseid) {
3131
var self = this;
32+
33+
// Handle ParamValue (anonymous data table) - wrap execution
34+
if (this.table instanceof yy.ParamValue) {
35+
return yy.compileParamValue(this.table.param, 'DELETE', true, databaseid, self, 'table');
36+
}
37+
3238
databaseid = this.table.databaseid || databaseid;
3339
var tableid = this.table.tableid;
3440
var statement;

src/74update.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ yy.SetColumn.prototype.toString = function () {
4040
yy.Update.prototype.compile = function (databaseid) {
4141
var self = this;
4242
// console.log(this);
43+
44+
// Handle ParamValue (anonymous data table) - wrap execution
45+
if (this.table instanceof yy.ParamValue) {
46+
return yy.compileParamValue(this.table.param, 'UPDATE', false, databaseid, self, 'table');
47+
}
48+
4349
databaseid = this.table.databaseid || databaseid;
4450
var tableid = this.table.tableid;
4551

src/alasqlparser.jison

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,13 @@ Table
996996
{ $$ = new yy.Table({tableid: $1});}
997997
;
998998

999+
TargetTable
1000+
: Table
1001+
{ $$ = $1; }
1002+
| ParamValue
1003+
{ $$ = $1; }
1004+
;
1005+
9991006
JoinTablesList
10001007
: JoinTablesList JoinTable
10011008
{ $$ = $1; $1.push($2); }
@@ -1836,9 +1843,9 @@ AllSome
18361843
/* UPDATE */
18371844

18381845
Update
1839-
: UPDATE Table SET SetColumnsList WHERE Expression OutputClause
1846+
: UPDATE TargetTable SET SetColumnsList WHERE Expression OutputClause
18401847
{ $$ = new yy.Update({table:$2, columns:$4, where:$6}); yy.extend($$,$7); }
1841-
| UPDATE Table SET SetColumnsList OutputClause
1848+
| UPDATE TargetTable SET SetColumnsList OutputClause
18421849
{ $$ = new yy.Update({table:$2, columns:$4}); yy.extend($$,$5); }
18431850
;
18441851

@@ -1860,52 +1867,52 @@ SetColumn
18601867
/* DELETE */
18611868

18621869
Delete
1863-
: DELETE FROM Table WHERE Expression OutputClause
1870+
: DELETE FROM TargetTable WHERE Expression OutputClause
18641871
{ $$ = new yy.Delete({table:$3, where:$5}); yy.extend($$,$6);}
1865-
| DELETE FROM Table OutputClause
1872+
| DELETE FROM TargetTable OutputClause
18661873
{ $$ = new yy.Delete({table:$3}); yy.extend($$,$4);}
18671874
;
18681875

18691876
/* INSERT */
18701877

18711878
Insert
1872-
: INSERT Into Table Values ValuesListsList OutputClause
1879+
: INSERT Into TargetTable Values ValuesListsList OutputClause
18731880
{ $$ = new yy.Insert({into:$3, values: $5}); yy.extend($$,$6); }
1874-
| INSERT Into Table ValuesListsList OutputClause
1881+
| INSERT Into TargetTable ValuesListsList OutputClause
18751882
{ $$ = new yy.Insert({into:$3, values: $4}); yy.extend($$,$5); }
1876-
| INSERT IGNORE Into Table Values ValuesListsList OutputClause
1883+
| INSERT IGNORE Into TargetTable Values ValuesListsList OutputClause
18771884
{ $$ = new yy.Insert({into:$4, values: $6, ignore:true}); yy.extend($$,$7); }
1878-
| INSERT IGNORE Into Table ValuesListsList OutputClause
1885+
| INSERT IGNORE Into TargetTable ValuesListsList OutputClause
18791886
{ $$ = new yy.Insert({into:$4, values: $5, ignore:true}); yy.extend($$,$6); }
1880-
| INSERT IGNORE Into Table LPAR ColumnsList RPAR Values ValuesListsList OutputClause
1887+
| INSERT IGNORE Into TargetTable LPAR ColumnsList RPAR Values ValuesListsList OutputClause
18811888
{ $$ = new yy.Insert({into:$4, columns: $6, values: $9, ignore:true}); yy.extend($$,$10); }
1882-
| INSERT IGNORE Into Table LPAR ColumnsList RPAR ValuesListsList OutputClause
1889+
| INSERT IGNORE Into TargetTable LPAR ColumnsList RPAR ValuesListsList OutputClause
18831890
{ $$ = new yy.Insert({into:$4, columns: $6, values: $8, ignore:true}); yy.extend($$,$9); }
1884-
| INSERT IGNORE Into Table Select OutputClause
1891+
| INSERT IGNORE Into TargetTable Select OutputClause
18851892
{ $$ = new yy.Insert({into:$4, select: $5, ignore:true}); yy.extend($$,$6); }
1886-
| INSERT IGNORE Into Table LPAR ColumnsList RPAR Select OutputClause
1893+
| INSERT IGNORE Into TargetTable LPAR ColumnsList RPAR Select OutputClause
18871894
{ $$ = new yy.Insert({into:$4, columns: $6, select: $8, ignore:true}); yy.extend($$,$9); }
1888-
| INSERT OR REPLACE Into Table Values ValuesListsList OutputClause
1895+
| INSERT OR REPLACE Into TargetTable Values ValuesListsList OutputClause
18891896
{ $$ = new yy.Insert({into:$5, values: $7, orreplace:true}); yy.extend($$,$8); }
1890-
| INSERT OR REPLACE Into Table ValuesListsList OutputClause
1897+
| INSERT OR REPLACE Into TargetTable ValuesListsList OutputClause
18911898
{ $$ = new yy.Insert({into:$5, values: $6, orreplace:true}); yy.extend($$,$7); }
1892-
| REPLACE Into Table Values ValuesListsList OutputClause
1899+
| REPLACE Into TargetTable Values ValuesListsList OutputClause
18931900
{ $$ = new yy.Insert({into:$3, values: $5, orreplace:true}); yy.extend($$,$6); }
1894-
| REPLACE Into Table ValuesListsList OutputClause
1901+
| REPLACE Into TargetTable ValuesListsList OutputClause
18951902
{ $$ = new yy.Insert({into:$3, values: $4, orreplace:true}); yy.extend($$,$5); }
1896-
| INSERT Into Table DEFAULT Values OutputClause
1903+
| INSERT Into TargetTable DEFAULT Values OutputClause
18971904
{ $$ = new yy.Insert({into:$3, "default": true}); yy.extend($$,$6); }
1898-
| INSERT Into Table LPAR ColumnsList RPAR Values ValuesListsList OutputClause
1905+
| INSERT Into TargetTable LPAR ColumnsList RPAR Values ValuesListsList OutputClause
18991906
{ $$ = new yy.Insert({into:$3, columns: $5, values: $8}); yy.extend($$,$9); }
1900-
| INSERT Into Table LPAR ColumnsList RPAR ValuesListsList OutputClause
1907+
| INSERT Into TargetTable LPAR ColumnsList RPAR ValuesListsList OutputClause
19011908
{ $$ = new yy.Insert({into:$3, columns: $5, values: $7}); yy.extend($$,$9); }
1902-
| INSERT Into Table Select OutputClause
1909+
| INSERT Into TargetTable Select OutputClause
19031910
{ $$ = new yy.Insert({into:$3, select: $4}); yy.extend($$,$5); }
1904-
| INSERT OR REPLACE Into Table Select OutputClause
1911+
| INSERT OR REPLACE Into TargetTable Select OutputClause
19051912
{ $$ = new yy.Insert({into:$5, select: $6, orreplace:true}); yy.extend($$,$7); }
1906-
| INSERT Into Table LPAR ColumnsList RPAR Select OutputClause
1913+
| INSERT Into TargetTable LPAR ColumnsList RPAR Select OutputClause
19071914
{ $$ = new yy.Insert({into:$3, columns: $5, select: $7}); yy.extend($$,$9); }
1908-
| INSERT Into Table SET SetColumnsList OutputClause
1915+
| INSERT Into TargetTable SET SetColumnsList OutputClause
19091916
{ $$ = new yy.Insert({into:$3, setcolumns: $5}); yy.extend($$,$6); }
19101917
;
19111918

src/alasqlparser.js

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

test/test2348.js

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
if (typeof exports === 'object') {
2+
var assert = require('assert');
3+
var alasql = require('..');
4+
}
5+
6+
describe('Test 2348 - Anonymous data tables', function () {
7+
const test = '2348';
8+
9+
before(function () {
10+
alasql('create database test' + test);
11+
alasql('use test' + test);
12+
});
13+
14+
after(function () {
15+
alasql('drop database test' + test);
16+
});
17+
18+
it('A) UPDATE anonymous data table with WHERE clause', function () {
19+
var mydata = [
20+
{type: 1, status: 'off'},
21+
{type: 4, status: 'off'},
22+
{type: 2, status: 'off'},
23+
{type: 4, status: 'off'},
24+
];
25+
26+
var res = alasql("UPDATE ? SET status = 'on' WHERE type = 4", [mydata]);
27+
28+
// UPDATE should return the number of rows updated
29+
assert.equal(res, 2);
30+
31+
// Check that the complete data matches expected output
32+
assert.deepEqual(mydata, [
33+
{type: 1, status: 'off'},
34+
{type: 4, status: 'on'},
35+
{type: 2, status: 'off'},
36+
{type: 4, status: 'on'},
37+
]);
38+
});
39+
40+
it('B) UPDATE anonymous data table without WHERE clause', function () {
41+
var mydata = [
42+
{name: 'Alice', age: 25},
43+
{name: 'Bob', age: 30},
44+
];
45+
46+
var res = alasql('UPDATE ? SET age = age + 1', [mydata]);
47+
48+
assert.equal(res, 2);
49+
assert.deepEqual(mydata, [
50+
{name: 'Alice', age: 26},
51+
{name: 'Bob', age: 31},
52+
]);
53+
});
54+
55+
it('C) DELETE from anonymous data table with WHERE clause', function () {
56+
var mydata = [
57+
{id: 1, active: true},
58+
{id: 2, active: false},
59+
{id: 3, active: true},
60+
{id: 4, active: false},
61+
];
62+
63+
var res = alasql('DELETE FROM ? WHERE active = false', [mydata]);
64+
65+
// DELETE should return the number of rows deleted
66+
assert.equal(res, 2);
67+
68+
// Check that the complete data matches expected output
69+
assert.deepEqual(mydata, [
70+
{id: 1, active: true},
71+
{id: 3, active: true},
72+
]);
73+
});
74+
75+
it('D) DELETE from anonymous data table without WHERE clause', function () {
76+
var mydata = [{name: 'test1'}, {name: 'test2'}, {name: 'test3'}];
77+
78+
var res = alasql('DELETE FROM ?', [mydata]);
79+
80+
assert.equal(res, 3);
81+
assert.deepEqual(mydata, []);
82+
});
83+
84+
it('E) INSERT into anonymous data table from VALUES', function () {
85+
var mydata = [{id: 1, name: 'Alice'}];
86+
87+
var res = alasql('INSERT INTO ? VALUES (2, "Bob")', [mydata]);
88+
89+
// INSERT should return the number of rows inserted
90+
assert.equal(res, 1);
91+
92+
// Check that the complete data matches expected output
93+
assert.deepEqual(mydata, [{id: 1, name: 'Alice'}, [2, 'Bob']]);
94+
});
95+
96+
it('F) INSERT into anonymous data table from SELECT', function () {
97+
var mydata = [];
98+
var sourcedata = [
99+
{id: 1, name: 'Alice'},
100+
{id: 2, name: 'Bob'},
101+
];
102+
103+
var res = alasql('INSERT INTO ? SELECT * FROM ?', [mydata, sourcedata]);
104+
105+
assert.equal(res, 2);
106+
assert.deepEqual(mydata, [
107+
{id: 1, name: 'Alice'},
108+
{id: 2, name: 'Bob'},
109+
]);
110+
});
111+
112+
it('G) Complex UPDATE with expression', function () {
113+
var mydata = [
114+
{price: 100, discount: 10},
115+
{price: 200, discount: 20},
116+
];
117+
118+
var res = alasql('UPDATE ? SET price = price - discount WHERE price > 150', [mydata]);
119+
120+
assert.equal(res, 1);
121+
assert.deepEqual(mydata, [
122+
{price: 100, discount: 10},
123+
{price: 180, discount: 20},
124+
]);
125+
});
126+
});

0 commit comments

Comments
 (0)