/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The name "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
package org.apache.ws.jaxme.sqls.impl;
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.ws.jaxme.sqls.BinaryColumn;
import org.apache.ws.jaxme.sqls.BooleanConstraint;
import org.apache.ws.jaxme.sqls.Column;
import org.apache.ws.jaxme.sqls.ColumnReference;
import org.apache.ws.jaxme.sqls.CombinedConstraint;
import org.apache.ws.jaxme.sqls.Constraint;
import org.apache.ws.jaxme.sqls.DeleteStatement;
import org.apache.ws.jaxme.sqls.ForeignKey;
import org.apache.ws.jaxme.sqls.Function;
import org.apache.ws.jaxme.sqls.Index;
import org.apache.ws.jaxme.sqls.Parts;
import org.apache.ws.jaxme.sqls.RawSQLCode;
import org.apache.ws.jaxme.sqls.SQLGenerator;
import org.apache.ws.jaxme.sqls.InsertStatement;
import org.apache.ws.jaxme.sqls.JoinReference;
import org.apache.ws.jaxme.sqls.Schema;
import org.apache.ws.jaxme.sqls.SelectStatement;
import org.apache.ws.jaxme.sqls.SelectTableReference;
import org.apache.ws.jaxme.sqls.SetStatement;
import org.apache.ws.jaxme.sqls.Statement;
import org.apache.ws.jaxme.sqls.StringColumn;
import org.apache.ws.jaxme.sqls.Table;
import org.apache.ws.jaxme.sqls.TableReference;
import org.apache.ws.jaxme.sqls.UpdateStatement;
import org.apache.ws.jaxme.sqls.Value;
/** <p>Default implementation of an SQL generator.</p>
*
* @author <a href="mailto:joe@ispsoft.de">Jochen Wiedmann</a>
*/
public class SQLGeneratorImpl implements SQLGenerator {
private String statementTerminator, lineTerminator;
public String getStatementTerminator() {
return statementTerminator;
}
public void setStatementTerminator(String pStatementTerminator) {
statementTerminator = pStatementTerminator;
}
public String getLineTerminator() {
return lineTerminator;
}
public void setLineTerminator(String pLineTerminator) {
lineTerminator = pLineTerminator;
}
protected String newStatement(String pStatement) {
String s = getStatementTerminator();
return s == null ? pStatement : pStatement + s;
}
public Collection getCreate(Schema pSchema) {
if (pSchema.getName() == null) {
// Default schema cannot be created
return Collections.EMPTY_LIST;
}
List result = new ArrayList();
result.add(newStatement("CREATE SCHEMA " + pSchema.getName()));
return result;
}
public Collection getDrop(Schema pSchema) {
if (pSchema.getName() == null) {
// Default schema cannot be dropped
return Collections.EMPTY_LIST;
}
List result = new ArrayList();
result.add(newStatement("DROP SCHEMA " + pSchema.getName()));
return result;
}
protected String getIndent() { return " "; }
protected String getTypeName(Column.Type pType) {
if (pType.equals(Column.Type.BIGINT)) {
return "BIGINT";
} else if (pType.equals(Column.Type.BINARY)) {
return "BINARY";
} else if (pType.equals(Column.Type.BIT)) {
return "BIT";
} else if (pType.equals(Column.Type.CHAR)) {
return "CHAR";
} else if (pType.equals(Column.Type.DATE)) {
return "DATE";
} else if (pType.equals(Column.Type.DOUBLE)) {
return "DOUBLE";
} else if (pType.equals(Column.Type.FLOAT)) {
return "FLOAT";
} else if (pType.equals(Column.Type.INTEGER)) {
return "INT";
} else if (pType.equals(Column.Type.SMALLINT)) {
return "SMALLINT";
} else if (pType.equals(Column.Type.TIME)) {
return "TIME";
} else if (pType.equals(Column.Type.TIMESTAMP)) {
return "TIMESTAMP";
} else if (pType.equals(Column.Type.TINYINT)) {
return "TINYINT";
} else if (pType.equals(Column.Type.VARBINARY)) {
return "VARBINARY";
} else if (pType.equals(Column.Type.VARCHAR)) {
return "VARCHAR";
} else if (pType.equals(Column.Type.CLOB)) {
return "CLOB";
} else if (pType.equals(Column.Type.BLOB)) {
return "BLOB";
} else if (pType.equals(Column.Type.OTHER)) {
return "BLOB";
} else {
throw new IllegalStateException("Unknown column type: " + pType);
}
}
protected String getCreate(Column pColumn) {
StringBuffer sb = new StringBuffer();
sb.append(pColumn.getName()).append(" ");
Column.Type type = pColumn.getType();
Long length = null;
if (type.equals(Column.Type.BINARY)
|| type.equals(Column.Type.VARBINARY)
|| type.equals(Column.Type.BLOB)
|| type.equals(Column.Type.OTHER)) {
length = ((BinaryColumn) pColumn).getLength();
if (length == null) {
throw new IllegalStateException("The length of column " + pColumn.getQName() + " is not set.");
}
} else if (type.equals(Column.Type.CHAR)
|| type.equals(Column.Type.VARCHAR)
|| type.equals(Column.Type.CLOB)) {
length = ((StringColumn) pColumn).getLength();
if (length == null) {
throw new IllegalStateException("The length of column " + pColumn.getQName() + " is not set.");
}
}
sb.append(getTypeName(type));
if (length != null) {
sb.append("(").append(length).append(")");
}
if (!pColumn.isNullable()) {
sb.append(" NOT NULL");
}
return sb.toString();
}
/** <p>Returns whether the primary key requires special handling
* (in which case {@link #isPrimaryKeyPartOfCreateTable()} and
* {@link #createPrimaryKeyAsPartOfCreateTable(Table)} are used)
* or nor (in which case {@link #isUniqueIndexPartOfCreateTable()}
* and {@link #createIndexAsPartOfCreateTable(Index)} apply).</p>
*/
protected boolean isPrimaryKeyUniqueIndex() { return false; }
/** <p>Returns whether a <code>CREATE TABLE</code> statement may contain
* a <code>PRIMARY KEY</code> clause.</p>
*/
protected boolean isPrimaryKeyPartOfCreateTable() { return false; }
/** <p>Returns whether a <code>CREATE TABLE</code> statement may contain
* a <code>UNIQUE</code> clause.</p>
*/
protected boolean isUniqueIndexPartOfCreateTable() { return false; }
/** <p>Returns whether a <code>CREATE TABLE</code> statement may contain
* an <code>INDEX</code> clause.</p>
*/
protected boolean isNonUniqueIndexPartOfCreateTable() { return false; }
/** <p>Returns whether a <code>CREATE TABLE</code> statement may contain
* a <code>FOREIGN KEY</code> clause.</p>
*/
protected boolean isForeignKeyPartOfCreateTable() { return false; }
protected String createPrimaryKeyAsPartOfCreateTable(Table pTable) {
Index index = pTable.getPrimaryKey();
if (index == null) {
return null;
}
StringBuffer sb = new StringBuffer();
sb.append("PRIMARY KEY").append(" (");
boolean first = true;
for (Iterator iter = index.getColumns(); iter.hasNext(); ) {
if (first) {
first = false;
} else {
sb.append(", ");
}
sb.append(((Column) iter.next()).getName().getName());
}
sb.append(")");
return sb.toString();
}
protected String createIndexAsPartOfCreateTable(Index pIndex) {
StringBuffer sb = new StringBuffer();
sb.append(pIndex.isUnique() ? "UNIQUE" : "KEY").append(" (");
boolean first = true;
for (Iterator iter = pIndex.getColumns(); iter.hasNext(); ) {
if (first) {
first = false;
} else {
sb.append(", ");
}
sb.append(((Column) iter.next()).getName().getName());
}
sb.append(")");
return sb.toString();
}
protected String createForeignKeyAsPartOfCreateTable(ForeignKey pKey) {
StringBuffer sb = new StringBuffer();
sb.append("FOREIGN KEY (");
boolean first = true;
Iterator iter = pKey.getColumnLinks();
if (!iter.hasNext()) {
throw new IllegalStateException("Foreign key on " +
pKey.getTable().getQName() +
" referencing " +
pKey.getReferencedTable().getQName() +
" doesn't have any columns.");
}
while (iter.hasNext()) {
ForeignKey.ColumnLink link = (ForeignKey.ColumnLink) iter.next();
if (first) {
first = false;
} else {
sb.append(", ");
}
sb.append(link.getLocalColumn().getName().getName());
}
sb.append(") REFERENCES ");
sb.append(pKey.getReferencedTable().getQName());
sb.append(" (");
first = true;
iter = pKey.getColumnLinks();
while (iter.hasNext()) {
ForeignKey.ColumnLink link = (ForeignKey.ColumnLink) iter.next();
if (first) {
first = false;
} else {
sb.append(", ");
}
sb.append(link.getReferencedColumn().getName().getName());
}
sb.append(")");
ForeignKey.Mode deleteMode = pKey.getOnDelete();
if (deleteMode != null) {
if (ForeignKey.Mode.CASCADE.equals(deleteMode)) {
sb.append(" ON DELETE CASCADE");
} else if (ForeignKey.Mode.REJECT.equals(deleteMode)) {
sb.append(" ON DELETE RESTRICT");
} else if (ForeignKey.Mode.SETNULL.equals(deleteMode)) {
sb.append(" ON DELETE SET NULL");
} else {
throw new IllegalStateException("Unknown foreign key mode for ON DELETE: " + deleteMode);
}
}
ForeignKey.Mode updateMode = pKey.getOnUpdate();
if (updateMode != null) {
if (ForeignKey.Mode.CASCADE.equals(updateMode)) {
sb.append(" ON UPDATE CASCADE");
} else if (ForeignKey.Mode.REJECT.equals(updateMode)) {
sb.append(" ON UPDATE RESTRICT");
} else {
throw new IllegalStateException("Unknown foreign key mode for ON UPDATE: " + updateMode);
}
}
return sb.toString();
}
protected String getCreateTableHeader(Table pTable) {
return "CREATE TABLE " + pTable.getQName();
}
public Collection getCreate(Table pTable) {
String lf = getLineTerminator() == null ? "" : getLineTerminator();
String indent = lf == null ? "" : getIndent();
StringBuffer sb = new StringBuffer();
sb.append(getCreateTableHeader(pTable)).append(" (");
String s = lf + indent;
for (Iterator iter = pTable.getColumns(); iter.hasNext(); ) {
sb.append(s).append(getCreate((Column) iter.next()));
s = "," + lf + indent;
}
for (Iterator iter = pTable.getIndexes(); iter.hasNext(); ) {
Index index = (Index) iter.next();
String st;
if (index.isPrimaryKey() && !isPrimaryKeyUniqueIndex()) {
if (!isPrimaryKeyPartOfCreateTable()) {
continue;
}
st = createPrimaryKeyAsPartOfCreateTable(pTable);
} else if (index.isUnique()) {
if (!isUniqueIndexPartOfCreateTable()) {
continue;
}
st = createIndexAsPartOfCreateTable(index);
} else {
if (!isNonUniqueIndexPartOfCreateTable()) {
continue;
}
st = createIndexAsPartOfCreateTable(index);
}
if (st != null) {
sb.append(s).append(st);
}
}
if (isForeignKeyPartOfCreateTable()) {
for (Iterator iter = pTable.getForeignKeys(); iter.hasNext(); ) {
ForeignKey key = (ForeignKey) iter.next();
String st = createForeignKeyAsPartOfCreateTable(key);
if (st != null) {
sb.append(s).append(st);
}
}
}
sb.append(lf).append(")").append(lf);
List result = new ArrayList();
result.add(newStatement(sb.toString()));
return result;
}
public Collection getDrop(Table pTable) {
List result = new ArrayList();
result.add(newStatement("DROP TABLE " + pTable.getQName()));
return result;
}
public String getInsertQuery(InsertStatement pQuery) {
StringBuffer result = new StringBuffer();
result.append("INSERT INTO ");
result.append(getTableAlias(pQuery.getTableReference()));
boolean first = true;
for (Iterator iter = pQuery.getSetValues(); iter.hasNext(); ) {
if (first) {
result.append(" (");
first = false;
} else {
result.append(", ");
}
SetStatement.SetValue setValue = (SetStatement.SetValue) iter.next();
result.append(getColumnAlias(null, setValue.getColumnReference()));
}
if (!first) {
result.append(")");
}
result.append(" VALUES (");
first = true;
for (Iterator iter = pQuery.getSetValues(); iter.hasNext(); ) {
if (first) {
first = false;
} else {
result.append(", ");
}
SetStatement.SetValue setValue = (SetStatement.SetValue) iter.next();
result.append(getValue(setValue.getValue()));
}
result.append(")");
return newStatement(result.toString());
}
protected String getValue(Value pValue) {
Value.Type type = pValue.getType();
Object o = pValue.getValue();
if (Value.Type.BOOLEAN.equals(type)) {
return o == null ? "null" : (((Boolean) o).booleanValue() ? "TRUE" : "FALSE");
} else if (Value.Type.BYTE.equals(type) ||
Value.Type.SHORT.equals(type) ||
Value.Type.INT.equals(type) ||
Value.Type.LONG.equals(type) ||
Value.Type.FLOAT.equals(type) ||
Value.Type.DOUBLE.equals(type)) {
return o == null ? "null" : o.toString();
} else if (Value.Type.DATE.equals(type) ||
Value.Type.DATETIME.equals(type) ||
Value.Type.TIME.equals(type)) {
throw new IllegalStateException("Date/time handling not yet implemented.");
} else if (Value.Type.DATE.equals(type)) {
throw new IllegalStateException("Date handling not yet implemented.");
} else if (Value.Type.PLACEHOLDER.equals(type)) {
return "?";
} else if (Value.Type.STRING.equals(type)) {
return o == null ? "null" : getEscapedString(o.toString());
} else if (Value.Type.NULL.equals(type)) {
return "null";
} else {
throw new IllegalStateException("Unknown value type: " + type);
}
}
protected String getUpdateQuery(UpdateStatement pQuery) {
StringBuffer sb = new StringBuffer();
sb.append("UPDATE ");
sb.append(getTableAlias(pQuery.getTableReference()));
sb.append(" SET ");
boolean first = true;
for (Iterator iter = pQuery.getSetValues(); iter.hasNext(); ) {
UpdateStatement.SetValue setValue = (UpdateStatement.SetValue) iter.next();
if (first) {
first = false;
} else {
sb.append(", ");
}
sb.append(getColumnAlias(null, setValue.getColumnReference()));
sb.append("=");
sb.append(getValue(setValue.getValue()));
}
String s = getWhereClause(null, pQuery.getWhere());
if (s != null) {
sb.append(" WHERE ").append(s);
}
return newStatement(sb.toString());
}
protected String getDeleteQuery(DeleteStatement pQuery) {
StringBuffer result = new StringBuffer("DELETE FROM ");
result.append(getTableAlias(pQuery.getTableReference()));
String s = getWhereClause(null, pQuery.getWhere());
if (s != null) {
result.append(" WHERE ");
result.append(s);
}
return result.toString();
}
protected boolean isQualifiedColumn(SelectStatementMetaData pData, ColumnReference pColumn) {
if (pData == null) {
return false;
}
Integer num = (Integer) pData.getColumnNames().get(pColumn.getColumn().getName().toString().toUpperCase());
if (num == null) {
throw new IllegalStateException("Column not in map of column counts.");
}
return num.intValue() > 1;
}
protected String getColumnAlias(SelectStatementMetaData pData, ColumnReference pColumn) {
Column col = pColumn.getColumn();
String s = col.getName().toString();
if (col.isVirtual()) {
VirtualColumn virtCol = (VirtualColumn) col;
return virtCol.getValue() + " AS " + s;
} else {
if (isQualifiedColumn(pData, pColumn)) {
TableReference tableReference = pColumn.getTableReference();
if (tableReference.getAlias() != null) {
s = tableReference.getAlias().getName() + "." + s;
} else {
s = tableReference.getTable().getName() + "." + s;
}
}
if (pColumn.getAlias() != null) {
s = s + " AS " + pColumn.getAlias().getName();
}
}
return s;
}
protected String getColumnAlias(SelectStatementMetaData pData, ColumnReference[] pColumn) {
StringBuffer sb = new StringBuffer("(");
for (int i = 0; i < pColumn.length; i++) {
if (i > 0) sb.append(", ");
sb.append(getColumnAlias(pData, pColumn[i]));
}
sb.append(")");
return sb.toString();
}
protected boolean isTableAliasUsingAs() { return true; }
protected String getTableAlias(TableReference pTable) {
Schema.Name schemaName = pTable.getTable().getSchema().getName();
String tableName = pTable.getTable().getName().getName();
if (schemaName != null) {
tableName = schemaName.getName() + "." + tableName;
}
Table.Name alias = pTable.getAlias();
if (alias != null) {
if (isTableAliasUsingAs()) {
tableName += " AS " + alias.getName();
} else {
tableName += " " + alias.getName();
}
}
return tableName;
}
protected String getJoinAlias(SelectStatementMetaData pData, JoinReference pJoinReference) {
StringBuffer result = new StringBuffer();
if (pJoinReference.isLeftOuterJoin()) {
result.append(" LEFT OUTER JOIN ");
} else if (pJoinReference.isJoin()) {
result.append(" JOIN ");
} else {
throw new IllegalStateException("Unknown join type");
}
result.append(getTableAlias(pJoinReference));
String s = getWhereClause(pData, pJoinReference.getOn());
if (s != null) {
result.append(" ON ");
result.append(s);
}
return result.toString();
}
protected String getEscapedString(String s) {
if (s.indexOf('\n') > -1 || s.indexOf('\r') > -1 || s.indexOf('\f') > -1) {
throw new IllegalArgumentException("Don't know how to handle line or page terminators.");
}
if (s.indexOf('\'') > -1) {
throw new IllegalArgumentException("Don't know how to handle the char ' in strings.");
}
return "'" + s + "'";
}
protected String getParts(SelectStatementMetaData pData, Iterator pParts) {
StringBuffer sb = new StringBuffer();
while (pParts.hasNext()) {
if (sb.length() > 0) {
sb.append(", ");
}
sb.append(getBooleanConstraintPart(pData, pParts.next()));
}
return sb.toString();
}
protected String getBooleanConstraintPart(SelectStatementMetaData pData, Object o) {
if (o instanceof Value) {
return getValue((Value) o);
} else if (o instanceof ColumnReference) {
return getColumnAlias(pData, (ColumnReference) o);
} else if (o instanceof ColumnReference[]) {
return getColumnAlias(pData, (ColumnReference[]) o);
} else if (o instanceof SelectStatement) {
return '(' + getQuery((SelectStatement) o) + ')';
} else if (o instanceof RawSQLCode) {
return ((RawSQLCode) o).getRawSQL();
} else if (o instanceof Function) {
Function f = (Function) o;
return f.getName() + '(' + getParts(pData, f.getParts()) + ')';
} else {
throw new IllegalArgumentException("Invalid part of a boolean constraint: " + o.getClass().getName());
}
}
protected String getBooleanConstraintType(BooleanConstraint.Type pType) {
if (BooleanConstraint.Type.EQ.equals(pType)) {
return "=";
} else if (BooleanConstraint.Type.NE.equals(pType)) {
return "<>";
} else if (BooleanConstraint.Type.GT.equals(pType)) {
return ">";
} else if (BooleanConstraint.Type.LT.equals(pType)) {
return "<";
} else if (BooleanConstraint.Type.GE.equals(pType)) {
return ">=";
} else if (BooleanConstraint.Type.LE.equals(pType)) {
return "<=";
} else if (BooleanConstraint.Type.LIKE.equals(pType)) {
return " LIKE ";
} else {
throw new IllegalArgumentException("Invalid type: " + pType);
}
}
protected String getBooleanConstraint(SelectStatementMetaData pData,
BooleanConstraint pConstraint) {
BooleanConstraint.Type type = pConstraint.getType();
Iterator parts = pConstraint.getParts();
if (!parts.hasNext()) {
throw new NullPointerException("A boolean constraint must have its parts set.");
}
if (BooleanConstraint.Type.IN.equals(type)) {
Object o = parts.next();
return getBooleanConstraintPart(pData, o) + " IN (" + getParts(pData, parts) + ')';
}
StringBuffer result = new StringBuffer();
int expected;
if (BooleanConstraint.Type.EXISTS.equals(type)) {
SelectStatement selectStatement = (SelectStatement) parts.next();
result.append("EXISTS(");
result.append(getSelectQuery(selectStatement, pData));
result.append(")");
expected = 1;
} else {
result.append(getBooleanConstraintPart(pData, parts.next()));
if (BooleanConstraint.Type.EQ.equals(type) ||
BooleanConstraint.Type.NE.equals(type) ||
BooleanConstraint.Type.GT.equals(type) ||
BooleanConstraint.Type.LT.equals(type) ||
BooleanConstraint.Type.GE.equals(type) ||
BooleanConstraint.Type.LE.equals(type) ||
BooleanConstraint.Type.LIKE.equals(type)) {
expected = 2;
if (!parts.hasNext()) {
throw new NullPointerException("The boolean constraint " + type +
" must have exactly two parts set.");
}
result.append(getBooleanConstraintType(type));
result.append(getBooleanConstraintPart(pData, parts.next()));
} else if (BooleanConstraint.Type.ISNULL.equals(type)) {
expected = 1;
result.append(" IS NULL");
} else {
throw new IllegalArgumentException("Invalid boolean constraint type: " + type);
}
}
if (expected != 0 && parts.hasNext()) {
throw new NullPointerException("The boolean constraint " + type +
" must have exactly " + expected +
" parts set, but has " + pConstraint.getNumParts());
}
return result.toString();
}
protected String getCombinedConstraint(SelectStatementMetaData pData,
CombinedConstraint pConstraint) {
if (pConstraint.getNumParts() == 0) {
return null;
}
List parts = new ArrayList();
for (Iterator iter = pConstraint.getParts(); iter.hasNext(); ) {
String s;
Object o = iter.next();
if (o == null) {
throw new NullPointerException("A CombinedConstraints part must not be null");
}
if (o instanceof CombinedConstraint) {
s = getCombinedConstraint(pData, (CombinedConstraint) o);
} else if (o instanceof BooleanConstraint) {
s = getBooleanConstraint(pData, (BooleanConstraint) o);
} else {
throw new IllegalArgumentException("Invalid part: " + o.getClass().getName());
}
if (s != null) {
parts.add(s);
}
}
switch (parts.size()) {
case 0: return null;
case 1: return (String) parts.get(0);
default:
StringBuffer sb = new StringBuffer();
for (int i = 0; i < parts.size(); i++) {
if (i == 0) {
sb.append("(");
} else {
sb.append(" ");
sb.append(pConstraint.getType());
sb.append(" ");
}
sb.append(parts.get(i));
}
if (parts.size() > 0) {
sb.append(")");
}
return sb.toString();
}
}
public String getConstraint(SelectStatementMetaData pData, Constraint pConstraint) {
if (pConstraint instanceof CombinedConstraint) {
return getWhereClause(pData, (CombinedConstraint) pConstraint);
} else if (pConstraint instanceof BooleanConstraint) {
return getBooleanConstraint(pData, (BooleanConstraint) pConstraint);
} else {
throw new IllegalArgumentException("Unknown constraint type: " +
pConstraint.getClass().getName());
}
}
protected String getWhereClause(SelectStatementMetaData pData,
CombinedConstraint pWhereClause) {
return getCombinedConstraint(pData, pWhereClause);
}
protected class SelectStatementMetaData {
private final Map aliases = new HashMap();
private final Map columnNames = new HashMap();
private final List tables = new ArrayList();
public SelectStatementMetaData(SelectStatement pQuery) {
addSelectStatement(pQuery);
createTableAliases();
createColumnNames();
}
protected void addSelectStatement(SelectStatement pQuery) {
for (Iterator tableIter = pQuery.getSelectTableReferences(); tableIter.hasNext(); ) {
TableReference tableReference = (TableReference) tableIter.next();
Table.Name alias = tableReference.getAlias();
if (alias != null) {
if (aliases.containsKey(alias.getName())) {
throw new NullPointerException("The alias " + alias +
" is used twice for the tables " +
((TableReference) aliases.get(alias)).getTable().getName() +
" and " + tableReference.getTable().getName());
}
aliases.put(alias.getName(), tableReference);
}
tables.add(tableReference);
if (tableReference instanceof JoinReference) {
addCombinedConstraint(((JoinReference) tableReference).getOn());
}
}
addCombinedConstraint(pQuery.getWhere());
}
protected void addCombinedConstraint(CombinedConstraint pConstraint) {
for (Iterator iter = pConstraint.getParts(); iter.hasNext(); ) {
Object o = iter.next();
if (o instanceof CombinedConstraint) {
addCombinedConstraint((CombinedConstraint) o);
} else if (o instanceof BooleanConstraint) {
addBooleanConstraint((BooleanConstraint) o);
} else {
throw new IllegalStateException("Invalid part type in CombinedConstraint: " + o);
}
}
}
protected void addParts(Parts pParts) {
for (Iterator iter = pParts.getParts(); iter.hasNext(); ) {
Object o = iter.next();
if (o instanceof SelectStatement) {
addSelectStatement((SelectStatement) o);
} else if (o instanceof CombinedConstraint) {
addCombinedConstraint((CombinedConstraint) o);
} else if (o instanceof Function) {
addParts((Function) o);
} else if (o instanceof ColumnReference
|| o instanceof Value
|| o instanceof RawSQLCode) {
// Ignore me
} else {
throw new IllegalStateException("Invalid part in boolean constraint: " + o);
}
}
}
protected void addBooleanConstraint(BooleanConstraint pConstraint) {
BooleanConstraint.Type type = pConstraint.getType();
if (BooleanConstraint.Type.EQ.equals(type)
|| BooleanConstraint.Type.EXISTS.equals(type)
|| BooleanConstraint.Type.GE.equals(type)
|| BooleanConstraint.Type.GT.equals(type)
|| BooleanConstraint.Type.IN.equals(type)
|| BooleanConstraint.Type.ISNULL.equals(type)
|| BooleanConstraint.Type.LE.equals(type)
|| BooleanConstraint.Type.LIKE.equals(type)
|| BooleanConstraint.Type.LT.equals(type)
|| BooleanConstraint.Type.NE.equals(type)) {
addParts(pConstraint);
} else {
throw new IllegalStateException("Invalid part type in BooleanConstraint: " + type);
}
}
protected String getUniqueAlias(String pSuggestion, Map pAliases) {
String prefix;
if (pSuggestion == null) {
prefix = "";
} else {
prefix = pSuggestion;
}
if (!pAliases.containsKey(prefix)) {
return prefix;
}
for (char c = '0'; c <= '9'; c++) {
String s = prefix + c;
if (!pAliases.containsKey(s)) {
return s;
}
}
for (char c = 'A'; c <= 'Z'; c++) {
String s = prefix + c;
if (!pAliases.containsKey(s)) {
return s;
}
}
return getUniqueAlias(prefix + '0', pAliases);
}
protected void createTableAliases() {
if (tables.size() > 1) {
// Make sure that all tables have an alias
for (Iterator iter = tables.iterator(); iter.hasNext(); ) {
TableReference tableReference = (TableReference) iter.next();
if (tableReference.getAlias() == null) {
String alias = getUniqueAlias(tableReference.getTable().getName().getName(), aliases);
aliases.put(alias, tableReference);
if (!alias.equals(tableReference.getTable().getName().getName())) {
tableReference.setAlias(alias);
}
}
}
}
}
protected void createColumnNames() {
// Create a Map of all column names, that may be referenced.
// maps key is the column name, and the maps value is the
// number of possible references. In other words: If an entry
// in the map has a value > 1, then its column name must be
// qualified, because it is used in multiple tables.
for (int i = 0; i < tables.size(); i++) {
TableReference table = (TableReference) tables.get(i);
for (Iterator iter = table.getTable().getColumns(); iter.hasNext(); ) {
Column col = (Column) iter.next();
String key = col.getName().toString().toUpperCase();
Integer num = (Integer) columnNames.get(key);
if (num == null) {
num = new Integer(1);
} else {
num = new Integer(num.intValue() + 1);
}
columnNames.put(key, num);
}
}
}
public Map getColumnNames() {
return columnNames;
}
}
protected String getSelectQuery(SelectStatement pQuery) {
SelectStatementMetaData selectStatementMetaData = new SelectStatementMetaData(pQuery);
return getSelectQuery(pQuery, selectStatementMetaData);
}
protected String getSelectQuery(SelectStatement pQuery, SelectStatementMetaData pData) {
StringBuffer sb = new StringBuffer("SELECT");
if (pQuery.isDistinct()) {
sb.append(" DISTINCT");
}
Iterator columnIter = pQuery.getResultColumns();
if (!columnIter.hasNext()) {
sb.append(" *");
} else {
boolean first = true;
do {
ColumnReference column = (ColumnReference) columnIter.next();
if (first) {
sb.append(" ");
first = false;
} else {
sb.append(", ");
}
sb.append(getColumnAlias(pData, column));
} while (columnIter.hasNext());
}
SelectTableReference selectTableReference = pQuery.getSelectTableReference();
if (selectTableReference != null) {
sb.append(" FROM ");
sb.append(getTableAlias(selectTableReference));
for (JoinReference joinReference = selectTableReference.getRightJoinedTableReference();
joinReference != null;
joinReference = joinReference.getRightJoinedTableReference()) {
sb.append(getJoinAlias(pData, joinReference));
}
}
String whereClause = getWhereClause(pData, pQuery.getWhere());
if (whereClause != null) {
sb.append(" WHERE ");
sb.append(whereClause);
}
return sb.toString();
}
public String getQuery(Statement pStatement) {
String s;
if (pStatement instanceof InsertStatement) {
s = getInsertQuery((InsertStatement) pStatement);
} else if (pStatement instanceof UpdateStatement) {
s = getUpdateQuery((UpdateStatement) pStatement);
} else if (pStatement instanceof DeleteStatement) {
s = getDeleteQuery((DeleteStatement) pStatement);
} else if (pStatement instanceof SelectStatement) {
s = getSelectQuery((SelectStatement) pStatement);
} else {
throw new IllegalArgumentException("The Statement is neither an INSERT, UPDATE, DELETE nor a SELECT statement.");
}
return newStatement(s);
}
public Collection getCreate(Schema pSchema, boolean pAll) {
if (!pAll) { return getCreate(pSchema); }
List result = new ArrayList();
result.addAll(getCreate(pSchema));
for (Iterator iter = pSchema.getTables(); iter.hasNext(); ) {
Table table = (Table) iter.next();
result.addAll(getCreate(table, true));
}
return result;
}
public Collection getDrop(Schema pSchema, boolean pAll) {
if (!pAll) { return getDrop(pSchema); }
List result = new ArrayList();
List tables = new ArrayList();
for (Iterator iter = pSchema.getTables(); iter.hasNext(); ) {
tables.add(iter.next());
}
Collections.reverse(tables);
for (Iterator iter = tables.iterator(); iter.hasNext(); ) {
Table table = (Table) iter.next();
result.addAll(getDrop(table, true));
}
result.addAll(getDrop(pSchema));
return result;
}
public Collection getCreate(Table pTable, boolean pAll) {
if (!pAll) { return getCreate(pTable); }
List result = new ArrayList();
result.addAll(getCreate(pTable));
for (Iterator iter = pTable.getIndexes(); iter.hasNext(); ) {
Index index = (Index) iter.next();
if (index.isPrimaryKey() && !isPrimaryKeyUniqueIndex()) {
if (isPrimaryKeyPartOfCreateTable()) {
continue;
}
} else if (index.isUnique()) {
if (isUniqueIndexPartOfCreateTable()) {
continue;
}
} else {
if (isNonUniqueIndexPartOfCreateTable()) {
continue;
}
}
result.addAll(getCreate(index));
}
if (!isForeignKeyPartOfCreateTable()) {
for (Iterator iter = pTable.getForeignKeys(); iter.hasNext(); ) {
ForeignKey key = (ForeignKey) iter.next();
result.addAll(getCreate(key));
}
}
return result;
}
public Collection getDrop(Table pTable, boolean pAll) {
if (!pAll) { return getDrop(pTable); }
List result = new ArrayList();
for (Iterator iter = pTable.getIndexes(); iter.hasNext(); ) {
Index index = (Index) iter.next();
result.addAll(getDrop(index));
}
for (Iterator iter = pTable.getForeignKeys(); iter.hasNext(); ) {
ForeignKey key = (ForeignKey) iter.next();
result.addAll(getDrop(key));
}
result.addAll(getDrop(pTable));
return result;
}
public Collection getCreate(Index pIndex) {
List result = new ArrayList();
String s = createIndexAsPartOfCreateTable(pIndex);
if (s != null) {
StringBuffer sb = new StringBuffer();
sb.append("CREATE");
if (pIndex.isUnique()) {
sb.append(" UNIQUE");
}
sb.append(" INDEX");
sb.append(" ");
sb.append(pIndex.getName());
sb.append(" ON ").append(pIndex.getTable().getQName()).append(" (");
boolean first = true;
for (Iterator iter = pIndex.getColumns(); iter.hasNext(); ) {
if (first) {
first = false;
} else {
sb.append(", ");
}
sb.append(((Column) iter.next()).getName().getName());
}
sb.append(")");
result.add(newStatement(sb.toString()));
}
return result;
}
public Collection getDrop(Index pIndex) {
return Collections.EMPTY_SET;
}
public Collection getCreate(ForeignKey pKey) {
List result = new ArrayList();
String s = createForeignKeyAsPartOfCreateTable(pKey);
if (s != null) {
result.add(newStatement("CREATE " + s));
}
return result;
}
public Collection getDrop(ForeignKey pKey) {
return Collections.EMPTY_SET;
}
public String getConstraint(Constraint pConstraint) {
if (pConstraint instanceof CombinedConstraint) {
CombinedConstraint cc = (CombinedConstraint) pConstraint;
Statement st = cc.getConstrainedStatement();
if (st instanceof SelectStatement) {
SelectStatementMetaData selectStatementMetaData = new SelectStatementMetaData((SelectStatement) st);
return getCombinedConstraint(selectStatementMetaData, cc);
} else {
return getCombinedConstraint(null, cc);
}
} else if (pConstraint instanceof BooleanConstraint) {
BooleanConstraint bc = (BooleanConstraint) pConstraint;
Statement st = bc.getConstrainedStatement();
if (st instanceof SelectStatement) {
SelectStatementMetaData selectStatementMetaData = new SelectStatementMetaData((SelectStatement) st);
return getBooleanConstraint(selectStatementMetaData, bc);
} else {
return getBooleanConstraint(null, bc);
}
} else {
throw new IllegalStateException("Invalid type of Constraint: " + pConstraint);
}
}
}