HSQL support:
- HSQL largely follows SQL92, thus to preserve case we have to enclose identifiers such as column and table names in quotes. - HSQL has a method for auto-generating primary key values, but its syntax is different from MySQL and PostgreSQL -- just when we got to celebrate that those two giants had agreed on a convenient alias. - HSQL can't handle uniqueness as a column-level constraint. It can certainly handle unique columns, it just seems to feel that's a table level constraint and thus to be specified as a prologue during table creation. Thus we override createTableIfMissing to strip uniqueness from column definitions and append those instead to the table-level unique constraints. We might want to switch to this approach for all the databases. - We override expandDefinition() to magically translate a default value of IDENTITY into a HSQL-specific column mark-up that triggers the primary key value auto-generation. This is for Depot's benefit, and I would not be surprised if this method was soon moved to that project. git-svn-id: https://samskivert.googlecode.com/svn/trunk@2501 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
@@ -20,11 +20,23 @@
|
|||||||
|
|
||||||
package com.samskivert.jdbc;
|
package com.samskivert.jdbc;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.Statement;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import com.samskivert.jdbc.ColumnDefinition;
|
||||||
|
import com.samskivert.util.ArrayUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles liaison for HSQLDB. NOTE: incomplete and doesn't work yet!
|
* Handles liaison for HSQLDB.
|
||||||
*/
|
*/
|
||||||
public class HsqldbLiaison extends BaseLiaison
|
public class HsqldbLiaison extends BaseLiaison
|
||||||
{
|
{
|
||||||
@@ -37,50 +49,50 @@ public class HsqldbLiaison extends BaseLiaison
|
|||||||
// from DatabaseLiaison
|
// from DatabaseLiaison
|
||||||
public String columnSQL (String column)
|
public String columnSQL (String column)
|
||||||
{
|
{
|
||||||
return column;
|
return "\"" + column + "\"";
|
||||||
}
|
}
|
||||||
|
|
||||||
// from DatabaseLiaison
|
// from DatabaseLiaison
|
||||||
public String tableSQL (String table)
|
public String tableSQL (String table)
|
||||||
{
|
{
|
||||||
return table;
|
return "\"" + table + "\"";
|
||||||
}
|
}
|
||||||
|
|
||||||
// from DatabaseLiaison
|
// from DatabaseLiaison
|
||||||
public String indexSQL (String index)
|
public String indexSQL (String index)
|
||||||
{
|
{
|
||||||
return index;
|
return "\"" + index + "\"";
|
||||||
}
|
}
|
||||||
|
|
||||||
// from DatabaseLiaison
|
// from DatabaseLiaison
|
||||||
public void createGenerator (Connection conn, String tableName, String columnName, int initValue)
|
public void createGenerator (Connection conn, String tableName,
|
||||||
|
String columnName, int initValue)
|
||||||
throws SQLException
|
throws SQLException
|
||||||
{
|
{
|
||||||
// TODO: is there any way we can set the initial AUTO_INCREMENT value?
|
// HSQL's IDENTITY() does not create any database entities
|
||||||
}
|
}
|
||||||
|
|
||||||
// from DatabaseLiaison
|
// from DatabaseLiaison
|
||||||
public void deleteGenerator (Connection conn, String tableName, String columnName)
|
public void deleteGenerator (Connection conn, String tableName, String columnName)
|
||||||
throws SQLException
|
throws SQLException
|
||||||
{
|
{
|
||||||
// AUTO_INCREMENT does not create any database entities that we need to delete
|
// HSQL's IDENTITY() does not create any database entities that we need to delete
|
||||||
}
|
}
|
||||||
|
|
||||||
// from DatabaseLiaison
|
// from DatabaseLiaison
|
||||||
public int lastInsertedId (Connection conn, String table, String column) throws SQLException
|
public int lastInsertedId (Connection conn, String table, String column) throws SQLException
|
||||||
{
|
{
|
||||||
throw new UnsupportedOperationException();
|
// HSQL does not keep track of per-table-and-column insertion data, so we are pretty much
|
||||||
// // MySQL does not keep track of per-table-and-column insertion data, so we are pretty much
|
// going on blind faith here that we're fetching the right ID. In the overwhelming number
|
||||||
// // going on blind faith here that we're fetching the right ID. In the overwhelming number
|
// of cases that will be so, but it's still not pretty.
|
||||||
// // of cases that will be so, but it's still not pretty.
|
Statement stmt = null;
|
||||||
// Statement stmt = null;
|
try {
|
||||||
// try {
|
stmt = conn.createStatement();
|
||||||
// stmt = conn.createStatement();
|
ResultSet rs = stmt.executeQuery("call IDENTITY()");
|
||||||
// ResultSet rs = stmt.executeQuery("select LAST_INSERT_ID()");
|
return rs.next() ? rs.getInt(1) : -1;
|
||||||
// return rs.next() ? rs.getInt(1) : -1;
|
} finally {
|
||||||
// } finally {
|
JDBCUtil.close(stmt);
|
||||||
// JDBCUtil.close(stmt);
|
}
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// from DatabaseLiaison
|
// from DatabaseLiaison
|
||||||
@@ -92,8 +104,87 @@ public class HsqldbLiaison extends BaseLiaison
|
|||||||
// from DatabaseLiaison
|
// from DatabaseLiaison
|
||||||
public boolean isDuplicateRowException (SQLException sqe)
|
public boolean isDuplicateRowException (SQLException sqe)
|
||||||
{
|
{
|
||||||
throw new UnsupportedOperationException();
|
// Violation of unique constraint SYS_PK_51: duplicate value(s) for column(s) FOO
|
||||||
// String msg = sqe.getMessage(); // TODO: not sure
|
String msg = sqe.getMessage();
|
||||||
// return (msg != null && msg.indexOf("Duplicate entry") != -1);
|
return (msg != null && msg.indexOf("duplicate value(s)") != -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BaseLiaison's implementation of table creation accepts unique constraints both as
|
||||||
|
// part of the column definition and as a separate argument, and merrily passes this
|
||||||
|
// duality onto the database. Postgres and MySQL both handle this fine but HSQL seems
|
||||||
|
// to simply not allow uniqueness in the column definitions. So, for HSQL, we transfer
|
||||||
|
// uniqueness from the ColumnDefinitions to the uniqueConstraintColumns before we pass
|
||||||
|
// it in to the super implementation.
|
||||||
|
//
|
||||||
|
// TODO: Consider making this the general MO instead of a subclass override. In fact
|
||||||
|
// it may be that uniqueness should be removed from ColumnDefinition.
|
||||||
|
@Override // from DatabaseLiaison
|
||||||
|
public boolean createTableIfMissing (
|
||||||
|
Connection conn, String table, String[] columns, ColumnDefinition[] definitions,
|
||||||
|
String[][] uniqueConstraintColumns, String[] primaryKeyColumns)
|
||||||
|
throws SQLException
|
||||||
|
{
|
||||||
|
Preconditions.checkArgument(columns.length == definitions.length,
|
||||||
|
"Column name and definition number mismatch");
|
||||||
|
|
||||||
|
// make a set of unique constraints already provided
|
||||||
|
Set<List<String>> uColSet = Sets.newHashSet();
|
||||||
|
if (uniqueConstraintColumns != null) {
|
||||||
|
for (String[] uCols : uniqueConstraintColumns) {
|
||||||
|
uColSet.add(Arrays.asList(uCols));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// go through the columns and find any that are unique; these we replace with a
|
||||||
|
// non-unique variant, and instead add a new entry to the table unique constraint
|
||||||
|
ColumnDefinition[] newDefinitions = new ColumnDefinition[definitions.length];
|
||||||
|
for (int ii = 0; ii < definitions.length; ii ++) {
|
||||||
|
ColumnDefinition def = definitions[ii];
|
||||||
|
if (def.unique) {
|
||||||
|
// let's be nice and not mutate the caller's object
|
||||||
|
newDefinitions[ii] = new ColumnDefinition(
|
||||||
|
def.type, def.nullable, false, def.defaultValue);
|
||||||
|
// if a uniqueness constraint for this column was not in the
|
||||||
|
// uniqueConstraintColumns parameter, add such an entry
|
||||||
|
if (!uColSet.contains(Sets.newHashSet(columns[ii]))) {
|
||||||
|
String[] newConstraint = new String[] { columns[ii] };
|
||||||
|
uniqueConstraintColumns = (uniqueConstraintColumns == null) ?
|
||||||
|
new String[][] { newConstraint } :
|
||||||
|
ArrayUtil.append(uniqueConstraintColumns, newConstraint);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newDefinitions[ii] = def;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// now call the real implementation with our modified data
|
||||||
|
return super.createTableIfMissing(
|
||||||
|
conn, table, columns, newDefinitions, uniqueConstraintColumns, primaryKeyColumns);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // from DatabaseLiaison
|
||||||
|
protected String expandDefinition (
|
||||||
|
String type, boolean nullable, boolean unique, String defaultValue)
|
||||||
|
{
|
||||||
|
StringBuilder builder = new StringBuilder(type);
|
||||||
|
|
||||||
|
// append the default value if one was specified
|
||||||
|
if (defaultValue != null) {
|
||||||
|
if ("IDENTITY".equals(defaultValue)) {
|
||||||
|
// this is a blatant hack, we need this method to join Depot's SQLBuilder
|
||||||
|
builder.append(" GENERATED BY DEFAULT AS IDENTITY (START WITH 1)");
|
||||||
|
} else {
|
||||||
|
builder.append(" DEFAULT ").append(defaultValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nullable) {
|
||||||
|
builder.append(" NOT NULL");
|
||||||
|
}
|
||||||
|
if (unique) {
|
||||||
|
throw new IllegalArgumentException("HSQL can't deal with column uniqueness here");
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user