From: James Ahlborn Date: Mon, 10 Nov 2008 03:05:42 +0000 (+0000) Subject: initial work towards supporting reading/formatting access queries X-Git-Tag: rel_1_1_19~13 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=b0e9d0a4dca4262b92de7532513c3a38b3007b1e;p=jackcess.git initial work towards supporting reading/formatting access queries git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@387 f203690c-595d-4dc9-a70b-905162fa7fd2 --- diff --git a/src/changes/changes.xml b/src/changes/changes.xml index ff1c146..fb479cb 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -5,6 +5,11 @@ James Ahlborn + + + Add Query reading support. + + Add ErrorHandler utility for customizing error handling during row diff --git a/src/java/com/healthmarketscience/jackcess/query/AppendQuery.java b/src/java/com/healthmarketscience/jackcess/query/AppendQuery.java new file mode 100644 index 0000000..38842e1 --- /dev/null +++ b/src/java/com/healthmarketscience/jackcess/query/AppendQuery.java @@ -0,0 +1,84 @@ +/* +Copyright (c) 2008 Health Market Science, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +USA + +You can contact Health Market Science at info@healthmarketscience.com +or at the following address: + +Health Market Science +2700 Horizon Drive +Suite 200 +King of Prussia, PA 19406 +*/ + +package com.healthmarketscience.jackcess.query; + +import java.util.List; + +import static com.healthmarketscience.jackcess.query.QueryFormat.*; + + +/** + * @author James Ahlborn + */ +public class AppendQuery extends BaseSelectQuery +{ + + public AppendQuery(String name, List rows, int objectId) { + super(name, rows, objectId, Type.APPEND); + } + + public String getTargetTable() { + return getTypeRow().name1; + } + + public String getRemoteDb() { + return getTypeRow().name2; + } + + protected List getValueRows() { + return filterRowsByFlag(super.getColumnRows(), APPEND_VALUE_FLAG); + } + + @Override + protected List getColumnRows() { + return filterRowsByNotFlag(super.getColumnRows(), APPEND_VALUE_FLAG); + } + + public List getValues() { + return new RowFormatter(getValueRows()) { + @Override protected void format(StringBuilder builder, Row row) { + builder.append(row.expression); + } + }.format(); + } + + @Override + protected void toSQLString(StringBuilder builder) + { + builder.append("INSERT INTO ").append(getTargetTable()); + toRemoteDb(builder, getRemoteDb()); + builder.append(NEWLINE); + List values = getValues(); + if(!values.isEmpty()) { + builder.append("VALUES (").append(values).append(")"); + } else { + toSQLSelectString(builder, true); + } + } + +} diff --git a/src/java/com/healthmarketscience/jackcess/query/BaseSelectQuery.java b/src/java/com/healthmarketscience/jackcess/query/BaseSelectQuery.java new file mode 100644 index 0000000..24a6f96 --- /dev/null +++ b/src/java/com/healthmarketscience/jackcess/query/BaseSelectQuery.java @@ -0,0 +1,169 @@ +/* +Copyright (c) 2008 Health Market Science, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +USA + +You can contact Health Market Science at info@healthmarketscience.com +or at the following address: + +Health Market Science +2700 Horizon Drive +Suite 200 +King of Prussia, PA 19406 +*/ + +package com.healthmarketscience.jackcess.query; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import static com.healthmarketscience.jackcess.query.QueryFormat.*; + + +/** + * @author James Ahlborn + */ +public abstract class BaseSelectQuery extends Query +{ + + protected BaseSelectQuery(String name, List rows, int objectId, + Type type) { + super(name, rows, objectId, type); + } + + protected void toSQLSelectString(StringBuilder builder, + boolean useSelectPrefix) + { + if(useSelectPrefix) { + builder.append("SELECT "); + String selectType = getSelectType(); + if(!DEFAULT_TYPE.equals(selectType)) { + builder.append(selectType).append(" "); + } + } + + builder.append(getSelectColumns()); + toSelectInto(builder); + + List fromTables = getFromTables(); + if(!fromTables.isEmpty()) { + builder.append(NEWLINE).append("FROM ").append(fromTables); + toRemoteDb(builder, getFromRemoteDb()); + } + + String whereExpr = getWhereExpression(); + if(whereExpr != null) { + builder.append(NEWLINE).append("WHERE ").append(whereExpr); + } + + List groupings = getGroupings(); + if(!groupings.isEmpty()) { + builder.append(NEWLINE).append("GROUP BY ").append(groupings); + } + + String havingExpr = getHavingExpression(); + if(havingExpr != null) { + builder.append(NEWLINE).append("HAVING ").append(havingExpr); + } + + List orderings = getOrderings(); + if(!orderings.isEmpty()) { + builder.append(NEWLINE).append("ORDER BY ").append(orderings); + } + } + + public String getSelectType() + { + if(hasFlag(DISTINCT_SELECT_TYPE)) { + return "DISTINCT"; + } + + if(hasFlag(DISTINCT_ROW_SELECT_TYPE)) { + return "DISTINCTROW"; + } + + if(hasFlag(TOP_SELECT_TYPE)) { + StringBuilder builder = new StringBuilder(); + builder.append("TOP ").append(getFlagRow().name1); + if(hasFlag(PERCENT_SELECT_TYPE)) { + builder.append(" PERCENT"); + } + return builder.toString(); + } + + return DEFAULT_TYPE; + } + + public List getSelectColumns() + { + List result = (new RowFormatter(getColumnRows()) { + @Override protected void format(StringBuilder builder, Row row) { + // note column expression are always quoted appropriately + builder.append(row.expression); + toAlias(builder, row.name1); + } + }).format(); + if(hasFlag(SELECT_STAR_SELECT_TYPE)) { + result.add("*"); + } + return result; + } + + protected void toSelectInto(StringBuilder builder) + { + // base does nothing + } + + @Override + public List getFromTables() + { + return super.getFromTables(); + } + + @Override + public String getFromRemoteDb() + { + return super.getFromRemoteDb(); + } + + @Override + public String getWhereExpression() + { + return super.getWhereExpression(); + } + + public List getGroupings() + { + return (new RowFormatter(getGroupByRows()) { + @Override protected void format(StringBuilder builder, Row row) { + builder.append(row.expression); + } + }).format(); + } + + public String getHavingExpression() + { + return getHavingRow().expression; + } + + @Override + public List getOrderings() + { + return super.getOrderings(); + } + +} diff --git a/src/java/com/healthmarketscience/jackcess/query/CrossTabQuery.java b/src/java/com/healthmarketscience/jackcess/query/CrossTabQuery.java new file mode 100644 index 0000000..e750998 --- /dev/null +++ b/src/java/com/healthmarketscience/jackcess/query/CrossTabQuery.java @@ -0,0 +1,95 @@ +/* +Copyright (c) 2008 Health Market Science, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +USA + +You can contact Health Market Science at info@healthmarketscience.com +or at the following address: + +Health Market Science +2700 Horizon Drive +Suite 200 +King of Prussia, PA 19406 +*/ + +package com.healthmarketscience.jackcess.query; + +import java.util.List; + +import static com.healthmarketscience.jackcess.query.QueryFormat.*; + + +/** + * @author James Ahlborn + */ +public class CrossTabQuery extends BaseSelectQuery +{ + + public CrossTabQuery(String name, List rows, int objectId) { + super(name, rows, objectId, Type.CROSS_TAB); + } + + protected Row getTransformRow() { + return getUniqueRow( + filterRowsByNotFlag(super.getColumnRows(), + (short)(CROSSTAB_PIVOT_FLAG | + CROSSTAB_NORMAL_FLAG))); + } + + @Override + protected List getColumnRows() { + return filterRowsByFlag(super.getColumnRows(), CROSSTAB_NORMAL_FLAG); + } + + @Override + protected List getGroupByRows() { + return filterRowsByFlag(super.getGroupByRows(), CROSSTAB_NORMAL_FLAG); + } + + protected Row getPivotRow() { + return getUniqueRow(filterRowsByFlag(super.getColumnRows(), + CROSSTAB_PIVOT_FLAG)); + } + + public String getTransformExpression() { + Row row = getTransformRow(); + if(row.expression == null) { + return null; + } + // note column expression are always quoted appropriately + StringBuilder builder = new StringBuilder(row.expression); + return toAlias(builder, row.name1).toString(); + } + + public String getPivotExpression() { + return getPivotRow().expression; + } + + @Override + protected void toSQLString(StringBuilder builder) + { + String transformExpr = getTransformExpression(); + if(transformExpr != null) { + builder.append("TRANSFORM ").append(transformExpr).append(NEWLINE); + } + + toSQLSelectString(builder, true); + + builder.append(NEWLINE).append("PIVOT ") + .append(getPivotExpression()); + } + +} diff --git a/src/java/com/healthmarketscience/jackcess/query/DataDefinitionQuery.java b/src/java/com/healthmarketscience/jackcess/query/DataDefinitionQuery.java new file mode 100644 index 0000000..282a6a0 --- /dev/null +++ b/src/java/com/healthmarketscience/jackcess/query/DataDefinitionQuery.java @@ -0,0 +1,63 @@ +/* +Copyright (c) 2008 Health Market Science, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +USA + +You can contact Health Market Science at info@healthmarketscience.com +or at the following address: + +Health Market Science +2700 Horizon Drive +Suite 200 +King of Prussia, PA 19406 +*/ + +package com.healthmarketscience.jackcess.query; + +import java.util.List; + + +/** + * SQL query which represents a DDL query. + * + * @author James Ahlborn + */ +public class DataDefinitionQuery extends Query +{ + + public DataDefinitionQuery(String name, List rows, int objectId) { + super(name, rows, objectId, Type.DATA_DEFINITION); + } + + public String getDDLString() { + return getTypeRow().expression; + } + + @Override + protected boolean supportsStandardClauses() { + return false; + } + + @Override + protected void toSQLString(StringBuilder builder) + { + String ddl = getDDLString(); + if(ddl != null) { + builder.append(ddl); + } + } + +} diff --git a/src/java/com/healthmarketscience/jackcess/query/DeleteQuery.java b/src/java/com/healthmarketscience/jackcess/query/DeleteQuery.java new file mode 100644 index 0000000..b2d0bc9 --- /dev/null +++ b/src/java/com/healthmarketscience/jackcess/query/DeleteQuery.java @@ -0,0 +1,50 @@ +/* +Copyright (c) 2008 Health Market Science, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +USA + +You can contact Health Market Science at info@healthmarketscience.com +or at the following address: + +Health Market Science +2700 Horizon Drive +Suite 200 +King of Prussia, PA 19406 +*/ + +package com.healthmarketscience.jackcess.query; + +import java.util.List; + + +/** + * @author James Ahlborn + */ +public class DeleteQuery extends BaseSelectQuery +{ + + public DeleteQuery(String name, List rows, int objectId) { + super(name, rows, objectId, Type.DELETE); + } + + @Override + protected void toSQLString(StringBuilder builder) + { + builder.append("DELETE "); + toSQLSelectString(builder, false); + } + +} diff --git a/src/java/com/healthmarketscience/jackcess/query/MakeTableQuery.java b/src/java/com/healthmarketscience/jackcess/query/MakeTableQuery.java new file mode 100644 index 0000000..4d62bba --- /dev/null +++ b/src/java/com/healthmarketscience/jackcess/query/MakeTableQuery.java @@ -0,0 +1,66 @@ +/* +Copyright (c) 2008 Health Market Science, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +USA + +You can contact Health Market Science at info@healthmarketscience.com +or at the following address: + +Health Market Science +2700 Horizon Drive +Suite 200 +King of Prussia, PA 19406 +*/ + +package com.healthmarketscience.jackcess.query; + +import java.util.List; + +import static com.healthmarketscience.jackcess.query.QueryFormat.*; + + +/** + * @author James Ahlborn + */ +public class MakeTableQuery extends BaseSelectQuery +{ + + public MakeTableQuery(String name, List rows, int objectId) { + super(name, rows, objectId, Type.MAKE_TABLE); + } + + public String getTargetTable() { + return getTypeRow().name1; + } + + public String getRemoteDb() { + return getTypeRow().name2; + } + + @Override + protected void toSelectInto(StringBuilder builder) + { + builder.append(" INTO ").append(getTargetTable()); + toRemoteDb(builder, getRemoteDb()); + } + + @Override + protected void toSQLString(StringBuilder builder) + { + toSQLSelectString(builder, true); + } + +} diff --git a/src/java/com/healthmarketscience/jackcess/query/PassthroughQuery.java b/src/java/com/healthmarketscience/jackcess/query/PassthroughQuery.java new file mode 100644 index 0000000..fc2fc61 --- /dev/null +++ b/src/java/com/healthmarketscience/jackcess/query/PassthroughQuery.java @@ -0,0 +1,67 @@ +/* +Copyright (c) 2008 Health Market Science, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +USA + +You can contact Health Market Science at info@healthmarketscience.com +or at the following address: + +Health Market Science +2700 Horizon Drive +Suite 200 +King of Prussia, PA 19406 +*/ + +package com.healthmarketscience.jackcess.query; + +import java.util.List; + + +/** + * SQL query which represents a query which will be executed via ODBC. + * + * @author James Ahlborn + */ +public class PassthroughQuery extends Query +{ + + public PassthroughQuery(String name, List rows, int objectId) { + super(name, rows, objectId, Type.PASSTHROUGH); + } + + public String getConnectionString() { + return getTypeRow().name1; + } + + public String getPassthroughString() { + return getTypeRow().expression; + } + + @Override + protected boolean supportsStandardClauses() { + return false; + } + + @Override + protected void toSQLString(StringBuilder builder) + { + String pt = getPassthroughString(); + if(pt != null) { + builder.append(pt); + } + } + +} diff --git a/src/java/com/healthmarketscience/jackcess/query/Query.java b/src/java/com/healthmarketscience/jackcess/query/Query.java new file mode 100644 index 0000000..7a78d79 --- /dev/null +++ b/src/java/com/healthmarketscience/jackcess/query/Query.java @@ -0,0 +1,636 @@ +/* +Copyright (c) 2008 Health Market Science, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +USA + +You can contact Health Market Science at info@healthmarketscience.com +or at the following address: + +Health Market Science +2700 Horizon Drive +Suite 200 +King of Prussia, PA 19406 +*/ + +package com.healthmarketscience.jackcess.query; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import static com.healthmarketscience.jackcess.query.QueryFormat.*; + + +/** + * @author James Ahlborn + */ +public abstract class Query +{ + protected static final Log LOG = LogFactory.getLog(Query.class); + + private static final Row EMPTY_ROW = + new Row(Collections.emptyMap()); + + public enum Type + { + SELECT(SELECT_QUERY_OBJECT_FLAG, 1), + MAKE_TABLE(MAKE_TABLE_QUERY_OBJECT_FLAG, 2), + APPEND(APPEND_QUERY_OBJECT_FLAG, 3), + UPDATE(UPDATE_QUERY_OBJECT_FLAG, 4), + DELETE(DELETE_QUERY_OBJECT_FLAG, 5), + CROSS_TAB(CROSS_TAB_QUERY_OBJECT_FLAG, 6), + DATA_DEFINITION(DATA_DEF_QUERY_OBJECT_FLAG, 7), + PASSTHROUGH(PASSTHROUGH_QUERY_OBJECT_FLAG, 8), + UNION(UNION_QUERY_OBJECT_FLAG, 9), + UNKNOWN(-1, -1); + + private final int _objectFlag; + private final short _value; + + private Type(int objectFlag, int value) { + _objectFlag = objectFlag; + _value = (short)value; + } + + public int getObjectFlag() { + return _objectFlag; + } + + public short getValue() { + return _value; + } + } + + private final String _name; + private final List _rows; + private final int _objectId; + private final Type _type; + + protected Query(String name, List rows, int objectId, Type type) { + _name = name; + _rows = rows; + _objectId = objectId; + _type = type; + + if(type != Type.UNKNOWN) { + short foundType = getShortValue(getQueryType(rows), + _type.getValue()); + if(foundType != _type.getValue()) { + throw new IllegalStateException("Unexpected query type " + foundType); + } + } + } + + public String getName() { + return _name; + } + + public Type getType() { + return _type; + } + + public int getObjectId() { + return _objectId; + } + + public int getObjectFlag() { + return getType().getObjectFlag(); + } + + public List getRows() { + return _rows; + } + + protected List getRowsByAttribute(Byte attribute) { + return getRowsByAttribute(getRows(), attribute); + } + + protected Row getRowByAttribute(Byte attribute) { + return getUniqueRow(getRowsByAttribute(getRows(), attribute)); + } + + protected Row getTypeRow() { + return getRowByAttribute(TYPE_ATTRIBUTE); + } + + protected List getParameterRows() { + return getRowsByAttribute(PARAMETER_ATTRIBUTE); + } + + protected Row getFlagRow() { + return getRowByAttribute(FLAG_ATTRIBUTE); + } + + protected Row getRemoteDatabaseRow() { + return getRowByAttribute(REMOTEDB_ATTRIBUTE); + } + + protected List getTableRows() { + return getRowsByAttribute(TABLE_ATTRIBUTE); + } + + protected List getColumnRows() { + return getRowsByAttribute(COLUMN_ATTRIBUTE); + } + + protected List getJoinRows() { + return getRowsByAttribute(JOIN_ATTRIBUTE); + } + + protected Row getWhereRow() { + return getRowByAttribute(WHERE_ATTRIBUTE); + } + + protected List getGroupByRows() { + return getRowsByAttribute(GROUPBY_ATTRIBUTE); + } + + protected Row getHavingRow() { + return getRowByAttribute(HAVING_ATTRIBUTE); + } + + protected List getOrderByRows() { + return getRowsByAttribute(ORDERBY_ATTRIBUTE); + } + + protected abstract void toSQLString(StringBuilder builder); + + protected void toSQLParameterString(StringBuilder builder) { + // handle any parameters + List params = getParameters(); + if(!params.isEmpty()) { + builder.append("PARAMETERS ").append(params) + .append(";").append(NEWLINE); + } + } + + public List getParameters() + { + return (new RowFormatter(getParameterRows()) { + @Override protected void format(StringBuilder builder, Row row) { + String name = row.name1; + String typeName = PARAM_TYPE_MAP.get(row.flag); + if(typeName == null) { + throw new IllegalStateException("Unknown param type " + row.flag); + } + + builder.append(name).append(" ").append(typeName); + if((TEXT_FLAG.equals(row.flag)) && (getIntValue(row.extra, 0) > 0)) { + builder.append("(").append(row.extra).append(")"); + } + } + }).format(); + } + + protected List getFromTables() + { + List joinExprs = new ArrayList(); + for(Row table : getTableRows()) { + StringBuilder builder = new StringBuilder(); + + if(table.expression != null) { + toQuotedExpr(builder, table.expression).append(IDENTIFIER_SEP_CHAR); + } + if(table.name1 != null) { + toOptionalQuotedExpr(builder, table.name1, true); + } + toAlias(builder, table.name2); + + String key = ((table.name2 != null) ? table.name2 : table.name1); + joinExprs.add(new Join(key, builder.toString())); + } + + + List joins = getJoinRows(); + if(!joins.isEmpty()) { + + for(Row join : joins) { + + String fromTable = join.name1; + String toTable = join.name2; + + Join fromExpr = getJoinExpr(fromTable, joinExprs); + Join toExpr = getJoinExpr(toTable, joinExprs); + String joinType = JOIN_TYPE_MAP.get(join.flag); + if(joinType == null) { + throw new IllegalStateException("Unknown join type " + join.flag); + } + + String expr = new StringBuilder().append(fromExpr) + .append(joinType).append(toExpr).append(" ON ") + .append(join.expression).toString(); + + fromExpr.join(toExpr, expr); + joinExprs.add(fromExpr); + } + } + + List result = new AppendableList(); + for(Join joinExpr : joinExprs) { + result.add(joinExpr.expression); + } + + return result; + } + + private Join getJoinExpr(String table, List joinExprs) + { + for(Iterator iter = joinExprs.iterator(); iter.hasNext(); ) { + Join joinExpr = iter.next(); + if(joinExpr.tables.contains(table)) { + iter.remove(); + return joinExpr; + } + } + throw new IllegalStateException("Cannot find join table " + table); + } + + protected String getFromRemoteDb() + { + return getRemoteDatabaseRow().name1; + } + + protected String getWhereExpression() + { + return getWhereRow().expression; + } + + protected List getOrderings() + { + return (new RowFormatter(getOrderByRows()) { + @Override protected void format(StringBuilder builder, Row row) { + builder.append(row.expression); + if(DESCENDING_FLAG.equalsIgnoreCase(row.name1)) { + builder.append(" DESC"); + } + } + }).format(); + } + + public String getOwnerAccessType() { + return(hasFlag(OWNER_ACCESS_SELECT_TYPE) ? + "WITH OWNERACCESS OPTION" : DEFAULT_TYPE); + } + + protected boolean hasFlag(int flagMask) + { + return hasFlag(getFlagRow(), flagMask); + } + + protected boolean supportsStandardClauses() { + return true; + } + + public String toSQLString() + { + StringBuilder builder = new StringBuilder(); + if(supportsStandardClauses()) { + toSQLParameterString(builder); + } + + toSQLString(builder); + + if(supportsStandardClauses()) { + + String accessType = getOwnerAccessType(); + if(!DEFAULT_TYPE.equals(accessType)) { + builder.append(NEWLINE).append(accessType); + } + + builder.append(";"); + } + return builder.toString(); + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + + public static Query create(int objectFlag, String name, List rows, + int objectId) + { + try { + switch(objectFlag) { + case SELECT_QUERY_OBJECT_FLAG: + return new SelectQuery(name, rows, objectId); + case MAKE_TABLE_QUERY_OBJECT_FLAG: + return new MakeTableQuery(name, rows, objectId); + case APPEND_QUERY_OBJECT_FLAG: + return new AppendQuery(name, rows, objectId); + case UPDATE_QUERY_OBJECT_FLAG: + return new UpdateQuery(name, rows, objectId); + case DELETE_QUERY_OBJECT_FLAG: + return new DeleteQuery(name, rows, objectId); + case CROSS_TAB_QUERY_OBJECT_FLAG: + return new CrossTabQuery(name, rows, objectId); + case DATA_DEF_QUERY_OBJECT_FLAG: + return new DataDefinitionQuery(name, rows, objectId); + case PASSTHROUGH_QUERY_OBJECT_FLAG: + return new PassthroughQuery(name, rows, objectId); + case UNION_QUERY_OBJECT_FLAG: + return new UnionQuery(name, rows, objectId); + default: + // unknown querytype + throw new IllegalStateException( + "unknown query object flag " + objectFlag); + } + } catch(IllegalStateException e) { + LOG.warn("Failed parsing query", e); + } + + // return unknown query + return new UnknownQuery(name, rows, objectId, objectFlag); + } + + private static Short getQueryType(List rows) + { + return getUniqueRow(getRowsByAttribute(rows, TYPE_ATTRIBUTE)).flag; + } + + private static List getRowsByAttribute(List rows, Byte attribute) { + List result = new ArrayList(); + for(Row row : rows) { + if(attribute.equals(row.attribute)) { + result.add(row); + } + } + return result; + } + + protected static Row getUniqueRow(List rows) { + if(rows.size() == 1) { + return rows.get(0); + } + if(rows.isEmpty()) { + return EMPTY_ROW; + } + throw new IllegalStateException("Unexpected number of rows for" + rows); + } + + protected static List filterRowsByFlag( + List rows, final short flag) + { + return new RowFilter() { + @Override protected boolean keep(Row row) { + return hasFlag(row, flag); + } + }.filter(rows); + } + + protected static List filterRowsByNotFlag( + List rows, final short flag) + { + return new RowFilter() { + @Override protected boolean keep(Row row) { + return !hasFlag(row, flag); + } + }.filter(rows); + } + + protected static boolean hasFlag(Row row, int flagMask) + { + return((getShortValue(row.flag, 0) & flagMask) != 0); + } + + protected static short getShortValue(Short s, int def) { + return ((s != null) ? (short)s : (short)def); + } + + protected static int getIntValue(Integer i, int def) { + return ((i != null) ? (int)i : def); + } + + protected static StringBuilder toOptionalQuotedExpr(StringBuilder builder, + String fullExpr, + boolean isIdentifier) + { + String[] exprs = (isIdentifier ? + IDENTIFIER_SEP_PAT.split(fullExpr) : + new String[]{fullExpr}); + for(int i = 0; i < exprs.length; ++i) { + String expr = exprs[i]; + if(QUOTABLE_CHAR_PAT.matcher(expr).find()) { + toQuotedExpr(builder, expr); + } else { + builder.append(expr); + } + if(i < (exprs.length - 1)) { + builder.append(IDENTIFIER_SEP_CHAR); + } + } + return builder; + } + + protected static StringBuilder toQuotedExpr(StringBuilder builder, + String expr) + { + return builder.append("[").append(expr).append("]"); + } + + protected static StringBuilder toRemoteDb(StringBuilder builder, + String remoteDb) { + if(remoteDb != null) { + builder.append(" IN ").append(remoteDb); + } + return builder; + } + + protected static StringBuilder toAlias(StringBuilder builder, + String alias) { + if(alias != null) { + toOptionalQuotedExpr(builder.append(" AS "), alias, false); + } + return builder; + } + + private static class UnknownQuery extends Query + { + private final int _objectFlag; + + private UnknownQuery(String name, List rows, int objectId, + int objectFlag) + { + super(name, rows, objectId, Type.UNKNOWN); + _objectFlag = objectFlag; + } + + @Override + public int getObjectFlag() { + return _objectFlag; + } + + @Override + protected void toSQLString(StringBuilder builder) { + throw new UnsupportedOperationException(); + } + } + + public static class Row + { + public final Byte attribute; + public final String expression; + public final Short flag; + public final Integer extra; + public final String name1; + public final String name2; + public final Integer objectId; + public final byte[] order; + + public Row(Map tableRow) { + this((Byte)tableRow.get(COL_ATTRIBUTE), + (String)tableRow.get(COL_EXPRESSION), + (Short)tableRow.get(COL_FLAG), + (Integer)tableRow.get(COL_EXTRA), + (String)tableRow.get(COL_NAME1), + (String)tableRow.get(COL_NAME2), + (Integer)tableRow.get(COL_OBJECTID), + (byte[])tableRow.get(COL_ORDER)); + } + + public Row(Byte attribute, String expression, Short flag, + Integer extra, String name1, String name2, + Integer objectId, byte[] order) + { + this.attribute = attribute; + this.expression = expression; + this.flag = flag; + this.extra = extra; + this.name1 = name1; + this.name2= name2; + this.objectId = objectId; + this.order = order; + } + + public Map toTableRow() + { + Map tableRow = new HashMap(); + + tableRow.put(COL_ATTRIBUTE, attribute); + tableRow.put(COL_EXPRESSION, expression); + tableRow.put(COL_FLAG, flag); + tableRow.put(COL_EXTRA, extra); + tableRow.put(COL_NAME1, name1); + tableRow.put(COL_NAME2, name2); + tableRow.put(COL_OBJECTID, objectId); + tableRow.put(COL_ORDER, order); + + return tableRow; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + } + + protected static abstract class RowFormatter + { + private final List _list; + + protected RowFormatter(List list) { + _list = list; + } + + public List format() { + return format(new AppendableList()); + } + + public List format(List strs) { + for(Row row : _list) { + StringBuilder builder = new StringBuilder(); + format(builder, row); + strs.add(builder.toString()); + } + return strs; + } + + protected abstract void format(StringBuilder builder, Row row); + } + + protected static abstract class RowFilter + { + protected RowFilter() { + } + + public List filter(List list) { + for(Iterator iter = list.iterator(); iter.hasNext(); ) { + if(!keep(iter.next())) { + iter.remove(); + } + } + return list; + } + + protected abstract boolean keep(Row row); + } + + protected static class AppendableList extends ArrayList + { + private static final long serialVersionUID = 0L; + + protected AppendableList() { + } + + protected AppendableList(Collection c) { + super(c); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + for(Iterator iter = iterator(); iter.hasNext(); ) { + builder.append(iter.next().toString()); + if(iter.hasNext()) { + builder.append(", "); + } + } + return builder.toString(); + } + } + + private static class Join + { + public final List tables = new ArrayList(); + public boolean isJoin; + public String expression; + + private Join(String table, String expr) { + tables.add(table); + expression = expr; + } + + public void join(Join other, String newExpr) { + tables.addAll(other.tables); + isJoin = true; + expression = newExpr; + } + + @Override + public String toString() { + return (isJoin ? ("(" + expression + ")") : expression); + } + } + +} diff --git a/src/java/com/healthmarketscience/jackcess/query/QueryFormat.java b/src/java/com/healthmarketscience/jackcess/query/QueryFormat.java new file mode 100644 index 0000000..ac58727 --- /dev/null +++ b/src/java/com/healthmarketscience/jackcess/query/QueryFormat.java @@ -0,0 +1,135 @@ +/* +Copyright (c) 2008 Health Market Science, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +USA + +You can contact Health Market Science at info@healthmarketscience.com +or at the following address: + +Health Market Science +2700 Horizon Drive +Suite 200 +King of Prussia, PA 19406 +*/ + +package com.healthmarketscience.jackcess.query; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Pattern; + +import com.healthmarketscience.jackcess.DataType; +import org.apache.commons.lang.SystemUtils; + +/** + * @author James Ahlborn + */ +public class QueryFormat +{ + + private QueryFormat() {} + + public static final int SELECT_QUERY_OBJECT_FLAG = 0; + public static final int MAKE_TABLE_QUERY_OBJECT_FLAG = 80; + public static final int APPEND_QUERY_OBJECT_FLAG = 64; + public static final int UPDATE_QUERY_OBJECT_FLAG = 48; + public static final int DELETE_QUERY_OBJECT_FLAG = 32; + public static final int CROSS_TAB_QUERY_OBJECT_FLAG = 16; + public static final int DATA_DEF_QUERY_OBJECT_FLAG = 96; + public static final int PASSTHROUGH_QUERY_OBJECT_FLAG = 112; + public static final int UNION_QUERY_OBJECT_FLAG = 128; + + public static final String COL_ATTRIBUTE = "Attribute"; + public static final String COL_EXPRESSION = "Expression"; + public static final String COL_FLAG = "Flag"; + public static final String COL_EXTRA = "LvExtra"; + public static final String COL_NAME1 = "Name1"; + public static final String COL_NAME2 = "Name2"; + public static final String COL_OBJECTID = "ObjectId"; + public static final String COL_ORDER = "Order"; + + public static final Byte START_ATTRIBUTE = 0; + public static final Byte TYPE_ATTRIBUTE = 1; + public static final Byte PARAMETER_ATTRIBUTE = 2; + public static final Byte FLAG_ATTRIBUTE = 3; + public static final Byte REMOTEDB_ATTRIBUTE = 4; + public static final Byte TABLE_ATTRIBUTE = 5; + public static final Byte COLUMN_ATTRIBUTE = 6; + public static final Byte JOIN_ATTRIBUTE = 7; + public static final Byte WHERE_ATTRIBUTE = 8; + public static final Byte GROUPBY_ATTRIBUTE = 9; + public static final Byte HAVING_ATTRIBUTE = 10; + public static final Byte ORDERBY_ATTRIBUTE = 11; + public static final Byte END_ATTRIBUTE = (byte)255; + + public static final short UNION_FLAG = 0x02; + + public static final Short TEXT_FLAG = (short)DataType.TEXT.getValue(); + + public static final String DESCENDING_FLAG = "D"; + + public static final short SELECT_STAR_SELECT_TYPE = 0x01; + public static final short DISTINCT_SELECT_TYPE = 0x02; + public static final short OWNER_ACCESS_SELECT_TYPE = 0x04; + public static final short DISTINCT_ROW_SELECT_TYPE = 0x08; + public static final short TOP_SELECT_TYPE = 0x10; + public static final short PERCENT_SELECT_TYPE = 0x20; + + public static final short APPEND_VALUE_FLAG = (short)0x8000; + + public static final short CROSSTAB_PIVOT_FLAG = 0x01; + public static final short CROSSTAB_NORMAL_FLAG = 0x02; + + public static final String UNION_PART1 = "X7YZ_____1"; + public static final String UNION_PART2 = "X7YZ_____2"; + + public static final String DEFAULT_TYPE = ""; + + public static final Pattern QUOTABLE_CHAR_PAT = Pattern.compile("\\W"); + + public static final Pattern IDENTIFIER_SEP_PAT = Pattern.compile("\\."); + public static final char IDENTIFIER_SEP_CHAR = '.'; + + public static final String NEWLINE = SystemUtils.LINE_SEPARATOR; + + + public static final Map PARAM_TYPE_MAP = + new HashMap(); + static { + PARAM_TYPE_MAP.put((short)0, "Value"); + PARAM_TYPE_MAP.put((short)DataType.BOOLEAN.getValue(), "Bit"); + PARAM_TYPE_MAP.put((short)DataType.TEXT.getValue(), "Text"); + PARAM_TYPE_MAP.put((short)DataType.BYTE.getValue(), "Byte"); + PARAM_TYPE_MAP.put((short)DataType.INT.getValue(), "Short"); + PARAM_TYPE_MAP.put((short)DataType.LONG.getValue(), "Long"); + PARAM_TYPE_MAP.put((short)DataType.MONEY.getValue(), "Currency"); + PARAM_TYPE_MAP.put((short)DataType.FLOAT.getValue(), "IEEESingle"); + PARAM_TYPE_MAP.put((short)DataType.DOUBLE.getValue(), "IEEEDouble"); + PARAM_TYPE_MAP.put((short)DataType.SHORT_DATE_TIME.getValue(), "DateTime"); + PARAM_TYPE_MAP.put((short)DataType.BINARY.getValue(), "Binary"); + PARAM_TYPE_MAP.put((short)DataType.OLE.getValue(), "LongBinary"); + PARAM_TYPE_MAP.put((short)DataType.GUID.getValue(), "Guid"); + } + + public static final Map JOIN_TYPE_MAP = + new HashMap(); + static { + JOIN_TYPE_MAP.put((short)1, " INNER JOIN "); + JOIN_TYPE_MAP.put((short)2, " LEFT OUTER JOIN "); + JOIN_TYPE_MAP.put((short)3, " RIGHT OUTER JOIN "); + } + +} diff --git a/src/java/com/healthmarketscience/jackcess/query/SelectQuery.java b/src/java/com/healthmarketscience/jackcess/query/SelectQuery.java new file mode 100644 index 0000000..c639286 --- /dev/null +++ b/src/java/com/healthmarketscience/jackcess/query/SelectQuery.java @@ -0,0 +1,49 @@ +/* +Copyright (c) 2008 Health Market Science, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +USA + +You can contact Health Market Science at info@healthmarketscience.com +or at the following address: + +Health Market Science +2700 Horizon Drive +Suite 200 +King of Prussia, PA 19406 +*/ + +package com.healthmarketscience.jackcess.query; + +import java.util.List; + + +/** + * @author James Ahlborn + */ +public class SelectQuery extends BaseSelectQuery +{ + + public SelectQuery(String name, List rows, int objectId) { + super(name, rows, objectId, Type.SELECT); + } + + @Override + protected void toSQLString(StringBuilder builder) + { + toSQLSelectString(builder, true); + } + +} diff --git a/src/java/com/healthmarketscience/jackcess/query/UnionQuery.java b/src/java/com/healthmarketscience/jackcess/query/UnionQuery.java new file mode 100644 index 0000000..e323b83 --- /dev/null +++ b/src/java/com/healthmarketscience/jackcess/query/UnionQuery.java @@ -0,0 +1,89 @@ +/* +Copyright (c) 2008 Health Market Science, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +USA + +You can contact Health Market Science at info@healthmarketscience.com +or at the following address: + +Health Market Science +2700 Horizon Drive +Suite 200 +King of Prussia, PA 19406 +*/ + +package com.healthmarketscience.jackcess.query; + +import java.util.List; + +import static com.healthmarketscience.jackcess.query.QueryFormat.*; + + +/** + * SQL query which represents a UNION query. + * + * @author James Ahlborn + */ +public class UnionQuery extends Query +{ + public UnionQuery(String name, List rows, int objectId) { + super(name, rows, objectId, Type.UNION); + } + + public String getUnionType() { + return(hasFlag(UNION_FLAG) ? DEFAULT_TYPE : "ALL"); + } + + public String getUnionString1() { + return getUnionString(UNION_PART1); + } + + public String getUnionString2() { + return getUnionString(UNION_PART2); + } + + @Override + public List getOrderings() { + return super.getOrderings(); + } + + private String getUnionString(String id) { + for(Row row : getTableRows()) { + if(id.equals(row.name2)) { + return row.expression; + } + } + throw new IllegalStateException( + "Could not find union query with id " + id); + } + + @Override + protected void toSQLString(StringBuilder builder) + { + builder.append(getUnionString1()).append(NEWLINE) + .append("UNION "); + String unionType = getUnionType(); + if(!DEFAULT_TYPE.equals(unionType)) { + builder.append(unionType).append(" "); + } + builder.append(getUnionString2()); + List orderings = getOrderings(); + if(!orderings.isEmpty()) { + builder.append(NEWLINE).append("ORDER BY ").append(orderings); + } + } + +} diff --git a/src/java/com/healthmarketscience/jackcess/query/UpdateQuery.java b/src/java/com/healthmarketscience/jackcess/query/UpdateQuery.java new file mode 100644 index 0000000..30c7769 --- /dev/null +++ b/src/java/com/healthmarketscience/jackcess/query/UpdateQuery.java @@ -0,0 +1,85 @@ +/* +Copyright (c) 2008 Health Market Science, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +USA + +You can contact Health Market Science at info@healthmarketscience.com +or at the following address: + +Health Market Science +2700 Horizon Drive +Suite 200 +King of Prussia, PA 19406 +*/ + +package com.healthmarketscience.jackcess.query; + +import java.util.List; + +import static com.healthmarketscience.jackcess.query.QueryFormat.*; + + +/** + * @author James Ahlborn + */ +public class UpdateQuery extends Query +{ + + public UpdateQuery(String name, List rows, int objectId) { + super(name, rows, objectId, Type.UPDATE); + } + + public List getTargetTables() + { + return super.getFromTables(); + } + + public String getRemoteDb() + { + return super.getFromRemoteDb(); + } + + public List getNewValues() + { + return (new RowFormatter(getColumnRows()) { + @Override protected void format(StringBuilder builder, Row row) { + toOptionalQuotedExpr(builder, row.name2, true) + .append(" = ").append(row.expression); + } + }).format(); + } + + @Override + public String getWhereExpression() + { + return super.getWhereExpression(); + } + + @Override + protected void toSQLString(StringBuilder builder) + { + builder.append("UPDATE ").append(getTargetTables()); + toRemoteDb(builder, getRemoteDb()); + + builder.append(NEWLINE).append("SET ").append(getNewValues()); + + String whereExpr = getWhereExpression(); + if(whereExpr != null) { + builder.append(NEWLINE).append("WHERE ").append(whereExpr); + } + } + +} diff --git a/test/src/java/com/healthmarketscience/jackcess/query/QueryTest.java b/test/src/java/com/healthmarketscience/jackcess/query/QueryTest.java new file mode 100644 index 0000000..bdba1d2 --- /dev/null +++ b/test/src/java/com/healthmarketscience/jackcess/query/QueryTest.java @@ -0,0 +1,184 @@ +/* +Copyright (c) 2007 Health Market Science, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +USA + +You can contact Health Market Science at info@healthmarketscience.com +or at the following address: + +Health Market Science +2700 Horizon Drive +Suite 200 +King of Prussia, PA 19406 +*/ + +package com.healthmarketscience.jackcess.query; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import com.healthmarketscience.jackcess.query.Query.Row; +import junit.framework.TestCase; +import org.apache.commons.lang.StringUtils; + +import static org.apache.commons.lang.SystemUtils.LINE_SEPARATOR; +import static com.healthmarketscience.jackcess.query.QueryFormat.*; + + +/** + * @author James Ahlborn + */ +public class QueryTest extends TestCase +{ + + public QueryTest(String name) throws Exception { + super(name); + } + + public void testUnionQuery() throws Exception + { + String expr1 = "Select * from Table1"; + String expr2 = "Select * from Table2"; + + UnionQuery query = (UnionQuery)newQuery( + Query.Type.UNION, + newRow(TABLE_ATTRIBUTE, expr1, null, UNION_PART1), + newRow(TABLE_ATTRIBUTE, expr2, null, UNION_PART2)); + setFlag(query, 3); + + assertEquals(multiline("Select * from Table1", + "UNION Select * from Table2;"), + query.toSQLString()); + + setFlag(query, 1); + + assertEquals(multiline("Select * from Table1", + "UNION ALL Select * from Table2;"), + query.toSQLString()); + + query.getRows().add(newRow(ORDERBY_ATTRIBUTE, "Table1.id", + null, null)); + + assertEquals(multiline("Select * from Table1", + "UNION ALL Select * from Table2", + "ORDER BY Table1.id;"), + query.toSQLString()); + + } + + public void testPassthroughQuery() throws Exception + { + String expr = "Select * from Table1"; + String constr = "ODBC;"; + + PassthroughQuery query = (PassthroughQuery)newQuery( + Query.Type.PASSTHROUGH, expr, constr); + + assertEquals(expr, query.toSQLString()); + assertEquals(constr, query.getConnectionString()); + } + + public void testDataDefinitionQuery() throws Exception + { + String expr = "Drop table Table1"; + + DataDefinitionQuery query = (DataDefinitionQuery)newQuery( + Query.Type.DATA_DEFINITION, expr, null); + + assertEquals(expr, query.toSQLString()); + } + + public void testUpdateQuery() throws Exception + { + UpdateQuery query = (UpdateQuery)newQuery( + Query.Type.UPDATE, + newRow(TABLE_ATTRIBUTE, null, "Table1", null), + newRow(COLUMN_ATTRIBUTE, "\"some string\"", null, "Table1.id"), + newRow(COLUMN_ATTRIBUTE, "42", null, "Table1.col1")); + + assertEquals( + multiline("UPDATE Table1", + "SET Table1.id = \"some string\", Table1.col1 = 42;"), + query.toSQLString()); + + query.getRows().add(newRow(WHERE_ATTRIBUTE, "(Table1.col2 < 13)", + null, null)); + + assertEquals( + multiline("UPDATE Table1", + "SET Table1.id = \"some string\", Table1.col1 = 42", + "WHERE (Table1.col2 < 13);"), + query.toSQLString()); + } + + private static Query newQuery(Query.Type type, Row... rows) + { + return newQuery(type, null, null, rows); + } + + private static Query newQuery(Query.Type type, String typeExpr, + String typeName1, Row... rows) + { + List rowList = new ArrayList(); + rowList.add(newRow(TYPE_ATTRIBUTE, typeExpr, type.getValue(), + null, typeName1, null)); + rowList.addAll(Arrays.asList(rows)); + return Query.create(type.getObjectFlag(), "TestQuery", rowList, 13); + } + + private static Row newRow(Byte attr, String expr, String name1, String name2) + { + return newRow(attr, expr, null, null, name1, name2); + } + + private static Row newRow(Byte attr, String expr, Number flagNum, + String name1, String name2) + { + return newRow(attr, expr, flagNum, null, name1, name2); + } + + private static Row newRow(Byte attr, String expr, Number flagNum, + Number extraNum, String name1, String name2) + { + Short flag = ((flagNum != null) ? flagNum.shortValue() : null); + Integer extra = ((extraNum != null) ? extraNum.intValue() : null); + return new Row(attr, expr, flag, extra, name1, name2, null, null); + } + + private static void setFlag(Query query, Number newFlagNum) + { + removeRows(query, FLAG_ATTRIBUTE); + query.getRows().add( + newRow(FLAG_ATTRIBUTE, null, newFlagNum, null, null, null)); + } + + private static void removeRows(Query query, Byte attr) + { + for(Iterator iter = query.getRows().iterator(); iter.hasNext(); ) { + if(attr.equals(iter.next().attribute)) { + iter.remove(); + } + } + } + + private static String multiline(String... strs) + { + return StringUtils.join(strs, LINE_SEPARATOR); + } + +}