There was never a good reason to do the SQL parameter binding as a second ExpressionVisitor recursion, and duplicating the logic structures for each visitor was getting increasingly cumbersome. Just encapsulate the binding actions and list'em up as we generate the SQL, then just iterate over them simple-like in the second pass. This permanently gets rid of anything BindVisitorish.

This commit is contained in:
Par Winzell
2009-04-01 05:48:11 +00:00
parent b2bd4b51f7
commit 9df2415678
6 changed files with 150 additions and 507 deletions
@@ -1,350 +0,0 @@
//
// $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.nio.ByteBuffer;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Map;
import java.util.Set;
import com.samskivert.util.StringUtil;
import com.samskivert.util.Tuple;
import com.samskivert.depot.ByteEnum;
import com.samskivert.depot.DatabaseException;
import com.samskivert.depot.Key;
import com.samskivert.depot.MultiKey;
import com.samskivert.depot.PersistentRecord;
import com.samskivert.depot.clause.FieldDefinition;
import com.samskivert.depot.clause.ForUpdate;
import com.samskivert.depot.clause.FromOverride;
import com.samskivert.depot.clause.GroupBy;
import com.samskivert.depot.clause.InsertClause;
import com.samskivert.depot.clause.Join;
import com.samskivert.depot.clause.Limit;
import com.samskivert.depot.clause.OrderBy.Order;
import com.samskivert.depot.clause.OrderBy;
import com.samskivert.depot.clause.SelectClause;
import com.samskivert.depot.clause.WhereClause;
import com.samskivert.depot.expression.ColumnExp;
import com.samskivert.depot.expression.EpochSeconds;
import com.samskivert.depot.expression.FunctionExp;
import com.samskivert.depot.expression.LiteralExp;
import com.samskivert.depot.expression.SQLExpression;
import com.samskivert.depot.expression.ValueExp;
import com.samskivert.depot.operator.Conditionals.Case;
import com.samskivert.depot.operator.Conditionals.Exists;
import com.samskivert.depot.operator.Conditionals.FullText;
import com.samskivert.depot.operator.Conditionals.In;
import com.samskivert.depot.operator.Conditionals.IsNull;
import com.samskivert.depot.operator.Logic.Not;
import com.samskivert.depot.operator.SQLOperator.BinaryOperator;
import com.samskivert.depot.operator.SQLOperator.MultiOperator;
import com.samskivert.depot.impl.clause.CreateIndexClause;
import com.samskivert.depot.impl.clause.DeleteClause;
import com.samskivert.depot.impl.clause.DropIndexClause;
import com.samskivert.depot.impl.clause.UpdateClause;
/**
* Implements the base functionality of the argument-binding pass of {@link SQLBuilder}. Dialectal
* subclasses of this should be created and returned from {@link SQLBuilder#getBindVisitor}.
*
* This class is intimately paired with {#link BuildVisitor}.
*/
public class BindVisitor implements ExpressionVisitor
{
public void visit (FromOverride override)
{
// nothing needed
}
public void visit (FieldDefinition fieldOverride)
{
// nothing needed
}
public void visit (Key.Expression<? extends PersistentRecord> key)
{
for (Comparable<?> value : key.getValues()) {
if (value != null) {
writeValueToStatement(value);
}
}
}
public void visit (MultiKey<? extends PersistentRecord> key)
{
for (Map.Entry<String, Comparable<?>> entry : key.getSingleFieldsMap().entrySet()) {
if (entry.getValue() != null) {
writeValueToStatement(entry.getValue());
}
}
Comparable<?>[] values = key.getMultiValues();
for (int ii = 0; ii < values.length; ii++) {
writeValueToStatement(values[ii]);
}
}
public void visit (FunctionExp functionExp)
{
visit(functionExp.getArguments());
}
public void visit (EpochSeconds epochSeconds)
{
epochSeconds.getArgument().accept(this);
}
public void visit (MultiOperator multiOperator)
{
visit(multiOperator.getConditions());
}
public void visit (BinaryOperator binaryOperator)
{
binaryOperator.getLeftHandSide().accept(this);
binaryOperator.getRightHandSide().accept(this);
}
public void visit (IsNull isNull)
{
}
public void visit (In in)
{
Comparable<?>[] values = in.getValues();
for (int ii = 0; ii < values.length; ii++) {
writeValueToStatement(values[ii]);
}
}
public void visit (FullText.Match match)
{
// we never get here
}
public void visit (FullText.Rank rank)
{
// we never get here
}
public void visit (Case caseExp)
{
for (Tuple<SQLExpression, SQLExpression> tuple : caseExp.getWhenExps()) {
tuple.left.accept(this);
tuple.right.accept(this);
}
SQLExpression elseExp = caseExp.getElseExp();
if (elseExp != null) {
elseExp.accept(this);
}
}
public void visit (ColumnExp columnExp)
{
// no arguments
}
public void visit (Not not)
{
not.getCondition().accept(this);
}
public void visit (GroupBy groupBy)
{
visit(groupBy.getValues());
}
public void visit (ForUpdate forUpdate)
{
// do nothing
}
public void visit (OrderBy orderBy)
{
visit(orderBy.getValues());
}
public void visit (WhereClause where)
{
where.getWhereExpression().accept(this);
}
public void visit (Join join)
{
join.getJoinCondition().accept(this);
}
public void visit (Limit limit)
{
try {
_stmt.setInt(_argIdx++, limit.getCount());
_stmt.setInt(_argIdx++, limit.getOffset());
} catch (SQLException sqe) {
throw new DatabaseException("Failed to configure statement with limit clause " +
"[count=" + limit.getCount() +
", offset=" + limit.getOffset() + "]", sqe);
}
}
public void visit (LiteralExp literalExp)
{
// do nothing
}
public void visit (ValueExp valueExp)
{
writeValueToStatement(valueExp.getValue());
}
public void visit (Exists<? extends PersistentRecord> exists)
{
exists.getSubClause().accept(this);
}
public void visit (SelectClause<? extends PersistentRecord> selectClause)
{
for (Join clause : selectClause.getJoinClauses()) {
clause.accept(this);
}
if (selectClause.getWhereClause() != null) {
selectClause.getWhereClause().accept(this);
}
if (selectClause.getGroupBy() != null) {
selectClause.getGroupBy().accept(this);
}
if (selectClause.getOrderBy() != null) {
selectClause.getOrderBy().accept(this);
}
if (selectClause.getLimit() != null) {
selectClause.getLimit().accept(this);
}
if (selectClause.getForUpdate() != null) {
selectClause.getForUpdate().accept(this);
}
}
public void visit (UpdateClause<? extends PersistentRecord> updateClause)
{
DepotMarshaller<?> marsh = _types.getMarshaller(updateClause.getPersistentClass());
// bind the update arguments
Object pojo = updateClause.getPojo();
if (pojo != null) {
for (String field : updateClause.getFields()) {
try {
marsh.getFieldMarshaller(field).getAndWriteToStatement(_stmt, _argIdx++, pojo);
} catch (Exception e) {
throw new DatabaseException(
"Failed to read field from persistent record and write it to prepared " +
"statement [field=" + field + "]", e);
}
}
} else {
visit(updateClause.getValues());
}
updateClause.getWhereClause().accept(this);
}
public void visit (InsertClause insertClause)
{
DepotMarshaller<?> marsh = _types.getMarshaller(insertClause.getPersistentClass());
Object pojo = insertClause.getPojo();
Set<String> idFields = insertClause.getIdentityFields();
for (String field : marsh.getColumnFieldNames()) {
if (!idFields.contains(field)) {
try {
marsh.getFieldMarshaller(field).getAndWriteToStatement(_stmt, _argIdx++, pojo);
} catch (Exception e) {
throw new DatabaseException(
"Failed to read field from persistent record and write it to prepared " +
"statement [field=" + field + "]", e);
}
}
}
}
public void visit (DeleteClause deleteClause)
{
deleteClause.getWhereClause().accept(this);
}
public void visit (CreateIndexClause createIndexClause)
{
for (Tuple<SQLExpression, Order> field : createIndexClause.getFields()) {
field.left.accept(this);
}
}
public void visit (DropIndexClause<? extends PersistentRecord> createIndexClause)
{
// do nothing
}
protected BindVisitor (DepotTypes types, Connection conn, PreparedStatement stmt)
{
_types = types;
_conn = conn;
_stmt = stmt;
_argIdx = 1;
}
protected void visit (SQLExpression[] expressions)
{
for (int ii = 0; ii < expressions.length; ii ++) {
expressions[ii].accept(this);
}
}
// write the value to the next argument slot in the prepared statement
protected void writeValueToStatement (Object value)
{
try {
// TODO: how can we abstract this fieldless marshalling
if (value instanceof ByteEnum) {
// byte enums require special conversion
_stmt.setByte(_argIdx++, ((ByteEnum)value).toByte());
} else if (value instanceof int[]) {
// int arrays require conversion to byte arrays
int[] data = (int[])value;
ByteBuffer bbuf = ByteBuffer.allocate(data.length * 4);
bbuf.asIntBuffer().put(data);
_stmt.setObject(_argIdx++, bbuf.array());
} else {
_stmt.setObject(_argIdx++, value);
}
} catch (SQLException sqe) {
throw new DatabaseException("Failed to write value to statement [idx=" + (_argIdx-1) +
", value=" + StringUtil.safeToString(value) + "]", sqe);
}
}
protected DepotTypes _types;
protected Connection _conn;
protected PreparedStatement _stmt;
protected int _argIdx;
}
@@ -20,14 +20,18 @@
package com.samskivert.depot.impl;
import java.nio.ByteBuffer;
import java.sql.PreparedStatement;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.samskivert.util.Tuple;
import com.samskivert.depot.ByteEnum;
import com.samskivert.depot.Key;
import com.samskivert.depot.MultiKey;
import com.samskivert.depot.PersistentRecord;
@@ -67,8 +71,6 @@ import com.samskivert.depot.impl.clause.UpdateClause;
/**
* Implements the base functionality of the SQL-building pass of {@link SQLBuilder}. Dialectal
* subclasses of this should be created and returned from {@link SQLBuilder#getBuildVisitor()}.
*
* This class is intimately paired with {#link BindVisitor}.
*/
public abstract class BuildVisitor implements ExpressionVisitor
{
@@ -77,6 +79,11 @@ public abstract class BuildVisitor implements ExpressionVisitor
return _builder.toString();
}
public Iterable<Bindable> getBindables ()
{
return _bindables;
}
public void visit (FromOverride override)
{
_builder.append(" from " );
@@ -121,7 +128,12 @@ public abstract class BuildVisitor implements ExpressionVisitor
_enableOverrides = true;
appendRhsColumn(pClass, keyFields[ii]);
_enableOverrides = saved;
_builder.append(values[ii] == null ? " is null " : " = ? ");
if (values[ii] == null) {
_builder.append(" is null ");
} else {
_builder.append(" = ");
bindValue(values[ii]);
}
}
}
@@ -141,7 +153,12 @@ public abstract class BuildVisitor implements ExpressionVisitor
_enableOverrides = true;
appendRhsColumn(key.getPersistentClass(), entry.getKey());
_enableOverrides = saved;
_builder.append(entry.getValue() == null ? " is null " : " = ? ");
if (entry.getValue() == null) {
_builder.append(" is null ");
} else {
_builder.append(" = ");
bindValue(entry.getValue());
}
}
if (!first) {
_builder.append(" and ");
@@ -154,7 +171,7 @@ public abstract class BuildVisitor implements ExpressionVisitor
if (ii > 0) {
_builder.append(", ");
}
_builder.append("?");
bindValue(values[ii]);
}
_builder.append(")");
}
@@ -210,7 +227,7 @@ public abstract class BuildVisitor implements ExpressionVisitor
if (ii > 0) {
_builder.append(", ");
}
_builder.append("?");
bindValue(values[ii]);
}
_builder.append(")");
}
@@ -304,7 +321,8 @@ public abstract class BuildVisitor implements ExpressionVisitor
public void visit (Limit limit)
{
_builder.append(" limit ? offset ? ");
_builder.append(" limit ").append(limit.getCount()).
append(" offset ").append(limit.getOffset());
}
public void visit (LiteralExp literalExp)
@@ -314,7 +332,7 @@ public abstract class BuildVisitor implements ExpressionVisitor
public void visit (ValueExp valueExp)
{
_builder.append("?");
bindValue(valueExp.getValue());
}
public void visit (Exists<? extends PersistentRecord> exists)
@@ -446,7 +464,8 @@ public abstract class BuildVisitor implements ExpressionVisitor
_builder.append(" = ");
if (pojo != null) {
_builder.append("?");
bindField(pClass, fields[ii], pojo);
} else {
values[ii].accept(this);
}
@@ -467,6 +486,7 @@ public abstract class BuildVisitor implements ExpressionVisitor
public void visit (InsertClause insertClause)
{
Class<? extends PersistentRecord> pClass = insertClause.getPersistentClass();
Object pojo = insertClause.getPojo();
DepotMarshaller<?> marsh = _types.getMarshaller(pClass);
_innerClause = true;
@@ -499,7 +519,7 @@ public abstract class BuildVisitor implements ExpressionVisitor
_builder.append(", ");
}
comma = true;
_builder.append("?");
bindField(pClass, field, pojo);
}
_builder.append(")");
}
@@ -545,6 +565,20 @@ public abstract class BuildVisitor implements ExpressionVisitor
appendIdentifier(dropIndexClause.getName());
}
protected void bindValue (Object object)
{
_bindables.add(newBindable(object));
_builder.append("?");
}
protected void bindField (Class<? extends PersistentRecord> pClass, String field, Object pojo)
{
final DepotMarshaller<?> marshaller = _types.getMarshaller(pClass);
_bindables.add(newBindable(marshaller, field, pojo));
_builder.append("?");
}
protected abstract void appendIdentifier (String field);
protected void appendTableName (Class<? extends PersistentRecord> type)
@@ -677,6 +711,9 @@ public abstract class BuildVisitor implements ExpressionVisitor
protected DepotTypes _types;
/** For each SQL parameter ? we add an {@link Comparable} to bind to this list. */
protected List<Bindable> _bindables = Lists.newLinkedList();
/** A StringBuilder to hold the constructed SQL. */
protected StringBuilder _builder = new StringBuilder();
@@ -693,4 +730,40 @@ public abstract class BuildVisitor implements ExpressionVisitor
protected boolean _enableOverrides = false;
protected boolean _enableAliasing = false;
protected static interface Bindable
{
void doBind (PreparedStatement stmt, int argIx) throws Exception;
}
protected static Bindable newBindable (
final DepotMarshaller<?> marshaller, final String field, final Object pojo)
{
return new Bindable() {
public void doBind (PreparedStatement stmt, int argIx) throws Exception {
marshaller.getFieldMarshaller(field).getAndWriteToStatement(stmt, argIx, pojo);
}
};
}
protected static Bindable newBindable (final Object value)
{
return new Bindable() {
public void doBind (PreparedStatement stmt, int argIx) throws Exception {
// TODO: how can we abstract this fieldless marshalling
if (value instanceof ByteEnum) {
// byte enums require special conversion
stmt.setByte(argIx++, ((ByteEnum)value).toByte());
} else if (value instanceof int[]) {
// int arrays require conversion to byte arrays
int[] data = (int[])value;
ByteBuffer bbuf = ByteBuffer.allocate(data.length * 4);
bbuf.asIntBuffer().put(data);
stmt.setObject(argIx++, bbuf.array());
} else {
stmt.setObject(argIx++, value);
}
}
};
}
}
@@ -26,7 +26,6 @@ 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.Time;
import java.sql.Timestamp;
@@ -123,7 +122,7 @@ public class HSQLBuilder
if (!virgin) {
_builder.append(", ");
}
_builder.append("?");
bit.accept(this);
virgin = false;
}
_builder.append(")");
@@ -159,18 +158,6 @@ public class HSQLBuilder
}
}
public class HBindVisitor extends BindVisitor
{
protected HBindVisitor (DepotTypes types, Connection conn, PreparedStatement stmt) {
super(types, conn, stmt);
}
@Override public void visit (FullText.Match match) {
_ftsCondition.accept(this);
_ftsCondition = null;
}
}
public HSQLBuilder (DepotTypes types)
{
super(types);
@@ -212,12 +199,6 @@ public class HSQLBuilder
return new HBuildVisitor(_types);
}
@Override
protected BindVisitor getBindVisitor (Connection conn, PreparedStatement stmt)
{
return new HBindVisitor(_types, conn, stmt);
}
@Override
protected void maybeMutateForGeneratedValue (GeneratedValue genValue, ColumnDefinition column)
{
@@ -24,7 +24,6 @@ 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;
@@ -34,7 +33,6 @@ import java.util.Set;
import com.samskivert.jdbc.JDBCUtil;
import com.samskivert.util.Tuple;
import com.samskivert.depot.DatabaseException;
import com.samskivert.depot.PersistentRecord;
import com.samskivert.depot.annotation.FullTextIndex;
import com.samskivert.depot.clause.OrderBy.Order;
@@ -154,33 +152,9 @@ public class MySQLBuilder
}
new ColumnExp(pClass, fields[ii]).accept(this);
}
_builder.append(") against (? in boolean mode)");
}
}
public class MSBindVisitor extends BindVisitor
{
protected MSBindVisitor (DepotTypes types, Connection conn, PreparedStatement stmt) {
super(types, conn, stmt);
}
@Override public void visit (FullText.Match match) {
bindMatch(match.getDefinition());
}
@Override public void visit (FullText.Rank rank) {
bindMatch(rank.getDefinition());
}
protected void bindMatch (FullText fullText)
{
try {
_stmt.setString(_argIdx++, fullText.getQuery());
} catch (SQLException sqe) {
throw new DatabaseException(
"Failed to configure full-text match column [idx=" + (_argIdx-1) +
", match=" + fullText + "]", sqe);
}
_builder.append(") against (");
bindValue(fullText.getQuery());
_builder.append(" in boolean mode)");
}
}
@@ -247,12 +221,6 @@ public class MySQLBuilder
return new MSBuildVisitor(_types);
}
@Override
protected BindVisitor getBindVisitor (Connection conn, PreparedStatement stmt)
{
return new MSBindVisitor(_types, conn, stmt);
}
@Override
protected <T> String getColumnType (FieldMarshaller<?> fm, int length)
{
@@ -24,7 +24,6 @@ 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;
@@ -37,7 +36,6 @@ import com.samskivert.jdbc.LiaisonRegistry;
import com.samskivert.util.ArrayUtil;
import com.samskivert.util.StringUtil;
import com.samskivert.depot.DatabaseException;
import com.samskivert.depot.PersistentRecord;
import com.samskivert.depot.annotation.FullTextIndex;
import com.samskivert.depot.annotation.FullTextIndex.Configuration;
@@ -59,7 +57,9 @@ public class PostgreSQLBuilder
appendIdentifier("ftsCol_" + match.getDefinition().getName());
_builder.append(" @@ to_tsquery('").
append(translateFTConfig(getFTIndex(match.getDefinition()).configuration())).
append("', ?)");
append("', ");
bindValue(massageFTQuery(match.getDefinition()));
_builder.append(")");
}
@Override public void visit (FullText.Rank rank) {
@@ -70,7 +70,9 @@ public class PostgreSQLBuilder
// TODO: The normalization parameter is really quite important, and should
// TODO: perhaps be configurable, but for the moment we hard-code it to 1:
// TODO: "divides the rank by the 1 + logarithm of the document length"
append("', ?), 1)");
append("', ");
bindValue(massageFTQuery(rank.getDefinition()));
_builder.append("), 1)");
}
@Override public void visit (EpochSeconds epochSeconds) {
@@ -85,58 +87,6 @@ public class PostgreSQLBuilder
// _builder.append(" = any (?)");
// }
protected FullTextIndex getFTIndex (FullText definition)
{
DepotMarshaller<?> marsh = _types.getMarshaller(definition.getPersistentClass());
return marsh.getFullTextIndex(definition.getName());
}
@Override protected void appendIdentifier (String field) {
_builder.append("\"").append(field).append("\"");
}
protected PGBuildVisitor (DepotTypes types) {
super(types);
}
}
public class PGBindVisitor extends BindVisitor
{
@Override public void visit (FullText.Rank rank) {
String query = massageQuery(rank.getDefinition());
try {
_stmt.setString(_argIdx++, query);
} catch (SQLException sqe) {
throw new DatabaseException("Failed to configure full-text match column " +
"[idx=" + (_argIdx-1) + ", query=" + query + "]", sqe);
}
}
@Override public void visit (FullText.Match match) {
String query = massageQuery(match.getDefinition());
try {
_stmt.setString(_argIdx++, query);
} catch (SQLException sqe) {
throw new DatabaseException("Failed to configure full-text match column " +
"[idx=" + (_argIdx-1) + ", query=" + query + "]", sqe);
}
}
protected String massageQuery (FullText match)
{
// The tsearch2 engine takes queries on the form
// (foo&bar)|goop
// so in this first simple implementation, we just take the user query, chop it into
// words by space/punctuation and 'or' those together like so:
// 'ho! who goes there?' -> 'ho|who|goes|there'
String[] searchTerms = match.getQuery().toLowerCase().split("\\W+");
if (searchTerms.length > 0 && searchTerms[0].length() == 0) {
searchTerms = ArrayUtil.splice(searchTerms, 0, 1);
}
return StringUtil.join(searchTerms, "|");
}
// TODO: enable when we can require 1.6 support
// @Override public void visit (In in) {
// Comparable<?>[] values = in.getValues();
// try {
@@ -160,11 +110,23 @@ public class PostgreSQLBuilder
// }
// }
protected PGBindVisitor (DepotTypes types, Connection conn, PreparedStatement stmt) {
super(types, conn, stmt);
protected FullTextIndex getFTIndex (FullText definition)
{
DepotMarshaller<?> marsh = _types.getMarshaller(definition.getPersistentClass());
return marsh.getFullTextIndex(definition.getName());
}
@Override protected void appendIdentifier (String field) {
_builder.append("\"").append(field).append("\"");
}
// TODO: enable when we can require 1.6 support
protected PGBuildVisitor (DepotTypes types) {
super(types);
}
}
public PostgreSQLBuilder (DepotTypes types)
{
super(types);
@@ -271,12 +233,6 @@ public class PostgreSQLBuilder
return new PGBuildVisitor(_types);
}
@Override
protected BindVisitor getBindVisitor (Connection conn, PreparedStatement stmt)
{
return new PGBindVisitor(_types, conn, stmt);
}
@Override
protected <T> String getColumnType (FieldMarshaller<?> fm, int length)
{
@@ -338,6 +294,21 @@ public class PostgreSQLBuilder
}
}
protected static String massageFTQuery (FullText match)
{
// The tsearch2 engine takes queries on the form
// (foo&bar)|goop
// so in this first simple implementation, we just take the user query, chop it into
// words by space/punctuation and 'or' those together like so:
// 'ho! who goes there?' -> 'ho|who|goes|there'
String[] searchTerms = match.getQuery().toLowerCase().split("\\W+");
if (searchTerms.length > 0 && searchTerms[0].length() == 0) {
searchTerms = ArrayUtil.splice(searchTerms, 0, 1);
}
return StringUtil.join(searchTerms, "|");
}
// Translate the mildly abstracted full-text parser/dictionary configuration support
// in FullText to actual PostgreSQL configuration identifiers.
protected static String translateFTConfig (Configuration configuration)
@@ -39,10 +39,8 @@ import com.samskivert.depot.clause.QueryClause;
import static com.samskivert.depot.Log.log;
/**
* At the heart of Depot's SQL generation, this object constructs two {@link ExpressionVisitor}
* objects and executes them, one after another; the first one constructs SQL as it recurses, the
* other binds arguments in the {@link PreparedStatement}. This class must be subclassed by the
* database dialects we wish to support.
* At the heart of Depot's SQL generation, this object constructs an {@link ExpressionVisitor}
* object and executes it, constructing SQL and parameter bindings as it recurses.
*/
public abstract class SQLBuilder
{
@@ -81,8 +79,16 @@ public abstract class SQLBuilder
}
PreparedStatement stmt = conn.prepareStatement(_buildVisitor.getQuery());
_bindVisitor = getBindVisitor(conn, stmt);
_clause.accept(_bindVisitor);
int argIx = 1;
for (BuildVisitor.Bindable bindable : _buildVisitor.getBindables()) {
try {
bindable.doBind(stmt, argIx);
} catch (Exception e) {
log.warning("Failed to bind statement argument", "argIx", argIx);
}
argIx ++;
}
if (PersistenceContext.DEBUG) {
log.info("SQL: " + stmt.toString());
@@ -213,11 +219,6 @@ public abstract class SQLBuilder
*/
protected abstract BuildVisitor getBuildVisitor ();
/**
* Overridden by subclasses to create a dialect-specific {@link BindVisitor}.
*/
protected abstract BindVisitor getBindVisitor (Connection conn, PreparedStatement stmt);
/**
* Overridden by subclasses to figure the dialect-specific SQL type of the given field.
* @param length
@@ -229,5 +230,4 @@ public abstract class SQLBuilder
protected QueryClause _clause;
protected BuildVisitor _buildVisitor;
protected BindVisitor _bindVisitor;
}