diff --git a/pegjs/sqlite.pegjs b/pegjs/sqlite.pegjs index 0f7ed0640..691a86a4a 100644 --- a/pegjs/sqlite.pegjs +++ b/pegjs/sqlite.pegjs @@ -2448,13 +2448,16 @@ on_update_current_timestamp } over_partition - = KW_OVER __ LPAREN __ KW_PARTITION __ KW_BY __ bc:column_clause __ l:order_by_clause? __ RPAREN { + = KW_OVER __ LPAREN __ p:partition_by_clause? __ l:order_by_clause? __ RPAREN { return { - partitionby: bc, + partitionby: p, orderby: l } } / on_update_current_timestamp + +partition_by_clause + = KW_PARTITION __ KW_BY __ bc:column_clause { return bc; } aggr_fun_count = name:(KW_COUNT / KW_GROUP_CONCAT) __ LPAREN __ arg:count_arg __ RPAREN __ bc:over_partition? { return { diff --git a/src/over.js b/src/over.js index 43f6ed7fa..ce9c71f98 100644 --- a/src/over.js +++ b/src/over.js @@ -13,10 +13,8 @@ function overToSQL(over) { if (parentheses) onUpdate = `${onUpdate}(${args.join(', ')})` return onUpdate } - if (over.partitionby) { - return ['OVER', `(${orderOrPartitionByToSQL(over.partitionby, 'partition by')}`, `${orderOrPartitionByToSQL(over.orderby, 'order by')})`].filter(hasVal).join(' ') - } - throw new Error('unknown over type') + if (type) throw new Error('unknown over type') + return ['OVER', `(${orderOrPartitionByToSQL(over.partitionby, 'partition by')}`, `${orderOrPartitionByToSQL(over.orderby, 'order by')})`].filter(hasVal).join(' ') } export { diff --git a/test/sqlite.spec.js b/test/sqlite.spec.js index e9b8cee69..142b7821b 100644 --- a/test/sqlite.spec.js +++ b/test/sqlite.spec.js @@ -183,6 +183,25 @@ describe('sqlite', () => { expect(getParsedSql(sql)).to.be.equal(`SELECT "b"."brand_name", "p"."prompt_text", "m"."model_name", "mr"."brand_visibility_score", AVG("mr"."brand_visibility_score") OVER (PARTITION BY "b"."brand_name" ) AS "avg_brand_visibility", AVG("mr"."brand_visibility_score") OVER (PARTITION BY "m"."model_name" ) AS "avg_model_visibility", AVG("mr"."brand_visibility_score") OVER (PARTITION BY "p"."prompt_id" ) AS "avg_prompt_visibility" FROM "model_responses" AS "mr" INNER JOIN "experiment_runs" AS "er" ON "mr"."run_id" = "er"."run_id" INNER JOIN "brands" AS "b" ON "er"."brand_id" = "b"."brand_id" INNER JOIN "models" AS "m" ON "mr"."model_id" = "m"."model_id" INNER JOIN "prompts" AS "p" ON "mr"."prompt_id" = "p"."prompt_id" WHERE "b"."brand_name" IN ('prod1', 'prod2', 'prod3') AND "mr"."error_occurred" = 0 ORDER BY "b"."brand_name" ASC, "mr"."brand_visibility_score" DESC, "m"."model_name" ASC, "p"."prompt_text" ASC LIMIT 100`) }) + it('should support window function with empty OVER()', () => { + let sql = 'SELECT ROW_NUMBER() OVER () FROM t' + expect(getParsedSql(sql)).to.be.equal('SELECT ROW_NUMBER() OVER ( ) FROM "t"') + sql = 'SELECT id, SUM(amount) OVER () AS total FROM payments' + expect(getParsedSql(sql)).to.be.equal('SELECT "id", SUM("amount") OVER ( ) AS "total" FROM "payments"') + }) + + it('should support window function with ORDER BY only (no PARTITION BY)', () => { + let sql = 'SELECT ROW_NUMBER() OVER (ORDER BY id) FROM t' + expect(getParsedSql(sql)).to.be.equal('SELECT ROW_NUMBER() OVER ( ORDER BY "id" ASC) FROM "t"') + sql = 'SELECT SUM(x) OVER (ORDER BY a DESC, b ASC) FROM t' + expect(getParsedSql(sql)).to.be.equal('SELECT SUM("x") OVER ( ORDER BY "a" DESC, "b" ASC) FROM "t"') + }) + + it('should support window function with PARTITION BY and ORDER BY', () => { + const sql = 'SELECT SUM(x) OVER (PARTITION BY a, b ORDER BY c DESC, d ASC) FROM t' + expect(getParsedSql(sql)).to.be.equal('SELECT SUM("x") OVER (PARTITION BY "a", "b" ORDER BY "c" DESC, "d" ASC) FROM "t"') + }) + it('should support create or drop view', () => { let sql = 'create view v1 as select * from t1' expect(getParsedSql(sql)).to.be.equal('CREATE VIEW "v1" AS SELECT * FROM "t1"')