From 0b439978fbd78422e4190b98f8e2ad7049ee26fa Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Tue, 25 Nov 2008 20:13:43 +0000 Subject: [PATCH] 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. --- .../com/samskivert/depot/HSQLBuilder.java | 277 ++++++++++++++++++ .../samskivert/depot/PersistenceContext.java | 8 +- 2 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 src/java/com/samskivert/depot/HSQLBuilder.java diff --git a/src/java/com/samskivert/depot/HSQLBuilder.java b/src/java/com/samskivert/depot/HSQLBuilder.java new file mode 100644 index 0000000..a612ad4 --- /dev/null +++ b/src/java/com/samskivert/depot/HSQLBuilder.java @@ -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 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 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 type) + { + _builder.append(_types.getTableName(type)); + } + + @Override protected void appendTableAbbreviation (Class 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 columns, Iterable indexes, Set target) + { + for (String index : indexes) { + if (index.startsWith("ftsIx_")) { + target.add(index.substring("ftsIx_".length())); + } + } + } + + @Override + public boolean addFullTextSearch ( + Connection conn, DepotMarshaller marshaller, FullTextIndex fts) + throws SQLException + { + Class 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 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()); + } + } +} diff --git a/src/java/com/samskivert/depot/PersistenceContext.java b/src/java/com/samskivert/depot/PersistenceContext.java index e628ba3..67a97d7 100644 --- a/src/java/com/samskivert/depot/PersistenceContext.java +++ b/src/java/com/samskivert/depot/PersistenceContext.java @@ -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 bin = _cache.getCache(cacheId); CacheAdapter.CachedValue element = bin.lookup(cacheKey);