From 2b17d03b57207a7c544d5ecbba0b254479e90fc8 Mon Sep 17 00:00:00 2001 From: Jon Knight Date: Wed, 7 Jun 2023 15:22:01 +0100 Subject: [PATCH] Add morf-redshift --- morf-redshift/pom.xml | 53 + .../morf/jdbc/redshift/Redshift.java | 97 ++ .../morf/jdbc/redshift/RedshiftDialect.java | 826 ++++++++++ .../redshift/RedshiftMetaDataProvider.java | 174 ++ .../org.alfasoftware.morf.jdbc.DatabaseType | 1 + .../morf/jdbc/redshift/package.html | 9 + .../redshift/TestRedshiftDatabaseType.java | 112 ++ .../jdbc/redshift/TestRedshiftDialect.java | 1409 +++++++++++++++++ pom.xml | 3 +- 9 files changed, 2683 insertions(+), 1 deletion(-) create mode 100644 morf-redshift/pom.xml create mode 100644 morf-redshift/src/main/java/org/alfasoftware/morf/jdbc/redshift/Redshift.java create mode 100644 morf-redshift/src/main/java/org/alfasoftware/morf/jdbc/redshift/RedshiftDialect.java create mode 100644 morf-redshift/src/main/java/org/alfasoftware/morf/jdbc/redshift/RedshiftMetaDataProvider.java create mode 100644 morf-redshift/src/main/resources/META-INF/services/org.alfasoftware.morf.jdbc.DatabaseType create mode 100644 morf-redshift/src/main/resources/org/alfasoftware/morf/jdbc/redshift/package.html create mode 100644 morf-redshift/src/test/java/org/alfasoftware/morf/jdbc/redshift/TestRedshiftDatabaseType.java create mode 100644 morf-redshift/src/test/java/org/alfasoftware/morf/jdbc/redshift/TestRedshiftDialect.java diff --git a/morf-redshift/pom.xml b/morf-redshift/pom.xml new file mode 100644 index 000000000..445a793d7 --- /dev/null +++ b/morf-redshift/pom.xml @@ -0,0 +1,53 @@ + + 4.0.0 + + + org.alfasoftware + morf-parent + 2.5.1-SNAPSHOT + + + Morf - Redshift + Morf is a library for cross-platform evolutionary relational database mechanics, database access and database imaging/cloning. + https://github.com/alfasoftware/morf + + morf-redshift + + + ${basedir}/../${aggregate.report.dir} + + + + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + + + + + org.alfasoftware + morf-core + + + org.alfasoftware + morf-testsupport + test + + + log4j + log4j + test + + + \ No newline at end of file diff --git a/morf-redshift/src/main/java/org/alfasoftware/morf/jdbc/redshift/Redshift.java b/morf-redshift/src/main/java/org/alfasoftware/morf/jdbc/redshift/Redshift.java new file mode 100644 index 000000000..f528086ec --- /dev/null +++ b/morf-redshift/src/main/java/org/alfasoftware/morf/jdbc/redshift/Redshift.java @@ -0,0 +1,97 @@ +package org.alfasoftware.morf.jdbc.redshift; + +import java.sql.Connection; +import java.util.Optional; +import java.util.Stack; + +import javax.sql.XADataSource; + +import org.alfasoftware.morf.jdbc.AbstractDatabaseType; +import org.alfasoftware.morf.jdbc.JdbcUrlElements; +import org.alfasoftware.morf.jdbc.SqlDialect; +import org.alfasoftware.morf.metadata.Schema; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Support for Redshift database hosts. + * + * @author Copyright (c) Alfa Financial Software 2019 + */ +public final class Redshift extends AbstractDatabaseType { + + private static final Log log = LogFactory.getLog(Redshift.class); + + public static final String IDENTIFIER = "REDSHIFT"; + + public Redshift() { + super("com.amazon.redshift.Driver", IDENTIFIER); + } + + + @Override + public String formatJdbcUrl(JdbcUrlElements jdbcUrlElements) { + return "jdbc:redshift://" + + jdbcUrlElements.getHostName() + + (jdbcUrlElements.getPort() == 0 ? "" : ":" + jdbcUrlElements.getPort()) + + "/" + jdbcUrlElements.getDatabaseName() + ; + } + + + @Override + public Schema openSchema(Connection connection, String databaseName, String schemaName) { + return new RedshiftMetaDataProvider(connection, schemaName); + } + + + /** + * Returns a Redshift XA data source. + * + * @throws IllegalStateException If the data source cannot be created. + * + * @see org.alfasoftware.morf.jdbc.DatabaseType#getXADataSource(String, String, String) + */ + @Override + public XADataSource getXADataSource(String jdbcUrl, String username, String password) { + throw new UnsupportedOperationException("Morf-Redshift does not implement XA connections. "); + } + + + @Override + public SqlDialect sqlDialect(String schemaName) { + return new RedshiftDialect(schemaName); + } + + + @Override + public boolean matchesProduct(String product) { + return product.equalsIgnoreCase("Redshift"); + } + + + @Override + public Optional extractJdbcUrl(String url) { + Stack splitURL = splitJdbcUrl(url); + + String scheme = splitURL.pop(); + + if (!scheme.equalsIgnoreCase("redshift")) { + return Optional.empty(); + } + + if (!splitURL.pop().equals("://")) { + // If the next characters are not "://" then die + throw new IllegalArgumentException("Expected '//' to follow the scheme name in [" + url + "]"); + } + + JdbcUrlElements.Builder connectionDetails = extractHostAndPort(splitURL); + + // Now get the path + String path = extractPath(splitURL); + + connectionDetails.withDatabaseName(path); + + return Optional.of(connectionDetails.build()); + } +} diff --git a/morf-redshift/src/main/java/org/alfasoftware/morf/jdbc/redshift/RedshiftDialect.java b/morf-redshift/src/main/java/org/alfasoftware/morf/jdbc/redshift/RedshiftDialect.java new file mode 100644 index 000000000..e0ddc13af --- /dev/null +++ b/morf-redshift/src/main/java/org/alfasoftware/morf/jdbc/redshift/RedshiftDialect.java @@ -0,0 +1,826 @@ +package org.alfasoftware.morf.jdbc.redshift; + +import static org.alfasoftware.morf.metadata.SchemaUtils.namesOfColumns; +import static org.alfasoftware.morf.metadata.SchemaUtils.primaryKeysForTable; +import static org.alfasoftware.morf.sql.SelectStatement.select; +import static org.alfasoftware.morf.sql.SqlUtils.field; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import java.util.StringJoiner; +import org.alfasoftware.morf.jdbc.DatabaseType; +import org.alfasoftware.morf.jdbc.NamedParameterPreparedStatement; +import org.alfasoftware.morf.jdbc.SqlDialect; +import org.alfasoftware.morf.metadata.Column; +import org.alfasoftware.morf.metadata.DataType; +import org.alfasoftware.morf.metadata.DataValueLookup; +import org.alfasoftware.morf.metadata.Index; +import org.alfasoftware.morf.metadata.SchemaUtils; +import org.alfasoftware.morf.metadata.Table; +import org.alfasoftware.morf.metadata.View; +import org.alfasoftware.morf.sql.DeleteStatement; +import org.alfasoftware.morf.sql.DeleteStatementBuilder; +import org.alfasoftware.morf.sql.Hint; +import org.alfasoftware.morf.sql.MergeStatement; +import org.alfasoftware.morf.sql.OptimiseForRowCount; +import org.alfasoftware.morf.sql.ParallelQueryHint; +import org.alfasoftware.morf.sql.SelectFirstStatement; +import org.alfasoftware.morf.sql.SelectStatement; +import org.alfasoftware.morf.sql.SelectStatementBuilder; +import org.alfasoftware.morf.sql.UseImplicitJoinOrder; +import org.alfasoftware.morf.sql.UseIndex; +import org.alfasoftware.morf.sql.element.AliasedField; +import org.alfasoftware.morf.sql.element.BlobFieldLiteral; +import org.alfasoftware.morf.sql.element.Cast; +import org.alfasoftware.morf.sql.element.ConcatenatedField; +import org.alfasoftware.morf.sql.element.Function; +import org.alfasoftware.morf.sql.element.FunctionType; +import org.alfasoftware.morf.sql.element.SqlParameter; +import org.alfasoftware.morf.sql.element.TableReference; +import org.apache.commons.lang3.StringUtils; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +class RedshiftDialect extends SqlDialect { + + public RedshiftDialect(String schemaName) { + super(schemaName); + } + + + @Override + public DatabaseType getDatabaseType() { + return DatabaseType.Registry.findByIdentifier(Redshift.IDENTIFIER); + } + + + @Override + public String schemaNamePrefix() { + String schemaName = getSchemaName(); + + if (StringUtils.isEmpty(schemaName)) { + return ""; + } + + return schemaName + "."; + } + + + private String schemaNamePrefix(TableReference tableRef) { + if(tableRef.isTemporary()) { + return ""; + } + if (StringUtils.isEmpty(tableRef.getSchemaName())) { + return schemaNamePrefix(); + } else { + return tableRef.getSchemaName() + "."; + } + } + + + @Override + protected String schemaNamePrefix(Table table) { + if (table.isTemporary()) { + return ""; + } + return schemaNamePrefix(); + } + + + @Override + protected String getSqlForRowNumber(){ + return "ROW_NUMBER() OVER()"; + } + + + @Override + protected String getSqlForWindowFunction(Function function) { + FunctionType functionType = function.getType(); + switch (functionType) { + case ROW_NUMBER: + return "ROW_NUMBER()"; + + default: + return super.getSqlForWindowFunction(function); + } + } + + + @Override + protected String getDataTypeRepresentation(DataType dataType, int width, int scale) { + switch (dataType) { + case STRING: + return String.format("VARCHAR(%d)", width); + + case DECIMAL: + return String.format("DECIMAL(%d,%d)", width, scale); + + case DATE: + return "DATE"; + + case BOOLEAN: + return "BOOLEAN"; + + case BIG_INTEGER: + return "NUMERIC(19)"; + + case INTEGER: + return "INTEGER"; + + case BLOB: + return "BYTEA"; + + case CLOB: + return "TEXT"; + + default: + throw new UnsupportedOperationException("Cannot map column with type [" + dataType + "]"); + } + } + + + @Override + protected String getColumnRepresentation(DataType dataType, int width, int scale) { + if (dataType == DataType.STRING) { + return getDataTypeRepresentation(dataType, width, scale) + " COLLATE \"POSIX\""; + } + + return getDataTypeRepresentation(dataType, width, scale); + } + + + @Override + protected String getSqlFrom(Cast cast) { + if (cast.getDataType() == DataType.STRING) { + return super.getSqlFrom(cast) + " COLLATE \"POSIX\""; + } + + return super.getSqlFrom(cast); + } + + + @Override + protected Collection internalTableDeploymentStatements(Table table) { + return ImmutableList.builder() + .addAll(createTableStatement(table)) + .addAll(createCommentStatements(table)) + .build(); + } + + + @Override + public Collection addTableFromStatementsWithCasting(Table table, SelectStatement selectStatement) { + return internalAddTableFromStatements(table, selectStatement, true); + } + + + private Collection internalAddTableFromStatements(Table table, SelectStatement selectStatement, boolean withCasting) { + return ImmutableList.builder() + .addAll(createTableStatement(table, selectStatement, withCasting)) + .addAll(createFieldStatements(table)) + .addAll(createCommentStatements(table)) + .addAll(createAllIndexStatements(table)) + .build(); + } + + + private List createTableStatement(Table table) { + List preStatements = new ArrayList<>(); + List postStatements = new ArrayList<>(); + + StringBuilder createTableStatement = new StringBuilder(); + beginTableStatement(table, createTableStatement); + + List primaryKeys = new ArrayList<>(); + boolean first = true; + + for (Column column : table.columns()) { + if (!first) { + createTableStatement.append(", "); + } + + createTableStatement.append(column.getName()); + createTableStatement.append(" ").append(sqlRepresentationOfColumnType(column)); + handleAutoNumberedColumn(table, preStatements, postStatements, createTableStatement, column, false); + + if (column.isPrimaryKey()) { + primaryKeys.add(column.getName()); + } + + first = false; + } + + if (!primaryKeys.isEmpty()) { + createTableStatement + .append(", CONSTRAINT ") + .append(table.getName()) + .append("_PK PRIMARY KEY(") + .append(Joiner.on(", ").join(primaryKeys)) + .append(")"); + } + + createTableStatement.append(")"); + + ImmutableList.Builder statements = ImmutableList.builder() + .addAll(preStatements) + .add(createTableStatement.toString()); + + statements.addAll(postStatements); + + return statements.build(); + } + + + private List createTableStatement(Table table, SelectStatement asSelect, boolean withCasting) { + List preStatements = new ArrayList<>(); + List postStatements = new ArrayList<>(); + + StringBuilder createTableStatement = new StringBuilder(); + beginTableStatement(table, createTableStatement); + + boolean first = true; + + for (Column column : table.columns()) { + if (!first) { + createTableStatement.append(", "); + } + + createTableStatement.append(column.getName()); + createTableStatement.append(sqlRepresentationOfColumnType(column, false, false, false)); + handleAutoNumberedColumn(table, preStatements, postStatements, createTableStatement, column, true); + + first = false; + } + + createTableStatement.append(")"); + + String selectStatement = withCasting ? convertStatementToSQL(addCastsToSelect(table, asSelect)) : convertStatementToSQL(asSelect); + createTableStatement.append(" AS ").append(selectStatement); + + ImmutableList.Builder statements = ImmutableList.builder() + .addAll(preStatements) + .add(createTableStatement.toString()); + + statements.addAll(postStatements); + + return statements.build(); + } + + + private void beginTableStatement(Table table, StringBuilder createTableStatement) { + createTableStatement.append("CREATE "); + + if (table.isTemporary()) { + createTableStatement.append("TEMP "); + } + + createTableStatement.append("TABLE ") + .append(schemaNamePrefix(table)) + .append(table.getName()) + .append(" ("); + } + + + private void handleAutoNumberedColumn(Table table, List preStatements, List postStatements, StringBuilder createTableStatement, Column column, boolean asSelect) { + if(column.isAutoNumbered()) { + int autoNumberStart = column.getAutoNumberStart() == -1 ? 1 : column.getAutoNumberStart(); + String autoNumberSequenceName = schemaNamePrefix() + table.getName() + "_" + column.getName() + "_seq"; + preStatements.add("DROP SEQUENCE IF EXISTS " + autoNumberSequenceName); + preStatements.add("CREATE SEQUENCE " + autoNumberSequenceName + " START " + autoNumberStart); + + if (asSelect) { + postStatements.add("ALTER TABLE " + table.getName() + " ALTER " + column.getName() + " SET DEFAULT nextval('" + autoNumberSequenceName + "')"); + } else { + createTableStatement.append(" DEFAULT nextval('").append(autoNumberSequenceName).append("')"); + } + + postStatements.add("ALTER SEQUENCE " + autoNumberSequenceName + " OWNED BY " + schemaNamePrefix() + table.getName() + "." + column.getName()); + } + } + + + private List createFieldStatements(Table table) { + List fieldStatements = new ArrayList<>(); + List primaryKeys = new ArrayList<>(); + + StringJoiner joiner = new StringJoiner(",", "ALTER TABLE " + table.getName(), ""); + + for (Column column : table.columns()) { + if (column.isPrimaryKey()) { + primaryKeys.add(column.getName()); + } + + if (!column.isNullable()) { + joiner.add(" ALTER " + column.getName() + " SET NOT NULL"); + } + + if (StringUtils.isNotEmpty(column.getDefaultValue()) && !column.isAutoNumbered()) { + joiner.add(" ALTER " + column.getName() + " SET DEFAULT " + column.getDefaultValue()); + } + } + + if (!primaryKeys.isEmpty()) { + joiner.add(" ADD CONSTRAINT " + table.getName() + "_PK PRIMARY KEY(" + Joiner.on(", ").join(primaryKeys) + ")"); + } + + fieldStatements.add(joiner.toString()); + return fieldStatements; + } + + + private Collection createCommentStatements(Table table) { + List commentStatements = Lists.newArrayList(); + + commentStatements.add(addTableComment(table)); + for (Column column : table.columns()) { + commentStatements.add(addColumnComment(table, column)); + } + + return commentStatements; + } + + + private String addTableComment(Table table) { + return "COMMENT ON TABLE " + schemaNamePrefix(table) + table.getName() + " IS '"+REAL_NAME_COMMENT_LABEL+":[" + table.getName() + "]'"; + } + + + @Override + public Collection renameTableStatements(Table from, Table to) { + Iterable renameTable = ImmutableList.of("ALTER TABLE " + schemaNamePrefix(from) + from.getName() + " RENAME TO " + to.getName()); + + Iterable renamePk = SchemaUtils.primaryKeysForTable(from).isEmpty() + ? ImmutableList.of() + : renameIndexStatements(from, from.getName() + "_pk", to.getName() + "_pk"); + + Iterable renameSeq = SchemaUtils.autoNumbersForTable(from).isEmpty() + ? ImmutableList.of() + : renameSequenceStatements(from.getName() + "_seq", to.getName() + "_seq"); + + return ImmutableList.builder() + .addAll(renameTable) + .addAll(renamePk) + .addAll(renameSeq) + .add(addTableComment(to)) + .build(); + } + + + @Override + public Collection renameIndexStatements(Table table, String fromIndexName, String toIndexName) { + return ImmutableList.builder() + .addAll(super.renameIndexStatements(table, fromIndexName, toIndexName)) + .add(addIndexComment(toIndexName)) + .build(); + } + + + private Collection renameSequenceStatements(String fromSeqName, String toSeqName) { + return ImmutableList.of(String.format("ALTER SEQUENCE %s RENAME TO %s", fromSeqName, toSeqName)); + } + + + @Override + public Collection changePrimaryKeyColumns(Table table, List oldPrimaryKeyColumns, List newPrimaryKeyColumns) { + List result = new ArrayList<>(); + + if (!oldPrimaryKeyColumns.isEmpty()) { + result.add(dropPrimaryKeyConstraint(table)); + } + + if (!newPrimaryKeyColumns.isEmpty()) { + result.add(addPrimaryKeyConstraint(table)); + } + + return result; + } + + + @Override + public Collection viewDeploymentStatements(View view) { + return ImmutableList.builder() + .addAll(super.viewDeploymentStatements(view)) + .add(addViewComment(view.getName())) + .build(); + } + + + private String addViewComment(String viewName) { + return "COMMENT ON VIEW " + viewName + " IS '"+REAL_NAME_COMMENT_LABEL+":[" + viewName + "]'"; + } + + + @Override + public String connectionTestStatement() { + return "SELECT 1"; + } + + + @Override + public Collection dropStatements(View view) { + return ImmutableList.of("DROP VIEW IF EXISTS " + schemaNamePrefix() + view.getName() + " CASCADE"); + } + + + @Override + protected String getFromDummyTable() { + return ""; + } + + + @Override + protected String getSqlFrom(ConcatenatedField concatenatedField) { + List sql = new ArrayList<>(); + for (AliasedField field : concatenatedField.getConcatenationFields()) { + sql.add(getSqlFrom(field)); + } + return "CONCAT(" + StringUtils.join(sql, ", ") + ")"; + } + + @Override + protected String getSqlFrom(BlobFieldLiteral field) { + return String.format("E'\\x%s'", field.getValue()); + } + + @Override + protected String getSqlForDaysBetween(AliasedField toDate, AliasedField fromDate) { + return "(" + getSqlFrom(toDate) + ") - (" + getSqlFrom(fromDate) + ")"; + } + + + @Override + protected String getSqlForMonthsBetween(AliasedField toDate, AliasedField fromDate) { + String toDateStr = getSqlFrom(toDate); + String fromDateStr = getSqlFrom(fromDate); + return "(" + + "(EXTRACT(YEAR FROM "+toDateStr+") - EXTRACT(YEAR FROM "+fromDateStr+")) * 12" + + " + (EXTRACT(MONTH FROM "+toDateStr+") - EXTRACT(MONTH FROM "+fromDateStr+"))" + + " + CASE WHEN "+toDateStr+" > "+fromDateStr + + " THEN CASE WHEN EXTRACT(DAY FROM "+toDateStr+") >= EXTRACT(DAY FROM "+fromDateStr+") THEN 0" + + " WHEN EXTRACT(MONTH FROM "+toDateStr+") <> EXTRACT(MONTH FROM "+toDateStr+" + 1) THEN 0" + + " ELSE -1 END" + + " ELSE CASE WHEN EXTRACT(MONTH FROM "+fromDateStr+") <> EXTRACT(MONTH FROM "+fromDateStr+" + 1) THEN 0" + + " WHEN EXTRACT(DAY FROM "+fromDateStr+") >= EXTRACT(DAY FROM "+toDateStr+") THEN 0" + + " ELSE 1 END" + + " END" + + ")"; + } + + + @Override + protected String getSqlForLastDayOfMonth(AliasedField date) { + return String.format( + "(DATE_TRUNC('MONTH', (%s)) + INTERVAL '1 MONTH' - INTERVAL '1 DAY') :: DATE", + getSqlFrom(date)); + } + + + @Override + protected String getSqlForDateToYyyymmdd(Function function) { + AliasedField field = function.getArguments().get(0); + return "TO_CHAR("+ getSqlFrom(field) + ",'YYYYMMDD') :: NUMERIC"; + } + + + @Override + protected String getSqlForDateToYyyymmddHHmmss(Function function) { + AliasedField field = function.getArguments().get(0); + return "TO_CHAR("+ getSqlFrom(field) + ",'YYYYMMDDHH24MISS') :: NUMERIC"; + } + + + @Override + protected String getSqlForYYYYMMDDToDate(Function function) { + AliasedField field = function.getArguments().get(0); + return "TO_DATE(("+ getSqlFrom(field) + ") :: TEXT,'YYYYMMDD')"; + } + + + @Override + protected String getSqlForNow(Function function) { + return "NOW()"; + } + + + @Override + protected String getSqlForAddDays(Function function) { + AliasedField date = function.getArguments().get(0); + AliasedField days = function.getArguments().get(1); + return String.format( + "(((%s) + (%s) * INTERVAL '1 DAY') :: DATE)", + getSqlFrom(date), getSqlFrom(days)); + } + + + @Override + protected String getSqlForAddMonths(Function function) { + AliasedField date = function.getArguments().get(0); + AliasedField months = function.getArguments().get(1); + return String.format( + "(((%s) + (%s) * INTERVAL '1 MONTH') :: DATE)", + getSqlFrom(date), getSqlFrom(months)); + } + + + @Override + protected String getSqlForRandomString(Function function) { + String lengthSql = getSqlFrom(function.getArguments().get(0)); + String randomString = "MD5(RANDOM() :: TEXT)"; + return "UPPER(SUBSTRING(" + randomString + ", 1, (" + lengthSql + ") :: INT))"; + } + + + @Override + protected String getSqlForRandom() { + return "RANDOM()"; + } + + + @Override + protected String getSqlForRound(Function function) { + return "ROUND((" + getSqlFrom(function.getArguments().get(0)) + ") :: NUMERIC, " + getSqlFrom(function.getArguments().get(1)) + ")"; + } + + + @Override + protected String getSqlFrom(MergeStatement statement) { + if (StringUtils.isBlank(statement.getTable().getName())) { + throw new IllegalArgumentException("Cannot create SQL for a blank table"); + } + + checkSelectStatementHasNoHints(statement.getSelectStatement(), "MERGE may not be used with SELECT statement hints"); + + Iterable destinationFields = Iterables.transform( + statement.getSelectStatement().getFields(), + AliasedField::getImpliedName); + + Iterable updateExpressions = getMergeStatementUpdateExpressions(statement); + String updateExpressionsSql = getMergeStatementAssignmentsSql(updateExpressions); + + Iterable keyFields = Iterables.transform( + statement.getTableUniqueKey(), + AliasedField::getImpliedName); + + StringBuilder sqlBuilder = new StringBuilder(); + + sqlBuilder.append("INSERT INTO ") + .append(tableNameWithSchemaName(statement.getTable())) + .append(" (") + .append(Joiner.on(", ").join(destinationFields)) + .append(") ") + .append(getSqlFrom(statement.getSelectStatement())) + .append(" ON CONFLICT (") + .append(Joiner.on(",").join(keyFields)) + .append(")"); + + if (getNonKeyFieldsFromMergeStatement(statement).iterator().hasNext()) { + sqlBuilder.append(" DO UPDATE SET ") + .append(updateExpressionsSql); + } else { + sqlBuilder.append(" DO NOTHING"); + } + + return sqlBuilder.toString(); + } + + + @Override + protected String getSqlFrom(MergeStatement.InputField field) { + return "EXCLUDED." + field.getName(); + } + + + @Override + protected String getSqlFrom(SelectFirstStatement stmt) { + StringBuilder result = new StringBuilder("SELECT "); + // Start by adding the field + result.append(getSqlFrom(stmt.getFields().get(0))); + + appendFrom(result, stmt); + appendJoins(result, stmt, innerJoinKeyword(stmt)); + appendWhere(result, stmt); + appendOrderBy(result, stmt); + + result.append(" LIMIT 1 OFFSET 0"); + + return result.toString().trim(); + } + + + @Override + protected String selectStatementPreFieldDirectives(SelectStatement selectStatement) { + //TODO - we don't have indexes or hints for Redshift so we return an empty string + return ""; + } + + + @Override + public Collection alterTableAddColumnStatements(Table table, Column column) { + return ImmutableList.of("ALTER TABLE " + schemaNamePrefix(table) + table.getName() + " ADD COLUMN " + column.getName() + " " + sqlRepresentationOfColumnType(column, true), + addColumnComment(table, column)); + } + + + @Override + public Collection getSqlForAnalyseTable(Table table) { + return ImmutableList.of("ANALYZE " + schemaNamePrefix(table) + table.getName()); + } + + + @Override + public Collection alterTableChangeColumnStatements(Table table, Column oldColumn, Column newColumn) { + List statements = new ArrayList<>(); + + Table oldTable = oldTableForChangeColumn(table, oldColumn, newColumn); + + boolean recreatePrimaryKey = oldColumn.isPrimaryKey() || newColumn.isPrimaryKey(); + + if (recreatePrimaryKey && !primaryKeysForTable(oldTable).isEmpty()) { + statements.add(dropPrimaryKeyConstraint(table)); + } + + if (oldColumn.isAutoNumbered() && !newColumn.isAutoNumbered()) { + String autoNumberSequenceName = schemaNamePrefix() + table.getName() + "_" + oldColumn.getName() + "_seq"; + statements.add("DROP SEQUENCE IF EXISTS " + autoNumberSequenceName + " CASCADE"); + } + + if(!oldColumn.getName().equalsIgnoreCase(newColumn.getName())) { + statements.add("ALTER TABLE " + schemaNamePrefix(table) + table.getName() + " RENAME " + oldColumn.getName() + " TO " + newColumn.getName()); + } + + boolean alterNullable = oldColumn.isNullable() != newColumn.isNullable(); + boolean alterType = oldColumn.getType() != newColumn.getType() || oldColumn.getScale() != newColumn.getScale() || oldColumn.getWidth() != newColumn.getWidth(); + boolean alterDefaultValue = oldColumn.getDefaultValue() != newColumn.getDefaultValue(); + + if(alterNullable || alterType || alterDefaultValue) { + statements.add(addAlterTableConstraint(table, newColumn, alterNullable, alterType, alterDefaultValue)); + } + + if (recreatePrimaryKey && !primaryKeysForTable(table).isEmpty()) { + statements.add(addPrimaryKeyConstraint(table)); + } + + statements.add(addColumnComment(table, newColumn)); + + return statements; + } + + + private String addAlterTableConstraint(Table table, Column newColumn, boolean alterNullable, boolean alterType, + boolean alterDefaultValue) { + StringBuilder sqlBuilder = new StringBuilder(); + sqlBuilder.append("ALTER TABLE " + schemaNamePrefix(table) + table.getName() + + (alterNullable ? " ALTER COLUMN " + newColumn.getName() + (newColumn.isNullable() ? " DROP NOT NULL" : " SET NOT NULL") : "") + + (alterNullable && alterType ? "," : "") + + (alterType ? " ALTER COLUMN " + newColumn.getName() + " TYPE " + sqlRepresentationOfColumnType(newColumn, false, false, true) : "") + + (alterDefaultValue && (alterNullable || alterType) ? "," : "") + + (alterDefaultValue ? " ALTER COLUMN " + newColumn.getName() + (!newColumn.getDefaultValue().isEmpty() ? " SET DEFAULT " + sqlForDefaultClauseLiteral(newColumn) : " DROP DEFAULT") : "") + ); + return sqlBuilder.toString(); + } + + + private String addColumnComment(Table table, Column column) { + StringBuilder comment = new StringBuilder ("COMMENT ON COLUMN " + schemaNamePrefix(table) + table.getName() + "." + column.getName() + " IS '"+REAL_NAME_COMMENT_LABEL+":[" + column.getName() + "]/TYPE:[" + column.getType().toString() + "]"); + if(column.isAutoNumbered()) { + int autoNumberStart = column.getAutoNumberStart() == -1 ? 1 : column.getAutoNumberStart(); + comment.append("/AUTONUMSTART:[" + autoNumberStart + "]"); + } + comment.append("'"); + return comment.toString(); + } + + + private String dropPrimaryKeyConstraint(Table table) { + return "ALTER TABLE " + schemaNamePrefix(table) + table.getName() + " DROP CONSTRAINT " + table.getName() + "_PK"; + } + + + private String addPrimaryKeyConstraint(Table table) { + return "ALTER TABLE " + schemaNamePrefix(table) + table.getName() + " ADD CONSTRAINT " + table.getName() + "_PK PRIMARY KEY(" + Joiner.on(", ").join(namesOfColumns(SchemaUtils.primaryKeysForTable(table))) + ")"; + } + + + @Override + public Collection alterTableDropColumnStatements(Table table, Column column) { + return ImmutableList.of("ALTER TABLE " + schemaNamePrefix(table) + table.getName() + " DROP COLUMN " + column.getName()); + } + + + @Override + protected Collection indexDeploymentStatements(Table table, Index index) { + StringBuilder statement = new StringBuilder(); + + statement.append("CREATE "); + if (index.isUnique()) { + statement.append("UNIQUE "); + } + statement.append("INDEX ") + .append(index.getName()) + .append(" ON ") + .append(schemaNamePrefix(table)) + .append(table.getName()) + .append(" (") + .append(Joiner.on(", ").join(index.columnNames())) + .append(")"); + + return ImmutableList.builder() + .add(statement.toString()) + .add(addIndexComment(index.getName())) + .build(); + } + + + private String addIndexComment(String indexName) { + return "COMMENT ON INDEX " + indexName + " IS '"+REAL_NAME_COMMENT_LABEL+":[" + indexName + "]'"; + } + + + @Override + public void prepareStatementParameters(NamedParameterPreparedStatement statement, DataValueLookup values, SqlParameter parameter) throws SQLException { + switch (parameter.getMetadata().getType()) { + case BLOB: + byte[] blobVal = values.getByteArray(parameter.getImpliedName()); + if (blobVal == null) { + InputStream inputStream = new ByteArrayInputStream(new byte[]{}); + statement.setBinaryStream(parameter, inputStream); + } else { + InputStream inputStream = new ByteArrayInputStream(blobVal); + statement.setBinaryStream(parameter, inputStream); + } + return; + + default: + super.prepareStatementParameters(statement, values, parameter); + return; + } + } + + + @Override + protected String getSqlFrom(Boolean literalValue) { + return literalValue ? "TRUE" : "FALSE"; + } + + + @Override + protected String getSqlForSome(Function function) { + return "BOOL_OR(" + getSqlFrom(function.getArguments().get(0)) + ")"; + } + + + @Override + protected String getSqlForEvery(Function function) { + return "BOOL_AND(" + getSqlFrom(function.getArguments().get(0)) + ")"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.SqlDialect#getSqlFrom(DeleteStatement) + */ + @Override + protected String getSqlFrom(DeleteStatement statement) { + if (!statement.getLimit().isPresent()) { + return super.getSqlFrom(statement); + } + + StringBuilder sqlBuilder = new StringBuilder(); + + DeleteStatementBuilder deleteStatement = DeleteStatement.delete(statement.getTable()); + sqlBuilder.append(super.getSqlFrom(deleteStatement.build())); + + // Now add the limit clause, using the current table id. + sqlBuilder.append(" WHERE ctid IN ("); + + SelectStatementBuilder selectStatement = select().fields(field("ctid")).from(statement.getTable()); + if (statement.getWhereCriterion() != null ) { + selectStatement = selectStatement.where(statement.getWhereCriterion()); + } + sqlBuilder.append(getSqlFrom(selectStatement.build())); + + sqlBuilder.append(" LIMIT " + statement.getLimit().get() + ")"); + + return sqlBuilder.toString(); + } + + + /** + * @see org.alfasoftware.morf.jdbc.SqlDialect#tableNameWithSchemaName(org.alfasoftware.morf.sql.element.TableReference) + */ + @Override + protected String tableNameWithSchemaName(TableReference tableRef) { + if (StringUtils.isEmpty(tableRef.getDblink())) { + return schemaNamePrefix(tableRef) + tableRef.getName(); + } else { + return tableRef.getDblink() + "." + tableRef.getName(); + } + } +} \ No newline at end of file diff --git a/morf-redshift/src/main/java/org/alfasoftware/morf/jdbc/redshift/RedshiftMetaDataProvider.java b/morf-redshift/src/main/java/org/alfasoftware/morf/jdbc/redshift/RedshiftMetaDataProvider.java new file mode 100644 index 000000000..3977a42fb --- /dev/null +++ b/morf-redshift/src/main/java/org/alfasoftware/morf/jdbc/redshift/RedshiftMetaDataProvider.java @@ -0,0 +1,174 @@ +package org.alfasoftware.morf.jdbc.redshift; + +import static org.alfasoftware.morf.jdbc.DatabaseMetaDataProviderUtils.getAutoIncrementStartValue; +import static org.alfasoftware.morf.jdbc.DatabaseMetaDataProviderUtils.getDataTypeFromColumnComment; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Types; +import java.util.Map; +import java.util.Optional; +import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.alfasoftware.morf.jdbc.DatabaseMetaDataProvider; +import org.alfasoftware.morf.jdbc.RuntimeSqlException; +import org.alfasoftware.morf.metadata.DataType; +import org.alfasoftware.morf.metadata.SchemaUtils.ColumnBuilder; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableMap; + +/** + * Provides meta data from a Redshift database connection. + * + * @author Copyright (c) Alfa Financial Software 2019 + */ +public class RedshiftMetaDataProvider extends DatabaseMetaDataProvider { + + private static final Log log = LogFactory.getLog(RedshiftMetaDataProvider.class); + + private static final Pattern REALNAME_COMMENT_MATCHER = Pattern.compile(".*"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":\\[([^\\]]*)\\](/TYPE:\\[([^\\]]*)\\])?.*"); + + private final Supplier> allIndexNames = Suppliers.memoize(this::loadAllIndexNames); + + public RedshiftMetaDataProvider(Connection connection, String schemaName) { + super(connection, schemaName); + } + + + @Override + protected boolean isPrimaryKeyIndex(RealName indexName) { + return indexName.getDbName().endsWith("_pk"); + } + + + @Override + protected DataType dataTypeFromSqlType(int sqlType, String typeName, int width) { + + if (sqlType == Types.VARCHAR) { + if (typeName.equals("text")) { + return DataType.CLOB; + } + return super.dataTypeFromSqlType(sqlType, typeName, width); + } + + return super.dataTypeFromSqlType(sqlType, typeName, width); + } + + + @Override + protected ColumnBuilder setAdditionalColumnMetadata(RealName tableName, ColumnBuilder columnBuilder, ResultSet columnMetaData) throws SQLException { + columnBuilder = super.setAdditionalColumnMetadata(tableName, columnBuilder, columnMetaData); + + // read autonumber from comments + if (columnBuilder.isAutoNumbered()) { + int startValue = getAutoIncrementStartValue(columnMetaData.getString(COLUMN_REMARKS)); + columnBuilder = columnBuilder.autoNumbered(startValue == -1 ? 1 : startValue); + } + + // read datatype from comments + Optional dataTypeComment = getDataTypeFromColumnComment(columnMetaData.getString(COLUMN_REMARKS)); + if(dataTypeComment.isPresent() && dataTypeComment.get().equals("BIG_INTEGER")){ + columnBuilder = columnBuilder.dataType(DataType.BIG_INTEGER); + } + + return columnBuilder; + } + + + @Override + protected RealName readColumnName(ResultSet columnResultSet) throws SQLException { + String columnName = columnResultSet.getString(COLUMN_NAME); + String comment = columnResultSet.getString(COLUMN_REMARKS); + String realName = matchComment(comment); + return StringUtils.isNotBlank(realName) + ? createRealName(columnName, realName) + : super.readColumnName(columnResultSet); + } + + + @Override + protected RealName readTableName(ResultSet tableResultSet) throws SQLException { + String tableName = tableResultSet.getString(TABLE_NAME); + String comment = tableResultSet.getString(TABLE_REMARKS); + String realName = matchComment(comment); + return StringUtils.isNotBlank(realName) + ? createRealName(tableName, realName) + : super.readTableName(tableResultSet); + } + + + @Override + protected RealName readViewName(ResultSet viewResultSet) throws SQLException { + String viewName = viewResultSet.getString(TABLE_NAME); + String comment = viewResultSet.getString(TABLE_REMARKS); + String realName = matchComment(comment); + return StringUtils.isNotBlank(realName) + ? createRealName(viewName, realName) + : super.readViewName(viewResultSet); + } + + + protected Map loadAllIndexNames() { + final ImmutableMap.Builder indexNames = ImmutableMap.builder(); + + String schema = StringUtils.isNotBlank(schemaName) + ? " JOIN pg_catalog.pg_namespace n ON n.oid = ci.relnamespace AND n.nspname = '" + schemaName + "'" + : ""; + + String sql = "SELECT ci.relname AS indexName, d.description AS indexRemark" + + " FROM pg_catalog.pg_index i" + + " JOIN pg_catalog.pg_class ci ON ci.oid = i.indexrelid" + + schema + + " JOIN pg_description d ON d.objoid = ci.oid"; + + try (Statement createStatement = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)) { + try (ResultSet indexResultSet = createStatement.executeQuery(sql)) { + while (indexResultSet.next()) { + String indexName = indexResultSet.getString(1); + String comment = indexResultSet.getString(2); + String realName = matchComment(comment); + + if (log.isDebugEnabled()) { + log.debug("Found index [" + indexName + "] with remark [" + comment + "] parsed as [" + realName + "] in schema [" + schemaName + "]"); + } + + if (StringUtils.isNotBlank(realName)) { + RealName realIndexName = createRealName(indexName, realName); + indexNames.put(realIndexName, realIndexName); + } + } + + return indexNames.build(); + } + } + catch (SQLException e) { + throw new RuntimeSqlException(e); + } + } + + + @Override + protected RealName readIndexName(ResultSet indexResultSet) throws SQLException { + RealName readIndexName = super.readIndexName(indexResultSet); + return allIndexNames.get().getOrDefault(readIndexName, readIndexName); + } + + + private String matchComment(String comment) { + if (StringUtils.isNotBlank(comment)) { + Matcher matcher = REALNAME_COMMENT_MATCHER.matcher(comment); + if (matcher.matches()) { + return matcher.group(1); + } + } + return null; + } +} diff --git a/morf-redshift/src/main/resources/META-INF/services/org.alfasoftware.morf.jdbc.DatabaseType b/morf-redshift/src/main/resources/META-INF/services/org.alfasoftware.morf.jdbc.DatabaseType new file mode 100644 index 000000000..a3f41d605 --- /dev/null +++ b/morf-redshift/src/main/resources/META-INF/services/org.alfasoftware.morf.jdbc.DatabaseType @@ -0,0 +1 @@ +org.alfasoftware.morf.jdbc.redshift.Redshift \ No newline at end of file diff --git a/morf-redshift/src/main/resources/org/alfasoftware/morf/jdbc/redshift/package.html b/morf-redshift/src/main/resources/org/alfasoftware/morf/jdbc/redshift/package.html new file mode 100644 index 000000000..0db9a2962 --- /dev/null +++ b/morf-redshift/src/main/resources/org/alfasoftware/morf/jdbc/redshift/package.html @@ -0,0 +1,9 @@ + + + + Package details + + +

Implementations specific to Redshift databases.

+ + diff --git a/morf-redshift/src/test/java/org/alfasoftware/morf/jdbc/redshift/TestRedshiftDatabaseType.java b/morf-redshift/src/test/java/org/alfasoftware/morf/jdbc/redshift/TestRedshiftDatabaseType.java new file mode 100644 index 000000000..72a2cf4fa --- /dev/null +++ b/morf-redshift/src/test/java/org/alfasoftware/morf/jdbc/redshift/TestRedshiftDatabaseType.java @@ -0,0 +1,112 @@ +package org.alfasoftware.morf.jdbc.redshift; + +import static org.alfasoftware.morf.jdbc.DatabaseTypeIdentifierTestUtils.mockDataSourceFor; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.verify; + +import java.sql.SQLException; +import java.util.Optional; + +import javax.sql.DataSource; + +import org.alfasoftware.morf.jdbc.DatabaseType; +import org.alfasoftware.morf.jdbc.DatabaseTypeIdentifier; +import org.alfasoftware.morf.jdbc.JdbcUrlElements; +import org.alfasoftware.morf.jdbc.JdbcUrlElements.Builder; +import org.junit.Before; +import org.junit.Test; + +/** + * Tests for DatabaseType.PGSQL + * + * @author Copyright (c) Alfa Financial Software 2019 + */ +public class TestRedshiftDatabaseType { + + private DatabaseType databaseType; + + @Before + public void setup() { + databaseType = DatabaseType.Registry.findByIdentifier(Redshift.IDENTIFIER); + } + + + /** + * Test the JDBC URL construction. + */ + @Test + public void testFormatJdbcUrl() { + assertEquals("jdbc:redshift://foo.com:123/alfa", databaseType.formatJdbcUrl(jdbcUrlElementBuilder().withHost("foo.com").withPort(123).withDatabaseName("alfa").build())); + assertEquals("jdbc:redshift://foo.com/alfa", databaseType.formatJdbcUrl(jdbcUrlElementBuilder().withHost("foo.com").withDatabaseName("alfa").build())); + assertEquals("jdbc:redshift://localhost/data", databaseType.formatJdbcUrl(jdbcUrlElementBuilder().withHost("localhost").withDatabaseName("data").build())); + } + + + /** + * Tests Redshift formatted JDBC URLs. + */ + @Test + public void testBuildConnectionDetailsFromMySQL() { + JdbcUrlElements result = databaseType.extractJdbcUrl("jdbc:redshift://localhost:3306/alfa").get(); + + assertEquals("Should have the correct type", Redshift.IDENTIFIER, result.getDatabaseType()); + assertEquals("Should have the correct host", "localhost", result.getHostName()); + assertEquals("Should have the correct port", 3306, result.getPort()); + assertEquals("Should have the correct database name", "alfa", result.getDatabaseName()); + } + + + /** + * Tests Redshift formatted JDBC URLs. + */ + @Test + public void testBuildConnectionDetailsFromMySQLNoPort() { + JdbcUrlElements result = databaseType.extractJdbcUrl("jdbc:redshift://localhost/alfa").get(); + + assertEquals("Should have the correct type", Redshift.IDENTIFIER, result.getDatabaseType()); + assertEquals("Should have the correct host", "localhost", result.getHostName()); + assertEquals("Should have the correct database name", "alfa", result.getDatabaseName()); + } + + + /** + * Checks the bidirectionality of our URL split and combine behaviour. + */ + @Test + public void testUrlRoundTrips() { + comparerUrlRoundtrips(JdbcUrlElements.forDatabaseType(Redshift.IDENTIFIER).withHost("hostname").withPort(3306).withDatabaseName("databasename").build()); + comparerUrlRoundtrips(JdbcUrlElements.forDatabaseType(Redshift.IDENTIFIER).withHost("hostname").withDatabaseName("databasename").build()); + } + + + /** + * Tests a URL <- split -> URL round-trip. + */ + private void comparerUrlRoundtrips(JdbcUrlElements jdbcUrlElements) { + String jdbcURL = databaseType.formatJdbcUrl(jdbcUrlElements); + JdbcUrlElements cd = databaseType.extractJdbcUrl(jdbcURL).get(); + assertEquals(jdbcUrlElements, cd); + } + + + /** + * Test identification of a database platform. + */ + @Test + public void testIdentifyFromMetaData() throws SQLException { + // -- Unknown and resource management... + // + DataSource dataSource = mockDataSourceFor("FictiousDB", "9.9.9", 9, 9); + assertEquals(Optional.empty(), new DatabaseTypeIdentifier(dataSource).identifyFromMetaData()); + verify(dataSource.getConnection()).close(); + + // -- Support platforms... + // + assertEquals(databaseType, new DatabaseTypeIdentifier(mockDataSourceFor("Redshift", "1.3.167", 1, 3)).identifyFromMetaData().get()); + } + + + private Builder jdbcUrlElementBuilder() { + return JdbcUrlElements.forDatabaseType(Redshift.IDENTIFIER); + } +} diff --git a/morf-redshift/src/test/java/org/alfasoftware/morf/jdbc/redshift/TestRedshiftDialect.java b/morf-redshift/src/test/java/org/alfasoftware/morf/jdbc/redshift/TestRedshiftDialect.java new file mode 100644 index 000000000..44259dcdf --- /dev/null +++ b/morf-redshift/src/test/java/org/alfasoftware/morf/jdbc/redshift/TestRedshiftDialect.java @@ -0,0 +1,1409 @@ +package org.alfasoftware.morf.jdbc.redshift; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; + +import java.io.InputStream; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.alfasoftware.morf.jdbc.AbstractSqlDialectTest; +import org.alfasoftware.morf.jdbc.SqlDialect; +import org.alfasoftware.morf.sql.SelectStatement; +import org.alfasoftware.morf.sql.element.ConcatenatedField; +import org.alfasoftware.morf.sql.element.FieldLiteral; +import org.alfasoftware.morf.sql.element.FieldReference; +import org.alfasoftware.morf.sql.element.SqlParameter; +import org.alfasoftware.morf.sql.element.TableReference; +import org.mockito.Mockito; + +import com.google.common.collect.ImmutableList; + +/** + * Tests SQL statements generated for Redshift. + * + * @author Copyright (c) Alfa Financial Software 2019 + */ +public class TestRedshiftDialect extends AbstractSqlDialectTest { + + @Override + protected String expectedSelectFirstOrderByNullsLastDesc() { + return "SELECT stringField FROM testschema.Alternate ORDER BY stringField DESC NULLS LAST LIMIT 1 OFFSET 0"; + } + + + @Override + protected String expectedRowNumber() { + return "ROW_NUMBER() OVER()"; + } + + + @Override + protected String expectedRandomFunction() { + return "RANDOM()"; + } + + + @Override + protected String expectedRound() { + return "SELECT ROUND((field1) :: NUMERIC, 2) FROM " + tableName("schedule"); + } + + + @Override + protected String expectedSqlForMathOperationsForExistingDataFix1() { + return "ROUND((doublevalue / 1000 * doublevalue) :: NUMERIC, 2)"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#createTestDialect() + */ + @Override + protected SqlDialect createTestDialect() { + return new RedshiftDialect("testschema"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedCreateTableStatements() + */ + @Override + protected List expectedCreateTableStatements() { + return Arrays + .asList( + "CREATE TABLE testschema.Test (id NUMERIC(19) NOT NULL, version INTEGER DEFAULT 0, stringField VARCHAR(3) COLLATE \"POSIX\", intField INTEGER, floatField DECIMAL(13,2) NOT NULL, dateField DATE, booleanField BOOLEAN, charField VARCHAR(1) COLLATE \"POSIX\", blobField BYTEA, bigIntegerField NUMERIC(19) DEFAULT 12345, clobField TEXT, CONSTRAINT Test_PK PRIMARY KEY(id))", + "COMMENT ON TABLE testschema.Test IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[Test]'", + "COMMENT ON COLUMN testschema.Test.id IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[id]/TYPE:[BIG_INTEGER]'", + "COMMENT ON COLUMN testschema.Test.version IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[version]/TYPE:[INTEGER]'", + "COMMENT ON COLUMN testschema.Test.stringField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[stringField]/TYPE:[STRING]'", + "COMMENT ON COLUMN testschema.Test.intField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[intField]/TYPE:[INTEGER]'", + "COMMENT ON COLUMN testschema.Test.floatField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[floatField]/TYPE:[DECIMAL]'", + "COMMENT ON COLUMN testschema.Test.dateField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[dateField]/TYPE:[DATE]'", + "COMMENT ON COLUMN testschema.Test.booleanField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[booleanField]/TYPE:[BOOLEAN]'", + "COMMENT ON COLUMN testschema.Test.charField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[charField]/TYPE:[STRING]'", + "COMMENT ON COLUMN testschema.Test.blobField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[blobField]/TYPE:[BLOB]'", + "COMMENT ON COLUMN testschema.Test.bigIntegerField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[bigIntegerField]/TYPE:[BIG_INTEGER]'", + "COMMENT ON COLUMN testschema.Test.clobField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[clobField]/TYPE:[CLOB]'", + "CREATE UNIQUE INDEX Test_NK ON testschema.Test (stringField)", + "COMMENT ON INDEX Test_NK IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[Test_NK]'", + "CREATE UNIQUE INDEX Test_1 ON testschema.Test (intField, floatField)", + "COMMENT ON INDEX Test_1 IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[Test_1]'", + "CREATE TABLE testschema.Alternate (id NUMERIC(19) NOT NULL, version INTEGER DEFAULT 0, stringField VARCHAR(3) COLLATE \"POSIX\", CONSTRAINT Alternate_PK PRIMARY KEY(id))", + "COMMENT ON TABLE testschema.Alternate IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[Alternate]'", + "COMMENT ON COLUMN testschema.Alternate.id IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[id]/TYPE:[BIG_INTEGER]'", + "COMMENT ON COLUMN testschema.Alternate.version IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[version]/TYPE:[INTEGER]'", + "COMMENT ON COLUMN testschema.Alternate.stringField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[stringField]/TYPE:[STRING]'", + "CREATE INDEX Alternate_1 ON testschema.Alternate (stringField)", + "COMMENT ON INDEX Alternate_1 IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[Alternate_1]'", + "CREATE TABLE testschema.NonNull (id NUMERIC(19) NOT NULL, version INTEGER DEFAULT 0, stringField VARCHAR(3) COLLATE \"POSIX\" NOT NULL, intField DECIMAL(8,0) NOT NULL, booleanField BOOLEAN NOT NULL, dateField DATE NOT NULL, blobField BYTEA NOT NULL, CONSTRAINT NonNull_PK PRIMARY KEY(id))", + "COMMENT ON TABLE testschema.NonNull IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[NonNull]'", + "COMMENT ON COLUMN testschema.NonNull.id IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[id]/TYPE:[BIG_INTEGER]'", + "COMMENT ON COLUMN testschema.NonNull.version IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[version]/TYPE:[INTEGER]'", + "COMMENT ON COLUMN testschema.NonNull.stringField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[stringField]/TYPE:[STRING]'", + "COMMENT ON COLUMN testschema.NonNull.intField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[intField]/TYPE:[DECIMAL]'", + "COMMENT ON COLUMN testschema.NonNull.booleanField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[booleanField]/TYPE:[BOOLEAN]'", + "COMMENT ON COLUMN testschema.NonNull.dateField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[dateField]/TYPE:[DATE]'", + "COMMENT ON COLUMN testschema.NonNull.blobField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[blobField]/TYPE:[BLOB]'", + "CREATE TABLE testschema.CompositePrimaryKey (id NUMERIC(19) NOT NULL, version INTEGER DEFAULT 0, stringField VARCHAR(3) COLLATE \"POSIX\" NOT NULL, secondPrimaryKey VARCHAR(3) COLLATE \"POSIX\" NOT NULL, CONSTRAINT CompositePrimaryKey_PK PRIMARY KEY(id, secondPrimaryKey))", + "COMMENT ON TABLE testschema.CompositePrimaryKey IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[CompositePrimaryKey]'", + "COMMENT ON COLUMN testschema.CompositePrimaryKey.id IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[id]/TYPE:[BIG_INTEGER]'", + "COMMENT ON COLUMN testschema.CompositePrimaryKey.version IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[version]/TYPE:[INTEGER]'", + "COMMENT ON COLUMN testschema.CompositePrimaryKey.stringField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[stringField]/TYPE:[STRING]'", + "COMMENT ON COLUMN testschema.CompositePrimaryKey.secondPrimaryKey IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[secondPrimaryKey]/TYPE:[STRING]'", + "DROP SEQUENCE IF EXISTS testschema.AutoNumber_intField_seq", + "CREATE SEQUENCE testschema.AutoNumber_intField_seq START 5", + "CREATE TABLE testschema.AutoNumber (intField NUMERIC(19) DEFAULT nextval('testschema.AutoNumber_intField_seq'), CONSTRAINT AutoNumber_PK PRIMARY KEY(intField))", + "ALTER SEQUENCE testschema.AutoNumber_intField_seq OWNED BY testschema.AutoNumber.intField", + "COMMENT ON TABLE testschema.AutoNumber IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[AutoNumber]'", + "COMMENT ON COLUMN testschema.AutoNumber.intField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[intField]/TYPE:[BIG_INTEGER]/AUTONUMSTART:[5]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedCreateTemporaryTableStatements() + */ + @Override + protected List expectedCreateTemporaryTableStatements() { + return Arrays + .asList( + "CREATE TEMP TABLE TempTest (id NUMERIC(19) NOT NULL, version INTEGER DEFAULT 0, stringField VARCHAR(3) COLLATE \"POSIX\", intField INTEGER, floatField DECIMAL(13,2) NOT NULL, dateField DATE, booleanField BOOLEAN, charField VARCHAR(1) COLLATE \"POSIX\", blobField BYTEA, bigIntegerField NUMERIC(19) DEFAULT 12345, clobField TEXT, CONSTRAINT TempTest_PK PRIMARY KEY(id))", + "COMMENT ON TABLE TempTest IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[TempTest]'", + "COMMENT ON COLUMN TempTest.id IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[id]/TYPE:[BIG_INTEGER]'", + "COMMENT ON COLUMN TempTest.version IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[version]/TYPE:[INTEGER]'", + "COMMENT ON COLUMN TempTest.stringField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[stringField]/TYPE:[STRING]'", + "COMMENT ON COLUMN TempTest.intField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[intField]/TYPE:[INTEGER]'", + "COMMENT ON COLUMN TempTest.floatField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[floatField]/TYPE:[DECIMAL]'", + "COMMENT ON COLUMN TempTest.dateField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[dateField]/TYPE:[DATE]'", + "COMMENT ON COLUMN TempTest.booleanField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[booleanField]/TYPE:[BOOLEAN]'", + "COMMENT ON COLUMN TempTest.charField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[charField]/TYPE:[STRING]'", + "COMMENT ON COLUMN TempTest.blobField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[blobField]/TYPE:[BLOB]'", + "COMMENT ON COLUMN TempTest.bigIntegerField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[bigIntegerField]/TYPE:[BIG_INTEGER]'", + "COMMENT ON COLUMN TempTest.clobField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[clobField]/TYPE:[CLOB]'", + "CREATE UNIQUE INDEX TempTest_NK ON TempTest (stringField)", + "COMMENT ON INDEX TempTest_NK IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[TempTest_NK]'", + "CREATE INDEX TempTest_1 ON TempTest (intField, floatField)", + "COMMENT ON INDEX TempTest_1 IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[TempTest_1]'", + "CREATE TEMP TABLE TempAlternate (id NUMERIC(19) NOT NULL, version INTEGER DEFAULT 0, stringField VARCHAR(3) COLLATE \"POSIX\", CONSTRAINT TempAlternate_PK PRIMARY KEY(id))", + "COMMENT ON TABLE TempAlternate IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[TempAlternate]'", + "COMMENT ON COLUMN TempAlternate.id IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[id]/TYPE:[BIG_INTEGER]'", + "COMMENT ON COLUMN TempAlternate.version IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[version]/TYPE:[INTEGER]'", + "COMMENT ON COLUMN TempAlternate.stringField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[stringField]/TYPE:[STRING]'", + "CREATE INDEX TempAlternate_1 ON TempAlternate (stringField)", + "COMMENT ON INDEX TempAlternate_1 IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[TempAlternate_1]'", + "CREATE TEMP TABLE TempNonNull (id NUMERIC(19) NOT NULL, version INTEGER DEFAULT 0, stringField VARCHAR(3) COLLATE \"POSIX\" NOT NULL, intField DECIMAL(8,0) NOT NULL, booleanField BOOLEAN NOT NULL, dateField DATE NOT NULL, blobField BYTEA NOT NULL, CONSTRAINT TempNonNull_PK PRIMARY KEY(id))", + "COMMENT ON TABLE TempNonNull IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[TempNonNull]'", + "COMMENT ON COLUMN TempNonNull.id IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[id]/TYPE:[BIG_INTEGER]'", + "COMMENT ON COLUMN TempNonNull.version IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[version]/TYPE:[INTEGER]'", + "COMMENT ON COLUMN TempNonNull.stringField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[stringField]/TYPE:[STRING]'", + "COMMENT ON COLUMN TempNonNull.intField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[intField]/TYPE:[DECIMAL]'", + "COMMENT ON COLUMN TempNonNull.booleanField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[booleanField]/TYPE:[BOOLEAN]'", + "COMMENT ON COLUMN TempNonNull.dateField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[dateField]/TYPE:[DATE]'", + "COMMENT ON COLUMN TempNonNull.blobField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[blobField]/TYPE:[BLOB]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedCreateTableStatementsWithLongTableName() + */ + @Override + protected List expectedCreateTableStatementsWithLongTableName() { + return Arrays + .asList("CREATE TABLE testschema." + + TABLE_WITH_VERY_LONG_NAME + + " (id NUMERIC(19) NOT NULL, version INTEGER DEFAULT 0, stringField VARCHAR(3) COLLATE \"POSIX\", intField DECIMAL(8,0), floatField DECIMAL(13,2) NOT NULL, dateField DATE, booleanField BOOLEAN, charField VARCHAR(1) COLLATE \"POSIX\", CONSTRAINT " + TABLE_WITH_VERY_LONG_NAME + "_PK PRIMARY KEY(id))", + "COMMENT ON TABLE testschema.tableWithANameThatExceedsTwentySevenCharactersToMakeSureSchemaNameDoesNotGetFactoredIntoOracleNameTruncation IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[tableWithANameThatExceedsTwentySevenCharactersToMakeSureSchemaNameDoesNotGetFactoredIntoOracleNameTruncation]'", + "COMMENT ON COLUMN testschema.tableWithANameThatExceedsTwentySevenCharactersToMakeSureSchemaNameDoesNotGetFactoredIntoOracleNameTruncation.id IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[id]/TYPE:[BIG_INTEGER]'", + "COMMENT ON COLUMN testschema.tableWithANameThatExceedsTwentySevenCharactersToMakeSureSchemaNameDoesNotGetFactoredIntoOracleNameTruncation.version IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[version]/TYPE:[INTEGER]'", + "COMMENT ON COLUMN testschema.tableWithANameThatExceedsTwentySevenCharactersToMakeSureSchemaNameDoesNotGetFactoredIntoOracleNameTruncation.stringField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[stringField]/TYPE:[STRING]'", + "COMMENT ON COLUMN testschema.tableWithANameThatExceedsTwentySevenCharactersToMakeSureSchemaNameDoesNotGetFactoredIntoOracleNameTruncation.intField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[intField]/TYPE:[DECIMAL]'", + "COMMENT ON COLUMN testschema.tableWithANameThatExceedsTwentySevenCharactersToMakeSureSchemaNameDoesNotGetFactoredIntoOracleNameTruncation.floatField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[floatField]/TYPE:[DECIMAL]'", + "COMMENT ON COLUMN testschema.tableWithANameThatExceedsTwentySevenCharactersToMakeSureSchemaNameDoesNotGetFactoredIntoOracleNameTruncation.dateField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[dateField]/TYPE:[DATE]'", + "COMMENT ON COLUMN testschema.tableWithANameThatExceedsTwentySevenCharactersToMakeSureSchemaNameDoesNotGetFactoredIntoOracleNameTruncation.booleanField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[booleanField]/TYPE:[BOOLEAN]'", + "COMMENT ON COLUMN testschema.tableWithANameThatExceedsTwentySevenCharactersToMakeSureSchemaNameDoesNotGetFactoredIntoOracleNameTruncation.charField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[charField]/TYPE:[STRING]'", + "CREATE UNIQUE INDEX Test_NK ON testschema.tableWithANameThatExceedsTwentySevenCharactersToMakeSureSchemaNameDoesNotGetFactoredIntoOracleNameTruncation (stringField)", + "COMMENT ON INDEX Test_NK IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[Test_NK]'", + "CREATE INDEX Test_1 ON testschema.tableWithANameThatExceedsTwentySevenCharactersToMakeSureSchemaNameDoesNotGetFactoredIntoOracleNameTruncation (intField, floatField)", + "COMMENT ON INDEX Test_1 IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[Test_1]'" + ); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedDropTableStatements() + */ + @Override + protected List expectedDropTableStatements() { + return Arrays.asList("DROP TABLE testschema.Test"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedDropSingleTable() + */ + @Override + protected List expectedDropSingleTable() { + return Arrays.asList("DROP TABLE testschema.Test"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedDropTables() + */ + @Override + protected List expectedDropTables() { + return Arrays.asList("DROP TABLE testschema.Test, testschema.Other"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedDropTablesWithParameters() + */ + @Override + protected List expectedDropTablesWithParameters() { + return Arrays.asList("DROP TABLE IF EXISTS testschema.Test, testschema.Other CASCADE"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedDropTempTableStatements() + */ + @Override + protected List expectedDropTempTableStatements() { + return Arrays.asList("DROP TABLE TempTest"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedTruncateTableStatements() + */ + @Override + protected List expectedTruncateTableStatements() { + return Arrays.asList("TRUNCATE TABLE testschema.Test"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedTruncateTempTableStatements() + */ + @Override + protected List expectedTruncateTempTableStatements() { + return Arrays.asList("TRUNCATE TABLE TempTest"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedDeleteAllFromTableStatements() + */ + @Override + protected List expectedDeleteAllFromTableStatements() { + return Arrays.asList("DELETE FROM testschema.Test"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedParameterisedInsertStatement() + */ + @Override + protected String expectedParameterisedInsertStatement() { + return "INSERT INTO testschema.Test (id, version, stringField, intField, floatField, dateField, booleanField, charField, blobField, bigIntegerField, clobField) VALUES (5, :version, 'Escap''d', 7, :floatField, 20100405, TRUE, :charField, :blobField, :bigIntegerField, :clobField)"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedParameterisedInsertStatementWithTableInDifferentSchema() + */ + @Override + protected String expectedParameterisedInsertStatementWithTableInDifferentSchema() { + return "INSERT INTO MYSCHEMA.Test (id, version, stringField, intField, floatField, dateField, booleanField, charField, blobField, bigIntegerField, clobField) VALUES (5, :version, 'Escap''d', 7, :floatField, 20100405, TRUE, :charField, :blobField, :bigIntegerField, :clobField)"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAutoGenerateIdStatement() + */ + @Override + protected List expectedAutoGenerateIdStatement() { + return Arrays.asList( + "DELETE FROM idvalues where name = 'Test'", + "INSERT INTO idvalues (name, value) VALUES('Test', (SELECT COALESCE(MAX(id) + 1, 1) AS CurrentValue FROM testschema.Test))", + "INSERT INTO testschema.Test (version, stringField, id) SELECT version, stringField, (SELECT COALESCE(value, 0) FROM idvalues WHERE (name = 'Test')) + Other.id FROM testschema.Other" + ); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedInsertWithIdAndVersion() + */ + @Override + protected List expectedInsertWithIdAndVersion() { + return Arrays.asList( + "DELETE FROM idvalues where name = 'Test'", + "INSERT INTO idvalues (name, value) VALUES('Test', (SELECT COALESCE(MAX(id) + 1, 1) AS CurrentValue FROM testschema.Test))", + "INSERT INTO testschema.Test (stringField, id, version) SELECT stringField, (SELECT COALESCE(value, 0) FROM idvalues WHERE (name = 'Test')) + Other.id, 0 AS version FROM testschema.Other" + ); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedSpecifiedValueInsert() + */ + @Override + protected List expectedSpecifiedValueInsert() { + return Arrays.asList( + "DELETE FROM idvalues where name = 'Test'", + "INSERT INTO idvalues (name, value) VALUES('Test', (SELECT COALESCE(MAX(id) + 1, 1) AS CurrentValue FROM testschema.Test))", + "INSERT INTO testschema.Test (stringField, intField, floatField, dateField, booleanField, charField, id, version, blobField, bigIntegerField, clobField) VALUES ('Escap''d', 7, 11.25, 20100405, TRUE, 'X', (SELECT COALESCE(value, 1) FROM idvalues WHERE (name = 'Test')), 0, null, 12345, null)" + ); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedSpecifiedValueInsertWithTableInDifferentSchema() + */ + @Override + protected List expectedSpecifiedValueInsertWithTableInDifferentSchema() { + return Arrays.asList( + "DELETE FROM idvalues where name = 'Test'", + "INSERT INTO idvalues (name, value) VALUES('Test', (SELECT COALESCE(MAX(id) + 1, 1) AS CurrentValue FROM MYSCHEMA.Test))", + "INSERT INTO MYSCHEMA.Test (stringField, intField, floatField, dateField, booleanField, charField, id, version, blobField, bigIntegerField, clobField) VALUES ('Escap''d', 7, 11.25, 20100405, TRUE, 'X', (SELECT COALESCE(value, 1) FROM idvalues WHERE (name = 'Test')), 0, null, 12345, null)" + ); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedParameterisedInsertStatementWithNoColumnValues() + */ + @Override + protected String expectedParameterisedInsertStatementWithNoColumnValues() { + return "INSERT INTO testschema.Test (id, version, stringField, intField, floatField, dateField, booleanField, charField, blobField, bigIntegerField, clobField) VALUES (:id, :version, :stringField, :intField, :floatField, :dateField, :booleanField, :charField, :blobField, :bigIntegerField, :clobField)"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedEmptyStringInsertStatement() + */ + @Override + protected String expectedEmptyStringInsertStatement() { + return "INSERT INTO testschema.Test (stringField, id, version, intField, floatField, dateField, booleanField, charField, blobField, bigIntegerField, clobField) VALUES (NULL, (SELECT COALESCE(value, 1) FROM idvalues WHERE (name = 'Test')), 0, 0, 0, null, FALSE, NULL, null, 12345, null)"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedConcatenationWithCase() + */ + @Override + protected String expectedConcatenationWithCase() { + return "SELECT CONCAT(assetDescriptionLine1, CASE WHEN (taxVariationIndicator = 'Y') THEN exposureCustomerNumber ELSE invoicingCustomerNumber END) AS test FROM testschema.schedule"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedConcatenationWithFunction() + */ + @Override + protected String expectedConcatenationWithFunction() { + return "SELECT CONCAT(assetDescriptionLine1, MAX(scheduleStartDate)) AS test FROM testschema.schedule"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedConcatenationWithMultipleFieldLiterals() + */ + @Override + protected String expectedConcatenationWithMultipleFieldLiterals() { + return "SELECT CONCAT('ABC', ' ', 'DEF') AS assetDescription FROM testschema.schedule"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedNestedConcatenations() + */ + + SelectStatement stmt = new SelectStatement(new ConcatenatedField(new FieldReference("field1"), new ConcatenatedField( + new FieldReference("field2"), new FieldLiteral("XYZ"))).as("test")).from(new TableReference("schedule")); + + + + @Override + protected String expectedNestedConcatenations() { + return "SELECT CONCAT(field1, CONCAT(field2, 'XYZ')) AS test FROM testschema.schedule"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedSelectWithConcatenation1() + */ + @Override + protected String expectedSelectWithConcatenation1() { + return "SELECT CONCAT(assetDescriptionLine1, ' ', assetDescriptionLine2) AS assetDescription FROM testschema.schedule"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedSelectWithConcatenation2() + */ + @Override + protected String expectedSelectWithConcatenation2() { + return "SELECT CONCAT(assetDescriptionLine1, 'XYZ', assetDescriptionLine2) AS assetDescription FROM testschema.schedule"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedIsNull() + */ + @Override + protected String expectedIsNull() { + return "COALESCE('A', 'B')"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedMathsPlus() + */ + @Override + protected String expectedMathsPlus() { + return "1 + 1"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedMathsMinus() + */ + @Override + protected String expectedMathsMinus() { + return "1 - 1"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedMathsDivide() + */ + @Override + protected String expectedMathsDivide() { + return "1 / 1"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedMathsMultiply() + */ + @Override + protected String expectedMathsMultiply() { + return "1 * 1"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedStringCast() + */ + @Override + protected String expectedStringCast() { + return "CAST(value AS VARCHAR(10)) COLLATE \"POSIX\""; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedBigIntCast() + */ + @Override + protected String expectedBigIntCast() { + return "CAST(value AS NUMERIC(19))"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedBigIntFunctionCast() + */ + @Override + protected String expectedBigIntFunctionCast() { + return "CAST(MIN(value) AS NUMERIC(19))"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedBooleanCast() + */ + @Override + protected String expectedBooleanCast() { + return "CAST(value AS BOOLEAN)"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedDateCast() + */ + @Override + protected String expectedDateCast() { + return "CAST(value AS DATE)"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedDecimalCast() + */ + @Override + protected String expectedDecimalCast() { + return "CAST(value AS DECIMAL(10,2))"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedIntegerCast() + */ + @Override + protected String expectedIntegerCast() { + return "CAST(value AS INTEGER)"; + } + + + /** + * {@inheritDoc} + * + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedSelectWithUnion() + */ + @Override + protected String expectedSelectWithUnion() { + return "SELECT stringField FROM testschema.Other UNION SELECT stringField FROM testschema.Test UNION ALL SELECT stringField FROM testschema.Alternate ORDER BY stringField"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedLeftPad() + */ + @Override + protected String expectedLeftPad() { + return "SELECT LPAD(stringField, 10, 'j') FROM testschema.Test"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedBooleanLiteral(boolean) () + */ + @Override + protected String expectedBooleanLiteral(boolean value) { + return value ? "TRUE" : "FALSE"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedBlobLiteral(String) () + */ + @Override + protected String expectedBlobLiteral(String value) { + return String.format("E'\\x%s'", value); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterTableAddBlobColumnStatement() + */ + @Override + protected List expectedAlterTableAddBlobColumnStatement() { + return Arrays.asList("ALTER TABLE testschema.Test ADD COLUMN blobField_new BYTEA NULL", + "COMMENT ON COLUMN testschema.Test.blobField_new IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[blobField_new]/TYPE:[BLOB]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterTableAlterBlobColumnStatement() + */ + @Override + protected List expectedAlterTableAlterBlobColumnStatement() { + return Arrays.asList("ALTER TABLE testschema.Test ALTER COLUMN blobField SET NOT NULL", + "COMMENT ON COLUMN testschema.Test.blobField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[blobField]/TYPE:[BLOB]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterTableAlterBooleanColumnStatement() + */ + @Override + protected List expectedAlterTableAlterBooleanColumnStatement() { + return Arrays.asList("ALTER TABLE testschema.Test ALTER COLUMN booleanField SET NOT NULL", + "COMMENT ON COLUMN testschema.Test.booleanField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[booleanField]/TYPE:[BOOLEAN]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterTableAddBooleanColumnStatement() + */ + @Override + protected List expectedAlterTableAddBooleanColumnStatement() { + return Arrays.asList("ALTER TABLE testschema.Test ADD COLUMN booleanField_new BOOLEAN NULL", + "COMMENT ON COLUMN testschema.Test.booleanField_new IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[booleanField_new]/TYPE:[BOOLEAN]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterTableAddStringColumnStatement() + */ + @Override + protected List expectedAlterTableAddStringColumnStatement() { + return Arrays.asList("ALTER TABLE testschema.Test ADD COLUMN stringField_new VARCHAR(6) COLLATE \"POSIX\" NULL", + "COMMENT ON COLUMN testschema.Test.stringField_new IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[stringField_new]/TYPE:[STRING]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterTableAlterStringColumnStatement() + */ + @Override + protected List expectedAlterTableAlterStringColumnStatement() { + return Arrays.asList("ALTER TABLE testschema.Test ALTER COLUMN stringField TYPE VARCHAR(6) COLLATE \"POSIX\"", + "COMMENT ON COLUMN testschema.Test.stringField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[stringField]/TYPE:[STRING]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterTableAddIntegerColumnStatement() + */ + @Override + protected List expectedAlterTableAddIntegerColumnStatement() { + return Arrays.asList("ALTER TABLE testschema.Test ADD COLUMN intField_new INTEGER NULL", + "COMMENT ON COLUMN testschema.Test.intField_new IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[intField_new]/TYPE:[INTEGER]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterTableAlterIntegerColumnStatement() + */ + @Override + protected List expectedAlterTableAlterIntegerColumnStatement() { + return Arrays.asList("ALTER TABLE testschema.Test ALTER COLUMN intField SET NOT NULL", + "COMMENT ON COLUMN testschema.Test.intField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[intField]/TYPE:[INTEGER]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterTableAddDateColumnStatement() + */ + @Override + protected List expectedAlterTableAddDateColumnStatement() { + return Arrays.asList("ALTER TABLE testschema.Test ADD COLUMN dateField_new DATE NULL", + "COMMENT ON COLUMN testschema.Test.dateField_new IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[dateField_new]/TYPE:[DATE]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterTableAlterDateColumnStatement() + */ + @Override + protected List expectedAlterTableAlterDateColumnStatement() { + return Arrays.asList("ALTER TABLE testschema.Test ALTER COLUMN dateField SET NOT NULL", + "COMMENT ON COLUMN testschema.Test.dateField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[dateField]/TYPE:[DATE]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterTableAddDecimalColumnStatement() + */ + @Override + protected List expectedAlterTableAddDecimalColumnStatement() { + return Arrays.asList("ALTER TABLE testschema.Test ADD COLUMN floatField_new DECIMAL(6,3) NULL", + "COMMENT ON COLUMN testschema.Test.floatField_new IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[floatField_new]/TYPE:[DECIMAL]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterTableAlterDecimalColumnStatement() + */ + @Override + protected List expectedAlterTableAlterDecimalColumnStatement() { + return Arrays.asList("ALTER TABLE testschema.Test ALTER COLUMN floatField DROP NOT NULL, ALTER COLUMN floatField TYPE DECIMAL(14,3)", + "COMMENT ON COLUMN testschema.Test.floatField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[floatField]/TYPE:[DECIMAL]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterTableAddBigIntegerColumnStatement() + */ + @Override + protected List expectedAlterTableAddBigIntegerColumnStatement() { + return Arrays.asList("ALTER TABLE testschema.Test ADD COLUMN bigIntegerField_new NUMERIC(19) NULL", + "COMMENT ON COLUMN testschema.Test.bigIntegerField_new IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[bigIntegerField_new]/TYPE:[BIG_INTEGER]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterTableAlterBigIntegerColumnStatement() + */ + @Override + protected List expectedAlterTableAlterBigIntegerColumnStatement() { + return Arrays.asList("ALTER TABLE testschema.Test ALTER COLUMN bigIntegerField DROP DEFAULT", + "COMMENT ON COLUMN testschema.Test.bigIntegerField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[bigIntegerField]/TYPE:[BIG_INTEGER]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterTableAddColumnNotNullableStatement() + */ + @Override + protected List expectedAlterTableAddColumnNotNullableStatement() { + return Arrays.asList("ALTER TABLE testschema.Test ADD COLUMN dateField_new DATE DEFAULT DATE '2010-01-01' NOT NULL", + "COMMENT ON COLUMN testschema.Test.dateField_new IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[dateField_new]/TYPE:[DATE]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterTableAlterColumnFromNullableToNotNullableStatement() + */ + @Override + protected List expectedAlterTableAlterColumnFromNullableToNotNullableStatement() { + return Arrays.asList("ALTER TABLE testschema.Test ALTER COLUMN dateField SET NOT NULL", + "COMMENT ON COLUMN testschema.Test.dateField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[dateField]/TYPE:[DATE]'" + ); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterTableAlterColumnFromNotNullableToNotNullableStatement() + */ + @Override + protected List expectedAlterTableAlterColumnFromNotNullableToNotNullableStatement() { + return Arrays.asList("ALTER TABLE testschema.Test ALTER COLUMN floatField TYPE DECIMAL(20,3)", + "COMMENT ON COLUMN testschema.Test.floatField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[floatField]/TYPE:[DECIMAL]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterTableAlterColumnFromNotNullableToNullableStatement() + */ + @Override + protected List expectedAlterTableAlterColumnFromNotNullableToNullableStatement() { + return Arrays.asList("ALTER TABLE testschema.Test ALTER COLUMN floatField DROP NOT NULL, ALTER COLUMN floatField TYPE DECIMAL(20,3)", + "COMMENT ON COLUMN testschema.Test.floatField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[floatField]/TYPE:[DECIMAL]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterTableAddColumnWithDefaultStatement() + */ + @Override + protected List expectedAlterTableAddColumnWithDefaultStatement() { + return Arrays.asList("ALTER TABLE testschema.Test ADD COLUMN floatField_new DECIMAL(6,3) DEFAULT 20.33 NULL", + "COMMENT ON COLUMN testschema.Test.floatField_new IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[floatField_new]/TYPE:[DECIMAL]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterTableAlterColumnWithDefaultStatement() + */ + @Override + protected List expectedAlterTableAlterColumnWithDefaultStatement() { + return Arrays.asList("ALTER TABLE testschema.Test ALTER COLUMN bigIntegerField SET DEFAULT 54321", + "COMMENT ON COLUMN testschema.Test.bigIntegerField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[bigIntegerField]/TYPE:[BIG_INTEGER]'" + ); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedChangeIndexFollowedByChangeOfAssociatedColumnStatement() + */ + @Override + protected List expectedChangeIndexFollowedByChangeOfAssociatedColumnStatement() { + return Arrays.asList( + "DROP INDEX Test_1", + "CREATE INDEX Test_1 ON testschema.Test (intField)", + "COMMENT ON INDEX Test_1 IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[Test_1]'", + "ALTER TABLE testschema.Test ALTER COLUMN intField SET NOT NULL", + "COMMENT ON COLUMN testschema.Test.intField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[intField]/TYPE:[INTEGER]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAddIndexStatementsOnSingleColumn() + */ + @Override + protected List expectedAddIndexStatementsOnSingleColumn() { + return Arrays.asList("CREATE INDEX indexName ON testschema.Test (id)", + "COMMENT ON INDEX indexName IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[indexName]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAddIndexStatementsOnMultipleColumns() + */ + @Override + protected List expectedAddIndexStatementsOnMultipleColumns() { + return Arrays.asList("CREATE INDEX indexName ON testschema.Test (id, version)", + "COMMENT ON INDEX indexName IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[indexName]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAddIndexStatementsUnique() + */ + @Override + protected List expectedAddIndexStatementsUnique() { + return Arrays.asList("CREATE UNIQUE INDEX indexName ON testschema.Test (id)", + "COMMENT ON INDEX indexName IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[indexName]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAddIndexStatementsUniqueNullable() + */ + @Override + protected List expectedAddIndexStatementsUniqueNullable() { + return Arrays.asList("CREATE UNIQUE INDEX indexName ON testschema.Test (stringField, intField, floatField, dateField)", + "COMMENT ON INDEX indexName IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[indexName]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedIndexDropStatements() + */ + @Override + protected List expectedIndexDropStatements() { + return Arrays.asList("DROP INDEX indexName"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterColumnMakePrimaryStatements() + */ + @Override + protected List expectedAlterColumnMakePrimaryStatements() { + return Arrays.asList("ALTER TABLE testschema.Test DROP CONSTRAINT Test_PK", + "ALTER TABLE testschema.Test ADD CONSTRAINT Test_PK PRIMARY KEY(id, dateField)", + "COMMENT ON COLUMN testschema.Test.dateField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[dateField]/TYPE:[DATE]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterPrimaryKeyColumnCompositeKeyStatements() + */ + @Override + protected List expectedAlterPrimaryKeyColumnCompositeKeyStatements() { + return Arrays.asList("ALTER TABLE testschema.CompositePrimaryKey DROP CONSTRAINT CompositePrimaryKey_PK", + "ALTER TABLE testschema.CompositePrimaryKey ALTER COLUMN secondPrimaryKey TYPE VARCHAR(5) COLLATE \"POSIX\"", + "ALTER TABLE testschema.CompositePrimaryKey ADD CONSTRAINT CompositePrimaryKey_PK PRIMARY KEY(id, secondPrimaryKey)", + "COMMENT ON COLUMN testschema.CompositePrimaryKey.secondPrimaryKey IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[secondPrimaryKey]/TYPE:[STRING]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterRemoveColumnFromCompositeKeyStatements() + */ + @Override + protected List expectedAlterRemoveColumnFromCompositeKeyStatements() { + return ImmutableList.of( + "ALTER TABLE testschema.CompositePrimaryKey DROP CONSTRAINT CompositePrimaryKey_PK", + "ALTER TABLE testschema.CompositePrimaryKey ALTER COLUMN secondPrimaryKey DROP NOT NULL, ALTER COLUMN secondPrimaryKey TYPE VARCHAR(5) COLLATE \"POSIX\"", + "ALTER TABLE testschema.CompositePrimaryKey ADD CONSTRAINT CompositePrimaryKey_PK PRIMARY KEY(id)", + "COMMENT ON COLUMN testschema.CompositePrimaryKey.secondPrimaryKey IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[secondPrimaryKey]/TYPE:[STRING]'" + ); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterPrimaryKeyColumnStatements() + */ + @Override + protected List expectedAlterPrimaryKeyColumnStatements() { + return Arrays.asList( + "ALTER TABLE testschema.Test DROP CONSTRAINT Test_PK", + "ALTER TABLE testschema.Test RENAME id TO renamedId", + "ALTER TABLE testschema.Test ADD CONSTRAINT Test_PK PRIMARY KEY(renamedId)", + "COMMENT ON COLUMN testschema.Test.renamedId IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[renamedId]/TYPE:[BIG_INTEGER]'" + ); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterColumnRenamingAndChangingNullability() + */ + @Override + protected List expectedAlterColumnRenamingAndChangingNullability() { + return Arrays.asList("ALTER TABLE testschema.Other RENAME floatField TO blahField", + "ALTER TABLE testschema.Other ALTER COLUMN blahField DROP NOT NULL, ALTER COLUMN blahField TYPE DECIMAL(20,3)", + "COMMENT ON COLUMN testschema.Other.blahField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[blahField]/TYPE:[DECIMAL]'" + ); + } + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterTableAddStringColumnWithDefaultStatement() + */ + @Override + protected List expectedAlterTableAddStringColumnWithDefaultStatement() { + return Arrays.asList("ALTER TABLE testschema.Test ADD COLUMN stringField_with_default VARCHAR(6) COLLATE \"POSIX\" DEFAULT 'N' NOT NULL", + "COMMENT ON COLUMN testschema.Test.stringField_with_default IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[stringField_with_default]/TYPE:[STRING]'"); + } + + + /** + * {@inheritDoc} + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAutonumberUpdate() + */ + @Override + protected List expectedAutonumberUpdate() { + return Arrays.asList("MERGE INTO Autonumber (id, value) SELECT 'TestTable', (SELECT GREATEST((SELECT COALESCE(MAX(id) + 1, 1) AS CurrentValue FROM TestTable), (SELECT value from Autonumber WHERE name='TestTable'), 1))"); + } + + + /** + * {@inheritDoc} + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedUpdateWithSelectMinimum() + */ + @Override + protected String expectedUpdateWithSelectMinimum() { + String value1 = varCharCast("'S'"); + String value2 = varCharCast("'Y'"); + return "UPDATE " + tableName("Other") + " O SET intField = (SELECT MIN(intField) FROM " + tableName("Test") + " T WHERE ((T.charField = " + stringLiteralPrefix() + value1 + ") AND (T.stringField = O.stringField) AND (T.intField = O.intField))) WHERE (stringField = " + stringLiteralPrefix() + value2 + ")"; + } + + + /** + * {@inheritDoc} + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedUpdateUsingAliasedDestinationTable() + */ + @Override + protected String expectedUpdateUsingAliasedDestinationTable() { + return "UPDATE " + tableName("FloatingRateRate") + " A SET settlementFrequency = (SELECT settlementFrequency FROM " + tableName("FloatingRateDetail") + " B WHERE (A.floatingRateDetailId = B.id))"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedCreateViewStatements() + */ + @Override + protected List expectedCreateViewStatements() { + return Arrays.asList("CREATE VIEW " + tableName("TestView") + " AS (SELECT stringField FROM " + tableName("Test") + " WHERE (stringField = 'blah'))", + "COMMENT ON VIEW TestView IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[TestView]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedCreateViewOverUnionSelectStatements() + */ + @Override + protected List expectedCreateViewOverUnionSelectStatements() { + return Arrays.asList( + "CREATE VIEW " + tableName("TestView") + " AS (SELECT stringField FROM " + tableName(TEST_TABLE) + " WHERE (stringField = " + stringLiteralPrefix() + "'blah') UNION ALL SELECT stringField FROM " + tableName(OTHER_TABLE) + " WHERE (stringField = " + stringLiteralPrefix() + "'blah'))", + "COMMENT ON VIEW TestView IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[TestView]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedYYYYMMDDToDate() + */ + @Override + protected String expectedYYYYMMDDToDate() { + return "TO_DATE(('20100101') :: TEXT,'YYYYMMDD')"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedDateToYyyymmdd() + */ + @Override + protected String expectedDateToYyyymmdd() { + return "TO_CHAR(testField,'YYYYMMDD') :: NUMERIC"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedDateToYyyymmddHHmmss() + */ + @Override + protected String expectedDateToYyyymmddHHmmss() { + return "TO_CHAR(testField,'YYYYMMDDHH24MISS') :: NUMERIC"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedNow() + */ + @Override + protected String expectedNow() { + return "NOW()"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedDropViewStatements() + */ + @Override + protected List expectedDropViewStatements() { + return Arrays.asList("DROP VIEW IF EXISTS " + tableName("TestView") + " CASCADE"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedStringLiteralToIntegerCast() + */ + @Override + protected String expectedStringLiteralToIntegerCast() { + return "CAST(" + varCharCast("'1234567890'") + " AS INTEGER)"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedSubstring() + */ + @Override + protected String expectedSubstring() { + return "SELECT SUBSTRING(field1, 1, 3) FROM " + tableName("schedule"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAutonumberUpdateForNonIdColumn() + */ + @Override + protected List expectedAutonumberUpdateForNonIdColumn() { + return Arrays.asList("MERGE INTO Autonumber (id, value) SELECT 'TestTable', (SELECT GREATEST((SELECT COALESCE(MAX(generatedColumn) + 1, 1) AS CurrentValue FROM TestTable), (SELECT value from Autonumber WHERE name='TestTable'), 1))"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedStringFunctionCast() + */ + @Override + protected String expectedStringFunctionCast() { + return "CAST(MIN(field) AS VARCHAR(8)) COLLATE \"POSIX\""; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedDaysBetween() + */ + @Override + protected String expectedDaysBetween() { + return "SELECT (dateTwo) - (dateOne) FROM testschema.MyTable"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedMergeSimple() + */ + @Override + protected String expectedMergeSimple() { + return "INSERT INTO testschema.foo (id, bar)" + + " SELECT somewhere.newId AS id, somewhere.newBar AS bar FROM testschema.somewhere" + + " ON CONFLICT (id) DO UPDATE SET bar = EXCLUDED.bar"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedMergeComplex() + */ + @Override + protected String expectedMergeComplex() { + return "INSERT INTO testschema.foo (id, bar)" + + " SELECT somewhere.newId AS id, join.joinBar AS bar FROM testschema.somewhere INNER JOIN testschema.join ON (somewhere.newId = join.joinId)" + + " ON CONFLICT (id) DO UPDATE SET bar = EXCLUDED.bar"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedMergeSourceInDifferentSchema() + */ + @Override + protected String expectedMergeSourceInDifferentSchema() { + return "INSERT INTO testschema.foo (id, bar)" + + " SELECT somewhere.newId AS id, somewhere.newBar AS bar FROM MYSCHEMA.somewhere" + + " ON CONFLICT (id) DO UPDATE SET bar = EXCLUDED.bar"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedMergeTargetInDifferentSchema() + */ + @Override + protected String expectedMergeTargetInDifferentSchema() { + return "INSERT INTO MYSCHEMA.foo (id, bar)" + + " SELECT somewhere.newId AS id, somewhere.newBar AS bar FROM testschema.somewhere" + + " ON CONFLICT (id) DO UPDATE SET bar = EXCLUDED.bar"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedMergeForAllPrimaryKeys() + */ + @Override + protected String expectedMergeForAllPrimaryKeys() { + return "INSERT INTO testschema.foo (id)" + + " SELECT somewhere.newId AS id FROM testschema.somewhere" + + " ON CONFLICT (id) DO NOTHING"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedMergeWithUpdateExpressions() + */ + @Override + protected String expectedMergeWithUpdateExpressions() { + return "INSERT INTO testschema.foo (id, bar)" + + " SELECT somewhere.newId AS id, somewhere.newBar AS bar FROM testschema.somewhere" + + " ON CONFLICT (id) DO UPDATE SET bar = EXCLUDED.bar + foo.bar"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAddDays() + */ + @Override + protected String expectedAddDays() { + return "(((testField) + (-20) * INTERVAL '1 DAY') :: DATE)"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAddMonths() + */ + @Override + protected String expectedAddMonths() { + return "(((testField) + (-3) * INTERVAL '1 MONTH') :: DATE)"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterRemoveColumnFromSimpleKeyStatements() + */ + @Override + protected List expectedAlterRemoveColumnFromSimpleKeyStatements() { + return Collections.singletonList("ALTER TABLE testschema.Test DROP COLUMN id"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedRenameTableStatements() + */ + @Override + protected List expectedRenameTableStatements() { + return ImmutableList.of( + "ALTER TABLE testschema.Test RENAME TO Renamed", + "ALTER INDEX testschema.Test_pk RENAME TO Renamed_pk", + "COMMENT ON INDEX Renamed_pk IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[Renamed_pk]'", + "COMMENT ON TABLE testschema.Renamed IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[Renamed]'"); + } + + + /** + * @return the expected statements for renaming a table with a long name. + */ + @Override + protected List getRenamingTableWithLongNameStatements() { + return ImmutableList.of( + "ALTER TABLE testschema.123456789012345678901234567890XXX RENAME TO Blah", + "ALTER INDEX testschema.123456789012345678901234567890XXX_pk RENAME TO Blah_pk", + "COMMENT ON INDEX Blah_pk IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[Blah_pk]'", + "COMMENT ON TABLE testschema.Blah IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[Blah]'" + ); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedRenameIndexStatements() + */ + @Override + protected List expectedRenameIndexStatements() { + return ImmutableList.of("ALTER INDEX testschema.Test_1 RENAME TO Test_2", + "COMMENT ON INDEX Test_2 IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[Test_2]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedRenameIndexStatements() + */ + @Override + protected List expectedRenameTempIndexStatements() { + return ImmutableList.of("ALTER INDEX TempTest_1 RENAME TO TempTest_2", + "COMMENT ON INDEX TempTest_2 IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[TempTest_2]'"); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedRandomString() + */ + @Override + protected String expectedRandomString() { + return "UPPER(SUBSTRING(MD5(RANDOM() :: TEXT), 1, (10) :: INT))"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedSelectLiteralWithWhereClauseString() + */ + @Override + protected String expectedSelectLiteralWithWhereClauseString() { + return "SELECT 'LITERAL' WHERE ('ONE' = 'ONE')"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAddTableFromStatements() + */ + @Override + protected List expectedAddTableFromStatements() { + return ImmutableList.of( + "CREATE TABLE testschema.SomeTable (someField VARCHAR(3) COLLATE \"POSIX\" NOT NULL, otherField DECIMAL(3,0) NOT NULL, CONSTRAINT SomeTable_PK PRIMARY KEY(someField))", + "COMMENT ON TABLE testschema.SomeTable IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[SomeTable]'", + "COMMENT ON COLUMN testschema.SomeTable.someField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[someField]/TYPE:[STRING]'", + "COMMENT ON COLUMN testschema.SomeTable.otherField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[otherField]/TYPE:[DECIMAL]'", + "CREATE INDEX SomeTable_1 ON testschema.SomeTable (otherField)", + "COMMENT ON INDEX SomeTable_1 IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[SomeTable_1]'", + "INSERT INTO testschema.SomeTable SELECT someField, otherField FROM testschema.OtherTable" + ); + } + + + @Override + protected List expectedReplaceTableFromStatements() { + return ImmutableList.of( + "CREATE TABLE testschema.tmp_SomeTable (someField, otherField, thirdField) AS SELECT CAST(someField AS VARCHAR(3)) COLLATE \"POSIX\" AS someField, CAST(otherField AS DECIMAL(3,0)) AS otherField, CAST(thirdField AS DECIMAL(5,0)) AS thirdField FROM testschema.OtherTable", + "ALTER TABLE tmp_SomeTable ALTER someField SET NOT NULL, ALTER otherField SET NOT NULL, ALTER thirdField SET NOT NULL, ADD CONSTRAINT tmp_SomeTable_PK PRIMARY KEY(someField)", + "COMMENT ON TABLE testschema.tmp_SomeTable IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[tmp_SomeTable]'", + "COMMENT ON COLUMN testschema.tmp_SomeTable.someField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[someField]/TYPE:[STRING]'", + "COMMENT ON COLUMN testschema.tmp_SomeTable.otherField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[otherField]/TYPE:[DECIMAL]'", + "COMMENT ON COLUMN testschema.tmp_SomeTable.thirdField IS 'REALNAME:[thirdField]/TYPE:[DECIMAL]'", + "DROP TABLE testschema.SomeTable CASCADE", + "ALTER TABLE testschema.tmp_SomeTable RENAME TO SomeTable", + "ALTER INDEX testschema.tmp_SomeTable_pk RENAME TO SomeTable_pk", + "COMMENT ON INDEX SomeTable_pk IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[SomeTable_pk]'", + "COMMENT ON TABLE testschema.SomeTable IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[SomeTable]'", + "CREATE INDEX SomeTable_1 ON testschema.SomeTable (otherField)", + "COMMENT ON INDEX SomeTable_1 IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[SomeTable_1]'" + ); + } + + + protected List expectedReplaceTableWithAutonumber() { + return ImmutableList.of( + "DROP SEQUENCE IF EXISTS testschema.tmp_SomeTable_otherField_seq", + "CREATE SEQUENCE testschema.tmp_SomeTable_otherField_seq START 1", + "CREATE TABLE testschema.tmp_SomeTable (someField, otherField, thirdField) AS SELECT CAST(someField AS VARCHAR(3)) COLLATE \"POSIX\" AS someField, CAST(otherField AS DECIMAL(3,0)) AS otherField, CAST(thirdField AS DECIMAL(5,0)) AS thirdField FROM testschema.OtherTable", + "ALTER TABLE tmp_SomeTable ALTER otherField SET DEFAULT nextval('testschema.tmp_SomeTable_otherField_seq')", + "ALTER SEQUENCE testschema.tmp_SomeTable_otherField_seq OWNED BY testschema.tmp_SomeTable.otherField", + "ALTER TABLE tmp_SomeTable ALTER someField SET NOT NULL, ALTER otherField SET NOT NULL, ALTER thirdField SET NOT NULL, ADD CONSTRAINT tmp_SomeTable_PK PRIMARY KEY(someField)", + "COMMENT ON TABLE testschema.tmp_SomeTable IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[tmp_SomeTable]'", + "COMMENT ON COLUMN testschema.tmp_SomeTable.someField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[someField]/TYPE:[STRING]'", + "COMMENT ON COLUMN testschema.tmp_SomeTable.otherField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[otherField]/TYPE:[DECIMAL]/AUTONUMSTART:[1]'", + "COMMENT ON COLUMN testschema.tmp_SomeTable.thirdField IS 'REALNAME:[thirdField]/TYPE:[DECIMAL]'", + "DROP TABLE testschema.SomeTable CASCADE", + "ALTER TABLE testschema.tmp_SomeTable RENAME TO SomeTable", + "ALTER INDEX testschema.tmp_SomeTable_pk RENAME TO SomeTable_pk", + "COMMENT ON INDEX SomeTable_pk IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[SomeTable_pk]'", + "ALTER SEQUENCE tmp_SomeTable_seq RENAME TO SomeTable_seq", + "COMMENT ON TABLE testschema.SomeTable IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[SomeTable]'", + "CREATE INDEX SomeTable_1 ON testschema.SomeTable (otherField)", + "COMMENT ON INDEX SomeTable_1 IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[SomeTable_1]'" + ); + } + + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAlterTableDropColumnWithDefaultStatement() + */ + @Override + protected List expectedAlterTableDropColumnWithDefaultStatement() { + return Collections.singletonList("ALTER TABLE testschema.Test DROP COLUMN bigIntegerField"); + } + + /** + * @return The expected SQL for performing an update with a source table which lives in a different schema. + */ + @Override + protected String expectedUpdateUsingSourceTableInDifferentSchema() { + return "UPDATE " + tableName("FloatingRateRate") + " A SET settlementFrequency = (SELECT settlementFrequency FROM MYSCHEMA.FloatingRateDetail B WHERE (A.floatingRateDetailId = B.id))"; + } + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAnalyseTableSql() + */ + @Override + protected Collection expectedAnalyseTableSql() { + return Arrays.asList("ANALYZE TempTest"); + } + + + @Override + protected List expectedAlterColumnChangingLengthAndCase() { + return Arrays.asList("ALTER TABLE testschema.Other ALTER COLUMN FloatField TYPE DECIMAL(20,3)", + "COMMENT ON COLUMN testschema.Other.FloatField IS '"+RedshiftDialect.REAL_NAME_COMMENT_LABEL+":[FloatField]/TYPE:[DECIMAL]'"); + } + + + @Override + protected void verifyBlobColumnCallPrepareStatementParameter(SqlParameter blobColumn) throws SQLException { + verify(callPrepareStatementParameter(blobColumn, null)).setBinaryStream(Mockito.eq(blobColumn), any(InputStream.class)); + verify(callPrepareStatementParameter(blobColumn, "QUJD")).setBinaryStream(Mockito.eq(blobColumn), any(InputStream.class)); + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#tableName(java.lang.String) + */ + @Override + protected String tableName(String baseName) { + return "testschema." + baseName; + } + + + /** + * It is only necessary to cast for HSQLDB. Returns the value without casting. + * + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#varCharCast(java.lang.String) + */ + @Override + protected String varCharCast(String value) { + return value; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedSelectSome() + */ + @Override + protected String expectedSelectSome() { + return "SELECT BOOL_OR(booleanField) FROM testschema.Test"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedSelectEvery() + */ + @Override + protected String expectedSelectEvery() { + return "SELECT BOOL_AND(booleanField) FROM testschema.Test"; + } + + + /** + * @return The expected SQL for a delete statement with a limit and where criterion. + */ + @Override + protected String expectedDeleteWithLimitAndWhere(String value) { + return "DELETE FROM " + tableName(TEST_TABLE) + " WHERE ctid IN (" + + "SELECT ctid FROM " + tableName(TEST_TABLE) + " WHERE (" + TEST_TABLE + ".stringField = " + stringLiteralPrefix() + value + + ") LIMIT 1000)"; + }; + + + /** + * @return The expected SQL for a delete statement with a limit and where criterion. + */ + @Override + protected String expectedDeleteWithLimitAndComplexWhere(String value1, String value2) { + return "DELETE FROM " + tableName(TEST_TABLE) + " WHERE ctid IN (" + + "SELECT ctid FROM " + tableName(TEST_TABLE) + " WHERE ((Test.stringField = " + stringLiteralPrefix() + value1 + ") OR (Test.stringField = " + stringLiteralPrefix() + value2 + "))" + + " LIMIT 1000)"; + }; + + + /** + * @return The expected SQL for a delete statement with a limit and where criterion. + */ + @Override + protected String expectedDeleteWithLimitWithoutWhere() { + return "DELETE FROM " + tableName(TEST_TABLE) + " WHERE ctid IN (" + + "SELECT ctid FROM " + tableName(TEST_TABLE) + + " LIMIT 1000)"; + } + + @Override + protected String expectedHints1(int rowCount) { + return "SELECT * FROM SCHEMA2.Foo INNER JOIN testschema.Bar ON (a = b) LEFT OUTER JOIN testschema.Fo ON (a = b) INNER JOIN testschema.Fum Fumble ON (a = b) ORDER BY a"; + } + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedSelectWithExcept() + */ + @Override + protected String expectedSelectWithExcept() { + return "SELECT stringField FROM testschema.Test EXCEPT SELECT stringField FROM testschema.Other ORDER BY stringField"; + }; + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedSelectWithDbLink() + */ + @Override + protected String expectedSelectWithDbLink() { + return "SELECT stringField FROM MYDBLINKREF.Test"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedSelectWithExceptAndDbLinkFormer() + */ + @Override + protected String expectedSelectWithExceptAndDbLinkFormer() { + return "SELECT stringField FROM MYDBLINKREF.Test EXCEPT SELECT stringField FROM testschema.Other ORDER BY stringField"; + } + + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedSelectWithExceptAndDbLinkLatter() + */ + @Override + protected String expectedSelectWithExceptAndDbLinkLatter() { + return "SELECT stringField FROM testschema.Test EXCEPT SELECT stringField FROM MYDBLINKREF.Other ORDER BY stringField"; + } +} diff --git a/pom.xml b/pom.xml index 6831d62aa..b0ad4727f 100644 --- a/pom.xml +++ b/pom.xml @@ -24,6 +24,7 @@ morf-nuodb morf-oracle morf-postgresql + morf-redshift morf-sqlserver morf-integration-test @@ -523,4 +524,4 @@ https://oss.sonatype.org/service/local/staging/deploy/maven2/ - + \ No newline at end of file