Java tutorial
/** * Copyright 2010 The Apache Software Foundation * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.wasp.plan.parser.druid; import com.alibaba.druid.sql.SQLUtils; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.expr.SQLCharExpr; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr; import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; import com.alibaba.druid.sql.ast.expr.SQLNumberExpr; import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; import com.alibaba.druid.sql.ast.statement.SQLDropIndexStatement; import com.alibaba.druid.sql.ast.statement.SQLDropTableStatement; import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource; import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; import com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource; import com.alibaba.druid.sql.ast.statement.SQLTableSource; import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterTableStatement; import com.alibaba.druid.sql.parser.SQLStatementParser; import com.alibaba.wasp.DataType; import com.alibaba.wasp.meta.Field; import com.alibaba.wasp.plan.parser.ParseContext; import com.alibaba.wasp.plan.parser.Parser; import com.alibaba.wasp.plan.parser.UnsupportedException; import com.alibaba.wasp.plan.parser.druid.dialect.WaspSqlCreateIndexStatement; import com.alibaba.wasp.plan.parser.druid.dialect.WaspSqlCreateTableStatement; import com.alibaba.wasp.plan.parser.druid.dialect.WaspSqlDescribeStatement; import com.alibaba.wasp.plan.parser.druid.dialect.WaspSqlParserUtils; import com.alibaba.wasp.plan.parser.druid.dialect.WaspSqlShowCreateTableStatement; import com.alibaba.wasp.plan.parser.druid.dialect.WaspSqlShowIndexesStatement; import com.alibaba.wasp.plan.parser.druid.dialect.WaspSqlShowTablesStatement; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configurable; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.List; /** * Use Druid (https://github.com/AlibabaTech/druid) to parse the sql and * generate QueryPlan * */ public abstract class DruidParser extends Parser implements Configurable { private static final Log LOG = LogFactory.getLog(DruidParser.class); protected Configuration configuration; public DruidParser() { super(); } /** * @see org.apache.hadoop.conf.Configurable#getConf() */ @Override public Configuration getConf() { return configuration; } /** * @see org.apache.hadoop.conf.Configurable#setConf(org.apache.hadoop.conf.Configuration) */ @Override public void setConf(Configuration configuration) { this.configuration = configuration; } /** * Parse SQL into SQLStatement, assume just one SQLStatement, so return one * SQLStatement result. * */ @Override public void parseSqlToStatement(ParseContext context) throws IOException { LOG.debug("Parsing SQL " + context.getSql()); SQLStatementParser parser = WaspSqlParserUtils.createSQLStatementParser(context.getSql(), WaspSqlParserUtils.WASP); // JdbcUtils.MYSQL // JdbcUtils.HBASE // WaspSqlParserUtils.WASP List<SQLStatement> stmtList = parser.parseStatementList(); if (stmtList == null) { throw new UnsupportedException("Non SQLStatement"); } if (stmtList.size() > 1) { throw new UnsupportedException( "Multi SQLStatement Unsupported" + ", there is " + stmtList.size() + " SQLStatement"); } // StmtList could be a list,but we only use an element. context.setStmt(stmtList.get(0)); } /** * Parse The FROM clause. The FROM clause which indicates the table(s) from * which data is to be retrieved. */ public String parseFromClause(SQLTableSource from) throws UnsupportedException { // only support SQLExprTableSource now if (from instanceof SQLExprTableSource) { SQLExprTableSource fromExpr = (SQLExprTableSource) from; SQLExpr expr = fromExpr.getExpr(); // SQLIdentifierExpr return parseName(expr); } else if (from instanceof SQLJoinTableSource) { // TODO impl join clause throw new UnsupportedException("Join clause Unsupported"); } else if (from instanceof SQLSubqueryTableSource) { throw new UnsupportedException("Subquery clause Unsupported"); } else { throw new UnsupportedException("Unsupported"); } } public static String parseName(SQLExpr name) throws UnsupportedException { if (name instanceof SQLIdentifierExpr) { SQLIdentifierExpr id = (SQLIdentifierExpr) name; return id.getName(); } else { throw new UnsupportedException("Non SQLIdentifierExpr Unsupported, this is " + name); } } /** * Fetch Field instance from columns used by column name. * * @param name * @param columns * @return */ public static Field get(String name, List<Field> columns) { for (Field field : columns) { if (field.getName().equals(name)) { return field; } } return null; } public static String parseString(SQLExpr value) throws UnsupportedException { if (value instanceof SQLCharExpr) { SQLCharExpr charexpr = (SQLCharExpr) value; return ((SQLCharExpr) charexpr).getText(); } else { throw new UnsupportedException("Non SQLIdentifierExpr Unsupported, this is " + value); } } /** * Parse one column, get the column's name(String type) */ public String parseColumn(SQLExpr column) throws UnsupportedException { return parseName(column); } /** * Parse the SQLExpr value into byte value * */ public static byte[] convert(Field column, SQLExpr expr) throws UnsupportedException { if (expr instanceof SQLIntegerExpr) {// int, long Number number = ((SQLIntegerExpr) expr).getNumber(); if (column != null) { return convert(column.getType(), number); } else { return convert(null, number); } } else if (expr instanceof SQLCharExpr) {// String String value = ((SQLCharExpr) expr).getText(); if (column != null && column.getType() == DataType.DATETIME) { long timestamp = -1; try { SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); timestamp = formatter.parse(value).getTime(); } catch (ParseException e) { //TODO what should i do with the exception? e.printStackTrace(); } return Bytes.toBytes(timestamp); } else { return Bytes.toBytes(value); } } else if (expr instanceof SQLNumberExpr) {// float, double Number number = ((SQLNumberExpr) expr).getNumber(); if (column != null) { return convert(column.getType(), number); } else { return convert(null, number); } } else if (expr instanceof SQLMethodInvokeExpr) { // Function return convert(column.getType(), (SQLMethodInvokeExpr) expr); } return null; } public static int convertToInt(SQLExpr expr) throws UnsupportedException { if (expr instanceof SQLIntegerExpr) {// int, long Number number = ((SQLIntegerExpr) expr).getNumber(); return number.intValue(); } else { throw new UnsupportedException("Input is not int or long, it is " + expr); } } public void checkType(Field field, SQLExpr value) throws UnsupportedException { DataType type = field.getType(); if (type == DataType.INT32 || type == DataType.INT64) { if (value instanceof SQLIntegerExpr) { return; // OK } } else if (type == DataType.DOUBLE || type == DataType.FLOAT) { if (value instanceof SQLNumberExpr || value instanceof SQLIntegerExpr) { return; // OK } } else if (type == DataType.STRING) { if (value instanceof SQLCharExpr) { return; // OK } } else if (type == DataType.DATETIME) { if (value instanceof SQLCharExpr) { return; // OK } else if (value instanceof SQLMethodInvokeExpr) { // Function return; // OK } } throw new UnsupportedException("Unsupported DataType " + type + ", input is " + value); } /** * The abstract class Number is the superclass of classes BigDecimal, * BigInteger, Byte, Double, Float, Integer, Long, and Short. Subclasses of * Number must provide methods to convert the represented numeric value to * byte, double, float, int, long, and short. */ public static byte[] convert(DataType type, Number number) throws UnsupportedException { // BigDecimal, BigInteger, Byte, Double, Float, Integer, Long, and Short. if (number instanceof BigDecimal) { if (type == DataType.FLOAT) { return Bytes.toBytes(number.floatValue()); } else if (type == DataType.DOUBLE) { return Bytes.toBytes(number.doubleValue()); } else if (type == DataType.INT32) { return Bytes.toBytes(number.intValue()); } else if (type == DataType.INT64) { return Bytes.toBytes(number.longValue()); } return Bytes.toBytes((BigDecimal) number); } else if (number instanceof BigInteger) { throw new UnsupportedException(" BigInteger " + number + " Unsupported"); } else if (number instanceof Byte) { return Bytes.toBytes((Byte) number); } else if (number instanceof Double) { double value = number.doubleValue(); if (type == DataType.FLOAT) { return Bytes.toBytes((float) value); } else if (type == DataType.DOUBLE) { return Bytes.toBytes(value); } else if (type == DataType.INT32) { int iv = (int) value; return Bytes.toBytes(iv); } else if (type == DataType.INT64) { long lv = (long) value; return Bytes.toBytes(lv); } } else if (number instanceof Float) { float value = number.floatValue(); if (type == DataType.FLOAT) { return Bytes.toBytes(value); } else if (type == DataType.DOUBLE) { return Bytes.toBytes((float) value); } else if (type == DataType.INT32) { int iv = (int) value; return Bytes.toBytes(iv); } else if (type == DataType.INT64) { long lv = (long) value; return Bytes.toBytes(lv); } } else if (number instanceof Integer) { int value = number.intValue(); if (type == DataType.INT32) { return Bytes.toBytes((Integer) value); } else if (type == DataType.INT64) { return Bytes.toBytes((long) value); } else if (type == DataType.FLOAT) { float fv = (float) value; return Bytes.toBytes(fv); } else if (type == DataType.DOUBLE) { double fv = (double) value; return Bytes.toBytes(fv); } } else if (number instanceof Long) { long value = number.longValue(); if (type == DataType.INT32) { return Bytes.toBytes((int) value); } else if (type == DataType.INT64) { return Bytes.toBytes(value); } else if (type == DataType.FLOAT) { float fv = (float) value; return Bytes.toBytes(fv); } else if (type == DataType.DOUBLE) { double fv = (double) value; return Bytes.toBytes(fv); } } else if (number instanceof Short) { return Bytes.toBytes((Short) number); } throw new UnsupportedException("Unknown Number:" + number + " Type:" + type + " Unsupported "); } /** * Function http://www.w3schools.com/sql/func_now.asp, current NOW() is supported. */ public static byte[] convert(DataType type, SQLMethodInvokeExpr expr) throws UnsupportedException { String methodName = expr.getMethodName(); if (methodName.equalsIgnoreCase("NOW")) { // Date Function 'NOW()' if (type == DataType.DATETIME) { // Type must be DATETIME return Bytes.toBytes(EnvironmentEdgeManager.currentTimeMillis()); } else { throw new UnsupportedException(" DataType " + type + " not support " + " Function " + methodName); } } else { throw new UnsupportedException(" Functions " + methodName + " Unsupported"); } } @Override public SQLType getSQLType(ParseContext context) throws IOException { parseSqlToStatement(context); SQLStatement stmt = context.getStmt(); boolean isDDL = false; boolean isDQL = false; if (stmt instanceof WaspSqlCreateTableStatement) { // This is a Create Table SQL isDDL = true; } else if (stmt instanceof WaspSqlCreateIndexStatement) { // This is a Create Index SQL isDDL = true; } else if (stmt instanceof SQLDropTableStatement) { // This is a Drop Table SQL isDDL = true; } else if (stmt instanceof SQLDropIndexStatement) { // This is a Drop Index SQL isDDL = true; } else if (stmt instanceof MySqlAlterTableStatement) { // This is a Alter Table SQL isDDL = true; } else if (stmt instanceof WaspSqlShowTablesStatement) { // This is a Show Tables SQL isDDL = true; } else if (stmt instanceof WaspSqlShowCreateTableStatement) { // This is a Show Create Table SQL isDDL = true; } else if (stmt instanceof WaspSqlDescribeStatement) { // This is a DESCRIBE SQL isDDL = true; } else if (stmt instanceof WaspSqlShowIndexesStatement) { // This is a SHOW INDEXES SQL isDDL = true; } else if (stmt instanceof SQLSelectStatement) { // This is a Select SQL isDDL = false; isDQL = true; } else if (stmt instanceof SQLUpdateStatement) { // This is a Update SQL isDDL = false; } else if (stmt instanceof SQLInsertStatement) { // This is a Insert SQL isDDL = false; } else if (stmt instanceof SQLDeleteStatement) { // This is a Delete SQL isDDL = false; } else { throw new UnsupportedException("Unsupported SQLStatement " + SQLUtils.toSQLString(stmt)); } if (isDDL) { return SQLType.DDL; } else if (isDQL) { return SQLType.DQL; } else { return SQLType.DML; } } }