package com.planet_ink.fakedb;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.planet_ink.fakedb.Backend.ComparableValue;
import com.planet_ink.fakedb.Backend.FakeTable;
/*
Copyright 2001 Thomas Neumann
Copyright 2004-2019 Bo Zimmerman
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
public class Statement implements java.sql.Statement
{
protected ResultSet myResultSet = null;
protected boolean closeStatementOnResultSetClose = false;
static protected void log(final String x)
{
System.err.println("Statement: " + x);
}
protected Connection connection;
public String lastSQL = "null";
Statement(final Connection c)
{
connection = c;
}
@Override
public java.sql.Connection getConnection()
{
return connection;
}
public Connection getFakeConnection()
{
return connection;
}
protected String split(String sql, final String[] token)
{
while (true)
{
if (sql.length() == 0)
{
token[0] = "";
return "";
}
if (sql.charAt(0) == ' ')
{
sql = sql.substring(1);
continue;
}
int index;
for (index = 0; index < sql.length(); index++)
{
char c = sql.charAt(index);
if (c == ' ')
{
break;
}
else
if (c == '\'')
{
for (++index; index < sql.length(); index++)
{
c = sql.charAt(index);
if (c == '\\')
index++;
else
if (c == '\'')
break;
}
}
}
if (index >= sql.length())
{
token[0] = sql;
return "";
}
token[0] = sql.substring(0, index);
return sql.substring(index + 1);
}
}
protected String splitColumns(final String sql, final List<String> cols)
{
int s = 0;
while ((sql.length() > 0) && (s < sql.length()))
{
if ((s < sql.length()) && ((sql.charAt(s) == ' ') || (sql.charAt(s) == '\t')))
s++;
if (s >= sql.length())
return "";
int e = s;
while ((e < sql.length()) && (sql.charAt(e) != ' ') && (sql.charAt(e) != '\t') && (sql.charAt(e) != ','))
e++;
if (e >= sql.length()) // was whatever it was the last word.. done
return sql.substring(s);
final String word = sql.substring(s, e);
cols.add(word);
if (sql.charAt(e) != ',')
{
while ((e < sql.length()) && ((sql.charAt(e) == ' ') || (sql.charAt(e) == '\t')))
e++;
}
if ((e >= sql.length()) || (sql.charAt(e) != ','))
return sql.substring(e);
while (sql.charAt(e) == ',')
e++;
s = e;
}
return "";
}
@Override
public boolean isClosed() throws SQLException
{
return connection.isClosed();
}
@Override
public void setPoolable(final boolean isPoolable)
{
}
@Override
public boolean isPoolable() throws SQLException
{
return false;
}
@Override
public boolean isWrapperFor(final Class<?> arg0) throws SQLException
{
return false;
}
@Override
public <T> T unwrap(final Class<T> arg0) throws SQLException
{
return null;
}
public String parseWhereClause(final String tableName, final String sql, List<Backend.FakeCondition> conditions) throws java.sql.SQLException
{
int s = 0;
final String eow1 = " \t!=><";
final java.util.Stack<List<Backend.FakeCondition>> parenStack = new java.util.Stack<List<Backend.FakeCondition>>();
while (s < sql.length())
{
while ((s < sql.length()) && (sql.charAt(s) == ' ' || sql.charAt(s) == '\t'))
s++;
if (s >= sql.length())
break;
Backend.FakeCondition condition = null;
if (sql.charAt(s) == '(')
{
condition = connection.getBackend().buildFakeCondition(tableName, null, null, null, false);
conditions.add(condition);
parenStack.push(conditions);
condition.contains = new ArrayList<Backend.FakeCondition>();
conditions = condition.contains;
s++;
continue;
}
else
if (sql.charAt(s) == ')')
{
if (parenStack.size() == 0)
throw new java.sql.SQLException("Unexpected end parenthesis " + sql);
conditions = parenStack.pop();
condition = conditions.get(conditions.size() - 1);
s++;
}
else
{
int e = s;
boolean unPrepared = false;
while ((e < sql.length()) && (eow1.indexOf(sql.charAt(e)) < 0))
e++;
final String columnName = sql.substring(s, e);
if (e >= sql.length())
throw new java.sql.SQLException("Unexpected end of where clause in " + sql);
s = e;
while ((s < sql.length()) && (sql.charAt(s) == ' ' || sql.charAt(s) == '\t'))
s++;
e = s;
String comparitor;
if ((e < sql.length() - 5)
&& (Character.toLowerCase(sql.charAt(e)) == 'l')
&& (Character.toLowerCase(sql.charAt(e + 1)) == 'i')
&& (Character.toLowerCase(sql.charAt(e + 2)) == 'k')
&& (Character.toLowerCase(sql.charAt(e + 3)) == 'e')
&& (Character.toLowerCase(sql.charAt(e + 4)) == ' '))
{
comparitor = "like";
e += 5;
}
else
if ((e < sql.length()) && (eow1.indexOf(sql.charAt(e)) > 0))
{
while ((e < sql.length()) && (eow1.indexOf(sql.charAt(e)) > 0))
e++;
comparitor = sql.substring(s, e).trim();
}
else
throw new java.sql.SQLException("Illegal comparator " + sql);
if (e >= sql.length() || comparitor.length() == 0)
throw new java.sql.SQLException("Unexpected end of where clause in " + sql);
s = e;
while ((sql.charAt(s) == ' ' || sql.charAt(s) == '\t') && (s < sql.length()))
s++;
if (s >= sql.length())
throw new java.sql.SQLException("Unexpected end of where clause in " + sql);
String value;
e = s;
if (sql.charAt(s) == '\'')
{
e++;
final StringBuilder str = new StringBuilder("");
while ((e < sql.length()) && (sql.charAt(e) != '\''))
{
if (sql.charAt(e) == '\\')
e++;
if (e < sql.length())
str.append(sql.charAt(e));
e++;
}
if (e >= sql.length())
throw new java.sql.SQLException("Unexpected end of where clause in " + sql);
e++;
value = str.toString();
}
else
{
while ((e < sql.length()) && (sql.charAt(e) != ' ') && (sql.charAt(e) != '\t'))
e++;
value = sql.substring(s, e);
if (value.equalsIgnoreCase("?"))
unPrepared = true;
}
s = e;
condition = connection.getBackend().buildFakeCondition(tableName, columnName, comparitor, value, unPrepared);
conditions.add(condition);
}
while ((s < sql.length()) && (sql.charAt(s) == ' ' || sql.charAt(s) == '\t'))
s++;
if (s >= sql.length())
break;
int e = s;
while ((e < sql.length()) && (sql.charAt(e) != ' ') && (sql.charAt(e) != '\t'))
e++;
if (condition == null)
continue;
final String peeker = sql.substring(s, e);
if (peeker.equalsIgnoreCase(")"))
{
}
else
if (peeker.equalsIgnoreCase("AND"))
{
s = e;
condition.connector = Backend.ConnectorType.AND;
}
else
if (peeker.equalsIgnoreCase("OR"))
{
s = e;
condition.connector = Backend.ConnectorType.OR;
}
else
break;
}
if (parenStack.size() > 0)
throw new java.sql.SQLException("Unended parenthesis " + sql);
if (s >= sql.length())
return "";
return sql.substring(s);
}
public Backend.ImplSelectStatement parseSelect(String sql, final String[] token) throws java.sql.SQLException
{
final List<String> cols = new ArrayList<String>();
sql = splitColumns(sql, cols);
if (cols.size() == 0)
throw new java.sql.SQLException("no columns given");
sql = split(sql, token);
if (!token[0].equalsIgnoreCase("from"))
throw new java.sql.SQLException("no from clause");
sql = split(sql, token);
final String tableName = token[0];
final List<Backend.FakeCondition> conditions = new ArrayList<Backend.FakeCondition>();
String[] orderVars = null;
String[] orderConditions = null;
if (sql.length() > 0)
{
sql = split(sql, token);
if (token[0].equalsIgnoreCase("where"))
{
sql = parseWhereClause(tableName, sql, conditions);
if (conditions.size() == 0)
throw new java.sql.SQLException("no more where clause!");
sql = split(sql, token);
}
if ((token[0] != null) && (token[0].equalsIgnoreCase("order")))
{
sql = split(sql, token);
if (!token[0].equalsIgnoreCase("by"))
throw new java.sql.SQLException("no by token");
sql = split(sql, token);
orderVars = new String[] { token[0] };
orderConditions = new String[1];
if (sql.length() > 0)
{
split(sql, token);
if (token[0].equalsIgnoreCase("ASC") || token[0].equalsIgnoreCase("DESC"))
{
orderConditions = new String[] { token[0].toUpperCase().trim() };
sql = split(sql, token);
}
}
}
if (sql.length() > 0)
throw new java.sql.SQLException("extra garbage: " + sql);
}
return new Backend.ImplSelectStatement(this, tableName, cols, conditions, orderVars, orderConditions);
}
@Override
public java.sql.ResultSet executeQuery(String sql) throws java.sql.SQLException
{
lastSQL = sql;
try
{
final String[] token = new String[1];
sql = split(sql, token);
if (!token[0].equalsIgnoreCase("select"))
throw new java.sql.SQLException("first query token not select");
final Backend.ImplSelectStatement stmt = parseSelect(sql, token);
return connection.getBackend().constructScan(stmt);
}
catch (final java.sql.SQLException e)
{
log("unsupported SQL in executeQuery: " + sql);
throw e;
}
}
protected static String skipWS(final String sql)
{
int index;
for (index = 0; index < sql.length(); index++)
{
final char c = sql.charAt(index);
if ((c != ' ') && (c != '\t') && (c != '\r') && (c != '\n'))
break;
}
if (index == 0)
return sql;
return sql.substring(index);
}
protected static String[] parseVal(String sql)
{
final String[] result = new String[3];
sql = skipWS(sql);
if (sql.length() == 0)
{
result[0] = result[1] = "";
result[2] = null;
}
else
if (sql.charAt(0) == '\'')
{
final StringBuffer buffer = new StringBuffer();
int index;
for (index = 1; index < sql.length(); ++index)
{
char c = sql.charAt(index);
if (c == '\'')
break;
if (c == '\\')
c = sql.charAt(++index);
buffer.append(c);
}
if (index >= sql.length())
index = sql.length() - 1;
result[0] = sql.substring(index + 1);
result[1] = buffer.toString();
result[2] = null;
}
else
{
final StringBuffer buffer = new StringBuffer();
int index;
for (index = 0; index < sql.length(); ++index)
{
final char c = sql.charAt(index);
if ((c == ' ') || (c == ',') || (c == ')'))
break;
buffer.append(c);
}
result[0] = sql.substring(index);
result[1] = buffer.toString();
result[2] = buffer.toString().equals("?") ? "" : null;
}
return result;
}
protected Backend.ImplInsertStatement parseInsert(String sql, final String[] token) throws java.sql.SQLException
{
sql = split(sql, token);
if (!token[0].equalsIgnoreCase("into"))
throw new java.sql.SQLException("no into token");
sql = split(sql, token);
final String tableName = token[0];
sql = skipWS(sql);
if ((sql.length() < 0) || (sql.charAt(0) != '('))
throw new java.sql.SQLException("no open paren");
sql = sql.substring(1);
final java.util.List<String> columnList = new java.util.LinkedList<String>();
while (true)
{
sql = skipWS(sql);
int index = sql.indexOf(',');
final int index2 = sql.indexOf(')');
if ((index < 0) || (index2 < index))
index = index2;
if (index < 0)
throw new java.sql.SQLException("no comma");
columnList.add(sql.substring(0, index).trim());
final char c = sql.charAt(index);
sql = skipWS(sql.substring(index + 1));
if (c == ')')
break;
}
sql = split(sql, token);
if (!token[0].equalsIgnoreCase("values"))
throw new java.sql.SQLException("no values");
sql = skipWS(sql);
if ((sql.length() < 0) || (sql.charAt(0) != '('))
throw new java.sql.SQLException("no value open paren");
sql = sql.substring(1);
final java.util.List<String> valuesList = new java.util.LinkedList<String>();
final java.util.List<Boolean> unPreparedValueList = new java.util.LinkedList<Boolean>();
while (true)
{
sql = skipWS(sql);
final String[] r = parseVal(sql);
final String val = r[1];
sql = skipWS(r[0]);
valuesList.add(val);
unPreparedValueList.add(Boolean.valueOf(r[2] != null));
if (sql.length() == 0)
throw new java.sql.SQLException("no sql again");
final char c = sql.charAt(0);
sql = skipWS(sql.substring(1));
if (c == ')')
break;
if (c != ',')
throw new java.sql.SQLException("no comma before last paren");
}
if ((sql.length() > 0) && (sql.charAt(0) == ';'))
sql = skipWS(sql.substring(1));
if ((sql.length() > 0) || (columnList.size() != valuesList.size()))
{
throw new java.sql.SQLException("something very bad");
}
return new Backend.ImplInsertStatement(tableName, columnList.toArray(new String[0]), valuesList.toArray(new String[0]), unPreparedValueList.toArray(new Boolean[0]));
}
protected Backend.ImplUpdateStatement parseUpdate(String sql, final String[] token) throws java.sql.SQLException
{
sql = split(sql, token);
final String tableName = token[0];
sql = split(sql, token);
if (!token[0].equalsIgnoreCase("set"))
throw new java.sql.SQLException("no set");
final java.util.List<String> columnList = new java.util.LinkedList<String>();
final java.util.List<String> valueList = new java.util.LinkedList<String>();
final java.util.List<Boolean> unPreparedValueList = new java.util.LinkedList<Boolean>();
final StringBuffer buffer = new StringBuffer();
while (true)
{
sql = skipWS(sql);
buffer.setLength(0);
while (sql.length() > 0)
{
final char c = sql.charAt(0);
if ((c == '=') || (c == ' '))
break;
buffer.append(c);
sql = sql.substring(1);
}
sql = skipWS(sql);
final String attr = buffer.toString();
if (sql.length() == 0)
throw new java.sql.SQLException("no more sql");
if (sql.charAt(0) != '=')
{
if (!attr.equalsIgnoreCase("where"))
throw new java.sql.SQLException("no where");
break;
}
sql = skipWS(sql.substring(1));
if (sql.length() == 0)
throw new java.sql.SQLException("no no sql no mo");
buffer.setLength(0);
if (sql.charAt(0) == '\'')
{
int sub = 1;
for (; sub < sql.length(); sub++)
{
char c = sql.charAt(sub);
if (c == '\'')
break;
if (c == '\\')
c = sql.charAt(++sub);
buffer.append(c);
}
columnList.add(attr);
valueList.add(buffer.toString());
sql = sql.substring(sub + 1);
unPreparedValueList.add(Boolean.valueOf(false));
}
else
{
final String[] r = parseVal(sql);
sql = r[0];
columnList.add(attr);
valueList.add(r[1]);
unPreparedValueList.add(Boolean.valueOf(r[2] != null));
}
sql = skipWS(sql);
if ((sql.length() > 0) && (sql.charAt(0) == ','))
sql = skipWS(sql.substring(1));
}
final List<Backend.FakeCondition> conditions = new ArrayList<Backend.FakeCondition>();
sql = parseWhereClause(tableName, sql, conditions);
if (conditions.size() == 0)
throw new java.sql.SQLException("no more where clause!");
return new Backend.ImplUpdateStatement(tableName, conditions, columnList.toArray(new String[0]), valueList.toArray(new String[0]), unPreparedValueList.toArray(new Boolean[0]));
}
protected Backend.ImplDeleteStatement parseDelete(String sql, final String[] token) throws java.sql.SQLException
{
sql = split(sql, token);
if (!token[0].equalsIgnoreCase("from"))
throw new java.sql.SQLException("no from clause");
sql = split(sql, token);
final String tableName = token[0];
sql = split(sql, token);
List<Backend.FakeCondition> conditions;
if (token[0].equalsIgnoreCase("where"))
{
sql = skipWS(sql);
conditions = new ArrayList<Backend.FakeCondition>();
sql = parseWhereClause(tableName, sql, conditions);
if (conditions.size() == 0)
throw new java.sql.SQLException("no more where clause!");
}
else
if (token.length > 0)
{
conditions = new ArrayList<Backend.FakeCondition>();
}
else
throw new java.sql.SQLException("no other where clause");
return new Backend.ImplDeleteStatement(tableName, conditions);
}
@Override
public int executeUpdate(String sql) throws java.sql.SQLException
{
lastSQL = sql;
// log("executeUpdate"+sql);
// insert into x (a,b,c) values (a,b,c)
// update x set a=A,b=B where x=y
// delete from x where x=y
final String originalSql = sql;
try
{
final String[] token = new String[1];
sql = split(sql, token);
if (token[0].equalsIgnoreCase("insert"))
{
final Backend.ImplInsertStatement stmt = parseInsert(sql, token);
connection.getBackend().dupKeyCheck(stmt.tableName, stmt.columns, stmt.sqlValues);
connection.getBackend().insertValues(stmt);
}
else
if (token[0].equalsIgnoreCase("update"))
{
final Backend.ImplUpdateStatement stmt = parseUpdate(sql, token);
connection.getBackend().updateRecord(stmt);
}
else
if (token[0].equalsIgnoreCase("delete"))
{
final Backend.ImplDeleteStatement stmt = parseDelete(sql, token);
connection.getBackend().deleteRecord(stmt);
}
else
throw new java.sql.SQLException("unimplemented command: " + token[0]);
return 1;
}
catch (final java.sql.SQLException e)
{
e.printStackTrace();
log("unsupported SQL in executeUpdate: " + originalSql);
throw e;
}
}
@Override
public int executeUpdate(final String sql, final int a) throws java.sql.SQLException
{
return executeUpdate(sql);
}
@Override
public int executeUpdate(final String sql, final int[] a) throws java.sql.SQLException
{
return executeUpdate(sql);
}
@Override
public int executeUpdate(final String sql, final String[] a) throws java.sql.SQLException
{
return executeUpdate(sql);
}
@Override
public void close() throws java.sql.SQLException
{
}
@Override
public int getMaxFieldSize() throws java.sql.SQLException
{
return 0;
}
@Override
public void setMaxFieldSize(final int max) throws java.sql.SQLException
{
}
@Override
public int getMaxRows() throws java.sql.SQLException
{
return 0;
}
@Override
public void setMaxRows(final int max) throws java.sql.SQLException
{
}
@Override
public void setEscapeProcessing(final boolean enable) throws java.sql.SQLException
{
}
@Override
public int getQueryTimeout() throws java.sql.SQLException
{
return 60;
}
@Override
public void setQueryTimeout(final int seconds) throws java.sql.SQLException
{
}
@Override
public void cancel() throws java.sql.SQLException
{
}
@Override
public java.sql.SQLWarning getWarnings() throws java.sql.SQLException
{
return null;
}
@Override
public void clearWarnings() throws java.sql.SQLException
{
}
@Override
public void setCursorName(final String Name) throws java.sql.SQLException
{
}
@Override
public boolean execute(String sql) throws java.sql.SQLException
{
lastSQL = sql;
try
{
final String[] token = new String[1];
sql = split(sql, token);
if (!token[0].equalsIgnoreCase("select"))
{
return executeUpdate(lastSQL) == 0;
}
final Backend.ImplSelectStatement stmt = parseSelect(sql, token);
myResultSet = (ResultSet) connection.getBackend().constructScan(stmt);
return true;
}
catch (final java.sql.SQLException e)
{
log("unsupported SQL in executeQuery: " + sql);
throw e;
}
}
@Override
public boolean execute(final String sql, final int a) throws java.sql.SQLException
{
return execute(sql, 0);
}
@Override
public boolean execute(final String sql, final int[] a) throws java.sql.SQLException
{
return execute(sql, 0);
}
@Override
public boolean execute(final String sql, final String[] a) throws java.sql.SQLException
{
return execute(sql, 0);
}
@Override
public java.sql.ResultSet getResultSet() throws java.sql.SQLException
{
return myResultSet;
}
@Override
public int getUpdateCount() throws java.sql.SQLException
{
log("getUpdateCount");
return -1;
}
public long getLongUpdateCount()
{
log("getLongUpdateCount");
return -1;
}
@Override
public boolean getMoreResults() throws java.sql.SQLException
{
return false;
}
@Override
public boolean getMoreResults(final int a) throws java.sql.SQLException
{
return false;
}
@Override
public int getResultSetHoldability() throws java.sql.SQLException
{
return 0;
}
@Override
public void setFetchDirection(final int i) throws java.sql.SQLException
{
}
@Override
public int getFetchDirection() throws java.sql.SQLException
{
return 0;
}
@Override
public void addBatch(final String a) throws java.sql.SQLException
{
}
@Override
public void clearBatch() throws java.sql.SQLException
{
}
@Override
public int[] executeBatch() throws java.sql.SQLException
{
return null;
}
@Override
public void setFetchSize(final int i) throws java.sql.SQLException
{
}
@Override
public int getFetchSize() throws java.sql.SQLException
{
return 0;
}
@Override
public int getResultSetConcurrency() throws java.sql.SQLException
{
return 0;
}
@Override
public int getResultSetType() throws java.sql.SQLException
{
return 0;
}
@Override
public java.sql.ResultSet getGeneratedKeys() throws java.sql.SQLException
{
return null;
}
public void closeOnCompletion() throws SQLException
{
closeStatementOnResultSetClose = true;
}
public boolean isCloseOnCompletion() throws SQLException
{
return closeStatementOnResultSetClose;
}
}