/*
* RHQ Management Platform
* Copyright (C) 2005-2011 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.core.db.builders;
import org.rhq.core.db.DatabaseType;
import org.rhq.core.db.FeatureNotSupportedException;
import org.rhq.core.db.H2DatabaseType;
import org.rhq.core.db.OracleDatabaseType;
import org.rhq.core.db.PostgresqlDatabaseType;
import org.rhq.core.db.SQLServerDatabaseType;
import java.util.HashMap;
/**
* @author Robert Buck
*/
public abstract class CreateSequenceExprBuilder {
public static final String KEY_SEQ_NAME = "SEQ_NAME";
public static final String KEY_SEQ_START = "SEQ_START";
public static final String KEY_SEQ_INCREMENT = "SEQ_INCREMENT";
public static final String KEY_SEQ_CACHE_SIZE = "SEQ_CACHE_SIZE";
public static CreateSequenceExprBuilder getBuilder(DatabaseType type) {
return getBuilder(type.getVendor());
}
public static CreateSequenceExprBuilder getBuilder(String type) {
if (OracleDatabaseType.VENDOR.equals(type)) {
return new OracleInnerBuilder();
}
if (SQLServerDatabaseType.VENDOR_NAME.equals(type)) {
return new SqlServerInnerBuilder();
}
if (PostgresqlDatabaseType.VENDOR_NAME.equals(type)) {
return new PostgresInnerBuilder();
}
if (H2DatabaseType.VENDOR_NAME.equals(type)) {
return new H2InnerBuilder();
}
throw new UnsupportedOperationException("Cannot create a CREATE SEQUENCE builder for the requested database type: "
+ type);
}
/**
* Indicates that the NOCACHE option should be used.
*/
public static final int USE_SEQID_NOCACHE_SIZE = 0;
@SuppressWarnings({"UnusedParameters"})
public static int getSafeSequenceCacheSize(CreateSequenceExprBuilder builder, String requestedSize) {
// we don't want to regress for the time being, and we don't want
// to default less than manufacturers recommended minimums...
int size = 10;
try {
// but if someone asks for lower values we provide it...
size = Integer.parseInt(requestedSize);
} catch (NumberFormatException e) {
// ... nada, previously validated in validateAttributes
}
return size;
}
/**
* Get the default factory sequence id cache size.
*
* @return the factory sequence id cache size
*/
public int getFactorySequenceCacheSize() {
return 0; // this is the global default
}
protected String getSeqIdNoCacheLiteral() {
return "";
}
protected String getSeqIdCacheLiteral() {
return "CACHE";
}
protected abstract StringBuilder appendCreateSeqStem(HashMap<String, Object> terms, StringBuilder builder);
protected StringBuilder appendSeqIdNoCacheTerms(HashMap<String, Object> terms, StringBuilder builder) {
return builder.append(" ").append(getSeqIdNoCacheLiteral());
}
protected StringBuilder appendSeqIdCacheTerms(HashMap<String, Object> terms, StringBuilder builder) {
return builder.append(" ").append(getSeqIdCacheLiteral()).append(" ").append(terms.get(KEY_SEQ_CACHE_SIZE));
}
protected StringBuilder appendCreateSeqCacheSize(HashMap<String, Object> terms, StringBuilder builder) {
final int specifiedCacheSize = (Integer) terms.get(KEY_SEQ_CACHE_SIZE);
// If the user specifies a cache size that is negative we use the factory
// default cache size.
if (specifiedCacheSize >= USE_SEQID_NOCACHE_SIZE) {
if (USE_SEQID_NOCACHE_SIZE == specifiedCacheSize) {
// values of zero map to 'no cache'...
appendSeqIdNoCacheTerms(terms, builder);
} else {
// otherwise use specified value, even if they are less than factory defaults...
appendSeqIdCacheTerms(terms, builder);
}
} else {
// use to factory defaults...
}
return builder;
}
public String build(HashMap<String, Object> terms) {
StringBuilder builder = new StringBuilder();
appendCreateSeqStem(terms, builder);
appendCreateSeqCacheSize(terms, builder);
return builder.toString();
}
/**
* @author Robert Buck
*/
private static class PostgresInnerBuilder extends CreateSequenceExprBuilder {
@Override
public int getFactorySequenceCacheSize() {
return 1; // believe it or not!
}
@Override
protected StringBuilder appendCreateSeqStem(HashMap<String, Object> terms, StringBuilder builder) {
builder.append("CREATE SEQUENCE ").append(terms.get(KEY_SEQ_NAME))
.append(" START ").append(terms.get(KEY_SEQ_START))
.append(" INCREMENT ").append(terms.get(KEY_SEQ_INCREMENT));
return builder;
}
/**
* Postgres does not support NO CACHE terms, so we fake it out by setting the
* CACHE size to one. Yes, no matter what postgres will lose at least one id
* when the database restarts. One is the minimum value as indicated by its
* documentation, and its the default.
*
* @param builder the builder to append to
* @return the string builder with ' CACHE 1' appended to it.
*/
@Override
protected StringBuilder appendSeqIdNoCacheTerms(HashMap<String, Object> terms, StringBuilder builder) {
// We do not pass in getFactorySeqIdCacheSize() here because if they ever
// changed the default we don't want to break the behavior intended here.
terms.put(KEY_SEQ_CACHE_SIZE, 1);
return appendSeqIdCacheTerms(terms, builder);
}
}
/**
* @author Robert Buck
*/
private static class OracleInnerBuilder extends CreateSequenceExprBuilder {
@Override
public int getFactorySequenceCacheSize() {
return 32;
}
@Override
protected StringBuilder appendCreateSeqStem(HashMap<String, Object> terms, StringBuilder builder) {
builder.append("CREATE SEQUENCE ").append(terms.get(KEY_SEQ_NAME))
.append(" START WITH ").append(terms.get(KEY_SEQ_START))
.append(" INCREMENT BY ").append(terms.get(KEY_SEQ_INCREMENT))
.append(" NOMAXVALUE NOCYCLE");
return builder;
}
@Override
protected StringBuilder appendSeqIdNoCacheTerms(HashMap<String, Object> terms, StringBuilder builder) {
return builder.append(" NOCACHE");
}
}
/**
* @author Robert Buck
*/
private static class SqlServerInnerBuilder extends CreateSequenceExprBuilder {
public static final String SEQ_SUFFIX = "_ID_SEQ";
@Override
public int getFactorySequenceCacheSize() {
return 10; // for identity columns only; not applicable for Denali! In Denali the default is 50
}
@Override
protected StringBuilder appendCreateSeqStem(HashMap<String, Object> terms, StringBuilder builder) {
final String name = ((String) terms.get(CreateSequenceExprBuilder.KEY_SEQ_NAME)).toUpperCase();
if (!name.endsWith(SEQ_SUFFIX)) {
throw new FeatureNotSupportedException(SQLServerDatabaseType.SEQ_ERROR_MSG);
}
String tableName = name.substring(0, name.length() - SEQ_SUFFIX.length());
builder.append("ALTER TABLE ").append(tableName)
.append(" ALTER COLUMN ID IDENTITY( ").append(terms.get(CreateSequenceExprBuilder.KEY_SEQ_START))
.append(", ").append(terms.get(CreateSequenceExprBuilder.KEY_SEQ_INCREMENT))
.append(")");
return builder;
}
@Override
protected StringBuilder appendCreateSeqCacheSize(HashMap<String, Object> terms, StringBuilder builder) {
// Only sql server 2011 (Denali) supports SEQUENCE..CACHE constructs,
// and this change does not introduce support for Denali. So we only
// apply the stem. This poses an interesting problem, how should the
// upgrades work for future implementations where the underlying
// SQL implementation undergoes such a fundamental change to the
// language?
return builder;
}
@Override
protected StringBuilder appendSeqIdNoCacheTerms(HashMap<String, Object> terms, StringBuilder builder) {
// Ditto, see comments above...
return builder;
}
}
/**
* @author Robert Buck
*/
private static class H2InnerBuilder extends CreateSequenceExprBuilder {
@Override
public int getFactorySequenceCacheSize() {
return 32;
}
@Override
protected StringBuilder appendCreateSeqStem(HashMap<String, Object> terms, StringBuilder builder) {
builder.append("CREATE SEQUENCE ").append(terms.get(KEY_SEQ_NAME))
.append(" START WITH ").append(terms.get(KEY_SEQ_START))
.append(" INCREMENT BY ").append(terms.get(KEY_SEQ_INCREMENT));
return builder;
}
@Override
protected StringBuilder appendSeqIdNoCacheTerms(HashMap<String, Object> terms, StringBuilder builder) {
// We do not pass in getFactorySeqIdCacheSize() here because if they ever
// changed the default we don't want to break the behavior intended here.
terms.put(KEY_SEQ_CACHE_SIZE, 1);
return appendSeqIdCacheTerms(terms, builder);
}
}
}