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