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);