18 package com.mysql.clusterj.jdbc;
20 import com.mysql.clusterj.ClusterJHelper;
21 import com.mysql.clusterj.ClusterJUserException;
22 import com.mysql.clusterj.SessionFactory;
23 import com.mysql.clusterj.core.query.QueryDomainTypeImpl;
24 import com.mysql.clusterj.core.spi.SessionSPI;
25 import com.mysql.clusterj.core.store.Dictionary;
26 import com.mysql.clusterj.core.util.I18NHelper;
27 import com.mysql.clusterj.core.util.Logger;
28 import com.mysql.clusterj.core.util.LoggerFactoryService;
29 import com.mysql.jdbc.Connection;
30 import com.mysql.jdbc.ResultSetInternalMethods;
31 import com.mysql.jdbc.Statement;
32 import com.mysql.clusterj.jdbc.antlr.ANTLRNoCaseStringStream;
33 import com.mysql.clusterj.jdbc.antlr.MySQL51Parser;
34 import com.mysql.clusterj.jdbc.antlr.MySQL51Lexer;
35 import com.mysql.clusterj.jdbc.antlr.QueuingErrorListener;
36 import com.mysql.clusterj.jdbc.antlr.node.Node;
37 import com.mysql.clusterj.jdbc.antlr.node.PlaceholderNode;
38 import com.mysql.clusterj.jdbc.antlr.node.SelectNode;
39 import com.mysql.clusterj.jdbc.antlr.node.WhereNode;
40 import com.mysql.clusterj.query.Predicate;
42 import com.mysql.clusterj.jdbc.SQLExecutor.Executor;
43 import java.sql.SQLException;
44 import java.sql.Savepoint;
45 import java.util.ArrayList;
46 import java.util.IdentityHashMap;
47 import java.util.List;
49 import java.util.Properties;
51 import org.antlr.runtime.CommonTokenStream;
52 import org.antlr.runtime.RecognitionException;
53 import org.antlr.runtime.Token;
54 import org.antlr.runtime.TokenStream;
55 import org.antlr.runtime.tree.CommonErrorNode;
56 import org.antlr.runtime.tree.CommonTree;
57 import org.antlr.runtime.tree.CommonTreeAdaptor;
58 import org.antlr.runtime.tree.TreeAdaptor;
82 static Map<String, Executor> parsedSqlMap =
new IdentityHashMap<String, Executor>();
85 private static Map<Connection, InterceptorImpl> interceptorImplMap =
86 new IdentityHashMap<Connection, InterceptorImpl>();
89 private Properties properties;
92 private Connection connection;
107 private boolean ready =
false;
109 private boolean autocommit;
111 private static String LOTSOBLANKS =
" ";
119 if (logger.isDebugEnabled()) logger.debug(
"constructed with properties: " + properties);
120 this.properties = properties;
121 this.connection = connection;
123 String dbname = properties.getProperty(
"com.mysql.clusterj.database",
124 properties.getProperty(
"DBNAME"));
125 properties.put(
"com.mysql.clusterj.database", dbname);
137 Connection connection, Properties properties) {
139 if (result.connectionLifecycleInterceptor != null) {
140 if (result.connectionLifecycleInterceptor != connectionLifecycleInterceptor) {
142 local.
message(
"ERR_Duplicate_Connection_Lifecycle_Interceptor"));
145 result.connectionLifecycleInterceptor = connectionLifecycleInterceptor;
147 if (result.statementInterceptor != null) {
162 Properties properties) {
164 if (result.statementInterceptor != null) {
166 local.
message(
"ERR_Duplicate_Statement_Interceptor"));
168 result.statementInterceptor = statementInterceptor;
169 if (result.connectionLifecycleInterceptor != null) {
183 synchronized(interceptorImplMap) {
184 result = interceptorImplMap.get(connection);
185 if (result == null) {
187 interceptorImplMap.put(connection, result);
199 synchronized (interceptorImplMap) {
200 return interceptorImplMap.get(connection);
205 public String toString() {
206 return "InterceptorImpl "
212 if (sessionFactory != null) {
213 if (session != null) {
216 sessionFactory.
close();
217 sessionFactory = null;
218 synchronized(interceptorImplMap) {
219 interceptorImplMap.remove(connection);
224 public SessionSPI getSession() {
225 if (session == null) {
226 session = (SessionSPI)sessionFactory.
getSession();
231 public boolean executeTopLevelOnly() {
233 boolean result =
true;
237 public ResultSetInternalMethods postProcess(
String sql, Statement
statement,
238 ResultSetInternalMethods result,
Connection connection,
int arg4,
239 boolean arg5,
boolean arg6, SQLException sqlException)
throws SQLException {
244 public ResultSetInternalMethods preProcess(
String sql, Statement
statement,
247 if (
statement instanceof com.mysql.jdbc.PreparedStatement) {
248 com.mysql.jdbc.PreparedStatement preparedStatement =
249 (com.mysql.jdbc.PreparedStatement)
statement;
251 String preparedSql = preparedStatement.getPreparedSql().intern();
253 Executor sQLExecutor = null;
254 synchronized(parsedSqlMap) {
255 sQLExecutor = parsedSqlMap.get(preparedSql);
258 if (sQLExecutor == null) {
259 sQLExecutor = createSQLExecutor(preparedSql);
260 if (sQLExecutor != null) {
262 synchronized(parsedSqlMap) {
263 parsedSqlMap.put(preparedSql, sQLExecutor);
267 return sQLExecutor.execute(
this, preparedStatement.getParameterBindings());
275 private Executor createSQLExecutor(
String preparedSql) {
276 if (logger.isDetailEnabled()) logger.detail(preparedSql);
277 Executor result = null;
279 CommonTree root =
parse(preparedSql);
281 int tokenType = root.getType();
284 CommonTree tableNode;
287 Dictionary dictionary;
288 DomainTypeHandlerImpl<?> domainTypeHandler;
289 QueryDomainTypeImpl<?> queryDomainType = null;
291 case MySQL51Parser.INSERT:
292 tableNode = (CommonTree)root.getFirstChildWithType(MySQL51Parser.TABLE);
293 tableName = getTableName(tableNode);
295 dictionary = session.getDictionary();
296 domainTypeHandler = getDomainTypeHandler(tableName, dictionary);
297 CommonTree insertValuesNode = (CommonTree)root.getFirstChildWithType(MySQL51Parser.INSERT_VALUES);
298 CommonTree columnsNode = (CommonTree)insertValuesNode.getFirstChildWithType(MySQL51Parser.COLUMNS);
300 for (CommonTree field: fields) {
301 columnNames.add(getColumnName(field));
303 if (logger.isDetailEnabled()) logger.detail(
304 "StatementInterceptorImpl.preProcess parse result INSERT INTO " + tableName
305 +
" COLUMNS " + columnNames);
306 result =
new SQLExecutor.Insert(domainTypeHandler, columnNames);
308 case MySQL51Parser.SELECT:
309 CommonTree fromNode = (CommonTree)root.getFirstChildWithType(MySQL51Parser.FROM);
310 if (fromNode == null) {
312 result =
new SQLExecutor.Noop();
317 tableNode = (CommonTree) fromNode.getFirstChildWithType(MySQL51Parser.TABLE);
318 tableName = getTableName(tableNode);
319 }
catch (Exception e) {
321 logger.info(
"Problem with FROM clause in SQL statement: " + preparedSql);
322 logger.info(walk(root));
323 result =
new SQLExecutor.Noop();
327 dictionary = session.getDictionary();
328 domainTypeHandler = getDomainTypeHandler(tableName, dictionary);
329 columnsNode = (CommonTree)root.getFirstChildWithType(MySQL51Parser.COLUMNS);
331 for (CommonTree selectExprNode: selectExprNodes) {
332 columnNames.add(getColumnName(getFieldNode(selectExprNode)));
334 String whereType =
"empty";
335 if (logger.isDetailEnabled()) logger.detail(
336 "SELECT FROM " + tableName
337 +
" COLUMNS " + columnNames);
342 whereNode = ((SelectNode)root).getWhereNode();
343 queryDomainType = (QueryDomainTypeImpl<?>) session.createQueryDomainType(domainTypeHandler);
344 if (whereNode == null) {
346 result =
new SQLExecutor.Select(domainTypeHandler, columnNames, queryDomainType);
349 Predicate predicate = whereNode.getPredicate(queryDomainType);
350 if (predicate != null) {
352 queryDomainType.where(predicate);
353 result =
new SQLExecutor.Select(domainTypeHandler, columnNames, queryDomainType);
354 whereType =
"clusterj";
357 result =
new SQLExecutor.Noop();
358 whereType =
"non-clusterj";
360 if (logger.isDetailEnabled()) logger.detail(walk(root));
362 if (logger.isDetailEnabled()) {
364 "SELECT FROM " + tableName
365 +
" COLUMNS " + columnNames +
" whereType " + whereType);
366 logger.detail(walk(root));
369 case MySQL51Parser.DELETE:
370 tableNode = (CommonTree)root.getFirstChildWithType(MySQL51Parser.TABLE);
371 tableName = getTableName(tableNode);
373 dictionary = session.getDictionary();
374 domainTypeHandler = getDomainTypeHandler(tableName, dictionary);
375 whereNode = ((WhereNode)root.getFirstChildWithType(MySQL51Parser.WHERE));
376 int numberOfParameters = 0;
377 if (whereNode == null) {
379 result =
new SQLExecutor.Delete(domainTypeHandler);
383 queryDomainType = (QueryDomainTypeImpl<?>) session.createQueryDomainType(domainTypeHandler);
384 Predicate predicate = whereNode.getPredicate(queryDomainType);
385 if (predicate != null) {
387 queryDomainType.where(predicate);
388 numberOfParameters = whereNode.getNumberOfParameters();
389 result =
new SQLExecutor.Delete(domainTypeHandler, queryDomainType, numberOfParameters);
390 whereType =
"clusterj";
393 result =
new SQLExecutor.Noop();
394 whereType =
"non-clusterj";
396 if (logger.isDetailEnabled()) logger.detail(walk(root));
398 if (logger.isDetailEnabled()) logger.detail(
399 "DELETE FROM " + tableName
400 +
" whereType " + whereType
401 +
" number of parameters " + numberOfParameters);
405 if (logger.isDetailEnabled()) logger.detail(
"ClusterJ cannot process this SQL statement: unsupported statement type.");
406 result =
new SQLExecutor.Noop();
411 private String getPrimaryKeyFieldName(CommonTree whereNode) {
413 CommonTree operation = (CommonTree) whereNode.getChild(0);
414 if (MySQL51Parser.EQUALS == operation.getType()) {
415 result = operation.getChild(0).getChild(0).getText();
417 throw new ClusterJUserException(
"Cannot find primary key in WHERE clause.");
422 private String walk(CommonTree tree) {
423 StringBuilder buffer =
new StringBuilder();
424 walk(tree, buffer, 0);
425 return buffer.toString();
428 @SuppressWarnings(
"unchecked")
429 private
void walk(CommonTree tree, StringBuilder buffer,
int level) {
430 String indent = LOTSOBLANKS.substring(0, level);
431 Token token = tree.token;
432 int tokenType = token.getType();
433 String tokenText = token.getText();
434 int childCount = tree.getChildCount();
435 int childIndex = tree.getChildIndex();
437 buffer.append(indent);
438 buffer.append(tokenText);
439 buffer.append(
" class: ");
440 buffer.append(tree.getClass().getName());
441 buffer.append(
" tokenType ");
442 buffer.append(tokenType);
443 buffer.append(
" child count ");
444 buffer.append(childCount);
445 buffer.append(
" child index ");
446 buffer.append(childIndex);
448 if (children == null) {
451 for (CommonTree child: children) {
452 walk(child, buffer, level + 2);
457 CommonTree result = null;
458 ANTLRNoCaseStringStream inputStream =
new ANTLRNoCaseStringStream(preparedSql);
459 MySQL51Lexer lexer =
new MySQL51Lexer(inputStream);
460 CommonTokenStream tokens =
new CommonTokenStream(lexer);
461 lexer.setErrorListener(
new QueuingErrorListener(lexer));
463 if (lexer.getErrorListener().hasErrors()) {
464 logger.warn(local.
message(
"ERR_Lexing_SQ",preparedSql));
467 PlaceholderNode.resetId();
468 MySQL51Parser parser =
new MySQL51Parser(tokens);
469 parser.setTreeAdaptor(mySQLTreeAdaptor);
470 parser.setErrorListener(
new QueuingErrorListener(parser));
472 CommonTree stmtTree = (CommonTree) parser.statement().getTree();
474 }
catch (RecognitionException e) {
475 logger.warn(local.
message(
"ERR_Parsing_SQL", preparedSql));
477 if (parser.getErrorListener().hasErrors()) {
478 logger.warn(local.
message(
"ERR_Parsing_SQL", preparedSql));
483 private TreeAdaptor mySQLTreeAdaptor =
new CommonTreeAdaptor() {
484 public Object create(Token token) {
return new Node(token); }
485 public Object dupNode(Object t) {
486 if ( t==null )
return null;
487 return create(((Node)t).token);
491 private String getTableName(CommonTree tableNode) {
492 return tableNode.getChild(0).getText();
495 private String getColumnName(CommonTree fieldNode) {
496 return fieldNode.getChild(0).getText();
499 private CommonTree getFieldNode(CommonTree selectExprNode) {
500 return (CommonTree)selectExprNode.getChild(0);
503 public void destroy(StatementInterceptor statementInterceptor) {
507 ConnectionLifecycleInterceptor connectionLifecycleInterceptor) {
510 private void assertReady() {
512 if (statementInterceptor == null) {
513 throw new ClusterJUserException(local.
message(
"ERR_No_Statement_Interceptor"));
515 if (connectionLifecycleInterceptor == null) {
516 throw new ClusterJUserException(local.
message(
"ERR_No_Connection_Lifecycle_Interceptor"));
519 if (sessionFactory == null) {
520 sessionFactory = ClusterJHelper.getSessionFactory(properties);
528 logStatus(
"setAutoCommit(" + autocommit +
")");
529 this.autocommit = autocommit;
545 public void close() {
548 public boolean commit() throws SQLException {
553 System.out.println(
"WARNING: commit called when session.transaction is not active");
559 public boolean rollback() throws SQLException {
560 logStatus(
"rollback");
566 public boolean rollback(Savepoint savepoint)
throws SQLException {
567 logStatus(
"rollback(Savepoint)");
571 public boolean setCatalog(
String catalog)
throws SQLException {
572 if (logger.isDebugEnabled()) logger.debug(
"catalog: " + catalog);
576 public boolean transactionCompleted() throws SQLException {
577 logStatus(
"transactionCompleted");
581 public boolean transactionBegun() throws SQLException {
582 logStatus(
"transactionBegun");
586 private DomainTypeHandlerImpl<?> getDomainTypeHandler(
String tableName, Dictionary dictionary) {
587 DomainTypeHandlerImpl<?> domainTypeHandler =
588 DomainTypeHandlerImpl.getDomainTypeHandler(tableName, dictionary);
589 return domainTypeHandler;
592 private void logStatus(
String s)
throws SQLException {
593 if (logger.isDetailEnabled()) {
594 StringBuilder builder =
new StringBuilder(
"In ");
596 builder.append(
" with");
597 if (connection != null) {
598 builder.append(
" connection.getAutocommit: " + connection.getAutoCommit());
600 if (session != null) {
603 builder.append(
'\n');
605 logger.detail(message);