Stubbed out Depot support for HSQLDB. This also doesn't work yet but will some

day be made to work and enable more sophisticated unit testing of Depot and
systems that use Depot.
This commit is contained in:
Michael Bayne
2008-11-25 20:13:43 +00:00
parent bc44c4e58e
commit 0b439978fb
2 changed files with 284 additions and 1 deletions
@@ -0,0 +1,277 @@
//
// $Id$
//
// Depot library - a Java relational persistence library
// Copyright (C) 2006-2008 Michael Bayne and 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.depot;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Set;
import com.samskivert.jdbc.JDBCUtil;
import com.samskivert.depot.FieldMarshaller.BooleanMarshaller;
import com.samskivert.depot.FieldMarshaller.ByteArrayMarshaller;
import com.samskivert.depot.FieldMarshaller.ByteEnumMarshaller;
import com.samskivert.depot.FieldMarshaller.ByteMarshaller;
import com.samskivert.depot.FieldMarshaller.DoubleMarshaller;
import com.samskivert.depot.FieldMarshaller.FloatMarshaller;
import com.samskivert.depot.FieldMarshaller.IntArrayMarshaller;
import com.samskivert.depot.FieldMarshaller.IntMarshaller;
import com.samskivert.depot.FieldMarshaller.LongMarshaller;
import com.samskivert.depot.FieldMarshaller.ObjectMarshaller;
import com.samskivert.depot.FieldMarshaller.ShortMarshaller;
import com.samskivert.depot.annotation.FullTextIndex;
import com.samskivert.depot.clause.DeleteClause;
import com.samskivert.depot.expression.ColumnExp;
import com.samskivert.depot.expression.EpochSeconds;
import com.samskivert.depot.operator.Conditionals.FullTextMatch;
import static com.samskivert.Log.log;
public class HSQLBuilder
extends SQLBuilder
{
public class HBuildVisitor extends BuildVisitor
{
@Override public void visit (FullTextMatch match)
{
_builder.append("match(");
Class<? extends PersistentRecord> pClass = match.getPersistentRecord();
String[] fields =
_types.getMarshaller(pClass).getFullTextIndex(match.getName()).fields();
for (int ii = 0; ii < fields.length; ii ++) {
if (ii > 0) {
_builder.append(", ");
}
new ColumnExp(pClass, fields[ii]).accept(this);
}
_builder.append(") against (? in boolean mode)");
}
@Override public void visit (DeleteClause<? extends PersistentRecord> deleteClause)
{
_builder.append("delete from ");
appendTableName(deleteClause.getPersistentClass());
_builder.append(" ");
// MySQL can't do DELETE FROM SomeTable AS T1, so we turn off abbreviations briefly.
boolean savedFlag = _types.getUseTableAbbreviations();
_types.setUseTableAbbreviations(false);
try {
deleteClause.getWhereClause().accept(this);
} finally {
_types.setUseTableAbbreviations(savedFlag);
}
}
public void visit (EpochSeconds epochSeconds)
{
_builder.append("unix_timestamp(");
epochSeconds.getArgument().accept(this);
_builder.append(")");
}
protected HBuildVisitor (DepotTypes types)
{
super(types);
}
@Override protected void appendTableName (Class<? extends PersistentRecord> type)
{
_builder.append(_types.getTableName(type));
}
@Override protected void appendTableAbbreviation (Class<? extends PersistentRecord> type)
{
_builder.append(_types.getTableAbbreviation(type));
}
@Override protected void appendIdentifier (String field)
{
_builder.append(field);
}
}
public class HBindVisitor extends BindVisitor
{
protected HBindVisitor (DepotTypes types, Connection conn, PreparedStatement stmt) {
super(types, conn, stmt);
}
@Override public void visit (FullTextMatch match) {
try {
_stmt.setString(_argIdx++, match.getQuery());
} catch (SQLException sqe) {
throw new DatabaseException("Failed to configure full-text match column " +
"[idx=" + (_argIdx-1) + ", match=" + match + "]", sqe);
}
}
}
public HSQLBuilder (DepotTypes types)
{
super(types);
}
@Override
public void getFtsIndexes (
Iterable<String> columns, Iterable<String> indexes, Set<String> target)
{
for (String index : indexes) {
if (index.startsWith("ftsIx_")) {
target.add(index.substring("ftsIx_".length()));
}
}
}
@Override
public <T extends PersistentRecord> boolean addFullTextSearch (
Connection conn, DepotMarshaller<T> marshaller, FullTextIndex fts)
throws SQLException
{
Class<T> pClass = marshaller.getPersistentClass();
StringBuilder update = new StringBuilder("ALTER TABLE ").
append(marshaller.getTableName()).append(" ADD FULLTEXT INDEX ftsIx_").
append(fts.name()).append(" (");
String[] fields = fts.fields();
for (int ii = 0; ii < fields.length; ii ++) {
if (ii > 0) {
update.append(", ");
}
update.append(_types.getColumnName(pClass, fields[ii]));
}
update.append(")");
Statement stmt = conn.createStatement();
try {
log.info("Adding full-text search index: ftsIx_" + fts.name());
stmt.executeUpdate(update.toString());
} finally {
JDBCUtil.close(stmt);
}
return true;
}
@Override
public boolean isPrivateColumn (String column)
{
// The HSQLDB builder does not yet have any private columns.
return false;
}
@Override
protected String getBooleanDefault ()
{
return "0";
}
@Override
protected BuildVisitor getBuildVisitor ()
{
return new HBuildVisitor(_types);
}
@Override
protected BindVisitor getBindVisitor (Connection conn, PreparedStatement stmt)
{
return new HBindVisitor(_types, conn, stmt);
}
@Override
protected <T> String getColumnType (FieldMarshaller<?> fm, int length)
{
if (fm instanceof ByteMarshaller) {
return "TINYINT";
} else if (fm instanceof ShortMarshaller) {
return "SMALLINT";
} else if (fm instanceof IntMarshaller) {
return "INTEGER";
} else if (fm instanceof LongMarshaller) {
return "BIGINT";
} else if (fm instanceof FloatMarshaller) {
return "FLOAT";
} else if (fm instanceof DoubleMarshaller) {
return "DOUBLE";
} else if (fm instanceof ObjectMarshaller) {
Class<?> ftype = fm.getField().getType();
if (ftype.equals(Byte.class)) {
return "SMALLINT";
} else if (ftype.equals(Short.class)) {
return "SMALLINT";
} else if (ftype.equals(Integer.class)) {
return "INTEGER";
} else if (ftype.equals(Long.class)) {
return "BIGINT";
} else if (ftype.equals(Float.class)) {
return "FLOAT";
} else if (ftype.equals(Double.class)) {
return "DOUBLE";
} else if (ftype.equals(String.class)) {
if (length < (1 << 15)) {
return "VARCHAR(" + length + ")";
}
return "TEXT";
} else if (ftype.equals(Date.class)) {
return "DATE";
} else if (ftype.equals(Time.class)) {
return "DATETIME";
} else if (ftype.equals(Timestamp.class)) {
return "DATETIME";
} else if (ftype.equals(Blob.class)) {
return "BYTEA";
} else if (ftype.equals(Clob.class)) {
return "TEXT";
} else {
throw new IllegalArgumentException(
"Don't know how to create SQL for " + ftype + ".");
}
} else if (fm instanceof ByteArrayMarshaller ||
fm instanceof IntArrayMarshaller) {
if (fm instanceof IntArrayMarshaller) {
length *= 4;
}
// semi-arbitrarily use VARBINARY() up to 32767
if (length < (1 << 15)) {
return "VARBINARY(" + length + ")";
}
// use BLOB to 65535
if (length < (1 << 16)) {
return "BLOB";
}
if (length < (1 << 24)) {
return "MEDIUMBLOB";
}
return "LONGBLOB";
} else if (fm instanceof ByteEnumMarshaller) {
return "TINYINT";
} else if (fm instanceof BooleanMarshaller) {
return "TINYINT";
} else {
throw new IllegalArgumentException("Unknown field marshaller type: " + fm.getClass());
}
}
}
@@ -37,6 +37,7 @@ import com.samskivert.util.StringUtil;
import com.samskivert.jdbc.ConnectionProvider;
import com.samskivert.jdbc.DatabaseLiaison;
import com.samskivert.jdbc.HsqldbLiaison;
import com.samskivert.jdbc.LiaisonRegistry;
import com.samskivert.jdbc.MySQLLiaison;
import com.samskivert.jdbc.PostgreSQLLiaison;
@@ -197,6 +198,9 @@ public class PersistenceContext
if (_liaison instanceof MySQLLiaison) {
return new MySQLBuilder(types);
}
if (_liaison instanceof HsqldbLiaison) {
return new HSQLBuilder(types);
}
throw new IllegalArgumentException("Unknown liaison type: " + _liaison.getClass());
}
@@ -375,7 +379,9 @@ public class PersistenceContext
if (_cache == null) {
return;
}
log.debug("invalidating [cacheId=" + cacheId + ", cacheKey=" + cacheKey + "]");
if (CACHE_DEBUG) {
log.info("Invalidating", "id", cacheId, "key", cacheKey);
}
CacheAdapter.CacheBin<T> bin = _cache.getCache(cacheId);
CacheAdapter.CachedValue<T> element = bin.lookup(cacheKey);