]> review.fuel-infra Code Review - packages/trusty/mysql-wsrep-5.6.git/blob
1a2f03288d0b5bfebf9b36fbb253682aaad75866
[packages/trusty/mysql-wsrep-5.6.git] /
1 /*
2  *  Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; version 2 of the License.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License
14  *  along with this program; if not, write to the Free Software
15  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
16  */
17
18 package com.mysql.clusterj.jdbc;
19
20 import com.mysql.clusterj.ClusterJFatalInternalException;
21 import com.mysql.clusterj.ClusterJUserException;
22 import com.mysql.clusterj.core.query.QueryDomainTypeImpl;
23 import com.mysql.clusterj.core.query.QueryExecutionContextImpl;
24 import com.mysql.clusterj.core.spi.DomainTypeHandler;
25 import com.mysql.clusterj.core.spi.QueryExecutionContext;
26 import com.mysql.clusterj.core.spi.SessionSPI;
27 import com.mysql.clusterj.core.spi.ValueHandler;
28 import com.mysql.clusterj.core.store.ResultData;
29 import com.mysql.clusterj.core.util.I18NHelper;
30 import com.mysql.clusterj.core.util.Logger;
31 import com.mysql.clusterj.core.util.LoggerFactoryService;
32 import com.mysql.jdbc.ParameterBindings;
33 import com.mysql.jdbc.ResultSetInternalMethods;
34
35 import java.sql.SQLException;
36 import java.util.Arrays;
37 import java.util.HashMap;
38 import java.util.List;
39 import java.util.Map;
40
41 /** This class contains behavior to execute various SQL commands. There is one subclass for each
42  * command to be executed. 
43  */
44 public class SQLExecutor {
45
46     /** My message translator */
47     static final I18NHelper local = I18NHelper.getInstance(SQLExecutor.class);
48
49     /** My logger */
50     static final Logger logger = LoggerFactoryService.getFactory().getInstance(SQLExecutor.class);
51
52     /** The domain type handler for this SQL statement */
53     DomainTypeHandler<?> domainTypeHandler = null;
54
55     /** The column names in the SQL statement */
56     protected List<String> columnNames = null;
57
58     /** The number of fields in the domain object (also the number of mapped columns) */
59     protected int numberOfFields;
60
61     /** The number of parameters in the where clause */
62     protected int numberOfParameters;
63
64     /** The map of field numbers to parameter numbers */
65     protected int[] fieldNumberToColumnNumberMap = null;
66
67     /** The map of column numbers to field numbers */
68     protected int[] columnNumberToFieldNumberMap = null;
69
70     /** The map of column names to parameter numbers */
71     protected Map<String, Integer> columnNameToFieldNumberMap = new HashMap<String, Integer>();
72
73     /** The query domain type for qualified SELECT and DELETE operations */
74     protected QueryDomainTypeImpl<?> queryDomainType;
75
76     public SQLExecutor(DomainTypeHandlerImpl<?> domainTypeHandler, List<String> columnNames, int numberOfParameters) {
77         this(domainTypeHandler, columnNames);
78         this.numberOfParameters = numberOfParameters;
79     }
80
81     public SQLExecutor(DomainTypeHandlerImpl<?> domainTypeHandler, List<String> columnNames) {
82         this.domainTypeHandler = domainTypeHandler;
83         this.columnNames  = columnNames;
84         initializeFieldNumberMap();
85     }
86
87     public SQLExecutor(DomainTypeHandlerImpl<?> domainTypeHandler) {
88         this.domainTypeHandler = domainTypeHandler;
89     }
90
91     public SQLExecutor(DomainTypeHandlerImpl<?> domainTypeHandler, List<String> columnNames,
92             QueryDomainTypeImpl<?> queryDomainType) {
93         this(domainTypeHandler, columnNames);
94         this.queryDomainType = queryDomainType;
95         initializeFieldNumberMap();
96     }
97     
98     public SQLExecutor(DomainTypeHandlerImpl<?> domainTypeHandler, QueryDomainTypeImpl<?> queryDomainType,
99             int numberOfParameters) {
100         this.domainTypeHandler = domainTypeHandler;
101         this.queryDomainType = queryDomainType;
102         this.numberOfParameters = numberOfParameters;
103     }
104
105     /** This is the public interface exposed to other parts of the component. Calling this
106      * method executes the SQL statement via the clusterj api, or returns null indicating that
107      * the JDBC driver should execute the SQL statement.
108      */
109     public interface Executor {
110
111         /** Execute the SQL command
112          * @param session the clusterj session which must not be null
113          * @param parameterBindings the parameter bindings from the prepared statement
114          * @return the result of executing the statement, or null
115          * @throws SQLException
116          */
117         ResultSetInternalMethods execute(InterceptorImpl interceptor,
118                 ParameterBindings parameterBindings) throws SQLException;
119     }
120
121     /** This class implements the Executor contract but returns null, indicating that
122      * the JDBC driver should implement the call itself.
123      */
124     public static class Noop implements Executor {
125
126         public ResultSetInternalMethods execute(InterceptorImpl interceptor,
127                 ParameterBindings parameterBindings) throws SQLException {
128             return null;
129         }
130     }
131
132     /** This class implements the Executor contract for Select operations.
133      */
134     public static class Select extends SQLExecutor implements Executor {
135
136         public Select(DomainTypeHandlerImpl<?> domainTypeHandler, List<String> columnNames, QueryDomainTypeImpl<?> queryDomainType) {
137             super(domainTypeHandler, columnNames, queryDomainType);
138             if (queryDomainType == null) {
139                 throw new ClusterJFatalInternalException("queryDomainType must not be null for Select.");
140             }
141         }
142
143         public ResultSetInternalMethods execute(InterceptorImpl interceptor,
144                 ParameterBindings parameterBindings) throws SQLException {
145             // create value handler to copy data from parameters to ndb
146             // count the parameters
147             int count = countParameters(parameterBindings);
148             SessionSPI session = interceptor.getSession();
149             Map<String, Object> parameters = createParameterMap(queryDomainType, parameterBindings, 0, count);
150             QueryExecutionContext context = new QueryExecutionContextImpl(session, parameters);
151             session.startAutoTransaction();
152             try {
153                 ResultData resultData = queryDomainType.getResultData(context);
154                 // session.endAutoTransaction();
155                 return new ResultSetInternalMethodsImpl(resultData, columnNumberToFieldNumberMap, 
156                         columnNameToFieldNumberMap, session);
157             } catch (Exception e) {
158                 e.printStackTrace();
159                 session.failAutoTransaction();
160                 return null;
161             }
162         }
163     }
164
165     /** This class implements the Executor contract for Delete operations.
166      */
167     public static class Delete extends SQLExecutor implements Executor {
168
169         public Delete (DomainTypeHandlerImpl<?> domainTypeHandler, QueryDomainTypeImpl<?> queryDomainType,
170                 int numberOfParameters) {
171             super(domainTypeHandler, queryDomainType, numberOfParameters);
172         }
173
174         public Delete (DomainTypeHandlerImpl<?> domainTypeHandler) {
175             super(domainTypeHandler);
176         }
177
178         public ResultSetInternalMethods execute(InterceptorImpl interceptor,
179                 ParameterBindings parameterBindings) throws SQLException {
180             SessionSPI session = interceptor.getSession();
181             if (queryDomainType == null) {
182                 int rowsDeleted = session.deletePersistentAll(domainTypeHandler);
183                 if (logger.isDebugEnabled()) logger.debug("deleteAll deleted: " + rowsDeleted);
184                 return new ResultSetInternalMethodsUpdateCount(rowsDeleted);
185             } else {
186                 int numberOfBoundParameters = countParameters(parameterBindings);
187                 int numberOfStatements = numberOfBoundParameters / numberOfParameters;
188                 long[] deleteResults = new long[numberOfStatements];
189                 if (logger.isDebugEnabled()) logger.debug(
190                         " numberOfParameters: " + numberOfParameters
191                         + " numberOfBoundParameters: " + numberOfBoundParameters
192                         + " numberOfStatements: " + numberOfStatements
193                         );
194                 QueryExecutionContextJDBCImpl context = 
195                     new QueryExecutionContextJDBCImpl(session, parameterBindings, numberOfParameters);
196                 for (int i = 0; i < numberOfStatements; ++i) {
197                     // this will execute each statement in the batch using different parameters
198                     int statementRowsDeleted = queryDomainType.deletePersistentAll(context);
199                     if (logger.isDebugEnabled()) logger.debug("statement " + i
200                             + " deleted " + statementRowsDeleted);
201                     deleteResults[i] = statementRowsDeleted;
202                     context.nextStatement();
203                 }
204                 return new ResultSetInternalMethodsUpdateCount(deleteResults);
205             }
206         }
207     }
208
209     /** This class implements the Executor contract for Insert operations.
210      */
211     public static class Insert extends SQLExecutor implements Executor {
212
213         public Insert(DomainTypeHandlerImpl<?> domainTypeHandler, List<String> columnNames) {
214             super(domainTypeHandler, columnNames, columnNames.size());
215         }
216
217         public ResultSetInternalMethods execute(InterceptorImpl interceptor,
218                 ParameterBindings parameterBindings) throws SQLException {
219             SessionSPI session = interceptor.getSession();
220             int numberOfBoundParameters = countParameters(parameterBindings);
221             int numberOfStatements = numberOfBoundParameters / numberOfParameters;
222             if (logger.isDebugEnabled()) logger.debug("SQLExecutor.Insert.execute"
223                     + " numberOfParameters: " + numberOfParameters
224                     + " numberOfBoundParameters: " + numberOfBoundParameters
225                     + " numberOfFields: " + numberOfFields
226                     + " numberOfStatements: " + numberOfStatements
227                     );
228             // interceptor.beforeClusterjStart();
229             // session asks for values by field number which are converted to parameter number
230             for (int offset = 0; offset < numberOfBoundParameters; offset += numberOfParameters) {
231                 ValueHandler valueHandler = getValueHandler(parameterBindings, fieldNumberToColumnNumberMap, offset);
232                 session.insert(domainTypeHandler, valueHandler);
233             }
234             session.flush();
235             // interceptor.afterClusterjStart();
236             return new ResultSetInternalMethodsUpdateCount(numberOfStatements);
237         }
238     }
239
240     /** Create the parameter map assigning each bound parameter a number.
241      * The result is a map in which the key is a String whose key is a cardinal number
242      * starting with 1 (for JDBC which uses 1-origin for numbering)
243      * and whose value is the parameter's value.
244      * @param queryDomainType the query domain type
245      * @param parameterBindings the parameter bindings
246      * @param offset the number of parameters to skip
247      * @param count the number of parameters to use
248      * @return
249      * @throws SQLException
250      */
251     protected Map<String, Object> createParameterMap(QueryDomainTypeImpl<?> queryDomainType,
252             ParameterBindings parameterBindings, int offset, int count) throws SQLException {
253         Map<String, Object> result = new HashMap<String, Object>();
254         int first = offset + 1;
255         int last = offset + count + 1;
256         for (int i = first; i < last; ++i) {
257             Object placeholder = parameterBindings.getObject(i);
258             if (logger.isDetailEnabled())
259                 logger.detail("Put placeholder " + i + " value: " + placeholder + " of type " + placeholder.getClass());
260             result.put(String.valueOf(i), placeholder);
261         }
262         return result;
263     }
264
265     /** Initialize the mappings between the Java representation of the row (domain type handler)
266      * and the JDBC/database representation of the row. The JDBC driver asks for columns by column
267      * index or column name, 1-origin. The domain type handler returns data by field number, 0-origin.
268      * The domain type handler has representations for all columns in the database, whereas the JDBC
269      * driver has a specific set of columns referenced by the SQL statement.
270      * For insert, the column number to field number mapping will map parameters to field numbers,
271      * e.g. INSERT INTO EMPLOYEE (id, name, age) VALUES (?, ?, ?)
272      * For select, the column number to field number mapping will map result set columns to field numbers,
273      * e.g. SELECT id, name, age FROM EMPLOYEE
274      */
275     private void initializeFieldNumberMap() {
276         // the index into the int[] is the 0-origin field number (columns in order of definition in the schema)
277         // the value is the index into the parameter bindings (columns in order of the sql insert statement)
278         String[] fieldNames = domainTypeHandler.getFieldNames();
279         numberOfFields = fieldNames.length;
280         fieldNumberToColumnNumberMap = new int[numberOfFields];
281         columnNumberToFieldNumberMap = new int[1 + columnNames.size()];
282         for (int i= 0; i < numberOfFields; ++i) {
283             columnNameToFieldNumberMap.put(fieldNames[i], i);
284             int index = columnNames.indexOf(fieldNames[i]);
285             if (index >= 0) {
286                 // index origin 1 for JDBC interfaces
287                 fieldNumberToColumnNumberMap[i] = index + 1;
288                 columnNumberToFieldNumberMap[index + 1] = i;
289             } else {
290                 // field is not in column list
291                 fieldNumberToColumnNumberMap[i] = -1;
292             }
293         }
294         // make sure all columns are fields and if not, throw an exception
295         for (String columnName: columnNames) {
296             if (columnNameToFieldNumberMap.get(columnName) == null) {
297                 throw new ClusterJUserException(
298                         local.message("ERR_Column_Name_Not_In_Table", columnName,
299                         Arrays.toString(fieldNames),
300                         domainTypeHandler.getTableName()));
301             }
302         }
303         if (logger.isDetailEnabled()) {
304             StringBuilder buffer = new StringBuilder();
305             for (int i = 0; i < fieldNumberToColumnNumberMap.length; ++i) {
306                 int columnNumber = fieldNumberToColumnNumberMap[i];
307                 buffer.append("field ");
308                 buffer.append(i);
309                 buffer.append(" mapped to ");
310                 buffer.append(columnNumber);
311                 buffer.append("[");
312                 buffer.append(columnNumber == -1?"nothing":(columnNames.get(columnNumber - 1)));
313                 buffer.append("];");
314             }
315             logger.detail(buffer.toString());
316         }
317     }
318
319     /** Create a value handler (part of the clusterj spi) to retrieve values from jdbc parameter bindings.
320      * @param parameterBindings the jdbc parameter bindings from prepared statements
321      * @param fieldNumberToParameterNumberMap map from field number to parameter number
322      * @param offset into the parameter bindings for this instance (used for batch execution)
323      * @return
324      */
325     protected ValueHandler getValueHandler(ParameterBindings parameterBindings,
326             int[] fieldNumberToParameterNumberMap, int offset) {
327         return new ValueHandlerImpl(parameterBindings, fieldNumberToParameterNumberMap, offset);
328     }
329
330     /** If detailed logging is enabled write the parameter bindings to the log.
331      * @param parameterBindings the jdbc parameter bindings
332      */
333     protected static void logParameterBindings(ParameterBindings parameterBindings) {
334         if (logger.isDetailEnabled()) {
335             int i = 0;
336             while (true) {
337                 try {
338                     String value = parameterBindings.getObject(++i).toString();
339                     // parameters are 1-origin per jdbc specification
340                     logger.detail("parameterBinding: parameter " + i + " has value: " + value);
341                 } catch (Exception e) {
342                     // we don't know how many parameters are bound...
343                     break;
344                 }
345             }
346         }
347     }
348
349     /** Count the number of bound parameters. If this is a batch execution, then the
350      * number of bound parameters is the number of statements in the batch times the
351      * number of parameters per statement.
352      * If detailed logging is enabled write the parameter bindings to the log.
353      * @param parameterBindings the jdbc parameter bindings
354      */
355     protected static int countParameters(ParameterBindings parameterBindings) {
356         int i = 0;
357         while (true) {
358             try {
359                 ++i;
360                 // parameters are 1-origin per jdbc specification
361                 Object objectValue = parameterBindings.getObject(i);
362                 if (logger.isDetailEnabled()) {
363                     logger.detail("parameterBinding: parameter " + i
364                             + " has value: " + objectValue
365                             + " of type " + objectValue.getClass());
366                 }
367             } catch (Exception e) {
368                 // we don't know how many parameters are bound...
369                 break;
370             }
371         }
372         return i - 1;
373     }
374
375 }