private static boolean mergeEqual(SQLExpr a, SQLExpr b) {
    if (!(a instanceof SQLBinaryOpExpr)) {
      return false;
    }
    if (!(b instanceof SQLBinaryOpExpr)) {
      return false;
    }

    SQLBinaryOpExpr binaryA = (SQLBinaryOpExpr) a;
    SQLBinaryOpExpr binaryB = (SQLBinaryOpExpr) b;

    if (binaryA.getOperator() != SQLBinaryOperator.Equality) {
      return false;
    }

    if (binaryB.getOperator() != SQLBinaryOperator.Equality) {
      return false;
    }

    if (!(binaryA.getRight() instanceof SQLLiteralExpr
        || binaryA.getRight() instanceof SQLVariantRefExpr)) {
      return false;
    }

    if (!(binaryB.getRight() instanceof SQLLiteralExpr
        || binaryB.getRight() instanceof SQLVariantRefExpr)) {
      return false;
    }

    return binaryA.getLeft().toString().equals(binaryB.getLeft().toString());
  }
Пример #2
0
  /**
   * 目前只支持这个操作 单条insert(非批量)
   *
   * @param schema
   * @param rrs
   * @param partitionColumn
   * @param tableName
   * @param insertStmt
   * @throws SQLNonTransientException
   */
  private void parserSingleInsert(
      SchemaConfig schema,
      RouteResultset rrs,
      String partitionColumn,
      String tableName,
      MySqlInsertStatement insertStmt)
      throws SQLNonTransientException {
    boolean isFound = false;
    // 将分片的键 作为 路由计算单元
    for (int i = 0; i < insertStmt.getColumns().size(); i++) {
      if (partitionColumn.equalsIgnoreCase(
          StringUtil.removeBackquote(insertStmt.getColumns().get(i).toString()))) {
        // 找到分片字段
        isFound = true;
        String column = StringUtil.removeBackquote(insertStmt.getColumns().get(i).toString());

        String value =
            StringUtil.removeBackquote(insertStmt.getValues().getValues().get(i).toString());

        RouteCalculateUnit routeCalculateUnit = new RouteCalculateUnit();
        routeCalculateUnit.addShardingExpr(tableName, column, value);

        ctx.addRouteCalculateUnit(routeCalculateUnit);

        // mycat是单分片键,找到了就返回
        break;
      }
    }
    if (!isFound) { // 分片表的
      String msg =
          "bad insert sql (sharding column:" + partitionColumn + " not provided," + insertStmt;
      logger.warn(msg);
      throw new SQLNonTransientException(msg);
    }

    // 这种语句不应该支持
    //  insert into .... on duplicateKey
    //  such as :   INSERT INTO TABLEName (a,b,c) VALUES (1,2,3) ON DUPLICATE KEY UPDATE
    // b=VALUES(b);
    //              INSERT INTO TABLEName (a,b,c) VALUES (1,2,3) ON DUPLICATE KEY UPDATE c=c+1;
    //              du plicate 处理!!
    if (insertStmt.getDuplicateKeyUpdate() != null) {
      List<SQLExpr> updateList = insertStmt.getDuplicateKeyUpdate();
      for (SQLExpr expr : updateList) {
        SQLBinaryOpExpr opExpr = (SQLBinaryOpExpr) expr;
        String column = StringUtil.removeBackquote(opExpr.getLeft().toString().toUpperCase());
        if (column.equals(partitionColumn)) {
          String msg = "partion key can't be updated: " + tableName + " -> " + partitionColumn;
          logger.warn(msg);
          throw new SQLNonTransientException(msg);
        }
      }
    }
  }
Пример #3
0
  public void test_binary() throws Exception {
    SQLExprParser exprParser = new SQLExprParser("AGE > 5");
    SQLBinaryOpExpr binaryOpExpr = (SQLBinaryOpExpr) exprParser.expr();

    Assert.assertEquals(SQLBinaryOperator.GreaterThan, binaryOpExpr.getOperator());

    SQLIdentifierExpr left = (SQLIdentifierExpr) binaryOpExpr.getLeft();
    SQLIntegerExpr right = (SQLIntegerExpr) binaryOpExpr.getRight();

    Assert.assertEquals("AGE", left.getName());
    Assert.assertEquals(5, right.getNumber().intValue());
  }
  private static boolean isLiteralExpr(SQLExpr expr) {
    if (expr instanceof SQLLiteralExpr) {
      return true;
    }

    if (expr instanceof SQLBinaryOpExpr) {
      SQLBinaryOpExpr binary = (SQLBinaryOpExpr) expr;
      return isLiteralExpr(binary.getLeft()) && isLiteralExpr(binary.getRight());
    }

    return false;
  }
  public static boolean visit(ParameterizedVisitor v, SQLNullExpr x) {
    SQLObject parent = x.getParent();
    if (parent instanceof SQLBinaryOpExpr) {
      SQLBinaryOpExpr binaryOpExpr = (SQLBinaryOpExpr) parent;
      if (binaryOpExpr.getOperator() == SQLBinaryOperator.IsNot
          || binaryOpExpr.getOperator() == SQLBinaryOperator.Is) {
        v.print("NULL");
        return false;
      }
    }

    v.print('?');
    v.incrementReplaceCunt();
    return false;
  }
  public static void exportParameter(final List<Object> parameters, SQLBinaryOpExpr x) {
    if (x.getLeft() instanceof SQLLiteralExpr
        && x.getRight() instanceof SQLLiteralExpr
        && x.getOperator().isRelational()) {
      return;
    }

    {
      SQLExpr leftResult = ExportParameterVisitorUtils.exportParameter(parameters, x.getLeft());
      if (leftResult != x.getLeft()) {
        x.setLeft(leftResult);
      }
    }

    {
      SQLExpr rightResult = exportParameter(parameters, x.getRight());
      if (rightResult != x.getRight()) {
        x.setRight(rightResult);
      }
    }
  }
Пример #7
0
  private void parseTableSourceQueryTableExpr(OracleSelectTableReference tableReference) {
    tableReference.setExpr(this.createExprParser().expr());

    if (identifierEquals("SAMPLE")) {
      lexer.nextToken();

      SampleClause sample = new SampleClause();

      if (identifierEquals("BLOCK")) {
        sample.setBlock(true);
      }

      accept(Token.LPAREN);

      if (lexer.token() != Token.LITERAL_INT) {
        throw new ParserException("syntax error : " + lexer.token());
      }
      sample.setPercent(lexer.integerValue().intValue());
      lexer.nextToken();

      accept(Token.RPAREN);

      if (identifierEquals("SEED")) {
        lexer.nextToken();
        accept(Token.LPAREN);
        sample.setSeedValue(expr());
        accept(Token.RPAREN);
      }

      tableReference.setSampleClause(sample);
    }

    if (identifierEquals("PARTITION")) {
      lexer.nextToken();
      PartitionExtensionClause partition = new PartitionExtensionClause();

      if (lexer.token() == Token.LPAREN) {
        lexer.nextToken();
        partition.setPartition(createExprParser().name());
        accept(Token.RPAREN);
      } else {
        accept(Token.FOR);
        accept(Token.LPAREN);
        createExprParser().names(partition.getFor());
        accept(Token.RPAREN);
      }

      tableReference.setPartition(partition);
    }

    if (identifierEquals("SUBPARTITION")) {
      lexer.nextToken();
      PartitionExtensionClause partition = new PartitionExtensionClause();
      partition.setSubPartition(true);

      if (lexer.token() == Token.LPAREN) {
        lexer.nextToken();
        partition.setPartition(createExprParser().name());
        accept(Token.RPAREN);
      } else {
        accept(Token.FOR);
        accept(Token.LPAREN);
        createExprParser().names(partition.getFor());
        accept(Token.RPAREN);
      }

      tableReference.setPartition(partition);
    }

    if (lexer.token() == Token.AS) {
      lexer.nextToken();

      if (identifierEquals("OF")) {
        lexer.nextToken();

        AsOfFlashbackQueryClause clause = new AsOfFlashbackQueryClause();
        if (identifierEquals("SCN")) {
          clause.setType(AsOfFlashbackQueryClause.Type.SCN);
          lexer.nextToken();
        } else {
          accept(Token.TIMESTAMP);
          clause.setType(AsOfFlashbackQueryClause.Type.TIMESTAMP);
        }
        clause.setExpr(createExprParser().primary());

        tableReference.setFlashback(clause);
      } else {
        throw new SQLParseException("TODO");
      }
    } else if (identifierEquals("VERSIONS")) {
      lexer.nextToken();

      if (lexer.token() == Token.BETWEEN) {
        lexer.nextToken();

        VersionsFlashbackQueryClause clause = new VersionsFlashbackQueryClause();
        if (identifierEquals("SCN")) {
          clause.setType(AsOfFlashbackQueryClause.Type.SCN);
          lexer.nextToken();
        } else {
          accept(Token.TIMESTAMP);
          clause.setType(AsOfFlashbackQueryClause.Type.TIMESTAMP);
        }

        SQLBinaryOpExpr binaryExpr = (SQLBinaryOpExpr) createExprParser().expr();
        if (binaryExpr.getOperator() != SQLBinaryOperator.BooleanAnd) {
          throw new SQLParseException("syntax error : " + binaryExpr.getOperator());
        }

        clause.setBegin(binaryExpr.getLeft());
        clause.setEnd(binaryExpr.getRight());

        tableReference.setFlashback(clause);
      } else {
        throw new SQLParseException("TODO");
      }
    }
  }
  public static SQLBinaryOpExpr merge(ParameterizedVisitor v, SQLBinaryOpExpr x) {
    SQLExpr left = x.getLeft();
    SQLExpr right = x.getRight();
    SQLObject parent = x.getParent();

    if (left instanceof SQLLiteralExpr && right instanceof SQLLiteralExpr) {
      if (x.getOperator() == SQLBinaryOperator.Equality //
          || x.getOperator() == SQLBinaryOperator.NotEqual) {
        if ((left instanceof SQLIntegerExpr) && (right instanceof SQLIntegerExpr)) {
          if (((SQLIntegerExpr) left).getNumber().intValue() < 100) {
            left.putAttribute(ATTR_PARAMS_SKIP, true);
          }
          if (((SQLIntegerExpr) right).getNumber().intValue() < 100) {
            right.putAttribute(ATTR_PARAMS_SKIP, true);
          }
        } else {
          left.putAttribute(ATTR_PARAMS_SKIP, true);
          right.putAttribute(ATTR_PARAMS_SKIP, true);
        }
      }
      return x;
    }

    for (; ; ) {
      if (x.getRight() instanceof SQLBinaryOpExpr) {
        if (x.getLeft() instanceof SQLBinaryOpExpr) {
          SQLBinaryOpExpr leftBinaryExpr = (SQLBinaryOpExpr) x.getLeft();
          if (leftBinaryExpr.getRight().equals(x.getRight())) {
            x = leftBinaryExpr;
            v.incrementReplaceCunt();
            continue;
          }
        }
        SQLExpr mergedRight = merge(v, (SQLBinaryOpExpr) x.getRight());
        if (mergedRight != x.getRight()) {
          x = new SQLBinaryOpExpr(x.getLeft(), x.getOperator(), mergedRight);
          v.incrementReplaceCunt();
        }
        x.setParent(parent);
      }

      break;
    }

    if (x.getLeft() instanceof SQLBinaryOpExpr) {
      SQLExpr mergedLeft = merge(v, (SQLBinaryOpExpr) x.getLeft());
      if (mergedLeft != x.getLeft()) {
        x = new SQLBinaryOpExpr(mergedLeft, x.getOperator(), x.getRight());
        v.incrementReplaceCunt();
      }
      x.setParent(parent);
    }

    // ID = ? OR ID = ? => ID = ?
    if (x.getOperator() == SQLBinaryOperator.BooleanOr) {
      if ((left instanceof SQLBinaryOpExpr) && (right instanceof SQLBinaryOpExpr)) {
        SQLBinaryOpExpr leftBinary = (SQLBinaryOpExpr) x.getLeft();
        SQLBinaryOpExpr rightBinary = (SQLBinaryOpExpr) x.getRight();

        if (mergeEqual(leftBinary, rightBinary)) {
          v.incrementReplaceCunt();
          return leftBinary;
        }

        if (isLiteralExpr(leftBinary.getLeft()) //
            && leftBinary.getOperator() == SQLBinaryOperator.BooleanOr) {
          if (mergeEqual(leftBinary.getRight(), right)) {
            v.incrementReplaceCunt();
            return leftBinary;
          }
        }
      }
    }

    return x;
  }
Пример #9
0
  public boolean visit(SQLBinaryOpExpr x) {
    x.getLeft().setParent(x);
    x.getRight().setParent(x);

    switch (x.getOperator()) {
      case Equality:
      case NotEqual:
      case GreaterThan:
      case GreaterThanOrEqual:
      case LessThan:
      case LessThanOrEqual:
      case LessThanOrEqualOrGreaterThan:
      case Like:
      case NotLike:
      case Is:
      case IsNot:
        handleCondition(x.getLeft(), x.getOperator().name, x.getRight());
        handleCondition(x.getRight(), x.getOperator().name, x.getLeft());

        handleRelationship(x.getLeft(), x.getOperator().name, x.getRight());
        break;
      default:
        break;
    }
    return true;
  }
Пример #10
0
  public static boolean visit(SQLEvalVisitor visitor, SQLBinaryOpExpr x) {
    SQLExpr left = x.getLeft();
    SQLExpr right = x.getRight();

    left.accept(visitor);
    if (!left.getAttributes().containsKey(EVAL_VALUE)) {
      return false;
    }

    right.accept(visitor);
    if (!right.getAttributes().containsKey(EVAL_VALUE)) {
      return false;
    }

    Object value = null;
    switch (x.getOperator()) {
      case Add:
        value = add(left.getAttribute(EVAL_VALUE), right.getAttributes().get(EVAL_VALUE));
        x.putAttribute(EVAL_VALUE, value);
        break;
      case Subtract:
        value = sub(left.getAttribute(EVAL_VALUE), right.getAttributes().get(EVAL_VALUE));
        x.putAttribute(EVAL_VALUE, value);
        break;
      case Multiply:
        value = multi(left.getAttribute(EVAL_VALUE), right.getAttributes().get(EVAL_VALUE));
        x.putAttribute(EVAL_VALUE, value);
        break;
      case Divide:
        value = div(left.getAttribute(EVAL_VALUE), right.getAttributes().get(EVAL_VALUE));
        x.putAttribute(EVAL_VALUE, value);
        break;
      case GreaterThan:
        value = gt(left.getAttribute(EVAL_VALUE), right.getAttributes().get(EVAL_VALUE));
        x.putAttribute(EVAL_VALUE, value);
        break;
      case GreaterThanOrEqual:
        value = gteq(left.getAttribute(EVAL_VALUE), right.getAttributes().get(EVAL_VALUE));
        x.putAttribute(EVAL_VALUE, value);
        break;
      case LessThan:
        value = lt(left.getAttribute(EVAL_VALUE), right.getAttributes().get(EVAL_VALUE));
        x.putAttribute(EVAL_VALUE, value);
        break;
      case LessThanOrEqual:
        value = lteq(left.getAttribute(EVAL_VALUE), right.getAttributes().get(EVAL_VALUE));
        x.putAttribute(EVAL_VALUE, value);
        break;
      case Is:
        value = eq(left.getAttribute(EVAL_VALUE), right.getAttributes().get(EVAL_VALUE));
        x.putAttribute(EVAL_VALUE, value);
        break;
      case IsNot:
        value = !eq(left.getAttribute(EVAL_VALUE), right.getAttributes().get(EVAL_VALUE));
        x.putAttribute(EVAL_VALUE, value);
        break;
      case RegExp:
      case RLike:
        {
          String pattern = _string(right.getAttributes().get(EVAL_VALUE));
          String input = _string(left.getAttributes().get(EVAL_VALUE));
          boolean matchResult = Pattern.matches(pattern, input);
          x.putAttribute(EVAL_VALUE, matchResult);
        }
        break;
      case NotRegExp:
      case NotRLike:
        {
          String pattern = _string(right.getAttributes().get(EVAL_VALUE));
          String input = _string(left.getAttributes().get(EVAL_VALUE));
          boolean matchResult = !Pattern.matches(pattern, input);
          x.putAttribute(EVAL_VALUE, matchResult);
        }
        break;
      default:
        break;
    }

    return false;
  }
Пример #11
0
  public void parseStatementList(List<SQLStatement> statementList, int max) {
    for (; ; ) {
      if (max != -1) {
        if (statementList.size() >= max) {
          return;
        }
      }

      if (lexer.token() == Token.EOF) {
        return;
      }
      if (lexer.token() == Token.END) {
        return;
      }
      if (lexer.token() == Token.ELSE) {
        return;
      }

      if (lexer.token() == (Token.SEMI)) {
        lexer.nextToken();
        continue;
      }

      if (lexer.token() == (Token.SELECT)) {
        SQLSelectStatement stmt =
            new SQLSelectStatement(
                new OracleSelectParser(this.exprParser).select(), JdbcConstants.ORACLE);
        statementList.add(stmt);
        continue;
      }

      if (lexer.token() == (Token.UPDATE)) {
        statementList.add(parseUpdateStatement());
        continue;
      }

      if (lexer.token() == (Token.CREATE)) {
        statementList.add(parseCreate());
        continue;
      }

      if (lexer.token() == Token.INSERT) {
        statementList.add(parseInsert());
        continue;
      }

      if (lexer.token() == (Token.DELETE)) {
        statementList.add(parseDeleteStatement());
        continue;
      }

      if (lexer.token() == (Token.SLASH)) {
        lexer.nextToken();
        statementList.add(new OraclePLSQLCommitStatement());
        continue;
      }

      if (lexer.token() == Token.ALTER) {
        statementList.add(parserAlter());
        continue;
      }

      if (lexer.token() == Token.WITH) {
        statementList.add(new SQLSelectStatement(new OracleSelectParser(this.exprParser).select()));
        continue;
      }

      if (lexer.token() == Token.LBRACE || identifierEquals("CALL")) {
        statementList.add(this.parseCall());
        continue;
      }

      if (lexer.token() == Token.MERGE) {
        statementList.add(this.parseMerge());
        continue;
      }

      if (lexer.token() == Token.BEGIN) {
        statementList.add(this.parseBlock());
        continue;
      }

      if (lexer.token() == Token.DECLARE) {
        statementList.add(this.parseBlock());
        continue;
      }

      if (lexer.token() == Token.LOCK) {
        statementList.add(this.parseLock());
        continue;
      }

      if (lexer.token() == Token.TRUNCATE) {
        statementList.add(this.parseTruncate());
        continue;
      }

      if (lexer.token() == Token.VARIANT) {
        SQLExpr variant = this.exprParser.primary();
        if (variant instanceof SQLBinaryOpExpr) {
          SQLBinaryOpExpr binaryOpExpr = (SQLBinaryOpExpr) variant;
          if (binaryOpExpr.getOperator() == SQLBinaryOperator.Assignment) {
            SQLSetStatement stmt =
                new SQLSetStatement(binaryOpExpr.getLeft(), binaryOpExpr.getRight(), getDbType());
            statementList.add(stmt);
            continue;
          }
        }
        accept(Token.COLONEQ);
        SQLExpr value = this.exprParser.expr();

        SQLSetStatement stmt = new SQLSetStatement(variant, value, getDbType());
        statementList.add(stmt);
        continue;
      }

      if (lexer.token() == Token.EXCEPTION) {
        statementList.add(this.parseException());
        continue;
      }

      if (identifierEquals("EXIT")) {
        lexer.nextToken();
        OracleExitStatement stmt = new OracleExitStatement();
        if (lexer.token() == Token.WHEN) {
          lexer.nextToken();
          stmt.setWhen(this.exprParser.expr());
        }
        statementList.add(stmt);
        continue;
      }

      if (lexer.token() == Token.FETCH || identifierEquals("FETCH")) {
        SQLStatement stmt = parseFetch();
        statementList.add(stmt);
        continue;
      }

      if (identifierEquals("ROLLBACK")) {
        SQLRollbackStatement stmt = parseRollback();

        statementList.add(stmt);
        continue;
      }

      if (lexer.token() == Token.EXPLAIN) {
        statementList.add(this.parseExplain());
        continue;
      }

      if (lexer.token() == Token.IDENTIFIER) {
        SQLExpr expr = exprParser.expr();
        OracleExprStatement stmt = new OracleExprStatement(expr);
        statementList.add(stmt);
        continue;
      }

      if (lexer.token() == Token.LPAREN) {
        char ch = lexer.current();
        int bp = lexer.bp();
        lexer.nextToken();

        if (lexer.token() == Token.SELECT) {
          lexer.reset(bp, ch, Token.LPAREN);
          statementList.add(this.parseSelect());
          continue;
        } else {
          throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
        }
      }

      if (lexer.token() == Token.SET) {
        statementList.add(this.parseSet());
        continue;
      }

      if (lexer.token() == Token.GRANT) {
        statementList.add(this.parseGrant());
        continue;
      }

      if (lexer.token() == Token.REVOKE) {
        statementList.add(this.parseRevoke());
        continue;
      }

      if (lexer.token() == Token.COMMENT) {
        statementList.add(this.parseComment());
        continue;
      }
      if (lexer.token() == Token.FOR) {
        statementList.add(this.parseFor());
        continue;
      }
      if (lexer.token() == Token.LOOP) {
        statementList.add(this.parseLoop());
        continue;
      }
      if (lexer.token() == Token.IF) {
        statementList.add(this.parseIf());
        continue;
      }

      if (lexer.token() == Token.GOTO) {
        lexer.nextToken();
        SQLName label = this.exprParser.name();
        OracleGotoStatement stmt = new OracleGotoStatement(label);
        statementList.add(stmt);
        continue;
      }

      if (lexer.token() == Token.COMMIT) {
        lexer.nextToken();

        if (identifierEquals("WORK")) {
          lexer.nextToken();
        }
        OracleCommitStatement stmt = new OracleCommitStatement();

        if (identifierEquals("WRITE")) {
          stmt.setWrite(true);
          lexer.nextToken();

          for (; ; ) {
            if (lexer.token() == Token.WAIT) {
              lexer.nextToken();
              stmt.setWait(Boolean.TRUE);
              continue;
            } else if (lexer.token() == Token.NOWAIT) {
              lexer.nextToken();
              stmt.setWait(Boolean.FALSE);
              continue;
            } else if (lexer.token() == Token.IMMEDIATE) {
              lexer.nextToken();
              stmt.setImmediate(Boolean.TRUE);
              continue;
            } else if (identifierEquals("BATCH")) {
              lexer.nextToken();
              stmt.setImmediate(Boolean.FALSE);
              continue;
            }

            break;
          }
        }

        statementList.add(stmt);
        continue;
      }

      if (lexer.token() == Token.SAVEPOINT) {
        lexer.nextToken();

        OracleSavePointStatement stmt = new OracleSavePointStatement();

        if (lexer.token() == Token.TO) {
          lexer.nextToken();
          stmt.setTo(this.exprParser.name());
        }

        statementList.add(stmt);
        continue;
      }

      if (lexer.token() == Token.LTLT) {
        lexer.nextToken();
        SQLName label = this.exprParser.name();
        OracleLabelStatement stmt = new OracleLabelStatement(label);
        accept(Token.GTGT);
        statementList.add(stmt);
        continue;
      }

      if (lexer.token() == Token.DROP) {
        lexer.nextToken();

        if (lexer.token() == Token.TABLE) {
          SQLDropTableStatement stmt = parseDropTable(false);
          statementList.add(stmt);
          continue;
        }

        boolean isPublic = false;
        if (identifierEquals("PUBLIC")) {
          lexer.nextToken();
          isPublic = true;
        }

        if (lexer.token() == Token.DATABASE) {
          lexer.nextToken();

          if (identifierEquals("LINK")) {
            lexer.nextToken();

            OracleDropDbLinkStatement stmt = new OracleDropDbLinkStatement();
            if (isPublic) {
              stmt.setPublic(isPublic);
            }

            stmt.setName(this.exprParser.name());

            statementList.add(stmt);
            continue;
          }
        }

        if (lexer.token() == Token.INDEX) {
          SQLStatement stmt = parseDropIndex();
          statementList.add(stmt);
          continue;
        }

        if (lexer.token() == Token.VIEW) {
          SQLStatement stmt = parseDropView(false);
          statementList.add(stmt);
          continue;
        }

        if (lexer.token() == Token.SEQUENCE) {
          SQLDropSequenceStatement stmt = parseDropSequece(false);
          statementList.add(stmt);
          continue;
        }

        if (lexer.token() == Token.TRIGGER) {
          SQLDropTriggerStatement stmt = parseDropTrigger(false);
          statementList.add(stmt);
          continue;
        }

        if (lexer.token() == Token.USER) {
          SQLDropUserStatement stmt = parseDropUser();
          statementList.add(stmt);
          continue;
        }

        throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
      }

      if (lexer.token() == Token.NULL) {
        lexer.nextToken();
        OracleExprStatement stmt = new OracleExprStatement(new SQLNullExpr());
        statementList.add(stmt);
        continue;
      }

      if (lexer.token() == Token.OPEN) {
        SQLStatement stmt = this.parseOpen();
        statementList.add(stmt);
        continue;
      }

      throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
    }
  }