Skip to content

Commit e38aa47

Browse files
authored
Fixed incorrect detecting of keywords (#65)
2 parents d046114 + 3e6f173 commit e38aa47

File tree

2 files changed

+110
-60
lines changed

2 files changed

+110
-60
lines changed

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

Lines changed: 49 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ public String parseSQL(String origin) throws SQLException {
124124
}
125125
break;
126126
default:
127+
lastKeywordIsOffsetLimit = lastKeywordIsOffsetLimit && Character.isWhitespace(ch);
128+
127129
if (keywordStart >= 0) {
128130
isInsideKeyword = Character.isJavaIdentifierPart(ch);
129131
break;
@@ -136,6 +138,7 @@ public String parseSQL(String origin) throws SQLException {
136138
break;
137139
}
138140

141+
139142
if (keywordStart >= 0 && (!isInsideKeyword || (i == chars.length - 1))) {
140143
lastKeywordIsOffsetLimit = false;
141144
int keywordLength = (isInsideKeyword ? i + 1 : keywordEnd) - keywordStart;
@@ -144,34 +147,35 @@ public String parseSQL(String origin) throws SQLException {
144147
batcher.readIdentifier(chars, keywordStart, keywordLength);
145148

146149
// Detect RETURNING keyword
147-
if (parenLevel == 0 && parseReturningKeyword(chars, keywordStart)) {
150+
if (parenLevel == 0 && parseReturningKeyword(chars, keywordStart, keywordLength)) {
148151
statement.setHasReturning(true);
149152
}
150153

151-
if (parseOffsetKeyword(chars, keywordStart) || parseLimitKeyword(chars, keywordStart)) {
152-
lastKeywordIsOffsetLimit = true;
154+
if (parseOffsetKeyword(chars, keywordStart, keywordLength)
155+
|| parseLimitKeyword(chars, keywordStart, keywordLength)) {
156+
lastKeywordIsOffsetLimit = Character.isWhitespace(ch);
153157
}
154158
} else {
155159
boolean skipped = false;
156160
if (isDetectQueryType) {
157161
// Detect scan expression - starts with SCAN
158-
if (parseScanKeyword(chars, keywordStart)) {
162+
if (parseScanKeyword(chars, keywordStart, keywordLength)) {
159163
type = QueryType.SCAN_QUERY;
160164
// Skip SCAN prefix
161165
parsed.append(chars, fragmentStart, keywordStart - fragmentStart);
162166
fragmentStart = isInsideKeyword ? keywordEnd + 1 : keywordEnd;
163167
skipped = true;
164168
}
165169
// Detect explain expression - starts with EXPLAIN
166-
if (parseExplainKeyword(chars, keywordStart)) {
170+
if (parseExplainKeyword(chars, keywordStart, keywordLength)) {
167171
type = QueryType.EXPLAIN_QUERY;
168172
// Skip EXPLAIN prefix
169173
parsed.append(chars, fragmentStart, keywordStart - fragmentStart);
170174
fragmentStart = isInsideKeyword ? keywordEnd + 1 : keywordEnd;
171175
skipped = true;
172176
}
173177
// Detect bulk upsert expression - starts with BULK
174-
if (parseBulkKeyword(chars, keywordStart)) {
178+
if (parseBulkKeyword(chars, keywordStart, keywordLength)) {
175179
type = QueryType.BULK_QUERY;
176180
// Skip BULK prefix
177181
parsed.append(chars, fragmentStart, keywordStart - fragmentStart);
@@ -185,33 +189,33 @@ public String parseSQL(String origin) throws SQLException {
185189
statement = new QueryStatement(type, QueryType.UNKNOWN, QueryCmd.UNKNOWN);
186190
// Detect data query expression - starts with SELECT, , UPSERT, DELETE, REPLACE
187191
// starts with SELECT
188-
if (parseSelectKeyword(chars, keywordStart)) {
192+
if (parseSelectKeyword(chars, keywordStart, keywordLength)) {
189193
statement = new QueryStatement(type, QueryType.DATA_QUERY, QueryCmd.SELECT);
190194
batcher.readIdentifier(chars, keywordStart, keywordLength);
191195
}
192196

193197
// starts with INSERT, UPSERT
194-
if (parseInsertKeyword(chars, keywordStart)) {
198+
if (parseInsertKeyword(chars, keywordStart, keywordLength)) {
195199
statement = new QueryStatement(type, QueryType.DATA_QUERY, QueryCmd.INSERT_UPSERT);
196200
batcher.readInsert();
197201
}
198-
if (parseUpsertKeyword(chars, keywordStart)) {
202+
if (parseUpsertKeyword(chars, keywordStart, keywordLength)) {
199203
statement = new QueryStatement(type, QueryType.DATA_QUERY, QueryCmd.INSERT_UPSERT);
200204
batcher.readUpsert();
201205
}
202206

203207
// starts with UPDATE, REPLACE, DELETE
204-
if (parseUpdateKeyword(chars, keywordStart)
205-
|| parseDeleteKeyword(chars, keywordStart)
206-
|| parseReplaceKeyword(chars, keywordStart)) {
208+
if (parseUpdateKeyword(chars, keywordStart, keywordLength)
209+
|| parseDeleteKeyword(chars, keywordStart, keywordLength)
210+
|| parseReplaceKeyword(chars, keywordStart, keywordLength)) {
207211
statement = new QueryStatement(type, QueryType.DATA_QUERY, QueryCmd.UPDATE_REPLACE_DELETE);
208212
batcher.readIdentifier(chars, keywordStart, keywordLength);
209213
}
210214

211215
// Detect scheme expression - starts with ALTER, DROP, CREATE
212-
if (parseAlterKeyword(chars, keywordStart)
213-
|| parseCreateKeyword(chars, keywordStart)
214-
|| parseDropKeyword(chars, keywordStart)) {
216+
if (parseAlterKeyword(chars, keywordStart, keywordLength)
217+
|| parseCreateKeyword(chars, keywordStart, keywordLength)
218+
|| parseDropKeyword(chars, keywordStart, keywordLength)) {
215219
statement = new QueryStatement(type, QueryType.SCHEME_QUERY, QueryCmd.CREATE_ALTER_DROP);
216220
batcher.readIdentifier(chars, keywordStart, keywordLength);
217221
}
@@ -349,8 +353,8 @@ private static int parseBlockComment(final char[] query, int offset) {
349353
return offset;
350354
}
351355

352-
private static boolean parseAlterKeyword(char[] query, int offset) {
353-
if (query.length < (offset + 5)) {
356+
private static boolean parseAlterKeyword(char[] query, int offset, int length) {
357+
if (length != 5) {
354358
return false;
355359
}
356360

@@ -361,8 +365,8 @@ private static boolean parseAlterKeyword(char[] query, int offset) {
361365
&& (query[offset + 4] | 32) == 'r';
362366
}
363367

364-
private static boolean parseCreateKeyword(char[] query, int offset) {
365-
if (query.length < (offset + 6)) {
368+
private static boolean parseCreateKeyword(char[] query, int offset, int length) {
369+
if (length != 6) {
366370
return false;
367371
}
368372

@@ -374,8 +378,8 @@ private static boolean parseCreateKeyword(char[] query, int offset) {
374378
&& (query[offset + 5] | 32) == 'e';
375379
}
376380

377-
private static boolean parseDropKeyword(char[] query, int offset) {
378-
if (query.length < (offset + 4)) {
381+
private static boolean parseDropKeyword(char[] query, int offset, int length) {
382+
if (length != 4) {
379383
return false;
380384
}
381385

@@ -385,8 +389,8 @@ private static boolean parseDropKeyword(char[] query, int offset) {
385389
&& (query[offset + 3] | 32) == 'p';
386390
}
387391

388-
private static boolean parseScanKeyword(char[] query, int offset) {
389-
if (query.length < (offset + 4)) {
392+
private static boolean parseScanKeyword(char[] query, int offset, int length) {
393+
if (length != 4) {
390394
return false;
391395
}
392396

@@ -396,8 +400,8 @@ private static boolean parseScanKeyword(char[] query, int offset) {
396400
&& (query[offset + 3] | 32) == 'n';
397401
}
398402

399-
private static boolean parseBulkKeyword(char[] query, int offset) {
400-
if (query.length < (offset + 4)) {
403+
private static boolean parseBulkKeyword(char[] query, int offset, int length) {
404+
if (length != 4) {
401405
return false;
402406
}
403407

@@ -407,8 +411,8 @@ private static boolean parseBulkKeyword(char[] query, int offset) {
407411
&& (query[offset + 3] | 32) == 'k';
408412
}
409413

410-
private static boolean parseExplainKeyword(char[] query, int offset) {
411-
if (query.length < (offset + 7)) {
414+
private static boolean parseExplainKeyword(char[] query, int offset, int length) {
415+
if (length != 7) {
412416
return false;
413417
}
414418

@@ -421,8 +425,8 @@ private static boolean parseExplainKeyword(char[] query, int offset) {
421425
&& (query[offset + 6] | 32) == 'n';
422426
}
423427

424-
private static boolean parseSelectKeyword(char[] query, int offset) {
425-
if (query.length < (offset + 6)) {
428+
private static boolean parseSelectKeyword(char[] query, int offset, int length) {
429+
if (length != 6) {
426430
return false;
427431
}
428432

@@ -434,8 +438,8 @@ private static boolean parseSelectKeyword(char[] query, int offset) {
434438
&& (query[offset + 5] | 32) == 't';
435439
}
436440

437-
private static boolean parseUpdateKeyword(char[] query, int offset) {
438-
if (query.length < (offset + 6)) {
441+
private static boolean parseUpdateKeyword(char[] query, int offset, int length) {
442+
if (length != 6) {
439443
return false;
440444
}
441445

@@ -447,8 +451,8 @@ private static boolean parseUpdateKeyword(char[] query, int offset) {
447451
&& (query[offset + 5] | 32) == 'e';
448452
}
449453

450-
private static boolean parseUpsertKeyword(char[] query, int offset) {
451-
if (query.length < (offset + 6)) {
454+
private static boolean parseUpsertKeyword(char[] query, int offset, int length) {
455+
if (length != 6) {
452456
return false;
453457
}
454458

@@ -460,8 +464,8 @@ private static boolean parseUpsertKeyword(char[] query, int offset) {
460464
&& (query[offset + 5] | 32) == 't';
461465
}
462466

463-
private static boolean parseInsertKeyword(char[] query, int offset) {
464-
if (query.length < (offset + 6)) {
467+
private static boolean parseInsertKeyword(char[] query, int offset, int length) {
468+
if (length != 6) {
465469
return false;
466470
}
467471

@@ -473,8 +477,8 @@ private static boolean parseInsertKeyword(char[] query, int offset) {
473477
&& (query[offset + 5] | 32) == 't';
474478
}
475479

476-
private static boolean parseDeleteKeyword(char[] query, int offset) {
477-
if (query.length < (offset + 6)) {
480+
private static boolean parseDeleteKeyword(char[] query, int offset, int length) {
481+
if (length != 6) {
478482
return false;
479483
}
480484

@@ -486,8 +490,8 @@ private static boolean parseDeleteKeyword(char[] query, int offset) {
486490
&& (query[offset + 5] | 32) == 'e';
487491
}
488492

489-
private static boolean parseReplaceKeyword(char[] query, int offset) {
490-
if (query.length < (offset + 7)) {
493+
private static boolean parseReplaceKeyword(char[] query, int offset, int length) {
494+
if (length != 7) {
491495
return false;
492496
}
493497

@@ -500,8 +504,8 @@ private static boolean parseReplaceKeyword(char[] query, int offset) {
500504
&& (query[offset + 6] | 32) == 'e';
501505
}
502506

503-
private static boolean parseReturningKeyword(char[] query, int offset) {
504-
if (query.length < (offset + 9)) {
507+
private static boolean parseReturningKeyword(char[] query, int offset, int length) {
508+
if (length != 9) {
505509
return false;
506510
}
507511

@@ -516,8 +520,8 @@ private static boolean parseReturningKeyword(char[] query, int offset) {
516520
&& (query[offset + 8] | 32) == 'g';
517521
}
518522

519-
private static boolean parseOffsetKeyword(char[] query, int offset) {
520-
if (query.length < (offset + 6)) {
523+
private static boolean parseOffsetKeyword(char[] query, int offset, int length) {
524+
if (length != 6) {
521525
return false;
522526
}
523527

@@ -529,8 +533,8 @@ private static boolean parseOffsetKeyword(char[] query, int offset) {
529533
&& (query[offset + 5] | 32) == 't';
530534
}
531535

532-
private static boolean parseLimitKeyword(char[] query, int offset) {
533-
if (query.length < (offset + 5)) {
536+
private static boolean parseLimitKeyword(char[] query, int offset, int length) {
537+
if (length != 5) {
534538
return false;
535539
}
536540

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

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,26 @@ public void schemeQueryTest(String sql) throws SQLException {
7171
Assertions.assertEquals(QueryCmd.CREATE_ALTER_DROP, statement.getCmd());
7272
}
7373

74+
@ParameterizedTest(name = "[{index}] {0} is data query")
75+
@ValueSource(strings = {
76+
"ALTERED;",
77+
"SCANER SELECT 1;",
78+
"bulked select 1;",
79+
"\ndrops;",
80+
"BuLK_INSERT;",
81+
})
82+
public void unknownQueryTest(String sql) throws SQLException {
83+
YdbQueryParser parser = new YdbQueryParser(true, true);
84+
String parsed = parser.parseSQL(sql);
85+
86+
Assertions.assertEquals(sql, parsed);
87+
88+
Assertions.assertEquals(1, parser.getStatements().size());
89+
QueryStatement statement = parser.getStatements().get(0);
90+
Assertions.assertEquals(QueryType.UNKNOWN, statement.getType());
91+
Assertions.assertEquals(QueryCmd.UNKNOWN, statement.getCmd());
92+
}
93+
7494
@Test
7595
public void wrongSqlCommandTest() throws SQLException {
7696
String query = "SC;";
@@ -134,29 +154,55 @@ public void scanSelectWithKeyTest() throws SQLException {
134154
Assertions.assertEquals(QueryType.SCAN_QUERY, parser.detectQueryType());
135155
}
136156

137-
@Test
138-
public void offsetParameterTest() throws SQLException {
139-
String query = ""
140-
+ "select * from test_table where true=true -- test request\n"
141-
+ " offset /* comment */ ? limit 20";
157+
@ParameterizedTest(name = "[{index}] {0} has offset or limit parameter")
158+
@ValueSource(strings = {
159+
"select * from test_table where true=true -- test request\noffset /* comment */ ? limit 20",
160+
"select * from test_table where true=true /*comm*/offset ?\tlimit\t\n?;",
161+
"select offset, limit from test_table offset 20 limit -- comment\n?;",
162+
})
163+
public void offsetParameterTest(String query) throws SQLException {
164+
YdbQueryParser parser = new YdbQueryParser(true, true);
165+
parser.parseSQL(query);
142166

167+
Assertions.assertEquals(1, parser.getStatements().size());
168+
169+
QueryStatement statement = parser.getStatements().get(0);
170+
Assertions.assertEquals(QueryType.DATA_QUERY, statement.getType());
171+
172+
Assertions.assertFalse(statement.getParams().isEmpty());
173+
int idx = 0;
174+
for (ParamDescription prm : statement.getParams()) {
175+
idx++;
176+
Assertions.assertEquals("$jp" + idx, prm.name());
177+
Assertions.assertNotNull(prm.type()); // forced UInt64 type
178+
Assertions.assertEquals(PrimitiveType.Uint64, prm.type().ydbType()); // forced UInt64 type
179+
}
180+
}
181+
182+
@ParameterizedTest(name = "[{index}] {0} hasn't offset or limit parameter")
183+
@ValueSource(strings = {
184+
"select * from test_table where limit = ? or offset = ?",
185+
"update test_table set limit = ?, offset = ? where id = ?",
186+
"select * from test_table where limit=? or offset=?",
187+
"update test_table set limit=?, offset=? where id=?",
188+
"select * from test_table where limit? or offset?",
189+
})
190+
public void noOffsetParameterTest(String query) throws SQLException {
143191
YdbQueryParser parser = new YdbQueryParser(true, true);
144-
String parsed = parser.parseSQL(query);
145-
Assertions.assertEquals(""
146-
+ "select * from test_table where true=true -- test request\n"
147-
+ " offset /* comment */ $jp1 limit 20",
148-
parsed);
192+
parser.parseSQL(query);
149193

150194
Assertions.assertEquals(1, parser.getStatements().size());
151195

152196
QueryStatement statement = parser.getStatements().get(0);
153197
Assertions.assertEquals(QueryType.DATA_QUERY, statement.getType());
154-
Assertions.assertEquals(1, statement.getParams().size());
155198

156-
ParamDescription prm1 = statement.getParams().get(0);
157-
Assertions.assertEquals("$jp1", prm1.name());
158-
Assertions.assertNotNull(prm1.type());
159-
Assertions.assertEquals(PrimitiveType.Uint64, prm1.type().ydbType());
199+
Assertions.assertFalse(statement.getParams().isEmpty());
200+
int idx = 0;
201+
for (ParamDescription prm : statement.getParams()) {
202+
idx++;
203+
Assertions.assertEquals("$jp" + idx, prm.name());
204+
Assertions.assertNull(prm.type()); // uknown type
205+
}
160206
}
161207

162208
@ParameterizedTest(name = "[{index}] {0} is batched insert query")

0 commit comments

Comments
 (0)