Skip to content

Commit 237ec14

Browse files
author
Vinayaga Sundar
committed
Issue amitshekhariitbhu#47 fix : now you can use sqlite keyword in table name
1 parent dae4257 commit 237ec14

File tree

3 files changed

+290
-1
lines changed

3 files changed

+290
-1
lines changed

app/src/main/java/com/sample/database/CarDBHelper.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ public void onCreate(SQLiteDatabase db) {
5252
"create table cars " +
5353
"(id integer primary key, name text, color text, mileage real)"
5454
);
55+
56+
db.execSQL("create table [transaction] (id integer primary key, name text)");
57+
58+
for (int i = 0; i < 10; i++) {
59+
db.execSQL("insert into [transaction] (name) values ('hello');");
60+
}
5561
}
5662

5763
@Override

debug-db/src/main/java/com/amitshekhar/utils/DatabaseHelper.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,16 @@
2222
import android.content.ContentValues;
2323
import android.database.Cursor;
2424
import android.database.sqlite.SQLiteDatabase;
25+
import android.text.TextUtils;
26+
import android.util.Log;
2527

2628
import com.amitshekhar.model.Response;
2729
import com.amitshekhar.model.RowDataRequest;
2830
import com.amitshekhar.model.TableDataResponse;
2931
import com.amitshekhar.model.UpdateRowResponse;
3032

3133
import java.util.ArrayList;
34+
import java.util.HashSet;
3235
import java.util.List;
3336

3437
/**
@@ -88,6 +91,11 @@ public static TableDataResponse getTableData(SQLiteDatabase db, String selectQue
8891
}
8992
tableData.isEditable = tableName != null && tableData.tableInfos != null && !isView;
9093

94+
95+
if (!TextUtils.isEmpty(tableName)) {
96+
selectQuery = selectQuery.replace(tableName, "[" + tableName + "]");
97+
}
98+
9199
try {
92100
cursor = db.rawQuery(selectQuery, null);
93101
} catch (Exception e) {
@@ -357,7 +365,16 @@ public static TableDataResponse exec(SQLiteDatabase database, String sql) {
357365
}
358366

359367
private static String getTableName(String selectQuery) {
360-
// TODO find tableName from query and also handle JOIN query
368+
// TODO: 24/4/17 Handle JOIN Query
369+
TableNameParser tableNameParser = new TableNameParser(selectQuery);
370+
HashSet<String> tableName = (HashSet<String>) tableNameParser.tables();
371+
372+
for (String table : tableName) {
373+
if (!TextUtils.isEmpty(table)) {
374+
return table;
375+
}
376+
}
377+
361378
return null;
362379
}
363380

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
/*
2+
*
3+
* * Copyright (C) 2016 Amit Shekhar
4+
* * Copyright (C) 2011 Android Open Source Project
5+
* *
6+
* * Licensed under the Apache License, Version 2.0 (the "License");
7+
* * you may not use this file except in compliance with the License.
8+
* * You may obtain a copy of the License at
9+
* *
10+
* * http://www.apache.org/licenses/LICENSE-2.0
11+
* *
12+
* * Unless required by applicable law or agreed to in writing, software
13+
* * distributed under the License is distributed on an "AS IS" BASIS,
14+
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* * See the License for the specific language governing permissions and
16+
* * limitations under the License.
17+
*
18+
*/
19+
package com.amitshekhar.utils;
20+
21+
import java.util.Arrays;
22+
import java.util.Collection;
23+
import java.util.HashMap;
24+
import java.util.HashSet;
25+
import java.util.List;
26+
import java.util.Map;
27+
import java.util.regex.Matcher;
28+
import java.util.regex.Pattern;
29+
30+
/**
31+
* Ultra light, Ultra fast parser to extract table name out SQLs, supports oracle dialect SQLs as well.
32+
*
33+
* @author Nadeem Mohammad
34+
* <p>
35+
* Ref : https://github.com/mnadeem/sql-table-name-parser
36+
*/
37+
public final class TableNameParser {
38+
39+
private static final int NO_INDEX = -1;
40+
private static final String SPACE = " ";
41+
private static final String REGEX_SPACE = "\\s+";
42+
43+
private static final String TOKEN_ORACLE_HINT_START = "/*+";
44+
private static final String TOKEN_ORACLE_HINT_END = "*/";
45+
private static final String TOKEN_SINGLE_LINE_COMMENT = "--";
46+
private static final String TOKEN_SEMI_COLON = ";";
47+
private static final String TOKEN_PARAN_START = "(";
48+
private static final String TOKEN_COMMA = ",";
49+
private static final String TOKEN_SET = "set";
50+
private static final String TOKEN_OF = "of";
51+
private static final String TOKEN_DUAL = "dual";
52+
private static final String TOKEN_DELETE = "delete";
53+
private static final String TOKEN_CREATE = "create";
54+
private static final String TOKEN_INDEX = "index";
55+
private static final String TOKEN_ASTERICK = "*";
56+
private static final String KEYWORD_JOIN = "join";
57+
private static final String KEYWORD_INTO = "into";
58+
private static final String KEYWORD_TABLE = "table";
59+
private static final String KEYWORD_FROM = "from";
60+
private static final String KEYWORD_USING = "using";
61+
private static final String KEYWORD_UPDATE = "update";
62+
private static final List<String> concerned = Arrays.asList(KEYWORD_TABLE, KEYWORD_INTO, KEYWORD_JOIN, KEYWORD_USING, KEYWORD_UPDATE);
63+
private static final List<String> ignored = Arrays.asList(TOKEN_PARAN_START, TOKEN_SET, TOKEN_OF, TOKEN_DUAL);
64+
private static String TOKEN_NEWLINE = "\\r\\n|\\r|\\n|\\n\\r";
65+
private Map<String, String> tables = new HashMap<String, String>();
66+
67+
/**
68+
* Extracts table names out of SQL
69+
*
70+
* @param sql
71+
*/
72+
public TableNameParser(final String sql) {
73+
String nocomments = removeComments(sql);
74+
String normalized = normalized(nocomments);
75+
String cleansed = clean(normalized);
76+
String[] tokens = cleansed.split(REGEX_SPACE);
77+
int index = 0;
78+
79+
String firstToken = tokens[index];
80+
if (isOracleSpecialDelete(firstToken, tokens, index)) {
81+
handleSpecialOracleSpecialDelete(firstToken, tokens, index);
82+
} else if (isCreateIndex(firstToken, tokens, index)) {
83+
handleCreateIndex(firstToken, tokens, index);
84+
} else {
85+
while (moreTokens(tokens, index)) {
86+
String currentToken = tokens[index++];
87+
88+
if (isFromToken(currentToken)) {
89+
processFromToken(tokens, index);
90+
} else if (shouldProcess(currentToken)) {
91+
String nextToken = tokens[index++];
92+
considerInclusion(nextToken);
93+
94+
if (moreTokens(tokens, index)) {
95+
nextToken = tokens[index++];
96+
}
97+
}
98+
}
99+
}
100+
}
101+
102+
private String removeComments(final String sql) {
103+
StringBuilder sb = new StringBuilder(sql);
104+
int nextCommentPosition = sb.indexOf(TOKEN_SINGLE_LINE_COMMENT);
105+
while (nextCommentPosition > -1) {
106+
int end = indexOfRegex(TOKEN_NEWLINE, sb.substring(nextCommentPosition));
107+
if (end == -1) {
108+
return sb.substring(0, nextCommentPosition);
109+
} else {
110+
sb.replace(nextCommentPosition, end + nextCommentPosition, "");
111+
}
112+
nextCommentPosition = sb.indexOf(TOKEN_SINGLE_LINE_COMMENT);
113+
}
114+
return sb.toString();
115+
}
116+
117+
private int indexOfRegex(String regex, String string) {
118+
Pattern pattern = Pattern.compile(regex);
119+
Matcher matcher = pattern.matcher(string);
120+
return matcher.find() ? matcher.start() : -1;
121+
}
122+
123+
private String normalized(final String sql) {
124+
String normalized = sql.trim().replaceAll(TOKEN_NEWLINE, SPACE).replaceAll(TOKEN_COMMA, " , ")
125+
.replaceAll("\\(", " ( ").replaceAll("\\)", " ) ");
126+
if (normalized.endsWith(TOKEN_SEMI_COLON)) {
127+
normalized = normalized.substring(0, normalized.length() - 1);
128+
}
129+
return normalized;
130+
}
131+
132+
private String clean(final String normalized) {
133+
int start = normalized.indexOf(TOKEN_ORACLE_HINT_START);
134+
int end = NO_INDEX;
135+
if (start != NO_INDEX) {
136+
end = normalized.indexOf(TOKEN_ORACLE_HINT_END);
137+
if (end != NO_INDEX) {
138+
String firstHalf = normalized.substring(0, start);
139+
String secondHalf = normalized.substring(end + 2, normalized.length());
140+
return firstHalf.trim() + SPACE + secondHalf.trim();
141+
}
142+
}
143+
return normalized;
144+
}
145+
146+
private boolean isOracleSpecialDelete(final String currentToken, final String[] tokens, int index) {
147+
index++;// Point to next token
148+
if (TOKEN_DELETE.equals(currentToken)) {
149+
if (moreTokens(tokens, index)) {
150+
String nextToken = tokens[index++];
151+
if (!KEYWORD_FROM.equals(nextToken) && !TOKEN_ASTERICK.equals(nextToken)) {
152+
return true;
153+
}
154+
}
155+
}
156+
return false;
157+
}
158+
159+
private void handleSpecialOracleSpecialDelete(final String currentToken, final String[] tokens, int index) {
160+
String tableName = tokens[index + 1];
161+
considerInclusion(tableName);
162+
}
163+
164+
private boolean isCreateIndex(String currentToken, String[] tokens, int index) {
165+
index++; // Point to next token
166+
if (TOKEN_CREATE.equals(currentToken.toLowerCase()) && hasIthToken(tokens, index, 3)) {
167+
String nextToken = tokens[index++];
168+
if (TOKEN_INDEX.equals(nextToken.toLowerCase())) {
169+
return true;
170+
}
171+
172+
}
173+
return false;
174+
}
175+
176+
private void handleCreateIndex(String currentToken, String[] tokens, int index) {
177+
String tableName = tokens[index + 4];
178+
considerInclusion(tableName);
179+
}
180+
181+
private boolean hasIthToken(String[] tokens, int currentIndex, int tokenNumber) {
182+
if (moreTokens(tokens, currentIndex) && tokens.length > currentIndex + tokenNumber) {
183+
return true;
184+
}
185+
return false;
186+
}
187+
188+
private boolean shouldProcess(final String currentToken) {
189+
return concerned.contains(currentToken.toLowerCase());
190+
}
191+
192+
private boolean isFromToken(final String currentToken) {
193+
return KEYWORD_FROM.equals(currentToken.toLowerCase());
194+
}
195+
196+
private void processFromToken(final String[] tokens, int index) {
197+
String currentToken = tokens[index++];
198+
considerInclusion(currentToken);
199+
200+
String nextToken = null;
201+
if (moreTokens(tokens, index)) {
202+
nextToken = tokens[index++];
203+
}
204+
205+
if (shouldProcessMultipleTables(nextToken)) {
206+
processNonAliasedMultiTables(tokens, index, nextToken);
207+
} else {
208+
processAliasedMultiTables(tokens, index, currentToken);
209+
}
210+
}
211+
212+
private void processNonAliasedMultiTables(final String[] tokens, int index, String nextToken) {
213+
while (nextToken.equals(TOKEN_COMMA)) {
214+
String currentToken = tokens[index++];
215+
considerInclusion(currentToken);
216+
if (moreTokens(tokens, index)) {
217+
nextToken = tokens[index++];
218+
} else {
219+
break;
220+
}
221+
}
222+
}
223+
224+
private void processAliasedMultiTables(final String[] tokens, int index, String currentToken) {
225+
String nextNextToken = null;
226+
if (moreTokens(tokens, index)) {
227+
nextNextToken = tokens[index++];
228+
}
229+
230+
if (shouldProcessMultipleTables(nextNextToken)) {
231+
while (moreTokens(tokens, index) && nextNextToken.equals(TOKEN_COMMA)) {
232+
if (moreTokens(tokens, index)) {
233+
currentToken = tokens[index++];
234+
}
235+
if (moreTokens(tokens, index)) {
236+
index++;
237+
}
238+
if (moreTokens(tokens, index)) {
239+
nextNextToken = tokens[index++];
240+
}
241+
considerInclusion(currentToken);
242+
}
243+
}
244+
}
245+
246+
private boolean shouldProcessMultipleTables(final String nextToken) {
247+
return nextToken != null && nextToken.equals(TOKEN_COMMA);
248+
}
249+
250+
private boolean moreTokens(final String[] tokens, int index) {
251+
return index < tokens.length;
252+
}
253+
254+
private void considerInclusion(final String token) {
255+
if (!ignored.contains(token.toLowerCase()) && !this.tables.containsKey(token.toLowerCase())) {
256+
this.tables.put(token.toLowerCase(), token);
257+
}
258+
}
259+
260+
/**
261+
* @return table names extracted out of sql
262+
*/
263+
public Collection<String> tables() {
264+
return new HashSet<String>(this.tables.values());
265+
}
266+
}

0 commit comments

Comments
 (0)