Don't pass around and store column definitions as SQL strings, but introduce an object to contain that data. PostgreSQL's extensions does column alteration in individual tweaks, where MySQL just accepts a raw string and figures out what to do.

git-svn-id: https://samskivert.googlecode.com/svn/trunk@2233 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
zell
2007-10-11 18:12:37 +00:00
parent 05b6eb6fd9
commit 1e50256ca2
7 changed files with 199 additions and 36 deletions
+43 -6
View File
@@ -160,19 +160,24 @@ public abstract class BaseLiaison implements DatabaseLiaison
}
// from DatabaseLiaison
public boolean changeColumn (Connection conn, String table, String column, String definition)
public boolean changeColumn (Connection conn, String table, String column, String type,
Boolean nullable, Boolean unique, String defaultValue)
throws SQLException
{
String defStr = expandDefinition(
type, nullable != null ? nullable : false, unique != null ? unique : false,
defaultValue);
executeQuery(conn, "ALTER TABLE " + tableSQL(table) + " CHANGE " +
columnSQL(column) + " " + columnSQL(column) + " " + definition);
columnSQL(column) + " " + columnSQL(column) + " " + defStr);
Log.info("Database column '" + column + "' of table '" + table + "' modified to have " +
"definition '" + definition + "'.");
"definition '" + defStr + "'.");
return true;
}
// from DatabaseLiaison
public boolean renameColumn (Connection conn, String table, String from, String to,
String newColumnDef)
ColumnDefinition newColumnDef)
throws SQLException
{
executeQuery(conn, "ALTER TABLE " + tableSQL(table) + " RENAME COLUMN " +
@@ -194,7 +199,7 @@ public abstract class BaseLiaison implements DatabaseLiaison
// from DatabaseLiaison
public boolean createTableIfMissing (
Connection conn, String table, String[] columns, String[] definitions,
Connection conn, String table, String[] columns, ColumnDefinition[] definitions,
String[][] uniqueConstraintColumns, String[] primaryKeyColumns)
throws SQLException
{
@@ -211,7 +216,8 @@ public abstract class BaseLiaison implements DatabaseLiaison
if (ii > 0) {
builder.append(", ");
}
builder.append(columnSQL(columns[ii])).append(" ").append(definitions[ii]);
builder.append(columnSQL(columns[ii])).append(" ");
builder.append(expandDefinition(definitions[ii]));
}
if (uniqueConstraintColumns != null && uniqueConstraintColumns.length > 0) {
@@ -245,6 +251,17 @@ public abstract class BaseLiaison implements DatabaseLiaison
return true;
}
/**
* Create an SQL string that summarizes a column definition in that format generally
* accepted in table creation and column addition statements, e.g.
* INTEGER UNIQUE NOT NULL DEFAULT 100
*/
public String expandDefinition (ColumnDefinition def)
{
return expandDefinition(
def.getType(), def.isNullable(), def.isUnique(), def.getDefaultValue());
}
protected int executeQuery (Connection conn, String query)
throws SQLException
{
@@ -255,4 +272,24 @@ public abstract class BaseLiaison implements DatabaseLiaison
JDBCUtil.close(stmt);
}
}
protected String expandDefinition (
String type, boolean nullable, boolean unique, String defaultValue)
{
StringBuilder builder = new StringBuilder(type);
if (!nullable) {
builder.append(" NOT NULL");
}
if (unique) {
builder.append(" UNIQUE");
}
// append the default value if one was specified
if (defaultValue != null) {
builder.append(" DEFAULT ").append(defaultValue);
}
return builder.toString();
}
}
@@ -0,0 +1,74 @@
//
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2006-2007 Michael Bayne, Pär Winzell
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.jdbc;
/**
* An object representing the configuration of a typical SQL column.
*/
public class ColumnDefinition
{
public ColumnDefinition (String type)
{
this(type, false, false, null);
}
public ColumnDefinition (
String type, boolean nullable, boolean unique, String defaultValue)
{
if (type == null) {
throw new IllegalArgumentException("cannot build column definition without type");
}
_type = type;
_nullable = nullable;
_unique = unique;
_defaultValue = defaultValue;
}
/** Returns the SQL type of this column, e.g. INTEGER. */
public String getType ()
{
return _type;
}
/** Determines whether or not this column should allow NULL values. */
public boolean isNullable ()
{
return _nullable;
}
/** Determines whether or not this column should reject duplicate values. */
public boolean isUnique ()
{
return _unique;
}
/** Returns the value to insert into this column instead of nulls, if any. */
public String getDefaultValue ()
{
return _defaultValue;
}
protected String _type;
protected boolean _nullable;
protected boolean _unique;
protected String _defaultValue;
}
@@ -65,6 +65,13 @@ public interface DatabaseLiaison
Connection conn, String table, String column, int first, int step)
throws SQLException;
/**
* This method deletes the column value auto-generator described in {@link
* #lastInsertedId}.
*/
public void deleteGenerator (Connection conn, String tableName, String columnName)
throws SQLException;
/**
* This method attempts as dialect-agnostic an interface as possible to the ability of certain
* databases to auto-generated numerical values for i.e. key columns; there is MySQL's
@@ -117,23 +124,25 @@ public interface DatabaseLiaison
/**
* Alter the definition, but not the name, of a given column on a given table. Returns true or
* false if the database did or did not report a schema modification.
*
* TODO: We may wish to further split column definitions into type, nullability, etc.
* false if the database did or did not report a schema modification. Any of the type,
* nullable, unique and defaultValue arguments may be null, in which case the implementation
* will attempt to leave that aspect of the column unchanged.
*/
public boolean changeColumn (
Connection conn, String table, String column, String definition)
throws SQLException;
Connection conn, String table, String column, String type, Boolean nullable,
Boolean unique, String defaultValue)
throws SQLException;
/**
* Alter the name, but not the definition, of a given column on a given table. Returns true or
* false if the database did or did not report a schema modification.
*
* @param newColumnDef the full definition of the new column, including its new name (MySQL
* @param columnDef the full definition of the new column, including its new name (MySQL
* requires this for a column rename).
*/
public boolean renameColumn (
Connection conn, String table, String oldColumn, String newColumn, String newColumnDef)
Connection conn, String table, String oldColumn, String newColumn,
ColumnDefinition columnDef)
throws SQLException;
/**
@@ -142,10 +151,17 @@ public interface DatabaseLiaison
* Returns true if the table was successfully created, false if it already existed.
*/
public boolean createTableIfMissing (
Connection conn, String table, String[] columns, String[] definitions,
Connection conn, String table, String[] columns, ColumnDefinition[] declarations,
String[][] uniqueConstraintColumns, String[] primaryKeyColumns)
throws SQLException;
/**
* Create an SQL string that summarizes a column definition in that format generally
* accepted in table creation and column addition statements, e.g.
* INTEGER UNIQUE NOT NULL DEFAULT 100
*/
public String expandDefinition (ColumnDefinition coldef);
/**
* Returns true if the specified table exists and contains an index of the specified name;
* false if either conditions does not hold true. <em>Note:</em> the names are case sensitive.
@@ -3,7 +3,7 @@
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001-2007 Michael Bayne
//
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
@@ -47,6 +47,13 @@ public class DefaultLiaison extends BaseLiaison
return false;
}
// from DatabaseLiaison
public void deleteGenerator (Connection conn, String tableName, String columnName)
throws SQLException
{
// nothing doing
}
// from DatabaseLiaison
public void initializeGenerator (
Connection conn, String table, String column, int value, int size)
@@ -59,6 +59,13 @@ public class MySQLLiaison extends BaseLiaison
// AUTO_INCREMENT does not allow this kind of control
}
public void deleteGenerator (Connection conn, String tableName, String columnName)
throws SQLException
{
// AUTO_INCREMENT does not create any database entities that we need to delete
}
// from DatabaseLiaison
public int lastInsertedId (Connection conn, String table, String column) throws SQLException
{
@@ -120,14 +127,14 @@ public class MySQLLiaison extends BaseLiaison
@Override // from BaseLiaison
public boolean renameColumn (Connection conn, String table, String oldColumnName,
String newColumnName, String newColumnDef)
String newColumnName, ColumnDefinition newColumnDef)
throws SQLException
{
if (!tableContainsColumn(conn, table, oldColumnName)) {
return false;
}
executeQuery(conn, "ALTER TABLE " + table + " CHANGE " + oldColumnName + " " +
newColumnName + " " + newColumnDef);
newColumnName + " " + expandDefinition(newColumnDef));
Log.info("Renamed column '" + oldColumnName + "' on table '" + table + "' to '" +
newColumnName + "'");
return true;
@@ -21,8 +21,6 @@
package com.samskivert.jdbc;
import java.sql.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.samskivert.Log;
@@ -85,23 +83,46 @@ public class PostgreSQLLiaison extends BaseLiaison
" restart with " + first + " increment " + step);
}
@Override // from DatabaseLiaison
public boolean changeColumn (Connection conn, String table, String column, String definition)
// from DatabaseLiaison
public void deleteGenerator (Connection conn, String table, String column)
throws SQLException
{
// we need to handle nullability separately; TODO: make this less of a hack
boolean notNull = false;
Matcher match = NOT_NULL.matcher(definition);
if (match.find()) {
definition = match.replaceFirst("");
notNull = true;
executeQuery(conn, "drop sequence if exists \"" + table + "_" + column + "_seq\"");
}
@Override // from DatabaseLiaison
public boolean changeColumn (Connection conn, String table, String column, String type,
Boolean nullable, Boolean unique, String defaultValue)
throws SQLException
{
StringBuilder log = new StringBuilder();
if (type != null) {
executeQuery(
conn, "ALTER TABLE " + tableSQL(table) + " ALTER COLUMN " + columnSQL(column) +
" TYPE " + type);
log.append("type=" + type);
}
if (nullable != null) {
executeQuery(
conn, "ALTER TABLE " + tableSQL(table) + " ALTER COLUMN " + columnSQL(column) +
" " + (nullable.booleanValue() ? "SET NOT NULL" : "DROP NOT NULL"));
if (log.length() > 0) log.append(", ");
log.append("nullable=" + nullable);
}
if (unique != null) {
// TODO: I think this requires ALTER TABLE DROP CONSTRAINT and so on
if (log.length() > 0) log.append(", ");
log.append("unique=" + unique + " (not implemented yet)");
}
if (defaultValue != null) {
executeQuery(
conn, "ALTER TABLE " + tableSQL(table) + " ALTER COLUMN " + columnSQL(column) +
" " + (defaultValue.length() > 0 ? "SET DEFAULT " + defaultValue : "DROP DEFAULT"));
if (log.length() > 0) log.append(", ");
log.append("defaultValue=" + defaultValue);
}
executeQuery(conn, "ALTER TABLE " + tableSQL(table) + " ALTER COLUMN " +
columnSQL(column) + " TYPE " + definition);
executeQuery(conn, "ALTER TABLE " + tableSQL(table) + " ALTER COLUMN " +
columnSQL(column) + " " + (notNull ? "SET NOT NULL" : "DROP NOT NULL"));
Log.info("Database column '" + column + "' of table '" + table + "' modified to have " +
"definition '" + definition + "'.");
"definition [" + log + "].");
return true;
}
@@ -122,7 +143,4 @@ public class PostgreSQLLiaison extends BaseLiaison
{
return "\"" + index + "\"";
}
protected static final Pattern NOT_NULL =
Pattern.compile(" NOT NULL", Pattern.CASE_INSENSITIVE);
}
@@ -203,7 +203,11 @@ public class TransitionRepository extends SimpleRepository
conn,
"TRANSITIONS",
new String[] { "CLASS", "NAME", "APPLIED" },
new String[] { "VARCHAR(200)", "VARCHAR(50)", "TIMESTAMP NOT NULL" },
new ColumnDefinition[] {
new ColumnDefinition("VARCHAR(200)", true, false, null),
new ColumnDefinition("VARCHAR(50)", true, false, null),
new ColumnDefinition("TIMESTAMP")
},
null,
new String[] { "CLASS", "NAME" });
}