Compare commits

...

1 Commits

Author SHA1 Message Date
Cursor Agent 3f2c430b59 Support both standard and StarRocks LIMIT/OFFSET syntax in SQL parser
Co-authored-by: huanmingwong <huanmingwong@gmail.com>
2025-09-02 12:00:10 +00:00
5 changed files with 290 additions and 7 deletions

View File

@ -5974,15 +5974,17 @@ public class AstBuilder extends StarRocksBaseVisitor<ParseNode> {
Expr limit;
Expr offset = new IntLiteral(0);
if (context.limit.INTEGER_VALUE() != null) {
limit = new IntLiteral(Long.parseLong(context.limit.INTEGER_VALUE().getText()));
} else if (context.limit.userVariable() != null) {
limit = (UserVariableExpr) visit(context.limit.userVariable());
} else {
throw new ParsingException("unsupported invalid limit value", createPos(context.limit));
// Check if this is the comma-separated syntax: LIMIT offset, limit
boolean isCommaSyntax = false;
for (int i = 0; i < context.getChildCount(); ++i) {
if (context.getChild(i).getText().equals(",")) {
isCommaSyntax = true;
break;
}
}
if (context.offset != null) {
if (isCommaSyntax) {
// Handle LIMIT offset, limit syntax (current StarRocks style)
if (context.offset.INTEGER_VALUE() != null) {
offset = new IntLiteral(Long.parseLong(context.offset.INTEGER_VALUE().getText()));
} else if (context.offset.userVariable() != null) {
@ -5990,7 +5992,35 @@ public class AstBuilder extends StarRocksBaseVisitor<ParseNode> {
} else {
throw new ParsingException("unsupported invalid offset value", createPos(context.offset));
}
if (context.limit.INTEGER_VALUE() != null) {
limit = new IntLiteral(Long.parseLong(context.limit.INTEGER_VALUE().getText()));
} else if (context.limit.userVariable() != null) {
limit = (UserVariableExpr) visit(context.limit.userVariable());
} else {
throw new ParsingException("unsupported invalid limit value", createPos(context.limit));
}
} else {
// Handle LIMIT limit [OFFSET offset] syntax (standard SQL style)
if (context.limit.INTEGER_VALUE() != null) {
limit = new IntLiteral(Long.parseLong(context.limit.INTEGER_VALUE().getText()));
} else if (context.limit.userVariable() != null) {
limit = (UserVariableExpr) visit(context.limit.userVariable());
} else {
throw new ParsingException("unsupported invalid limit value", createPos(context.limit));
}
if (context.offset != null) {
if (context.offset.INTEGER_VALUE() != null) {
offset = new IntLiteral(Long.parseLong(context.offset.INTEGER_VALUE().getText()));
} else if (context.offset.userVariable() != null) {
offset = (UserVariableExpr) visit(context.offset.userVariable());
} else {
throw new ParsingException("unsupported invalid offset value", createPos(context.offset));
}
}
}
return new LimitElement(offset, limit, createPos(context));
}

View File

@ -0,0 +1,67 @@
-- name: test_limit_offset_edge_cases
CREATE DATABASE db_${uuid0};
-- result:
-- !result
USE db_${uuid0};
-- result:
-- !result
CREATE TABLE test_table (
id INT,
value VARCHAR(50)
) DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 1;
-- result:
-- !result
INSERT INTO test_table VALUES
(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e');
-- result:
-- !result
SET @my_limit = 2;
-- result:
-- !result
SET @my_offset = 1;
-- result:
-- !result
SELECT id, value FROM test_table ORDER BY id LIMIT @my_limit OFFSET @my_offset;
-- result:
2 b
3 c
-- !result
SELECT id, value FROM test_table ORDER BY id LIMIT @my_offset, @my_limit;
-- result:
2 b
3 c
-- !result
SELECT id, value FROM test_table ORDER BY id LIMIT 3 OFFSET @my_offset;
-- result:
2 b
3 c
4 d
-- !result
SELECT id, value FROM test_table ORDER BY id LIMIT @my_limit OFFSET 3;
-- result:
4 d
5 e
-- !result
SELECT id, value FROM test_table ORDER BY id LIMIT 2 OFFSET 0;
-- result:
1 a
2 b
-- !result
SELECT id, value FROM test_table ORDER BY id LIMIT 0, 2;
-- result:
1 a
2 b
-- !result
SELECT id, value FROM test_table ORDER BY id LIMIT 2 OFFSET 10;
-- result:
-- !result
SELECT id, value FROM test_table ORDER BY id LIMIT 10, 2;
-- result:
-- !result
DROP TABLE test_table;
-- result:
-- !result
DROP DATABASE db_${uuid0};
-- result:
-- !result

View File

@ -0,0 +1,76 @@
-- name: test_limit_offset_syntax
CREATE DATABASE db_${uuid0};
-- result:
-- !result
USE db_${uuid0};
-- result:
-- !result
CREATE TABLE test_table (
id INT,
name VARCHAR(50)
) DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 1;
-- result:
-- !result
INSERT INTO test_table VALUES
(1, 'row1'),
(2, 'row2'),
(3, 'row3'),
(4, 'row4'),
(5, 'row5'),
(6, 'row6'),
(7, 'row7'),
(8, 'row8'),
(9, 'row9'),
(10, 'row10');
-- result:
-- !result
SELECT id, name FROM test_table ORDER BY id LIMIT 3 OFFSET 2;
-- result:
3 row3
4 row4
5 row5
-- !result
SELECT id, name FROM test_table ORDER BY id LIMIT 2, 3;
-- result:
3 row3
4 row4
5 row5
-- !result
SELECT id, name FROM test_table ORDER BY id LIMIT 2;
-- result:
1 row1
2 row2
-- !result
SELECT id, name FROM test_table ORDER BY id LIMIT 3 OFFSET 0;
-- result:
1 row1
2 row2
3 row3
-- !result
SELECT id, name FROM test_table ORDER BY id LIMIT 3 OFFSET 7;
-- result:
8 row8
9 row9
10 row10
-- !result
SELECT id, name FROM test_table ORDER BY id LIMIT 7, 3;
-- result:
8 row8
9 row9
10 row10
-- !result
SELECT id, name FROM test_table ORDER BY id LIMIT 1 OFFSET 9;
-- result:
10 row10
-- !result
SELECT id, name FROM test_table ORDER BY id LIMIT 9, 1;
-- result:
10 row10
-- !result
DROP TABLE test_table;
-- result:
-- !result
DROP DATABASE db_${uuid0};
-- result:
-- !result

View File

@ -0,0 +1,48 @@
-- name: test_limit_offset_edge_cases
-- Test edge cases for LIMIT OFFSET syntax
CREATE DATABASE db_${uuid0};
USE db_${uuid0};
-- Create test table
CREATE TABLE test_table (
id INT,
value VARCHAR(50)
) DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 1;
-- Insert test data
INSERT INTO test_table VALUES
(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e');
-- Test with user variables
SET @my_limit = 2;
SET @my_offset = 1;
-- Test 1: Standard syntax with user variables
SELECT id, value FROM test_table ORDER BY id LIMIT @my_limit OFFSET @my_offset;
-- Test 2: StarRocks syntax with user variables
SELECT id, value FROM test_table ORDER BY id LIMIT @my_offset, @my_limit;
-- Test 3: Mixed - literal limit, variable offset
SELECT id, value FROM test_table ORDER BY id LIMIT 3 OFFSET @my_offset;
-- Test 4: Mixed - variable limit, literal offset
SELECT id, value FROM test_table ORDER BY id LIMIT @my_limit OFFSET 3;
-- Test 5: Zero offset (standard syntax)
SELECT id, value FROM test_table ORDER BY id LIMIT 2 OFFSET 0;
-- Test 6: Zero offset (StarRocks syntax)
SELECT id, value FROM test_table ORDER BY id LIMIT 0, 2;
-- Test 7: Large offset that exceeds table size
SELECT id, value FROM test_table ORDER BY id LIMIT 2 OFFSET 10;
-- Test 8: Large offset that exceeds table size (StarRocks syntax)
SELECT id, value FROM test_table ORDER BY id LIMIT 10, 2;
-- Clean up
DROP TABLE test_table;
DROP DATABASE db_${uuid0};

View File

@ -0,0 +1,62 @@
-- name: test_limit_offset_syntax
-- Test both LIMIT syntaxes to ensure they work correctly after the fix
-- Create test database
CREATE DATABASE db_${uuid0};
USE db_${uuid0};
-- Create test table
CREATE TABLE test_table (
id INT,
name VARCHAR(50)
) DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 1;
-- Insert test data
INSERT INTO test_table VALUES
(1, 'row1'),
(2, 'row2'),
(3, 'row3'),
(4, 'row4'),
(5, 'row5'),
(6, 'row6'),
(7, 'row7'),
(8, 'row8'),
(9, 'row9'),
(10, 'row10');
-- Test 1: Standard SQL syntax - LIMIT count OFFSET offset
-- Should return rows with id 3, 4, 5 (3 rows starting from offset 2)
SELECT id, name FROM test_table ORDER BY id LIMIT 3 OFFSET 2;
-- Test 2: StarRocks syntax - LIMIT offset, count
-- Should return the same rows with id 3, 4, 5
SELECT id, name FROM test_table ORDER BY id LIMIT 2, 3;
-- Test 3: LIMIT without OFFSET (standard syntax)
-- Should return first 2 rows
SELECT id, name FROM test_table ORDER BY id LIMIT 2;
-- Test 4: LIMIT with OFFSET 0 (standard syntax)
-- Should return first 3 rows
SELECT id, name FROM test_table ORDER BY id LIMIT 3 OFFSET 0;
-- Test 5: Large offset (standard syntax)
-- Should return rows 8, 9, 10
SELECT id, name FROM test_table ORDER BY id LIMIT 3 OFFSET 7;
-- Test 6: Large offset (StarRocks syntax)
-- Should return the same rows 8, 9, 10
SELECT id, name FROM test_table ORDER BY id LIMIT 7, 3;
-- Test 7: Edge case - LIMIT 1 OFFSET 9
-- Should return only row 10
SELECT id, name FROM test_table ORDER BY id LIMIT 1 OFFSET 9;
-- Test 8: Edge case - LIMIT 9, 1 (equivalent to above)
-- Should return only row 10
SELECT id, name FROM test_table ORDER BY id LIMIT 9, 1;
-- Clean up
DROP TABLE test_table;
DROP DATABASE db_${uuid0};