1   package net.sourceforge.backpedal.impl.core;
2   
3   import net.mikehogan.veggie.exceptions.VeggieSystemException;
4   import net.mikehogan.veggie.sql.Sql;
5   import net.sourceforge.backpedal.api.core.BackpedalFactory;
6   import net.sourceforge.backpedal.api.core.BindVariableResolver;
7   import net.sourceforge.backpedal.api.core.ParsedStatement;
8   import net.sourceforge.backpedal.api.db.BindVariable;
9   import net.sourceforge.backpedal.api.db.BindVariableSet;
10  import net.sourceforge.backpedal.api.db.ColumnMetadata;
11  import net.sourceforge.backpedal.api.db.TableMetadata;
12  import net.sourceforge.backpedal.impl.AbstractComponent;
13  import net.sourceforge.backpedal.impl.db.BooleanBindVariable;
14  import net.sourceforge.backpedal.impl.db.CharBindVariable;
15  import net.sourceforge.backpedal.impl.db.DateBindVariable;
16  import net.sourceforge.backpedal.impl.db.IntegerBindVariable;
17  import net.sourceforge.backpedal.impl.db.StringBindVariable;
18  import net.sourceforge.backpedal.impl.db.TimestampBindVariable;
19  
20  import java.sql.Connection;
21  import java.sql.Date;
22  import java.sql.PreparedStatement;
23  import java.sql.ResultSet;
24  import java.sql.SQLException;
25  import java.sql.Timestamp;
26  import java.util.ArrayList;
27  import java.util.Collection;
28  import java.util.HashMap;
29  import java.util.Iterator;
30  import java.util.List;
31  import java.util.Map;
32  
33  public class BindVariableResolverImpl extends AbstractComponent implements BindVariableResolver {
34      private static final Map bindVariablePrototypeInstances = new HashMap();
35      private final BackpedalFactory backpedalFactory;
36  
37      public BindVariableResolverImpl(BackpedalFactory backpedalFactory) {
38          this.backpedalFactory = backpedalFactory;
39      }
40  
41      public Collection getBindVariableSetsForMutatedRows(ParsedStatement parsedStatement, BindVariableSet bindVariableSet, TableMetadata tableMetadata, Connection connection) {
42          final Collection result = new ArrayList();
43          if ("INSERT".equalsIgnoreCase(parsedStatement.getSqlKeyword())) {
44              result.add(bindVariableSet);
45          } else {
46              handleUpdateOrDelete(parsedStatement, connection, bindVariableSet, result, tableMetadata);
47          }
48          return result;
49      }
50  
51      private void handleUpdateOrDelete(ParsedStatement parsedStatement, Connection connection, BindVariableSet bindVariableSet, final Collection result, TableMetadata tableMetadata) {
52          PreparedStatement statement = null;
53          ResultSet resultSet = null;
54          try {
55              final String affectedRowsSql = getSelectForAffectedRows(parsedStatement);
56              statement = connection.prepareStatement(affectedRowsSql);
57              /***
58               * The WHERE clause in affectedRowsSql should be the same as the WHERE clause
59               * in parsedStatement.  But, bindVariableSet can contain bind variables that
60               * are used outside the WHERE clause (consider "UPDATE X SET Y=? WHERE Z=?").
61               * So, we'll figure out how many bind variables to skip in bindVariableSet to
62               * get us to the bind variables for the WHERE clause.  Its
63               * bindVariableSet.size() - parsedStatement.getWhereColumnNames.length
64               */
65              final List bindVariables = new ArrayList();
66              for (Iterator i = bindVariableSet.iterator(); i.hasNext();) {
67                  bindVariables.add(i.next());
68              }
69              final String[] whereColumnNames = parsedStatement.getWhereColumnNames();
70              int indexForWhereBindVariables = bindVariableSet.size() - whereColumnNames.length;
71              for (int i = 0; i < whereColumnNames.length; i++) {
72                  final BindVariable b = (BindVariable) bindVariables.get(i + indexForWhereBindVariables);
73                  log().debug("Setting bindvariable " + b + " into statement " + statement);
74                  b.setInto(statement);
75  
76              }
77              resultSet = statement.executeQuery();
78  
79              while (resultSet.next()) {
80                  result.add(createBindVariableSet(resultSet, tableMetadata, parsedStatement));
81              }
82          } catch (SQLException e) {
83              throw new VeggieSystemException("Exception trying to find backpedal bind variables", e);
84          } finally {
85              Sql.close(resultSet);
86              Sql.close(statement);
87          }
88      }
89  
90      /***
91       * Return a bind variable set for all the columns in a table, assuming
92       * resultSet contains a row from that table.
93       */
94      private BindVariableSet createBindVariableSet(final ResultSet resultSet, final TableMetadata tableMetadata, final ParsedStatement parsedStatement) {
95          final BindVariableSet bindVariableSet = backpedalFactory.createBindVariableSet();
96          int index = 1;
97          if (parsedStatement.getChangedColumnNames().length != 0) {
98              //We've got an INSERT or UPDATE with a specific set of changed cols followed by an optional WHERE clause with cols.
99              //So, first do the changed cols
100             index = createBindVariablesForColumns(parsedStatement.getChangedColumnNames(), bindVariableSet, index, tableMetadata, resultSet);
101             //And now do the where columns
102             createBindVariablesForColumns(parsedStatement.getWhereColumnNames(), bindVariableSet, index, tableMetadata, resultSet);
103         } else {
104             //This is a DELETE, so we need to store all cols in table for the affected row.
105             for (Iterator i = tableMetadata.getColumns(); i.hasNext();) {
106                 final ColumnMetadata columnMetadata = (ColumnMetadata) i.next();
107                 bindVariableSet.add(createBindVariable(columnMetadata, index, resultSet));
108                 index++;
109             }
110         }
111         return bindVariableSet;
112     }
113 
114     private int createBindVariablesForColumns(String[] columnNames, final BindVariableSet bindVariableSet, int index, final TableMetadata tableMetadata, final ResultSet resultSet) {
115         for (int i = 0; i < columnNames.length; i++) {
116             final ColumnMetadata columnMetadata = tableMetadata.getColumn(columnNames[i]);
117             bindVariableSet.add(createBindVariable(columnMetadata, index, resultSet));
118             index++;
119         }
120         return index;
121     }
122 
123     private BindVariable createBindVariable(final ColumnMetadata columnMetadata, int index, final ResultSet resultSet) {
124         BindVariable bindVariable;
125         log().debug("index = " + index + ", column name = " + columnMetadata.getName());
126         try {
127             final BindVariable prototype = (BindVariable) bindVariablePrototypeInstances.get(new Integer(columnMetadata.getSqlType()));
128             if (prototype != null) {
129                 bindVariable = (BindVariable) prototype.clone();
130                 bindVariable.setIndex(index);
131                 bindVariable.setValue(resultSet.getObject(index));
132                 bindVariable.setColumnName(columnMetadata.getName());
133             } else {
134                 throw new VeggieSystemException("No prototype instance registered for SQL type " + columnMetadata.getSqlType());
135             }
136         } catch (VeggieSystemException e) {
137             throw e;
138         } catch (Exception e) {
139             throw new VeggieSystemException("Exception trying to create a bind variable", e);
140         }
141         return bindVariable;
142     }
143 
144     private String getSelectForAffectedRows(ParsedStatement parsedStatement) {
145         final StringBuffer buffer = new StringBuffer("SELECT * FROM ");
146         buffer.append(parsedStatement.getTableName());
147         buffer.append(" WHERE ");
148         final String[] columnNames = parsedStatement.getWhereColumnNames();
149         appendWhereClause(columnNames, buffer);
150         return buffer.toString();
151     }
152 
153     /***
154      * Append the body of a WHERE clause to buffer using columnNames.
155      */
156     private void appendWhereClause(final String[] columnNames, final StringBuffer buffer) {
157         for (int i = 0; i < columnNames.length; i++) {
158             if (i > 0) {
159                 buffer.append(" AND ");
160             }
161             buffer.append(columnNames[i]);
162             buffer.append(" = ?");
163         }
164     }
165 
166 
167     static {
168         addPrototypeInstance(new StringBindVariable(1, "who cares","who cares"));
169         addPrototypeInstance(new IntegerBindVariable(1, 1,"who cares"));
170         addPrototypeInstance(new BooleanBindVariable(1, true,"who cares"));
171         addPrototypeInstance(new DateBindVariable(1, new Date(1),"who cares"));
172         addPrototypeInstance(new TimestampBindVariable(1, new Timestamp(1),"who cares"));
173         addPrototypeInstance(new CharBindVariable(1, "who cares","who cares"));
174     }
175 
176     private static void addPrototypeInstance(BindVariable bindVariable) {
177         bindVariablePrototypeInstances.put(new Integer(bindVariable.getSqlType()), bindVariable);
178     }
179 
180 }
This page was automatically generated by Maven