Files
depot/src/main/java/com/samskivert/depot/impl/MySQLBuilder.java
T

297 lines
9.3 KiB
Java

//
// $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.impl;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
import java.util.Set;
import com.samskivert.depot.PersistentRecord;
import com.samskivert.depot.annotation.FullTextIndex;
import com.samskivert.depot.expression.ColumnExp;
import com.samskivert.depot.operator.FullText;
import com.samskivert.depot.impl.clause.DeleteClause;
import com.samskivert.depot.impl.clause.DropIndexClause;
import com.samskivert.depot.impl.expression.DateFun.DatePart;
import com.samskivert.depot.impl.expression.DateFun.DateTruncate;
import com.samskivert.depot.impl.expression.DateFun.DatePart.Part;
import com.samskivert.depot.impl.expression.NumericalFun.Trunc;
import static com.samskivert.Log.log;
public class MySQLBuilder
extends SQLBuilder
{
public class MSBuildVisitor extends BuildVisitor
{
@Override public Void visit (FullText.Match match)
{
renderMatch(match.getDefinition());
return null;
}
@Override public Void visit (FullText.Rank rank)
{
renderMatch(rank.getDefinition());
return null;
}
@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);
}
return null;
}
@Override public Void visit (Trunc<?> exp)
{
return appendFunctionCall("truncate", exp.getArg());
}
@Override public Void visit (DatePart exp) {
return appendFunctionCall(getDateFunction(exp.getPart()), exp.getArg());
}
@Override
public Void visit (DateTruncate exp)
{
// exp.getTruncation() is currently always DAY
_builder.append(" cast(");
appendFunctionCall("date", exp.getArg());
_builder.append(" as datetime)");
return null;
}
protected String getDateFunction (Part part)
{
switch(part) {
case DAY_OF_MONTH:
return "dayofmonth";
case DAY_OF_WEEK:
return "dayofweek";
case DAY_OF_YEAR:
return "dayofyear";
case HOUR:
return "hour";
case MINUTE:
return "minute";
case MONTH:
return "month";
case SECOND:
return "second";
case WEEK:
return "week";
case YEAR:
return "year";
case EPOCH:
return "unix_timestamp";
}
throw new IllegalArgumentException("Unknown date part: " + part);
}
@Override
public Void visit (DropIndexClause dropIndexClause)
{
// MySQL's indexes are scoped on the table, not on the database, and the
// SQL syntax reflects it: DROP INDEX fooIx on fooTable
_builder.append("drop index ");
appendIdentifier(dropIndexClause.getName());
_builder.append(" on ");
appendTableName(dropIndexClause.getPersistentClass());
return null;
}
protected MSBuildVisitor (DepotTypes types)
{
super(types, false);
}
@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);
}
protected void renderMatch (FullText fullText)
{
_builder.append("match(");
Class<? extends PersistentRecord> pClass = fullText.getPersistentClass();
String[] fields = _types.getMarshaller(pClass).getFullTextIndex(
fullText.getName()).fields();
for (int ii = 0; ii < fields.length; ii ++) {
if (ii > 0) {
_builder.append(", ");
}
new ColumnExp<Object>(pClass, fields[ii]).accept(this);
}
_builder.append(") against (");
bindValue(fullText.getQuery());
_builder.append(" in boolean mode)");
}
}
public MySQLBuilder (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(")");
log.info("Adding full-text search index: ftsIx_" + fts.name());
conn.createStatement().executeUpdate(update.toString());
return true;
}
@Override
public boolean isPrivateColumn (String column, Map<String, FullTextIndex> fullTextIndexes)
{
// The MySQL builder does not yet have any private columns.
return false;
}
@Override
public boolean isPrivateIndex (String index, Map<String, FullTextIndex> fullTextIndexes)
{
// The MySQL builder does not yet have any private indexes.
return false;
}
@Override
protected String getBooleanDefault ()
{
return "0";
}
@Override
protected BuildVisitor getBuildVisitor ()
{
return new MSBuildVisitor(_types);
}
@Override
protected <T> String getColumnType (FieldMarshaller<?> fm, int length)
{
return fm.getColumnType(TYPER, length);
}
protected static final FieldMarshaller.ColumnTyper TYPER = new FieldMarshaller.ColumnTyper() {
public String getBooleanType (int length) {
return "TINYINT";
}
public String getByteType (int length) {
return "TINYINT";
}
public String getShortType (int length) {
return "SMALLINT";
}
public String getIntType (int length) {
return "INTEGER";
}
public String getLongType (int length) {
return "BIGINT";
}
public String getFloatType (int length) {
return "FLOAT";
}
public String getDoubleType (int length) {
return "DOUBLE";
}
public String getStringType (int length) {
return (length < (1 << 15)) ? "VARCHAR(" + length + ")" : "TEXT";
}
public String getDateType (int length) {
return "DATE";
}
public String getTimeType (int length) {
return "TIME";
}
public String getTimestampType (int length) {
return "DATETIME";
}
public String getBlobType (int length) {
// 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";
}
public String getClobType (int length) {
return "TEXT";
}
};
}