From dda1a16e467fb87e16894c47b2479085330046a0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 13:02:53 +0000 Subject: [PATCH 1/8] Initial plan From 34105c7c3a521423b89d60ed65d7ebd9f37f0397 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 13:22:41 +0000 Subject: [PATCH 2/8] Implement JavaScript property access in SQL queries - Add property access support for dot notation (name.length) - Detect when Column's tableid is actually a column name (not table) - Generate correct code for property access patterns - Enable and fix test341 tests 4 and 5 - All 2085 tests passing (2 new tests enabled) Co-authored-by: mathiasrw <1063454+mathiasrw@users.noreply.github.com> --- src/424select.js | 24 ++++++++++++++++++++++++ src/50expression.js | 11 +++++++++++ test/test341.js | 7 ++++--- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/424select.js b/src/424select.js index c6bb630a6e..35c338b568 100755 --- a/src/424select.js +++ b/src/424select.js @@ -198,6 +198,16 @@ yy.Select.prototype.compileSelect1 = function (query, params) { var tbid = col.tableid; // console.log(query.sources); var dbid = col.databaseid || query.sources[0].databaseid || query.database.databaseid; + + // Check if tableid is actually a column name (property access pattern like name.length) + // This handles cases where the parser sees "columnname.property" and interprets it as "table.column" + var isPropertyAccess = false; + if (tbid && query.defcols && query.defcols[tbid] && query.defcols[tbid] !== '-') { + // tbid is actually a column name, so this is property access + isPropertyAccess = true; + var actualTableid = query.defcols[tbid]; + } + if (!tbid) tbid = query.defcols[col.columnid]; if (!tbid) tbid = query.defaultTableid; if (col.columnid !== '_') { @@ -260,6 +270,20 @@ yy.Select.prototype.compileSelect1 = function (query, params) { .join(' ?? '); ss.push("'" + escapeq(col.as || col.columnid) + "':(" + searchExpr + ')'); + } else if (isPropertyAccess) { + // Property access pattern: columnname.property (e.g., name.length) + // Generate code to access the property on the column value + ss.push( + "'" + + escapeq(col.as || col.columnid) + + "':((p['" + + actualTableid + + "']['" + + col.tableid + + "'] || {}) ['" + + col.columnid + + "'])" + ); } else { ss.push( "'" + diff --git a/src/50expression.js b/src/50expression.js index 15691c01f6..bfa9a6be30 100755 --- a/src/50expression.js +++ b/src/50expression.js @@ -782,6 +782,17 @@ } if (this.tableid) { + // Check if tableid is actually a column name (property access pattern like name.length) + // This handles cases where the parser sees "columnname.property" and interprets it as "table.column" + if (defcols && defcols[this.tableid] && defcols[this.tableid] !== '-') { + // tableid is actually a column name, so this is property access + // Generate code to access the property on the column value + const actualTable = defcols[this.tableid]; + if (this.columnid !== '_') { + return `((${context}['${actualTable}']['${this.tableid}'] || {})['${this.columnid}'])`; + } + } + // Otherwise, tableid is a table name (normal table.column access) return this.columnid !== '_' ? `${context}['${this.tableid}']['${this.columnid}']` : context === 'g' diff --git a/test/test341.js b/test/test341.js index d838c60d3a..0094992024 100644 --- a/test/test341.js +++ b/test/test341.js @@ -35,13 +35,14 @@ describe('Test 341 Intellectual DOT operator', function () { done(); }); - it.skip('4. JavaScript way', function (done) { + it('4. JavaScript way', function (done) { var res = alasql('SET @a = "who".length'); - assert.deepEqual(res, [6, 6, 7]); + assert.deepEqual(res, 1); + assert.deepEqual(alasql.vars.a, 3); done(); }); - it.skip('5. JavaScript way', function (done) { + it('5. JavaScript way', function (done) { var res = alasql('SELECT COLUMN name.length FROM persons'); assert.deepEqual(res, [6, 6, 7]); done(); From ceb4dfcb93fffd35697df7074e942e3d9d8d455d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 13:26:22 +0000 Subject: [PATCH 3/8] Changes before error encountered Co-authored-by: mathiasrw <1063454+mathiasrw@users.noreply.github.com> --- src/424select.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/424select.js b/src/424select.js index 35c338b568..f7d29dcf2d 100755 --- a/src/424select.js +++ b/src/424select.js @@ -280,7 +280,7 @@ yy.Select.prototype.compileSelect1 = function (query, params) { actualTableid + "']['" + col.tableid + - "'] || {}) ['" + + "'] || {})['" + col.columnid + "'])" ); From ddb008879c8cf636a864b864525115bfdb3298ef Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Dec 2025 13:52:05 +0000 Subject: [PATCH 4/8] Add test for table.column.length syntax - Add test "6. JavaScript way with table.column.length" - Verifies three-level dot notation works correctly - Tests SELECT COLUMN persons.name.length FROM persons - All 2086 tests passing (1 new test added) Co-authored-by: mathiasrw <1063454+mathiasrw@users.noreply.github.com> --- test/test341.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/test341.js b/test/test341.js index 0094992024..a33af2541f 100644 --- a/test/test341.js +++ b/test/test341.js @@ -48,6 +48,12 @@ describe('Test 341 Intellectual DOT operator', function () { done(); }); + it('6. JavaScript way with table.column.length', function (done) { + var res = alasql('SELECT COLUMN persons.name.length FROM persons'); + assert.deepEqual(res, [6, 6, 7]); + done(); + }); + it.skip('5. FOREIGN KEY way', function (done) { var res = alasql('SELECT VALUE $0; SET $0 = 200; SELECT VALUE $0', [100]); assert.deepEqual(res.sort(), [100, 1, 200]); From 5c80ea784c4ff05c77bd65ac55b29d4ffd51e306 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 21 Dec 2025 05:25:04 +0000 Subject: [PATCH 5/8] Fix property access detection to not treat table.column as property access - Add check to ensure tableid is not a table name before treating as property access - Fixes issue where content.content was incorrectly treated as property access - Now correctly distinguishes between table.column and column.property patterns - Check query.defcols['.'][tbid] to verify if tbid is a table name - All 2286 tests passing (up from 2086 due to test2239 now passing) Co-authored-by: mathiasrw <1063454+mathiasrw@users.noreply.github.com> --- src/424select.js | 10 ++++++++-- src/50expression.js | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/424select.js b/src/424select.js index f7d29dcf2d..00fc10ae2a 100755 --- a/src/424select.js +++ b/src/424select.js @@ -202,8 +202,14 @@ yy.Select.prototype.compileSelect1 = function (query, params) { // Check if tableid is actually a column name (property access pattern like name.length) // This handles cases where the parser sees "columnname.property" and interprets it as "table.column" var isPropertyAccess = false; - if (tbid && query.defcols && query.defcols[tbid] && query.defcols[tbid] !== '-') { - // tbid is actually a column name, so this is property access + if ( + tbid && + query.defcols && + query.defcols[tbid] && + query.defcols[tbid] !== '-' && + !query.defcols['.'][tbid] + ) { + // tbid is actually a column name (not a table name), so this is property access isPropertyAccess = true; var actualTableid = query.defcols[tbid]; } diff --git a/src/50expression.js b/src/50expression.js index 34505ea4d2..3e40a12310 100755 --- a/src/50expression.js +++ b/src/50expression.js @@ -784,8 +784,14 @@ if (this.tableid) { // Check if tableid is actually a column name (property access pattern like name.length) // This handles cases where the parser sees "columnname.property" and interprets it as "table.column" - if (defcols && defcols[this.tableid] && defcols[this.tableid] !== '-') { - // tableid is actually a column name, so this is property access + if ( + defcols && + defcols[this.tableid] && + defcols[this.tableid] !== '-' && + defcols['.'] && + !defcols['.'][this.tableid] + ) { + // tableid is actually a column name (not a table name), so this is property access // Generate code to access the property on the column value const actualTable = defcols[this.tableid]; if (this.columnid !== '_') { From 940a721af69e7864dcc4643e6fa7935f36c64f35 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 21 Dec 2025 23:27:04 +0000 Subject: [PATCH 6/8] Use optional chaining and improve test documentation - Replace verbose null checks with optional chaining (?.) for cleaner code - Update src/50expression.js to use defcols?.[...] notation - Update src/424select.js to use query.defcols?.[...] notation - Improve test341.js test 4 with better comments explaining: - What SET returns (1 for successful variable assignment) - How to access the variable value via alasql.vars.a - How to use @a in subsequent queries with SELECT @a - All 2286 tests passing Co-authored-by: mathiasrw <1063454+mathiasrw@users.noreply.github.com> --- src/424select.js | 5 ++--- src/50expression.js | 6 ++---- test/test341.js | 8 ++++++++ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/424select.js b/src/424select.js index 00fc10ae2a..b56437c40c 100755 --- a/src/424select.js +++ b/src/424select.js @@ -204,10 +204,9 @@ yy.Select.prototype.compileSelect1 = function (query, params) { var isPropertyAccess = false; if ( tbid && - query.defcols && - query.defcols[tbid] && + query.defcols?.[tbid] && query.defcols[tbid] !== '-' && - !query.defcols['.'][tbid] + !query.defcols?.['.']?.[tbid] ) { // tbid is actually a column name (not a table name), so this is property access isPropertyAccess = true; diff --git a/src/50expression.js b/src/50expression.js index 3e40a12310..b232bae4dd 100755 --- a/src/50expression.js +++ b/src/50expression.js @@ -785,11 +785,9 @@ // Check if tableid is actually a column name (property access pattern like name.length) // This handles cases where the parser sees "columnname.property" and interprets it as "table.column" if ( - defcols && - defcols[this.tableid] && + defcols?.[this.tableid] && defcols[this.tableid] !== '-' && - defcols['.'] && - !defcols['.'][this.tableid] + !defcols?.['.']?.[this.tableid] ) { // tableid is actually a column name (not a table name), so this is property access // Generate code to access the property on the column value diff --git a/test/test341.js b/test/test341.js index 0ff616ba9b..c4da506b14 100644 --- a/test/test341.js +++ b/test/test341.js @@ -36,9 +36,17 @@ describe('Test 341 Intellectual DOT operator', function () { }); it('4. JavaScript way', function (done) { + // Test SET statement with JavaScript property access + // SET returns the number of rows affected (1 for variable assignment) var res = alasql('SET @a = "who".length'); assert.deepEqual(res, 1); + + // Verify the variable @a was set to the correct value (length of "who" = 3) assert.deepEqual(alasql.vars.a, 3); + + // Verify we can use the variable in subsequent queries + var res2 = alasql('SELECT @a AS result'); + assert.deepEqual(res2, [{result: 3}]); done(); }); From fa413a7a5d6a9e5d17a067230cdaa09128fd16c2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Dec 2025 02:14:59 +0000 Subject: [PATCH 7/8] Simplify property access checks by extracting values once - Store defcols?.[this.tableid] in variable to avoid duplicate access - Store query.defcols?.[tbid] in variable to avoid duplicate access - Reduces redundant property lookups and improves code clarity - Merges multi-line conditionals into single-line checks where appropriate - All 2286 tests passing Co-authored-by: mathiasrw <1063454+mathiasrw@users.noreply.github.com> --- src/424select.js | 17 ++++++++--------- src/50expression.js | 9 +++------ 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/424select.js b/src/424select.js index b56437c40c..9e5c9f5ee6 100755 --- a/src/424select.js +++ b/src/424select.js @@ -202,15 +202,14 @@ yy.Select.prototype.compileSelect1 = function (query, params) { // Check if tableid is actually a column name (property access pattern like name.length) // This handles cases where the parser sees "columnname.property" and interprets it as "table.column" var isPropertyAccess = false; - if ( - tbid && - query.defcols?.[tbid] && - query.defcols[tbid] !== '-' && - !query.defcols?.['.']?.[tbid] - ) { - // tbid is actually a column name (not a table name), so this is property access - isPropertyAccess = true; - var actualTableid = query.defcols[tbid]; + var actualTableid; + if (tbid) { + const tbidValue = query.defcols?.[tbid]; + if (tbidValue && tbidValue !== '-' && !query.defcols?.['.']?.[tbid]) { + // tbid is actually a column name (not a table name), so this is property access + isPropertyAccess = true; + actualTableid = tbidValue; + } } if (!tbid) tbid = query.defcols[col.columnid]; diff --git a/src/50expression.js b/src/50expression.js index b232bae4dd..4846823eb1 100755 --- a/src/50expression.js +++ b/src/50expression.js @@ -784,14 +784,11 @@ if (this.tableid) { // Check if tableid is actually a column name (property access pattern like name.length) // This handles cases where the parser sees "columnname.property" and interprets it as "table.column" - if ( - defcols?.[this.tableid] && - defcols[this.tableid] !== '-' && - !defcols?.['.']?.[this.tableid] - ) { + const tableidValue = defcols?.[this.tableid]; + if (tableidValue && tableidValue !== '-' && !defcols?.['.']?.[this.tableid]) { // tableid is actually a column name (not a table name), so this is property access // Generate code to access the property on the column value - const actualTable = defcols[this.tableid]; + const actualTable = tableidValue; if (this.columnid !== '_') { return `((${context}['${actualTable}']['${this.tableid}'] || {})['${this.columnid}'])`; } From ebb0f8b0ca8f3fdcdac301741fa1f3b831a64f88 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Dec 2025 05:38:22 +0000 Subject: [PATCH 8/8] Use optional chaining and add edge case test for same-name scenarios - Apply optional chaining to remaining query.defcols accesses in src/424select.js - Add comprehensive edge case test for table and column with same name - Test covers: table.column, table.column.property, and column.property patterns - Demonstrates that when table name = column name, explicit qualification is needed - All 2287 tests passing (1 new edge case test added) Co-authored-by: mathiasrw <1063454+mathiasrw@users.noreply.github.com> --- src/424select.js | 4 ++-- test/test341.js | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/424select.js b/src/424select.js index 9e5c9f5ee6..c3a735079c 100755 --- a/src/424select.js +++ b/src/424select.js @@ -212,10 +212,10 @@ yy.Select.prototype.compileSelect1 = function (query, params) { } } - if (!tbid) tbid = query.defcols[col.columnid]; + if (!tbid) tbid = query.defcols?.[col.columnid]; if (!tbid) tbid = query.defaultTableid; if (col.columnid !== '_') { - if (false && tbid && !query.defcols['.'][col.tableid] && !query.defcols[col.columnid]) { + if (false && tbid && !query.defcols?.['.']?.[col.tableid] && !query.defcols?.[col.columnid]) { ss.push( "'" + escapeq(col.as || col.columnid) + diff --git a/test/test341.js b/test/test341.js index c4da506b14..9edd41ee86 100644 --- a/test/test341.js +++ b/test/test341.js @@ -62,6 +62,30 @@ describe('Test 341 Intellectual DOT operator', function () { done(); }); + it('7. Edge case: table and column with same name', function (done) { + // Create a table named "item" with a column named "item" + alasql('CREATE TABLE item (id INT, item STRING)'); + alasql('INSERT INTO item VALUES (1, "Alpha"), (2, "Beta")'); + + // Test 1: table.column access (item.item should get the column value) + var res1 = alasql('SELECT item.item FROM item'); + assert.deepEqual(res1, [{item: 'Alpha'}, {item: 'Beta'}]); + + // Test 2: table.column.property access (item.item.length should get the length) + var res2 = alasql('SELECT COLUMN item.item.length FROM item'); + assert.deepEqual(res2, [5, 4]); + + // Test 3: When only one column exists with unique name, property access works + alasql('CREATE TABLE products (title STRING)'); + alasql('INSERT INTO products VALUES ("Product"), ("Item")'); + var res3 = alasql('SELECT COLUMN title.length FROM products'); + assert.deepEqual(res3, [7, 4]); + + alasql('DROP TABLE item'); + alasql('DROP TABLE products'); + done(); + }); + it('5. FOREIGN KEY way', function (done) { var res = alasql('SELECT VALUE $0; SET $0 = 200; SELECT VALUE $0', [100]); assert.deepEqual(res, [100, 1, 200]);