/**
 * This class implements a simple monotonically-growing pool of ByteBuffer. Each NdbRecord has its
 * own pool, including the value NdbRecord (all columns) and each index NdbRecord used for index
 * scans and delete operations.
 */
class FixedByteBufferPoolImpl {

  /** My message translator */
  static final I18NHelper local = I18NHelper.getInstance(FixedByteBufferPoolImpl.class);

  /** My logger */
  static final Logger logger =
      LoggerFactoryService.getFactory().getInstance(FixedByteBufferPoolImpl.class);

  /** The guard initialization bytes; to add a guard after the record, change the array size */
  static byte[] guard = new byte[0];

  static {
    for (int i = 0; i < guard.length; ++i) {
      guard[i] = (byte) 10;
    }
  }

  /** Initialize the guard for a specific buffer. */
  void initializeGuard(ByteBuffer buffer) {
    // the buffer has guard.length extra bytes in it, initialized with the guard bytes array
    buffer.position(buffer.capacity() - guard.length);
    buffer.put(guard);
    buffer.clear();
  }
  /** Check the guard */
  void checkGuard(ByteBuffer buffer, String where) {
    buffer.clear();
    boolean fail = false;
    // the buffer has guard.length extra bytes in it, initialized with the guard bytes
    buffer.position(buffer.capacity() - guard.length);
    for (int i = 0; i < guard.length; ++i) {
      byte actual = buffer.get();
      byte expected = guard[i];
      if (expected != actual) {
        fail = true;
        logger.warn(
            local.message(
                "WARN_Buffer_Pool_Guard_Check_Failed",
                where,
                (buffer.capacity() - guard.length),
                expected,
                actual,
                buffer.toString()));
      }
    }
    // reset it for next time
    initializeGuard(buffer);
  }

  /** The pool of ByteBuffer */
  ConcurrentLinkedQueue<ByteBuffer> pool;

  /** The name of this pool */
  String name;

  /** The length of each buffer */
  int bufferSize;

  /** The high water mark of this pool */
  int highWaterMark = 0;

  /** Construct an empty pool */
  public FixedByteBufferPoolImpl(int bufferSize, String name) {
    this.bufferSize = bufferSize;
    this.name = name;
    this.pool = new ConcurrentLinkedQueue<ByteBuffer>();
    logger.info("FixedByteBufferPoolImpl<init> for " + name + " bufferSize " + bufferSize);
  }

  /** Borrow a buffer from the pool. If none in the pool, create a new one. */
  public ByteBuffer borrowBuffer() {
    ByteBuffer buffer = pool.poll();
    if (buffer == null) {
      buffer = ByteBuffer.allocateDirect(bufferSize + guard.length);
      initializeGuard(buffer);
      if (logger.isDetailEnabled())
        logger.detail(
            "FixedByteBufferPoolImpl for "
                + name
                + " got new  buffer: position "
                + buffer.position()
                + " capacity "
                + buffer.capacity()
                + " limit "
                + buffer.limit());
    } else {
      if (logger.isDetailEnabled())
        logger.detail(
            "FixedByteBufferPoolImpl for "
                + name
                + " got used buffer: position "
                + buffer.position()
                + " capacity "
                + buffer.capacity()
                + " limit "
                + buffer.limit());
    }
    buffer.clear();
    return buffer;
  }

  /** Return a buffer to the pool. */
  public void returnBuffer(ByteBuffer buffer) {
    //        checkGuard(buffer, "returnBuffer"); // uncomment this to enable checking
    if (buffer.capacity() != bufferSize + guard.length) {
      String message =
          local.message(
              "ERR_Wrong_Buffer_Size_Returned_To_Pool", name, bufferSize, buffer.capacity());
      throw new ClusterJFatalInternalException(message);
    }
    pool.add(buffer);
  }
}
示例#2
0
/** This represents a named parameter that is bound at execution time to a value. */
public class ParameterImpl implements PredicateOperand {

  /** My message translator */
  static final I18NHelper local = I18NHelper.getInstance(ParameterImpl.class);

  /** My logger */
  static final Logger logger = LoggerFactoryService.getFactory().getInstance(ParameterImpl.class);

  /** My domain object. */
  protected QueryDomainTypeImpl<?> dobj;

  /** My property (set when bound) */
  protected PropertyImpl property;

  /** My parameter name */
  protected String parameterName;

  /** Is a value bound to this parameter? */
  protected boolean bound = false;

  /** Is this parameter marked (used in the query)? */
  protected boolean marked = false;

  public ParameterImpl(QueryDomainTypeImpl<?> dobj, String parameterName) {
    this.dobj = dobj;
    this.parameterName = parameterName;
  }

  public void mark() {
    marked = true;
  }

  boolean isMarkedAndUnbound(QueryExecutionContext context) {
    return marked && !context.isBound(parameterName);
  }

  void unmark() {
    marked = false;
  }

  public String getName() {
    return parameterName;
  }

  public Object getParameterValue(QueryExecutionContext context) {
    return property.getParameterValue(context, parameterName);
  }

  public Predicate equal(PredicateOperand predicateOperand) {
    throw new UnsupportedOperationException(local.message("ERR_NotImplemented"));
  }

  public Predicate between(PredicateOperand lower, PredicateOperand upper) {
    throw new UnsupportedOperationException(local.message("ERR_NotImplemented"));
  }

  public Predicate greaterThan(PredicateOperand other) {
    throw new UnsupportedOperationException(local.message("ERR_NotImplemented"));
  }

  public Predicate greaterEqual(PredicateOperand other) {
    throw new UnsupportedOperationException(local.message("ERR_NotImplemented"));
  }

  public Predicate lessThan(PredicateOperand other) {
    throw new UnsupportedOperationException(local.message("ERR_NotImplemented"));
  }

  public Predicate lessEqual(PredicateOperand other) {
    throw new UnsupportedOperationException(local.message("ERR_NotImplemented"));
  }

  public Predicate in(PredicateOperand other) {
    throw new UnsupportedOperationException(local.message("ERR_NotImplemented"));
  }

  public Predicate like(PredicateOperand other) {
    throw new UnsupportedOperationException(local.message("ERR_NotImplemented"));
  }

  public Predicate isNull() {
    throw new UnsupportedOperationException(local.message("ERR_NotImplemented"));
  }

  public Predicate isNotNull() {
    throw new UnsupportedOperationException(local.message("ERR_NotImplemented"));
  }

  public void setProperty(PropertyImpl property) {
    if (this.property != null && this.property.fmd.getType() != property.fmd.getType()) {
      throw new ClusterJUserException(
          local.message(
              "ERR_Multiple_Parameter_Usage",
              parameterName,
              this.property.fmd.getType().getName(),
              property.fmd.getType().getName()));
    } else {
      this.property = property;
    }
  }
}
示例#3
0
/**
 * This class implements the behavior associated with connection callbacks for statement execution
 * and connection lifecycle. There is a clusterj session associated with the interceptor that is
 * used to interact with the cluster. There is exactly one statement interceptor and one connection
 * lifecycle interceptor associated with the interceptor. All of the SQL post-parsing behavior is
 * contained here, and uses classes in the org.antlr.runtime and com.mysql.clusterj.jdbc.antlr
 * packages to perform the parsing of the SQL statement. Analysis of the parsed SQL statement occurs
 * here, and clusterj artifacts are constructed for use in other classes, in particular SQLExecutor
 * and its command-specific subclasses.
 */
public class InterceptorImpl {

  /** Register logger for JDBC stuff. */
  static {
    LoggerFactoryService.getFactory().registerLogger("com.mysql.clusterj.jdbc");
  }

  /** My message translator */
  static final I18NHelper local = I18NHelper.getInstance(InterceptorImpl.class);

  /** My logger */
  static final Logger logger = LoggerFactoryService.getFactory().getInstance(InterceptorImpl.class);

  static Map<String, Executor> parsedSqlMap = new IdentityHashMap<String, Executor>();

  /** The map of connection to interceptor */
  private static Map<Connection, InterceptorImpl> interceptorImplMap =
      new IdentityHashMap<Connection, InterceptorImpl>();

  /** The connection properties */
  private Properties properties;

  /** The connection being intercepted */
  private Connection connection;

  /** The session factory for this connection */
  SessionFactory sessionFactory;

  /** The current session (null if no session) */
  private SessionSPI session;

  /** The statement interceptor (only used during initialization) */
  private StatementInterceptor statementInterceptor;

  /** The connection lifecycle interceptor (only used during initialization) */
  private ConnectionLifecycleInterceptor connectionLifecycleInterceptor;

  /** The interceptor is ready (both interceptors are registered) */
  private boolean ready = false;

  private boolean autocommit;

  private static String LOTSOBLANKS =
      "                                                                          ";

  /**
   * Create the interceptor.
   *
   * @param connection the connection being intercepted
   * @param properties the connection properties
   */
  public InterceptorImpl(Connection connection, Properties properties) {
    if (logger.isDebugEnabled()) logger.debug("constructed with properties: " + properties);
    this.properties = properties;
    this.connection = connection;
    // if database name is not specified, translate DBNAME to the required clusterj property
    String dbname =
        properties.getProperty("com.mysql.clusterj.database", properties.getProperty("DBNAME"));
    properties.put("com.mysql.clusterj.database", dbname);
  }

  /**
   * Return the interceptor for the connection lifecycle callbacks.
   *
   * @param connectionLifecycleInterceptor the connection lifecycle interceptor
   * @param connection the connection
   * @param properties the connection properties
   * @return the interceptor delegate
   */
  public static InterceptorImpl getInterceptorImpl(
      ConnectionLifecycleInterceptor connectionLifecycleInterceptor,
      Connection connection,
      Properties properties) {
    InterceptorImpl result = getInterceptorImpl(connection, properties);
    if (result.connectionLifecycleInterceptor != null) {
      if (result.connectionLifecycleInterceptor != connectionLifecycleInterceptor) {
        throw new ClusterJUserException(
            local.message("ERR_Duplicate_Connection_Lifecycle_Interceptor"));
      }
    } else {
      result.connectionLifecycleInterceptor = connectionLifecycleInterceptor;
    }
    if (result.statementInterceptor != null) {
      result.ready = true;
    }
    return result;
  }

  /**
   * Return the interceptor for the statement interceptor callbacks.
   *
   * @param statementInterceptor the statement interceptor
   * @param connection the connection
   * @param properties the connection properties
   * @return the interceptor delegate
   */
  public static InterceptorImpl getInterceptorImpl(
      StatementInterceptor statementInterceptor, Connection connection, Properties properties) {
    InterceptorImpl result = getInterceptorImpl(connection, properties);
    if (result.statementInterceptor != null) {
      throw new ClusterJUserException(local.message("ERR_Duplicate_Statement_Interceptor"));
    }
    result.statementInterceptor = statementInterceptor;
    if (result.connectionLifecycleInterceptor != null) {
      result.ready = true;
    }
    return result;
  }

  /**
   * Create the interceptor to handle both connection lifecycle and statement interceptors.
   *
   * @param connection the connection
   * @param properties the connection properties
   * @return
   */
  public static InterceptorImpl getInterceptorImpl(Connection connection, Properties properties) {
    InterceptorImpl result;
    synchronized (interceptorImplMap) {
      result = interceptorImplMap.get(connection);
      if (result == null) {
        result = new InterceptorImpl(connection, properties);
        interceptorImplMap.put(connection, result);
      }
    }
    return result;
  }

  /**
   * Return the interceptor assigned to the connection. If there is no interceptor, return null.
   *
   * @param connection the connection
   * @return the interceptor for this connection or null if there is no interceptor
   */
  public static InterceptorImpl getInterceptorImpl(java.sql.Connection connection) {
    synchronized (interceptorImplMap) {
      return interceptorImplMap.get(connection);
    }
  }

  @Override
  public String toString() {
    return "InterceptorImpl "
    //        + " properties: "+ properties.toString()
    ;
  }

  void destroy() {
    if (sessionFactory != null) {
      if (session != null) {
        session.close();
      }
      sessionFactory.close();
      sessionFactory = null;
      synchronized (interceptorImplMap) {
        interceptorImplMap.remove(connection);
      }
    }
  }

  public SessionSPI getSession() {
    if (session == null) {
      session = (SessionSPI) sessionFactory.getSession();
    }
    return session;
  }

  public boolean executeTopLevelOnly() {
    //        assertReady();
    boolean result = true;
    return result;
  }

  public ResultSetInternalMethods postProcess(
      String sql,
      Statement statement,
      ResultSetInternalMethods result,
      Connection connection,
      int arg4,
      boolean arg5,
      boolean arg6,
      SQLException sqlException)
      throws SQLException {
    assertReady();
    return null;
  }

  public ResultSetInternalMethods preProcess(String sql, Statement statement, Connection connection)
      throws SQLException {
    assertReady();
    if (logger.isDebugEnabled() && statement != null)
      logger.debug(statement.getClass().getName() + ": " + sql);
    if (statement instanceof com.mysql.jdbc.PreparedStatement) {
      com.mysql.jdbc.PreparedStatement preparedStatement =
          (com.mysql.jdbc.PreparedStatement) statement;
      // key must be interned because we are using IdentityHashMap
      // TODO: in case of DELETE, the SQL has already been rewritten at this point,
      // and the original SQL is gone
      // so the key in the table is the rewritten DELETE SQL -- not what we want at all
      String nonRewrittenSql = preparedStatement.getNonRewrittenSql();
      String internedSql = nonRewrittenSql.intern();

      // see if we have a parsed version of this query
      Executor sQLExecutor = null;
      synchronized (parsedSqlMap) {
        sQLExecutor = parsedSqlMap.get(internedSql);
      }
      // if no cached SQLExecutor, create it, which might take some time
      if (sQLExecutor == null) {
        sQLExecutor = createSQLExecutor(internedSql);
        if (sQLExecutor != null) {
          // multiple thread might have created a SQLExecutor but it's ok
          synchronized (parsedSqlMap) {
            parsedSqlMap.put(internedSql, sQLExecutor);
          }
        }
      }
      try {
        return sQLExecutor.execute(this, preparedStatement);
      } catch (Throwable t) {
        t.printStackTrace();
        return null;
      }
    } else {
      if (logger.isDebugEnabled() && statement != null)
        logger.debug(
            statement.getClass().getName() + " is not instanceof com.mysql.jdbc.PreparedStatement");
      // not a prepared statement; won't execute this
      return null;
    }
  }

  /** @param preparedSql */
  private Executor createSQLExecutor(String preparedSql) {
    if (logger.isDetailEnabled()) logger.detail(preparedSql);
    Executor result = null;
    // parse the sql
    CommonTree root = parse(preparedSql);
    // get the root of the tree
    int tokenType = root.getType();
    // perform command-specific actions
    String tableName = "";
    CommonTree tableNode;
    WhereNode whereNode;
    String whereType = "empty";
    List<String> columnNames = new ArrayList<String>();
    Dictionary dictionary;
    DomainTypeHandlerImpl<?> domainTypeHandler;
    QueryDomainTypeImpl<?> queryDomainType = null;
    int numberOfParameters = 0;
    switch (tokenType) {
      case MySQL51Parser.INSERT:
        tableNode = (CommonTree) root.getFirstChildWithType(MySQL51Parser.TABLE);
        tableName = getTableName(tableNode);
        getSession();
        dictionary = session.getDictionary();
        domainTypeHandler = getDomainTypeHandler(tableName, dictionary);
        CommonTree insertValuesNode =
            (CommonTree) root.getFirstChildWithType(MySQL51Parser.INSERT_VALUES);
        CommonTree columnsNode =
            (CommonTree) insertValuesNode.getFirstChildWithType(MySQL51Parser.COLUMNS);
        List<CommonTree> fields = columnsNode.getChildren();
        for (CommonTree field : fields) {
          columnNames.add(getColumnName(field));
        }
        if (logger.isDetailEnabled())
          logger.detail(
              "StatementInterceptorImpl.preProcess parse result INSERT INTO "
                  + tableName
                  + " COLUMNS "
                  + columnNames);
        result = new SQLExecutor.Insert(domainTypeHandler, columnNames);
        break;
      case MySQL51Parser.SELECT:
        CommonTree fromNode = (CommonTree) root.getFirstChildWithType(MySQL51Parser.FROM);
        if (fromNode == null) {
          // no from clause; cannot handle this case so return a do-nothing ParsedSQL
          result = new SQLExecutor.Noop();
          break;
        }
        try {
          // this currently handles only FROM clauses with a single table
          tableNode = (CommonTree) fromNode.getFirstChildWithType(MySQL51Parser.TABLE);
          tableName = getTableName(tableNode);
        } catch (Exception e) {
          // trouble with the FROM clause; log the SQL statement and the parser output
          logger.info("Problem with FROM clause in SQL statement: " + preparedSql);
          logger.info(walk(root));
          result = new SQLExecutor.Noop();
          break;
        }
        boolean forUpdate = null != (CommonTree) root.getFirstChildWithType(MySQL51Parser.FOR);
        boolean lockShared = null != (CommonTree) root.getFirstChildWithType(MySQL51Parser.LOCK);
        LockMode lockMode = LockMode.READ_COMMITTED;
        if (forUpdate) {
          lockMode = LockMode.EXCLUSIVE;
        } else if (lockShared) {
          lockMode = LockMode.SHARED;
        }
        getSession();
        dictionary = session.getDictionary();
        domainTypeHandler = getDomainTypeHandler(tableName, dictionary);
        columnsNode = (CommonTree) root.getFirstChildWithType(MySQL51Parser.COLUMNS);
        List<CommonTree> selectExprNodes = columnsNode.getChildren();
        for (CommonTree selectExprNode : selectExprNodes) {
          columnNames.add(getColumnName(getFieldNode(selectExprNode)));
        }
        if (logger.isDetailEnabled())
          logger.detail("SELECT FROM " + tableName + " COLUMNS " + columnNames);
        // we need to distinguish three cases:
        // - no where clause (select all rows)
        // - where clause that cannot be executed by clusterj
        // - where clause that can be executed by clusterj
        whereNode = ((SelectNode) root).getWhereNode();
        queryDomainType = (QueryDomainTypeImpl<?>) session.createQueryDomainType(domainTypeHandler);
        if (whereNode == null) {
          // no where clause (select all rows)
          result =
              new SQLExecutor.Select(domainTypeHandler, columnNames, queryDomainType, lockMode);
        } else {
          // create a predicate from the tree
          Predicate predicate = whereNode.getPredicate(queryDomainType);
          if (predicate != null) {
            // where clause that can be executed by clusterj
            queryDomainType.where(predicate);
            numberOfParameters = whereNode.getNumberOfParameters();
            result =
                new SQLExecutor.Select(
                    domainTypeHandler, columnNames, queryDomainType, lockMode, numberOfParameters);
            whereType = "clusterj";
          } else {
            // where clause that cannot be executed by clusterj
            result = new SQLExecutor.Noop();
            whereType = "non-clusterj";
          }
          if (logger.isDetailEnabled()) logger.detail(walk(root));
        }
        if (logger.isDetailEnabled()) {
          logger.detail(
              "SELECT FROM " + tableName + " COLUMNS " + columnNames + " whereType " + whereType);
          logger.detail(walk(root));
        }
        logger.info(preparedSql + ": " + whereType);
        break;
      case MySQL51Parser.DELETE:
        tableNode = (CommonTree) root.getFirstChildWithType(MySQL51Parser.TABLE);
        tableName = getTableName(tableNode);
        getSession();
        dictionary = session.getDictionary();
        domainTypeHandler = getDomainTypeHandler(tableName, dictionary);
        whereNode = ((WhereNode) root.getFirstChildWithType(MySQL51Parser.WHERE));
        if (whereNode == null) {
          // no where clause (delete all rows)
          result = new SQLExecutor.Delete(domainTypeHandler);
          whereType = "empty";
        } else {
          // create a predicate from the tree
          queryDomainType =
              (QueryDomainTypeImpl<?>) session.createQueryDomainType(domainTypeHandler);
          Predicate predicate = whereNode.getPredicate(queryDomainType);
          if (predicate != null) {
            // where clause that can be executed by clusterj
            queryDomainType.where(predicate);
            numberOfParameters = whereNode.getNumberOfParameters();
            result = new SQLExecutor.Delete(domainTypeHandler, queryDomainType, numberOfParameters);
            whereType = "clusterj";
          } else {
            // where clause that cannot be executed by clusterj
            result = new SQLExecutor.Noop();
            whereType = "non-clusterj";
          }
          if (logger.isDetailEnabled()) logger.detail(walk(root));
        }
        if (logger.isDetailEnabled())
          logger.detail(
              "DELETE FROM "
                  + tableName
                  + " whereType "
                  + whereType
                  + " number of parameters "
                  + numberOfParameters);
        logger.info(preparedSql + ": " + whereType);
        break;
      case MySQL51Parser.UPDATE:
        // UPDATE table SET column = value, column = value WHERE where-clause
        tableNode = (CommonTree) root.getFirstChildWithType(MySQL51Parser.TABLE);
        tableName = getTableName(tableNode);
        getSession();
        dictionary = session.getDictionary();
        domainTypeHandler = getDomainTypeHandler(tableName, dictionary);
        CommonTree setNode = (CommonTree) root.getFirstChildWithType(MySQL51Parser.SET);
        // create list of columns to update
        // SET node has one child for each <field> = <value
        List<CommonTree> equalNodes = setNode.getChildren();
        List<Integer> parameterNumbers = new ArrayList<Integer>();
        for (CommonTree equalNode : equalNodes) {
          // each equalNode has a FIELD node and a parameter node
          columnNames.add(getColumnName(getFieldNode(equalNode)));
          PlaceholderNode parameterNode = (PlaceholderNode) equalNode.getChild(1);
          parameterNumbers.add(parameterNode.getId());
        }
        if (logger.isDetailEnabled())
          logger.detail("Update " + columnNames + " values " + parameterNumbers);
        whereNode = ((WhereNode) root.getFirstChildWithType(MySQL51Parser.WHERE));
        if (whereNode == null) {
          // no where clause (update all rows)
          whereType = "non-clusterj";
          // return a do-nothing ParsedSQL
          if (logger.isDetailEnabled())
            logger.detail(
                "ClusterJ cannot process this SQL statement: "
                    + "unsupported statement type (UPDATE without WHERE clause.");
          result = new SQLExecutor.Noop();
        } else {
          // create a predicate from the tree
          queryDomainType =
              (QueryDomainTypeImpl<?>) session.createQueryDomainType(domainTypeHandler);
          PredicateImpl predicate = (PredicateImpl) whereNode.getPredicate(queryDomainType);
          if (predicate != null) {
            // where clause that can be executed by clusterj
            queryDomainType.where(predicate);
            List<String> whereColumnNames = predicate.getTopLevelPropertyNames();
            numberOfParameters = equalNodes.size() + whereColumnNames.size();
            result =
                new SQLExecutor.Update(
                    domainTypeHandler,
                    queryDomainType,
                    numberOfParameters,
                    columnNames,
                    whereColumnNames);
            whereType = "clusterj";
          } else {
            // where clause that cannot be executed by clusterj
            result = new SQLExecutor.Noop();
            whereType = "non-clusterj";
          }
          if (logger.isDetailEnabled()) logger.detail(walk(root));
        }
        if (logger.isDetailEnabled())
          logger.detail(
              "UPDATE "
                  + tableName
                  + " whereType "
                  + whereType
                  + " number of parameters "
                  + numberOfParameters);
        logger.info(preparedSql + ": " + whereType);
        break;
      default:
        // return a do-nothing ParsedSQL
        if (logger.isDetailEnabled())
          logger.detail("ClusterJ cannot process this SQL statement: unsupported statement type.");
        result = new SQLExecutor.Noop();
    }
    return result;
  }

  private String getPrimaryKeyFieldName(CommonTree whereNode) {
    String result = null;
    CommonTree operation = (CommonTree) whereNode.getChild(0);
    if (MySQL51Parser.EQUALS == operation.getType()) {
      result = operation.getChild(0).getChild(0).getText();
    } else {
      throw new ClusterJUserException("Cannot find primary key in WHERE clause.");
    }
    return result;
  }

  private String walk(CommonTree tree) {
    StringBuilder buffer = new StringBuilder();
    walk(tree, buffer, 0);
    return buffer.toString();
  }

  @SuppressWarnings("unchecked") // tree.getChildren()
  private void walk(CommonTree tree, StringBuilder buffer, int level) {
    String indent = LOTSOBLANKS.substring(0, level);
    Token token = tree.token;
    int tokenType = token.getType();
    String tokenText = token.getText();
    int childCount = tree.getChildCount();
    int childIndex = tree.getChildIndex();
    buffer.append('\n');
    buffer.append(indent);
    buffer.append(tokenText);
    buffer.append(" class: ");
    buffer.append(tree.getClass().getName());
    buffer.append(" tokenType ");
    buffer.append(tokenType);
    buffer.append(" child count ");
    buffer.append(childCount);
    buffer.append(" child index ");
    buffer.append(childIndex);
    List<CommonTree> children = tree.getChildren();
    if (children == null) {
      return;
    }
    for (CommonTree child : children) {
      walk(child, buffer, level + 2);
    }
  }

  private CommonTree parse(String preparedSql) {
    CommonTree result = null;
    ANTLRNoCaseStringStream inputStream = new ANTLRNoCaseStringStream(preparedSql);
    MySQL51Lexer lexer = new MySQL51Lexer(inputStream);
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    lexer.setErrorListener(new QueuingErrorListener(lexer));
    tokens.getTokens();
    if (lexer.getErrorListener().hasErrors()) {
      logger.warn(local.message("ERR_Lexing_SQL", preparedSql));
      return result;
    }
    PlaceholderNode.resetId();
    MySQL51Parser parser = new MySQL51Parser(tokens);
    parser.setTreeAdaptor(mySQLTreeAdaptor);
    parser.setErrorListener(new QueuingErrorListener(parser));
    try {
      CommonTree stmtTree = (CommonTree) parser.statement().getTree();
      result = stmtTree;
    } catch (RecognitionException e) {
      logger.warn(local.message("ERR_Parsing_SQL", preparedSql));
    }
    if (parser.getErrorListener().hasErrors()) {
      logger.warn(local.message("ERR_Parsing_SQL", preparedSql));
    }
    return result;
  }

  private TreeAdaptor mySQLTreeAdaptor =
      new CommonTreeAdaptor() {
        public Object create(Token token) {
          return new Node(token);
        }

        public Object dupNode(Object t) {
          if (t == null) return null;
          return create(((Node) t).token);
        }
      };

  private String getTableName(CommonTree tableNode) {
    return tableNode.getChild(0).getText();
  }

  private String getColumnName(CommonTree fieldNode) {
    return fieldNode.getChild(0).getText();
  }

  private CommonTree getFieldNode(CommonTree selectExprNode) {
    return (CommonTree) selectExprNode.getChild(0);
  }

  public void destroy(StatementInterceptor statementInterceptor) {}

  public void destroy(ConnectionLifecycleInterceptor connectionLifecycleInterceptor) {}

  private void assertReady() {
    if (!ready) {
      if (statementInterceptor == null) {
        throw new ClusterJUserException(local.message("ERR_No_Statement_Interceptor"));
      }
      if (connectionLifecycleInterceptor == null) {
        throw new ClusterJUserException(local.message("ERR_No_Connection_Lifecycle_Interceptor"));
      }
    } else {
      if (sessionFactory == null) {
        sessionFactory = ClusterJHelper.getSessionFactory(properties);
      }
    }
  }

  /** TODO This needs to be rewritten with a proper state machine. */
  public boolean setAutoCommit(boolean autocommit) throws SQLException {
    assertReady();
    logStatus("setAutoCommit(" + autocommit + ")");
    this.autocommit = autocommit;
    getSession();
    if (!autocommit) {
      // start a transaction
      if (!session.currentTransaction().isActive()) {
        session.begin();
      }
    } else {
      // roll back the previous transaction if active
      if (session.currentTransaction().isActive()) {
        session.rollback();
      }
    }
    return true; // let the driver perform its own autocommit behavior
  }

  public void close() {}

  public boolean commit() throws SQLException {
    logStatus("commit");
    if (session.currentTransaction().isActive()) {
      session.commit();
    } else {
      System.out.println("WARNING: commit called when session.transaction is not active");
    }
    session.begin();
    return true;
  }

  public boolean rollback() throws SQLException {
    logStatus("rollback");
    session.rollback();
    session.begin();
    return true;
  }

  public boolean rollback(Savepoint savepoint) throws SQLException {
    logStatus("rollback(Savepoint)");
    return true;
  }

  public boolean setCatalog(String catalog) throws SQLException {
    if (logger.isDebugEnabled()) logger.debug("catalog: " + catalog);
    return true;
  }

  public boolean transactionCompleted() throws SQLException {
    logStatus("transactionCompleted");
    return true;
  }

  public boolean transactionBegun() throws SQLException {
    logStatus("transactionBegun");
    return true;
  }

  private DomainTypeHandlerImpl<?> getDomainTypeHandler(String tableName, Dictionary dictionary) {
    DomainTypeHandlerImpl<?> domainTypeHandler =
        DomainTypeHandlerImpl.getDomainTypeHandler(tableName, dictionary);
    return domainTypeHandler;
  }

  private void logStatus(String s) throws SQLException {
    if (logger.isDetailEnabled()) {
      StringBuilder builder = new StringBuilder("In ");
      builder.append(s);
      builder.append(" with");
      if (connection != null) {
        builder.append(" connection.getAutocommit: " + connection.getAutoCommit());
      }
      if (session != null) {
        builder.append(" session.isActive: " + session.currentTransaction().isActive());
      }
      builder.append('\n');
      String message = builder.toString();
      logger.detail(message);
    }
  }
}
class OperationImpl implements Operation {

  /** My message translator */
  static final I18NHelper local = I18NHelper.getInstance(OperationImpl.class);

  /** My logger */
  static final Logger logger = LoggerFactoryService.getFactory().getInstance(OperationImpl.class);

  private NdbOperation ndbOperation;

  protected List<Column> storeColumns = new ArrayList<Column>();

  protected ClusterTransactionImpl clusterTransaction;

  /** The size of the receive buffer for this operation (may be zero for non-read operations) */
  protected int bufferSize;

  /** The maximum column id for this operation (may be zero for non-read operations) */
  protected int maximumColumnId;

  /** The offsets into the buffer for each column (may be null for non-read operations) */
  protected int[] offsets;

  /** The lengths of fields in the buffer for each column (may be null for non-read operations) */
  protected int[] lengths;

  /** The maximum length of any column in this operation */
  protected int maximumColumnLength;

  protected BufferManager bufferManager;

  /**
   * Constructor used for insert and delete operations that do not need to read data.
   *
   * @param operation the operation
   * @param transaction the transaction
   */
  public OperationImpl(NdbOperation operation, ClusterTransactionImpl transaction) {
    this.ndbOperation = operation;
    this.clusterTransaction = transaction;
    this.bufferManager = clusterTransaction.getBufferManager();
  }

  /**
   * Constructor used for read operations. The table is used to obtain data used to lay out memory
   * for the result.
   *
   * @param storeTable the table
   * @param operation the operation
   * @param transaction the transaction
   */
  public OperationImpl(
      Table storeTable, NdbOperation operation, ClusterTransactionImpl transaction) {
    this(operation, transaction);
    TableImpl tableImpl = (TableImpl) storeTable;
    this.maximumColumnId = tableImpl.getMaximumColumnId();
    this.bufferSize = tableImpl.getBufferSize();
    this.offsets = tableImpl.getOffsets();
    this.lengths = tableImpl.getLengths();
    this.maximumColumnLength = tableImpl.getMaximumColumnLength();
  }

  public void equalBigInteger(Column storeColumn, BigInteger value) {
    ByteBuffer buffer = Utility.convertValue(storeColumn, value);
    int returnCode = ndbOperation.equal(storeColumn.getName(), buffer);
    handleError(returnCode, ndbOperation);
  }

  public void equalBoolean(Column storeColumn, boolean booleanValue) {
    byte value = (booleanValue ? (byte) 0x01 : (byte) 0x00);
    int returnCode = ndbOperation.equal(storeColumn.getName(), value);
    handleError(returnCode, ndbOperation);
  }

  public void equalByte(Column storeColumn, byte value) {
    int storeValue = Utility.convertByteValueForStorage(storeColumn, value);
    int returnCode = ndbOperation.equal(storeColumn.getName(), storeValue);
    handleError(returnCode, ndbOperation);
  }

  public void equalBytes(Column storeColumn, byte[] value) {
    if (logger.isDetailEnabled())
      logger.detail(
          "Column: "
              + storeColumn.getName()
              + " columnId: "
              + storeColumn.getColumnId()
              + " data length: "
              + value.length);
    ByteBuffer buffer = Utility.convertValue(storeColumn, value);
    int returnCode = ndbOperation.equal(storeColumn.getName(), buffer);
    handleError(returnCode, ndbOperation);
  }

  public void equalDecimal(Column storeColumn, BigDecimal value) {
    ByteBuffer buffer = Utility.convertValue(storeColumn, value);
    int returnCode = ndbOperation.equal(storeColumn.getName(), buffer);
    handleError(returnCode, ndbOperation);
  }

  public void equalDouble(Column storeColumn, double value) {
    ByteBuffer buffer = Utility.convertValue(storeColumn, value);
    int returnCode = ndbOperation.equal(storeColumn.getName(), buffer);
    handleError(returnCode, ndbOperation);
  }

  public void equalFloat(Column storeColumn, float value) {
    ByteBuffer buffer = Utility.convertValue(storeColumn, value);
    int returnCode = ndbOperation.equal(storeColumn.getName(), buffer);
    handleError(returnCode, ndbOperation);
  }

  public void equalInt(Column storeColumn, int value) {
    int returnCode = ndbOperation.equal(storeColumn.getName(), value);
    handleError(returnCode, ndbOperation);
  }

  public void equalShort(Column storeColumn, short value) {
    int storeValue = Utility.convertShortValueForStorage(storeColumn, value);
    int returnCode = ndbOperation.equal(storeColumn.getName(), storeValue);
    handleError(returnCode, ndbOperation);
  }

  public void equalLong(Column storeColumn, long value) {
    long storeValue = Utility.convertLongValueForStorage(storeColumn, value);
    int returnCode = ndbOperation.equal(storeColumn.getName(), storeValue);
    handleError(returnCode, ndbOperation);
  }

  public void equalString(Column storeColumn, String value) {
    ByteBuffer stringStorageBuffer = Utility.encode(value, storeColumn, bufferManager);
    int returnCode = ndbOperation.equal(storeColumn.getName(), stringStorageBuffer);
    bufferManager.clearStringStorageBuffer();
    handleError(returnCode, ndbOperation);
  }

  public void getBlob(Column storeColumn) {
    NdbBlob ndbBlob = ndbOperation.getBlobHandleM(storeColumn.getColumnId());
    handleError(ndbBlob, ndbOperation);
  }

  public Blob getBlobHandle(Column storeColumn) {
    NdbBlob blobHandle = ndbOperation.getBlobHandleM(storeColumn.getColumnId());
    handleError(blobHandle, ndbOperation);
    return new BlobImpl(blobHandle);
  }

  /**
   * Specify the columns to be used for the operation. For now, just save the columns. When
   * resultData is called, pass the columns to the ResultData constructor and then execute the
   * operation.
   */
  public void getValue(Column storeColumn) {
    storeColumns.add(storeColumn);
  }

  public void postExecuteCallback(Runnable callback) {
    clusterTransaction.postExecuteCallback(callback);
  }

  /** Construct a new ResultData using the saved column data and then execute the operation. */
  public ResultData resultData() {
    return resultData(true);
  }

  /** Construct a new ResultData and if requested, execute the operation. */
  public ResultData resultData(boolean execute) {
    if (logger.isDetailEnabled())
      logger.detail("storeColumns: " + Arrays.toString(storeColumns.toArray()));
    ResultDataImpl result;
    if (execute) {
      result =
          new ResultDataImpl(
              ndbOperation,
              storeColumns,
              maximumColumnId,
              bufferSize,
              offsets,
              lengths,
              bufferManager,
              false);
      clusterTransaction.executeNoCommit(false, true);
    } else {
      result =
          new ResultDataImpl(
              ndbOperation,
              storeColumns,
              maximumColumnId,
              bufferSize,
              offsets,
              lengths,
              bufferManager,
              true);
    }
    return result;
  }

  public void setBigInteger(Column storeColumn, BigInteger value) {
    ByteBuffer buffer = Utility.convertValue(storeColumn, value);
    int returnCode = ndbOperation.setValue(storeColumn.getColumnId(), buffer);
    handleError(returnCode, ndbOperation);
  }

  public void setBoolean(Column storeColumn, Boolean value) {
    byte byteValue = (value ? (byte) 0x01 : (byte) 0x00);
    setByte(storeColumn, byteValue);
  }

  public void setByte(Column storeColumn, byte value) {
    int storeValue = Utility.convertByteValueForStorage(storeColumn, value);
    int returnCode = ndbOperation.setValue(storeColumn.getColumnId(), storeValue);
    handleError(returnCode, ndbOperation);
  }

  public void setBytes(Column storeColumn, byte[] value) {
    // TODO use the string storage buffer instead of allocating a new buffer for each value
    int length = value.length;
    if (length > storeColumn.getLength()) {
      throw new ClusterJUserException(
          local.message(
              "ERR_Data_Too_Long", storeColumn.getName(), storeColumn.getLength(), length));
    }
    ByteBuffer buffer = Utility.convertValue(storeColumn, value);
    int returnCode = ndbOperation.setValue(storeColumn.getColumnId(), buffer);
    handleError(returnCode, ndbOperation);
  }

  public void setDecimal(Column storeColumn, BigDecimal value) {
    ByteBuffer buffer = Utility.convertValue(storeColumn, value);
    int returnCode = ndbOperation.setValue(storeColumn.getColumnId(), buffer);
    handleError(returnCode, ndbOperation);
  }

  public void setDouble(Column storeColumn, Double value) {
    int returnCode = ndbOperation.setValue(storeColumn.getColumnId(), value);
    handleError(returnCode, ndbOperation);
  }

  public void setFloat(Column storeColumn, Float value) {
    int returnCode = ndbOperation.setValue(storeColumn.getColumnId(), value);
    handleError(returnCode, ndbOperation);
  }

  public void setInt(Column storeColumn, Integer value) {
    int returnCode = ndbOperation.setValue(storeColumn.getColumnId(), value);
    handleError(returnCode, ndbOperation);
  }

  public void setLong(Column storeColumn, long value) {
    long storeValue = Utility.convertLongValueForStorage(storeColumn, value);
    int returnCode = ndbOperation.setValue(storeColumn.getColumnId(), storeValue);
    handleError(returnCode, ndbOperation);
  }

  public void setNull(Column storeColumn) {
    int returnCode = ndbOperation.setValue(storeColumn.getColumnId(), null);
    handleError(returnCode, ndbOperation);
  }

  public void setShort(Column storeColumn, Short value) {
    int storeValue = Utility.convertShortValueForStorage(storeColumn, value);
    int returnCode = ndbOperation.setValue(storeColumn.getName(), storeValue);
    handleError(returnCode, ndbOperation);
  }

  public void setString(Column storeColumn, String value) {
    ByteBuffer stringStorageBuffer = Utility.encode(value, storeColumn, bufferManager);
    int length = stringStorageBuffer.remaining() - storeColumn.getPrefixLength();
    if (length > storeColumn.getLength()) {
      throw new ClusterJUserException(
          local.message(
              "ERR_Data_Too_Long", storeColumn.getName(), storeColumn.getLength(), length));
    }
    int returnCode = ndbOperation.setValue(storeColumn.getColumnId(), stringStorageBuffer);
    bufferManager.clearStringStorageBuffer();
    handleError(returnCode, ndbOperation);
  }

  public int errorCode() {
    return ndbOperation.getNdbError().code();
  }

  protected void handleError(int returnCode, NdbOperation ndbOperation) {
    if (returnCode == 0) {
      return;
    } else {
      Utility.throwError(returnCode, ndbOperation.getNdbError());
    }
  }

  protected static void handleError(Object object, NdbOperation ndbOperation) {
    if (object != null) {
      return;
    } else {
      Utility.throwError(null, ndbOperation.getNdbError());
    }
  }

  public void beginDefinition() {
    // nothing to do
  }

  public void endDefinition() {
    // nothing to do
  }

  public int getErrorCode() {
    return ndbOperation.getNdbError().code();
  }

  public int getClassification() {
    return ndbOperation.getNdbError().classification();
  }

  public int getMysqlCode() {
    return ndbOperation.getNdbError().mysql_code();
  }

  public int getStatus() {
    return ndbOperation.getNdbError().status();
  }

  public void freeResourcesAfterExecute() {}
}
class OperationImpl implements Operation {

  /** My message translator */
  static final I18NHelper local = I18NHelper.getInstance(OperationImpl.class);

  /** My logger */
  static final Logger logger = LoggerFactoryService.getFactory().getInstance(OperationImpl.class);

  private NdbOperation ndbOperation;

  private ClusterTransactionImpl clusterTransaction;

  public OperationImpl(NdbOperation operation, ClusterTransactionImpl transaction) {
    this.ndbOperation = operation;
    this.clusterTransaction = transaction;
  }

  public void equalBoolean(Column storeColumn, boolean booleanValue) {
    throw new UnsupportedOperationException(local.message("ERR_NotImplemented"));
  }

  public void equalByte(Column storeColumn, byte b) {
    try {
      ndbOperation.equalInt(storeColumn.getName(), (int) b);
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void equalBytes(Column storeColumn, byte[] value) {
    try {
      ndbOperation.equalBytes(storeColumn.getName(), value);
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void equalDecimal(Column storeColumn, BigDecimal value) {
    try {
      ndbOperation.equalDecimal(storeColumn.getName(), value);
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void equalInt(Column storeColumn, int value) {
    try {
      ndbOperation.equalInt(storeColumn.getName(), value);
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void equalLong(Column storeColumn, long value) {
    try {
      ndbOperation.equalLong(storeColumn.getName(), value);
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void equalString(Column storeColumn, String value) {
    try {
      ndbOperation.equalString(storeColumn.getName(), value);
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void equalTimestamp(Column storeColumn, Timestamp value) {
    try {
      ndbOperation.equalTimestamp(storeColumn.getName(), value);
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void equalDatetime(Column storeColumn, Timestamp value) {
    try {
      ndbOperation.equalDatetime(storeColumn.getName(), value);
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void equalDate(Column storeColumn, Date value) {
    try {
      Timestamp timestamp = new Timestamp(((Date) value).getTime());
      ndbOperation.equalDatetime(storeColumn.getName(), timestamp);
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void equalTime(Column storeColumn, Time value) {
    try {
      Timestamp timestamp = new Timestamp(((Time) value).getTime());
      ndbOperation.equalDatetime(storeColumn.getName(), timestamp);
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void getBlob(Column storeColumn) {
    try {
      ndbOperation.getBlob(storeColumn.getName());
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public Blob getBlobHandle(Column storeColumn) {
    try {
      return new BlobImpl(ndbOperation.getBlobHandle(storeColumn.getName()));
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void getValue(Column storeColumn) {
    try {
      ndbOperation.getValue(storeColumn.getName());
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void postExecuteCallback(Runnable callback) {
    clusterTransaction.postExecuteCallback(callback);
  }

  public ResultData resultData() {
    // execute the transaction to get results
    clusterTransaction.executeNoCommit();
    return new ResultDataImpl(ndbOperation.resultData());
  }

  public void setBoolean(Column storeColumn, Boolean value) {
    throw new UnsupportedOperationException(local.message("ERR_NotImplemented"));
  }

  public void setByte(Column storeColumn, byte value) {
    try {
      ndbOperation.setInt(storeColumn.getName(), (int) value);
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void setBytes(Column storeColumn, byte[] value) {
    try {
      ndbOperation.setBytes(storeColumn.getName(), value);
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void setDate(Column storeColumn, Date value) {
    try {
      ndbOperation.setDate(storeColumn.getName(), value);
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void setDecimal(Column storeColumn, BigDecimal value) {
    try {
      ndbOperation.setDecimal(storeColumn.getName(), value);
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void setDouble(Column storeColumn, Double value) {
    try {
      ndbOperation.setDouble(storeColumn.getName(), value);
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void setFloat(Column storeColumn, Float value) {
    try {
      ndbOperation.setFloat(storeColumn.getName(), value);
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void setInt(Column storeColumn, Integer value) {
    try {
      ndbOperation.setInt(storeColumn.getName(), value);
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void setLong(Column storeColumn, long value) {
    try {
      ndbOperation.setLong(storeColumn.getName(), value);
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void setNull(Column storeColumn) {
    try {
      ndbOperation.setNull(storeColumn.getName());
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void setShort(Column storeColumn, Short value) {
    try {
      ndbOperation.setShort(storeColumn.getName(), value);
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void setString(Column storeColumn, String value) {
    try {
      ndbOperation.setString(storeColumn.getName(), value);
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void setTime(Column storeColumn, Time value) {
    try {
      ndbOperation.setTime(storeColumn.getName(), value);
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void setTimestamp(Column storeColumn, Timestamp value) {
    try {
      ndbOperation.setTimestamp(storeColumn.getName(), value);
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }

  public void setDatetime(Column storeColumn, Timestamp value) {
    try {
      ndbOperation.setDatetime(storeColumn.getName(), value);
    } catch (NdbApiException ndbApiException) {
      throw new ClusterJDatastoreException(local.message("ERR_Datastore"), ndbApiException);
    }
  }
}
示例#6
0
class ResultDataImpl implements ResultData {

  /** My message translator */
  static final I18NHelper local = I18NHelper.getInstance(ResultDataImpl.class);

  /** My logger */
  static final Logger logger = LoggerFactoryService.getFactory().getInstance(ResultDataImpl.class);

  /** Flags for iterating a scan */
  protected final int RESULT_READY = 0;

  protected final int SCAN_FINISHED = 1;
  protected final int CACHE_EMPTY = 2;

  /** The NdbOperation that defines the result */
  private NdbOperation ndbOperation = null;

  /** The NdbRecAttrs that specify the columns to retrieve */
  private NdbRecAttr[] ndbRecAttrs = null;

  /** The flag indicating that there are no more results */
  private boolean nextDone;

  /** The ByteBuffer containing the results, possibly obtained from buffer manager */
  private ByteBuffer byteBuffer = null;

  /** Offsets into the ByteBuffer containing the results */
  private int[] offsets = null;

  /** Lengths of the fields in the ByteBuffer containing the results */
  private int[] lengths = null;

  /** The Columns in this result */
  private final Column[] storeColumns;

  /** The buffer manager */
  private BufferManager bufferManager;

  /** The cluster connection */
  private ClusterConnectionImpl clusterConnection;

  /**
   * Construct the ResultDataImpl based on an NdbOperation, a list of columns to include in the
   * result, and the pre-computed buffer layout for the result.
   *
   * @param ndbOperation the NdbOperation
   * @param storeColumns the columns in the result
   * @param maximumColumnId the largest column id
   * @param bufferSize the size of the buffer needed
   * @param offsets the array of offsets indexed by column id
   * @param lengths the array of lengths indexed by column id
   * @param bufferManager the buffer manager
   * @param allocateNew true to allocate a new (unshared) result buffer
   */
  public ResultDataImpl(
      NdbOperation ndbOperation,
      List<Column> storeColumns,
      int maximumColumnId,
      int bufferSize,
      int[] offsets,
      int[] lengths,
      BufferManager bufferManager,
      boolean allocateNew) {
    this.ndbOperation = ndbOperation;
    this.bufferManager = bufferManager;
    // save the column list
    this.storeColumns = storeColumns.toArray(new Column[storeColumns.size()]);
    this.offsets = offsets;
    this.lengths = lengths;
    if (allocateNew) {
      byteBuffer = ByteBuffer.allocateDirect(bufferSize);
    } else {
      byteBuffer = bufferManager.getResultDataBuffer(bufferSize);
    }
    byteBuffer.order(ByteOrder.nativeOrder());
    // iterate the list of store columns and allocate an NdbRecAttr (via getValue) for each
    ndbRecAttrs = new NdbRecAttr[maximumColumnId + 1];
    for (Column storeColumn : storeColumns) {
      NdbRecAttr ndbRecAttr = null;
      int columnId = storeColumn.getColumnId();
      byteBuffer.position(offsets[columnId]);
      if (lengths[columnId] == 0) {
        // TODO: to help profiling
        ndbRecAttr = ndbOperation.getValue(columnId, null);
        //                ndbRecAttr = getValue(ndbOperation, columnId, null);
      } else {
        ndbRecAttr = ndbOperation.getValue(columnId, byteBuffer);
        //                ndbRecAttr = getValue(ndbOperation, columnId, byteBuffer);
      }
      handleError(ndbRecAttr, ndbOperation);
      ndbRecAttrs[columnId] = ndbRecAttr;
    }
  }

  public boolean next() {
    // NdbOperation has exactly zero or one result. ScanResultDataImpl handles scans...
    NdbErrorConst error = ndbOperation.getNdbError();
    // if the ndbOperation reports an error there is no result
    int errorCode = error.code();
    if (errorCode != 0) {
      setNoResult();
    }
    if (nextDone) {
      return false;
    } else {
      nextDone = true;
      return true;
    }
  }

  public Blob getBlob(int column) {
    return getBlob(storeColumns[column]);
  }

  public Blob getBlob(Column storeColumn) {
    NdbBlob ndbBlob = ndbOperation.getBlobHandle(storeColumn.getColumnId());
    handleError(ndbBlob, ndbOperation);
    return new BlobImpl(ndbBlob, clusterConnection.getByteBufferPool());
  }

  public boolean getBoolean(int column) {
    return getBoolean(storeColumns[column]);
  }

  public boolean getBoolean(Column storeColumn) {
    int index = storeColumn.getColumnId();
    NdbRecAttr ndbRecAttr = ndbRecAttrs[index];
    return Utility.getBoolean(storeColumn, ndbRecAttr);
  }

  public boolean[] getBooleans(int column) {
    return getBooleans(storeColumns[column]);
  }

  public boolean[] getBooleans(Column storeColumn) {
    throw new ClusterJFatalInternalException(local.message("ERR_Not_Implemented"));
  }

  public byte getByte(int column) {
    return getByte(storeColumns[column]);
  }

  public byte getByte(Column storeColumn) {
    int index = storeColumn.getColumnId();
    NdbRecAttr ndbRecAttr = ndbRecAttrs[index];
    return Utility.getByte(storeColumn, ndbRecAttr);
  }

  public short getShort(int column) {
    return getShort(storeColumns[column]);
  }

  public short getShort(Column storeColumn) {
    int index = storeColumn.getColumnId();
    NdbRecAttr ndbRecAttr = ndbRecAttrs[index];
    return Utility.getShort(storeColumn, ndbRecAttr);
  }

  public int getInt(int column) {
    return getInt(storeColumns[column]);
  }

  public int getInt(Column storeColumn) {
    int index = storeColumn.getColumnId();
    NdbRecAttr ndbRecAttr = ndbRecAttrs[index];
    return Utility.getInt(storeColumn, ndbRecAttr);
  }

  public long getLong(int column) {
    return getLong(storeColumns[column]);
  }

  public long getLong(Column storeColumn) {
    int index = storeColumn.getColumnId();
    NdbRecAttr ndbRecAttr = ndbRecAttrs[index];
    return Utility.getLong(storeColumn, ndbRecAttr.int64_value());
  }

  public float getFloat(int column) {
    return getFloat(storeColumns[column]);
  }

  public float getFloat(Column storeColumn) {
    int index = storeColumn.getColumnId();
    float result = ndbRecAttrs[index].float_value();
    return result;
  }

  public double getDouble(int column) {
    return getDouble(storeColumns[column]);
  }

  public double getDouble(Column storeColumn) {
    int index = storeColumn.getColumnId();
    double result = ndbRecAttrs[index].double_value();
    return result;
  }

  public String getString(int column) {
    return getString(storeColumns[column]);
  }

  public String getString(Column storeColumn) {
    int index = storeColumn.getColumnId();
    NdbRecAttr ndbRecAttr = ndbRecAttrs[index];
    if (ndbRecAttr.isNULL() == 1) return null;
    int prefixLength = storeColumn.getPrefixLength();
    int actualLength;
    int offset = offsets[index];
    byteBuffer.limit(byteBuffer.capacity());
    switch (prefixLength) {
      case 0:
        actualLength = lengths[index];
        break;
      case 1:
        actualLength = (byteBuffer.get(offset) + 256) % 256;
        offset += 1;
        break;
      case 2:
        actualLength = (byteBuffer.get(offset) + 256) % 256;
        int length2 = (byteBuffer.get(offset + 1) + 256) % 256;
        actualLength += 256 * length2;
        offset += 2;
        break;
      default:
        throw new ClusterJFatalInternalException(
            local.message("ERR_Invalid_Prefix_Length", prefixLength));
    }

    byteBuffer.position(offset);
    byteBuffer.limit(offset + actualLength);

    String result = Utility.decode(byteBuffer, storeColumn.getCharsetNumber(), bufferManager);
    byteBuffer.clear();
    return result;
  }

  public byte[] getBytes(int column) {
    return getBytes(storeColumns[column]);
  }

  public byte[] getBytes(Column storeColumn) {
    int index = storeColumn.getColumnId();
    NdbRecAttr ndbRecAttr = ndbRecAttrs[index];
    if (ndbRecAttr.isNULL() == 1) return null;
    int prefixLength = storeColumn.getPrefixLength();
    int actualLength = lengths[index];
    int offset = offsets[index];
    switch (prefixLength) {
      case 0:
        break;
      case 1:
        actualLength = (byteBuffer.get(offset) + 256) % 256;
        offset += 1;
        break;
      case 2:
        actualLength = (byteBuffer.get(offset) + 256) % 256;
        int length2 = (byteBuffer.get(offset + 1) + 256) % 256;
        actualLength += 256 * length2;
        offset += 2;
        break;
      default:
        throw new ClusterJFatalInternalException(
            local.message("ERR_Invalid_Prefix_Length", prefixLength));
    }
    byteBuffer.position(offset);
    byte[] result = new byte[actualLength];
    byteBuffer.get(result);
    return result;
  }

  public Object getObject(int column) {
    return getObject(storeColumns[column]);
  }

  public Object getObject(Column storeColumn) {
    throw new ClusterJFatalInternalException(local.message("ERR_Implementation_Should_Not_Occur"));
  }

  public boolean wasNull(Column storeColumn) {
    throw new ClusterJFatalInternalException(local.message("ERR_Implementation_Should_Not_Occur"));
  }

  public Boolean getObjectBoolean(int column) {
    return getObjectBoolean(storeColumns[column]);
  }

  public Boolean getObjectBoolean(Column storeColumn) {
    int index = storeColumn.getColumnId();
    NdbRecAttr ndbRecAttr = ndbRecAttrs[index];
    if (ndbRecAttr.isNULL() == 1) {
      return null;
    } else {
      byte value = ndbRecAttr.int8_value();
      Boolean result = (Boolean.valueOf((value & 0x01) == 0x01));
      return result;
    }
  }

  public Byte getObjectByte(int column) {
    return getObjectByte(storeColumns[column]);
  }

  public Byte getObjectByte(Column storeColumn) {
    int index = storeColumn.getColumnId();
    NdbRecAttr ndbRecAttr = ndbRecAttrs[index];
    return (ndbRecAttr.isNULL() == 1) ? null : Utility.getByte(storeColumn, ndbRecAttr);
  }

  public Short getObjectShort(int column) {
    return getObjectShort(storeColumns[column]);
  }

  public Short getObjectShort(Column storeColumn) {
    int index = storeColumn.getColumnId();
    NdbRecAttr ndbRecAttr = ndbRecAttrs[index];
    return (ndbRecAttr.isNULL() == 1) ? null : Utility.getShort(storeColumn, ndbRecAttr);
  }

  public Integer getObjectInteger(int column) {
    return getObjectInteger(storeColumns[column]);
  }

  public Integer getObjectInteger(Column storeColumn) {
    int index = storeColumn.getColumnId();
    NdbRecAttr ndbRecAttr = ndbRecAttrs[index];
    return (ndbRecAttr.isNULL() == 1) ? null : Utility.getInt(storeColumn, ndbRecAttr);
  }

  public Long getObjectLong(int column) {
    return getObjectLong(storeColumns[column]);
  }

  public Long getObjectLong(Column storeColumn) {
    int index = storeColumn.getColumnId();
    NdbRecAttr ndbRecAttr = ndbRecAttrs[index];
    return (ndbRecAttr.isNULL() == 1)
        ? null
        : Utility.getLong(storeColumn, ndbRecAttr.int64_value());
  }

  public Float getObjectFloat(int column) {
    return getObjectFloat(storeColumns[column]);
  }

  public Float getObjectFloat(Column storeColumn) {
    int index = storeColumn.getColumnId();
    NdbRecAttr ndbRecAttr = ndbRecAttrs[index];
    return (ndbRecAttr.isNULL() == 1) ? null : getFloat(storeColumn);
  }

  public Double getObjectDouble(int column) {
    return getObjectDouble(storeColumns[column]);
  }

  public Double getObjectDouble(Column storeColumn) {
    int index = storeColumn.getColumnId();
    NdbRecAttr ndbRecAttr = ndbRecAttrs[index];
    return (ndbRecAttr.isNULL() == 1) ? null : getDouble(storeColumn);
  }

  public BigInteger getBigInteger(int column) {
    return getBigInteger(storeColumns[column]);
  }

  public BigInteger getBigInteger(Column storeColumn) {
    int index = storeColumn.getColumnId();
    int offset = offsets[index];
    int precision = storeColumn.getPrecision();
    int scale = storeColumn.getScale();
    int length = Utility.getDecimalColumnSpace(precision, scale);
    byteBuffer.position(offset);
    return Utility.getBigInteger(byteBuffer, length, precision, scale);
  }

  public BigDecimal getDecimal(int column) {
    return getDecimal(storeColumns[column]);
  }

  public BigDecimal getDecimal(Column storeColumn) {
    int index = storeColumn.getColumnId();
    int offset = offsets[index];
    int precision = storeColumn.getPrecision();
    int scale = storeColumn.getScale();
    int length = Utility.getDecimalColumnSpace(precision, scale);
    byteBuffer.position(offset);
    return Utility.getDecimal(byteBuffer, length, precision, scale);
  }

  private void handleError(Object object, NdbOperation ndbOperation) {
    if (object == null) {
      Utility.throwError(object, ndbOperation.getNdbError());
    }
  }

  public void setNoResult() {
    nextDone = true;
  }

  public Column[] getColumns() {
    return storeColumns;
  }
}
class IndexScanOperationImpl extends ScanOperationImpl implements IndexScanOperation {

  /** My message translator */
  static final I18NHelper local = I18NHelper.getInstance(IndexScanOperationImpl.class);

  /** My logger */
  static final Logger logger =
      LoggerFactoryService.getFactory().getInstance(IndexScanOperationImpl.class);

  private NdbIndexScanOperation ndbIndexScanOperation;

  public IndexScanOperationImpl(
      Table storeTable,
      NdbIndexScanOperation ndbIndexScanOperation,
      ClusterTransactionImpl transaction) {
    super(storeTable, ndbIndexScanOperation, transaction);
    this.ndbIndexScanOperation = ndbIndexScanOperation;
  }

  public void setBoundBigInteger(Column storeColumn, BoundType type, BigInteger value) {
    int returnCode =
        ndbIndexScanOperation.setBound(
            storeColumn.getName(),
            convertBoundType(type),
            Utility.convertValue(storeColumn, value));
    handleError(returnCode, ndbIndexScanOperation);
  }

  public void setBoundByte(Column storeColumn, BoundType type, byte value) {
    int returnCode =
        ndbIndexScanOperation.setBound(
            storeColumn.getName(),
            convertBoundType(type),
            Utility.convertValue(storeColumn, value));
    handleError(returnCode, ndbIndexScanOperation);
  }

  public void setBoundBytes(Column storeColumn, BoundType type, byte[] value) {
    int returnCode =
        ndbIndexScanOperation.setBound(
            storeColumn.getName(),
            convertBoundType(type),
            Utility.convertValue(storeColumn, value));
    handleError(returnCode, ndbIndexScanOperation);
  }

  public void setBoundDecimal(Column storeColumn, BoundType type, BigDecimal value) {
    int returnCode =
        ndbIndexScanOperation.setBound(
            storeColumn.getName(),
            convertBoundType(type),
            Utility.convertValue(storeColumn, value));
    handleError(returnCode, ndbIndexScanOperation);
  }

  public void setBoundDouble(Column storeColumn, BoundType type, Double value) {
    int returnCode =
        ndbIndexScanOperation.setBound(
            storeColumn.getName(),
            convertBoundType(type),
            Utility.convertValue(storeColumn, value));
    handleError(returnCode, ndbIndexScanOperation);
  }

  public void setBoundFloat(Column storeColumn, BoundType type, Float value) {
    int returnCode =
        ndbIndexScanOperation.setBound(
            storeColumn.getName(),
            convertBoundType(type),
            Utility.convertValue(storeColumn, value));
    handleError(returnCode, ndbIndexScanOperation);
  }

  public void setBoundShort(Column storeColumn, BoundType type, short value) {
    int returnCode =
        ndbIndexScanOperation.setBound(
            storeColumn.getName(),
            convertBoundType(type),
            Utility.convertValue(storeColumn, value));
    handleError(returnCode, ndbIndexScanOperation);
  }

  public void setBoundInt(Column storeColumn, BoundType type, Integer value) {
    int returnCode =
        ndbIndexScanOperation.setBound(
            storeColumn.getName(),
            convertBoundType(type),
            Utility.convertValue(storeColumn, value));
    handleError(returnCode, ndbIndexScanOperation);
  }

  public void setBoundLong(Column storeColumn, BoundType type, long value) {
    int returnCode =
        ndbIndexScanOperation.setBound(
            storeColumn.getName(),
            convertBoundType(type),
            Utility.convertValue(storeColumn, value));
    handleError(returnCode, ndbIndexScanOperation);
  }

  public void setBoundString(Column storeColumn, BoundType type, String value) {
    int returnCode =
        ndbIndexScanOperation.setBound(
            storeColumn.getName(),
            convertBoundType(type),
            Utility.convertValue(storeColumn, value));
    handleError(returnCode, ndbIndexScanOperation);
  }

  private int convertBoundType(BoundType type) {
    switch (type) {
      case BoundEQ:
        return NdbIndexScanOperation.BoundType.BoundEQ;
      case BoundGE:
        return NdbIndexScanOperation.BoundType.BoundGE;
      case BoundGT:
        return NdbIndexScanOperation.BoundType.BoundGT;
      case BoundLE:
        return NdbIndexScanOperation.BoundType.BoundLE;
      case BoundLT:
        return NdbIndexScanOperation.BoundType.BoundLT;
      default:
        throw new ClusterJFatalInternalException(
            local.message("ERR_Implementation_Should_Not_Occur"));
    }
  }

  public void endBound(int rangeNumber) {
    if (logger.isDetailEnabled())
      logger.detail("IndexScanOperationImpl.endBound(" + rangeNumber + ")");
    int returnCode = ndbIndexScanOperation.end_of_bound(rangeNumber);
    handleError(returnCode, ndbIndexScanOperation);
  }
}