Skip to content

Commit 92cc3ce

Browse files
committed
Added forced UInt64 type for OFFSET & LIMIT
1 parent d2fc35c commit 92cc3ce

File tree

4 files changed

+104
-2
lines changed

4 files changed

+104
-2
lines changed

jdbc/src/main/java/tech/ydb/jdbc/query/YdbQueryParser.java

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import java.util.List;
88

99
import tech.ydb.jdbc.YdbConst;
10+
import tech.ydb.jdbc.common.TypeDescription;
11+
import tech.ydb.table.values.PrimitiveType;
1012

1113

1214
/**
@@ -48,6 +50,7 @@ public QueryType detectQueryType() throws SQLException {
4850
return type != null ? type : QueryType.DATA_QUERY;
4951
}
5052

53+
@SuppressWarnings("MethodLength")
5154
public String parseSQL(String origin) throws SQLException {
5255
this.statements.clear();
5356

@@ -59,6 +62,7 @@ public String parseSQL(String origin) throws SQLException {
5962

6063
int parenLevel = 0;
6164
int keywordStart = -1;
65+
boolean lastKeywordIsOffsetLimit = false;
6266

6367
char[] chars = origin.toCharArray();
6468

@@ -100,7 +104,11 @@ public String parseSQL(String origin) throws SQLException {
100104
i++; // make sure the coming ? is not treated as a bind
101105
} else {
102106
String binded = argNameGenerator.createArgName(origin);
103-
currStatement.addParameter(binded, null);
107+
// force type UInt64 for OFFSET and LIMIT parameters
108+
TypeDescription type = lastKeywordIsOffsetLimit
109+
? TypeDescription.of(PrimitiveType.Uint64)
110+
: null;
111+
currStatement.addParameter(binded, type);
104112
parsed.append(binded);
105113
}
106114
fragmentStart = i + 1;
@@ -120,12 +128,17 @@ public String parseSQL(String origin) throws SQLException {
120128
}
121129

122130
if (keywordStart >= 0 && (!isInsideKeyword || (i == chars.length - 1))) {
131+
lastKeywordIsOffsetLimit = false;
123132

124133
if (currStatement != null) {
125134
// Detect RETURNING keyword
126135
if (parenLevel == 0 && parseReturningKeyword(chars, keywordStart)) {
127136
currStatement.setHasReturning(true);
128137
}
138+
139+
if (parseOffsetKeyword(chars, keywordStart) || parseLimitKeyword(chars, keywordStart)) {
140+
lastKeywordIsOffsetLimit = true;
141+
}
129142
} else {
130143
// Detecting type of statement by the first keyword
131144
currStatement = new QueryStatement(QueryType.UNKNOWN, QueryCmd.UNKNOWN);
@@ -179,7 +192,6 @@ public String parseSQL(String origin) throws SQLException {
179192
|| parseDropKeyword(chars, i)) {
180193
currStatement = new QueryStatement(QueryType.SCHEME_QUERY, QueryCmd.CREATE_ALTER_DROP);
181194
statements.add(currStatement);
182-
break;
183195
}
184196
}
185197

@@ -441,4 +453,29 @@ private static boolean parseReturningKeyword(char[] query, int offset) {
441453
&& (query[offset + 7] | 32) == 'n'
442454
&& (query[offset + 8] | 32) == 'g';
443455
}
456+
457+
private static boolean parseOffsetKeyword(char[] query, int offset) {
458+
if (query.length < (offset + 6)) {
459+
return false;
460+
}
461+
462+
return (query[offset] | 32) == 'o'
463+
&& (query[offset + 1] | 32) == 'f'
464+
&& (query[offset + 2] | 32) == 'f'
465+
&& (query[offset + 3] | 32) == 's'
466+
&& (query[offset + 4] | 32) == 'e'
467+
&& (query[offset + 5] | 32) == 't';
468+
}
469+
470+
private static boolean parseLimitKeyword(char[] query, int offset) {
471+
if (query.length < (offset + 5)) {
472+
return false;
473+
}
474+
475+
return (query[offset] | 32) == 'l'
476+
&& (query[offset + 1] | 32) == 'i'
477+
&& (query[offset + 2] | 32) == 'm'
478+
&& (query[offset + 3] | 32) == 'i'
479+
&& (query[offset + 4] | 32) == 't';
480+
}
444481
}

jdbc/src/test/java/tech/ydb/jdbc/impl/YdbConnectionImplTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,25 @@ public void testReturingStatements() throws SQLException {
779779
}
780780
}
781781

782+
@Test
783+
public void testOffsetLimit() throws SQLException {
784+
String query = QUERIES.withTableName("SELECT * FROM #tableName ORDER BY key LIMIT ? OFFSET ?");
785+
786+
try (PreparedStatement ps = jdbc.connection().prepareStatement(query)) {
787+
ps.setInt(1, 0);
788+
ps.setInt(2, 0);
789+
try (ResultSet rs = ps.executeQuery()) {
790+
Assertions.assertFalse(rs.next());
791+
}
792+
793+
ps.setLong(1, 5);
794+
ps.setLong(2, 5);
795+
try (ResultSet rs = ps.executeQuery()) {
796+
Assertions.assertFalse(rs.next());
797+
}
798+
}
799+
}
800+
782801
@Test
783802
public void testWarningsInQuery() throws SQLException {
784803
String createTempTable = QUERIES.withTableName(

jdbc/src/test/java/tech/ydb/jdbc/impl/YdbQueryConnectionImplTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,25 @@ public void testReturingStatements() throws SQLException {
782782
}
783783
}
784784

785+
@Test
786+
public void testOffsetLimit() throws SQLException {
787+
String query = QUERIES.withTableName("SELECT * FROM #tableName ORDER BY key LIMIT ? OFFSET ?");
788+
789+
try (PreparedStatement ps = jdbc.connection().prepareStatement(query)) {
790+
ps.setInt(1, 0);
791+
ps.setInt(2, 0);
792+
try (ResultSet rs = ps.executeQuery()) {
793+
Assertions.assertFalse(rs.next());
794+
}
795+
796+
ps.setLong(1, 5);
797+
ps.setLong(2, 5);
798+
try (ResultSet rs = ps.executeQuery()) {
799+
Assertions.assertFalse(rs.next());
800+
}
801+
}
802+
}
803+
785804
@Test
786805
public void testWarningsInQuery() throws SQLException {
787806
String createTempTable = QUERIES.withTableName(

jdbc/src/test/java/tech/ydb/jdbc/query/YdbQueryParserTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import org.junit.jupiter.params.ParameterizedTest;
88
import org.junit.jupiter.params.provider.CsvSource;
99

10+
import tech.ydb.table.values.PrimitiveType;
11+
1012

1113
/**
1214
*
@@ -84,4 +86,29 @@ public void scanSelectWithKeyTest() throws SQLException {
8486

8587
Assertions.assertEquals(QueryType.SCAN_QUERY, parser.detectQueryType());
8688
}
89+
90+
@Test
91+
public void offsetParameterTest() throws SQLException {
92+
String query = ""
93+
+ "select * from test_table where true=true -- test request\n"
94+
+ " offset /* comment */ ? limit 20";
95+
96+
YdbQueryParser parser = new YdbQueryParser(true, true);
97+
String parsed = parser.parseSQL(query);
98+
Assertions.assertEquals(""
99+
+ "select * from test_table where true=true -- test request\n"
100+
+ " offset /* comment */ $jp1 limit 20",
101+
parsed);
102+
103+
Assertions.assertEquals(1, parser.getStatements().size());
104+
105+
QueryStatement statement = parser.getStatements().get(0);
106+
Assertions.assertEquals(QueryType.DATA_QUERY, statement.getType());
107+
Assertions.assertEquals(1, statement.getParams().size());
108+
109+
ParamDescription prm1 = statement.getParams().get(0);
110+
Assertions.assertEquals("$jp1", prm1.name());
111+
Assertions.assertNotNull(prm1.type());
112+
Assertions.assertEquals(PrimitiveType.Uint64, prm1.type().ydbType());
113+
}
87114
}

0 commit comments

Comments
 (0)