All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.dolphindb.jdbc.JDBCStatement Maven / Gradle / Ivy

package com.dolphindb.jdbc;

import com.xxdb.data.*;
import com.xxdb.data.Void;
import java.io.IOException;
import java.sql.*;
import java.text.MessageFormat;
import java.util.*;

public class JDBCStatement implements Statement {

    protected JDBCConnection connection;
    protected ResultSet resultSet;
    protected StringBuilder batch;
    protected Queue objectQueue;
    protected Object result;
    protected Deque resultSets;
    protected HashMap tableTypes;
    protected static final String IN_MEMORY_TABLE = "IN-MEMORY TABLE";
    protected boolean isClosed;
    private int timeout = 100;
    private int fetchSize = 0;
    private int maxRows = -1;

    public JDBCStatement(JDBCConnection cnn){
        this.connection = cnn;
        this.objectQueue = new LinkedList<>();
        this.resultSets = new LinkedList<>();
        this.batch = new StringBuilder();
    }

    @Override
    public long executeLargeUpdate(String sql, String[] columnNames) throws SQLException {
        return 0;
    }

    @Override
    public long executeLargeUpdate(String sql, int[] columnIndexes) throws SQLException {
        return 0;
    }

    @Override
    public long executeLargeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
        return 0;
    }

    @Override
    public long executeLargeUpdate(String sql) throws SQLException {
        return 0;
    }

    @Override
    public long[] executeLargeBatch() throws SQLException {
        return new long[0];
    }

    @Override
    public long getLargeMaxRows() throws SQLException {
        return 0;
    }

    @Override
    public void setLargeMaxRows(long max) throws SQLException {
        if (max < 0)
            throw new SQLException("The param max cannot less than 0.");

        if (max == 0)
            max = -1;

        this.maxRows = (int) max;
    }

    @Override
    public long getLargeUpdateCount() throws SQLException {
        return 0;
    }

    private String getTableType(String tableName) throws SQLException{
        if (tableTypes == null) {
            tableTypes = new LinkedHashMap<>();
        }
        String tableType = tableTypes.get(tableName);
        if (tableType == null) {
            try {
                tableType = connection.run("typestr " + tableName).getString();
            } catch (IOException e) {
                throw new SQLException(e);
            }
            tableTypes.put(tableName, tableType);
            return tableType;
        }else{
            return tableType;
        }
    }


    @Override
    public ResultSet executeQuery(String sql) throws SQLException {
        sql = Utils.changeCase(sql, connection);
        sql = sql.trim();
        while (sql.endsWith(";"))
        	sql = sql.substring(0, sql.length() - 1);
        sql = sql.trim();
        if (sql!=null&&sql.equals("select 1"))
            sql = "select 1 as val";
        sql = Utils.outerJoinToFullJoin(sql);
        sql = Utils.oracleToDolphin(sql);
        String[] strings = sql.split(";");
        String lastStatement = strings[strings.length - 1].trim();
        int dml = Utils.getDml(lastStatement);
        Entity entity;
        switch (dml){
            case Utils.DML_INSERT:
            case Utils.DML_UPDATE:
            case Utils.DML_DELETE:
                throw new SQLException("The given SQL statement produces anything other than a single ResultSet object.");
            case Utils.DML_SELECT:
            case Utils.DML_EXEC:
            case Utils.DML_OTHER:
                try {
                    if(this.fetchSize != 0) {
                        if (fetchSize < 8192) {
                            throw new SQLException("The fetchSize param must be greater than 8192.");
                        }
                        entity = connection.run(sql, fetchSize);
                    } else {
                        entity = connection.run(sql);
                    }

                    if (entity instanceof BasicTable || entity.getDataForm() == Entity.DATA_FORM.DF_SCALAR
                            || entity.getDataForm() == Entity.DATA_FORM.DF_VECTOR || entity.getDataForm() == Entity.DATA_FORM.DF_MATRIX) {
                        resultSet = new JDBCResultSet(connection, this, entity, sql, this.maxRows);
                        return resultSet;
                    } else if(entity instanceof EntityBlockReader) {
                        resultSet = new JDBCResultSet(connection, this, (EntityBlockReader) entity, sql, this.maxRows);
                        return resultSet;
                    } else {
                        throw new SQLException("The given SQL statement produces anything other than a single ResultSet object.");
                    }
                } catch (IOException e){
                    throw new SQLException(e);
                }
            default:
                throw new SQLException("The given SQL statement produces anything other than a single ResultSet object.");
        }
    }


    @Override
    public int executeUpdate(String sql) throws SQLException {
        sql = Utils.changeCase(sql, connection);
        sql = sql.trim();
        while (sql.endsWith(";"))
        	sql = sql.substring(0, sql.length() - 1);
        String[] strings = sql.split(";");
        String lastStatement = strings[strings.length - 1].trim();
        String tableName = Utils.getTableName(lastStatement, false);
        int dml = Utils.getDml(lastStatement);

        String tableType;
        switch (dml) {
            case Utils.DML_INSERT:
                if (tableName != null) {
                    tableType = getTableType(tableName);
                    if (tableType.equals(IN_MEMORY_TABLE)) {
                        try {
                            connection.run(sql);
                            return SUCCESS_NO_INFO;
                        } catch (IOException e) {
                            throw new SQLException(e);
                        }
                    } else {
                        String INSERT_SQL_COMMA_SPLIT_REGEX = ",(?=(?:[^()]*\\([^()]*\\))*[^()]*$)";
                        String[] values = lastStatement.substring(lastStatement.indexOf("values") + "values".length()).replaceAll("^\\(|\\)$", "").split(INSERT_SQL_COMMA_SPLIT_REGEX, -1);
                        String runSql = processValueInSql(tableName, values);
                        try {
                            connection.run(runSql);
                            return SUCCESS_NO_INFO;
                        } catch (IOException e) {
                            throw new SQLException(e);
                        }
                    }
                } else {
                    throw new SQLException("check the SQL " + sql);
                }
            case Utils.DML_UPDATE:
            case Utils.DML_DELETE:
                if (tableName != null) {
                    try {
                        connection.run(sql);
                        return SUCCESS_NO_INFO;
                    } catch (IOException e) {
                        throw new SQLException(e);
                    }
                } else {
                    throw new SQLException("check the Query " + sql);
                }
            case Utils.DML_SELECT:
            case Utils.DML_EXEC:
                throw new SQLException("Can not issue SELECT or EXEC via executeUpdate()");
            default:
                Entity entity;
                try {
                    entity = connection.run(sql);
                }catch (IOException e){
                    throw new SQLException(e);
                }
                if(entity instanceof BasicTable){
                    throw new SQLException("Can not produces ResultSet");
                }
                return 0;
        }
    }

    protected String processValueInSql(String tableName, String[] values) {
        StringBuilder sqlSb = new StringBuilder("append!(").append(tableName).append(",").append("table(");

        String colName = "col";
        int colIndex = 1;

        AbstractVector typeStringVec;
        try {
            typeStringVec = getNULLValueType(tableName);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        for (int i = 0; i < values.length; i++) {
            if (values[i].trim().equals("NULL")) {
                String nullValueType = typeStringVec.get(i).getString().toLowerCase();
                sqlSb.append(nullValueType).append("(").append(values[i]).append(")").append(" as ").append(colName+colIndex).append(",");
                colIndex++;
            } else if (values[i].trim().equals("")) {
                String nullValueType = typeStringVec.get(i).getString().toLowerCase();
                if (nullValueType.equals("complex") || nullValueType.equals("point")) {
                    sqlSb.append(nullValueType).append("(00i,00i)").append(" as ").append(colName+colIndex).append(",");
                } else if (nullValueType.contains("decimal")) {
                    String DECIMAL_REGEX = "decimal(32|64|128)\\((\\d+)\\)";
                    String NULL_replacement = "decimal$1(NULL, $2)";
                    String replacedDecimal = nullValueType.replaceAll(DECIMAL_REGEX, NULL_replacement);
                    sqlSb.append(replacedDecimal).append(" as ").append(colName+colIndex).append(",");
                } else if (nullValueType.equals("symbol")) {
                    sqlSb.append("array(SYMBOL, 0, 1)").append(" as ").append(colName+colIndex).append(",");
                } else if (nullValueType.equals("uuid")) {
                    sqlSb.append("uuid(\"\")").append(" as ").append(colName+colIndex).append(",");
                } else if (nullValueType.equals("ipaddr")) {
                    sqlSb.append("ipaddr(\"\")").append(" as ").append(colName+colIndex).append(",");
                } else if (nullValueType.equals("int128")) {
                    sqlSb.append("int128(\"\")").append(" as ").append(colName+colIndex).append(",");
                } else if (nullValueType.equals("blob")) {
                    sqlSb.append("blob(string(NULL))").append(" as ").append(colName+colIndex).append(",");
                } else {
                    sqlSb.append(nullValueType).append("(NULL)").append(" as ").append(colName+colIndex).append(",");
                }
                colIndex++;
            } else {
                sqlSb.append(values[i]).append(" as ").append(colName+colIndex).append(",");
                colIndex++;
            }

        }

        sqlSb.delete(sqlSb.length() - ",".length(), sqlSb.length());
        sqlSb.append("))");

        return sqlSb.toString();
    }

    protected AbstractVector getNULLValueType(String tableName) throws IOException {
        BasicDictionary schema = (BasicDictionary) connection.run(String.format("schema(%s)", tableName));
        BasicTable colDefs = (BasicTable) schema.get(new BasicString("colDefs"));
        AbstractVector typeStringVec = (AbstractVector) colDefs.getColumn("typeString");
       return typeStringVec;
    }

    @Override
    public void close() throws SQLException {
        isClosed = true;
        batch = null;
        result = null;
        if(objectQueue != null){
            objectQueue.clear();
            objectQueue = null;
        }

        if(resultSet != null) {
            resultSet.close();
            objectQueue = null;
        }

        if(tableTypes != null){
            tableTypes.clear();
            tableTypes = null;
        }
    }

    @Override
    public int getMaxFieldSize() throws SQLException {
        return 0;
    }

    @Override
    public void setMaxFieldSize(int maxFieldSize) throws SQLException {
    }

    @Override
    public int getMaxRows() throws SQLException {
        if (this.maxRows <= 0)
            return 0;

        return this.maxRows;
    }

    @Override
    public void setMaxRows(int maxRows) throws SQLException {
        setLargeMaxRows(maxRows);
    }

    @Override
    public void setEscapeProcessing(boolean b) throws SQLException {
        Driver.unused();
    }

    @Override
    public int getQueryTimeout() throws SQLException {
        return timeout;
    }

    @Override
    public void setQueryTimeout(int queryTimeout) throws SQLException {
        timeout = queryTimeout * 1000;
    }

    @Override
    public void cancel() throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public SQLWarning getWarnings() throws SQLException {
    	// TODO: implement warnings
    	return null;
    }

    @Override
    public void clearWarnings() throws SQLException {
    	// TODO: implement warnings
    }

    @Override
    public void setCursorName(String cursorName) throws SQLException {
    	throw new SQLFeatureNotSupportedException();
    }

    public String trimFirstAndLastChar(String str, String element){
        boolean beginIndexFlag = true;
        boolean endIndexFlag = true;
        do{
            int beginIndex = str.indexOf(element) == 0 ? 1 : 0;
            int endIndex = str.lastIndexOf(element) + 1 == str.length() ? str.lastIndexOf(element) : str.length();
            str = str.substring(beginIndex, endIndex);
            beginIndexFlag = (str.indexOf(element) == 0);
            endIndexFlag = (str.lastIndexOf(element) + 1 == str.length());
        } while (beginIndexFlag || endIndexFlag);
        return str;
    }

    @Override
    public boolean execute(String sql) throws SQLException {
        sql = Utils.changeCase(sql);
        sql = sql.trim();
        while (sql.endsWith(";"))
        	sql = sql.substring(0, sql.length() - 1);
        sql = sql.trim();
        if (sql!=null&&sql.equals("select 1"))
            sql = "select 1 as val";

        String[] strings;
        if(sql.startsWith("[")&&sql.endsWith("]")){
            String temp = trimFirstAndLastChar(sql,"[");
            sql = trimFirstAndLastChar(temp,"]");
            strings = sql.split(",");

            String[] statementbatch = strings;

            for(int i =0; i< statementbatch.length;i++){
                ResultSet resultSet_batch = executeQuery(statementbatch[i]);
                resultSets.offerLast(resultSet_batch);
                objectQueue.offer(resultSet_batch);

            }

            if (objectQueue.isEmpty()) {
                return false;
            } else {
                result = objectQueue.poll();
                return result instanceof ResultSet;
            }
        } else
            strings = sql.split(";");

        String lastStatement = strings[strings.length - 1].trim();
        int dml = Utils.getDml(lastStatement);

        switch (dml) {
            case Utils.DML_SELECT:
            case Utils.DML_EXEC: {
                ResultSet resultSet_ = executeQuery(sql);
                resultSets.offerLast(resultSet_);
                objectQueue.offer(resultSet_);
            }
            break;
            case Utils.DML_INSERT:
            case Utils.DML_UPDATE:
            case Utils.DML_DELETE:
                objectQueue.offer(executeUpdate(sql));
                break;
            default: {
                Entity entity;
                try {
                    entity = connection.run(sql);
                } catch (IOException e) {
                    throw new SQLException(e);
                }

                if (entity instanceof BasicTable || (entity.getDataForm() == Entity.DATA_FORM.DF_SCALAR && !(entity instanceof Void))
                        || entity.getDataForm() == Entity.DATA_FORM.DF_VECTOR || entity.getDataForm() == Entity.DATA_FORM.DF_MATRIX) {
                    ResultSet resultSet = new JDBCResultSet(connection, this, entity, sql, this.maxRows);
                    resultSets.offerLast(resultSet);
                    objectQueue.offer(resultSet);
                }
            }
        }

        if (objectQueue.isEmpty()) {
            return false;
        } else {
            result = objectQueue.poll();
            return result instanceof ResultSet;
        }
    }

    @Override
    public ResultSet getResultSet() throws SQLException {
        if(result == null) {
            resultSet = null;
        }else if(result instanceof ResultSet){
            resultSet = (JDBCResultSet) result;
        }else{
            resultSet = null;
        }
        result = null;
        return resultSet;
    }

    @Override
    public int getUpdateCount() throws SQLException {
        int updateCount;
        if(result == null) {
            updateCount = -1;
        }else if(result instanceof Integer){
            updateCount =  (int)result;
        }else{
            updateCount = -1;
        }
        result = null;
        return updateCount;
    }

    @Override
    public boolean getMoreResults() throws SQLException {
        while (!resultSets.isEmpty()){
            ResultSet resultSet_ = resultSets.pollFirst();
            if(resultSet_ == null){
                resultSet_.close();
            }
        }

        if(!objectQueue.isEmpty()){
            result = objectQueue.poll();
            return result instanceof ResultSet;
        }else{
            return false;
        }
    }

    @Override
    public void setFetchDirection(int direction) throws SQLException {
        switch (direction) {
            case java.sql.ResultSet.FETCH_FORWARD:
                break;
            default:
                throw new SQLException("DolpinDB JDBC Statement direction only suppport FETCH_FORWARD.");
        }
    }

    @Override
    public int getFetchDirection() throws SQLException {
        return java.sql.ResultSet.FETCH_FORWARD;
    }

    @Override
    public void setFetchSize(int fetchSize) throws SQLException {
        this.fetchSize = fetchSize;
    }

    @Override
    public int getFetchSize() throws SQLException {
       return fetchSize;
    }

    @Override
    public int getResultSetConcurrency() throws SQLException {
        Driver.unused();
        return 0;
    }

    @Override
    public int getResultSetType() throws SQLException {
        Driver.unused();
        return 0;
    }

    @Override
    public void addBatch(String sql) throws SQLException {
        batch.append(sql).append(";\n");
    }

    @Override
    public void clearBatch() throws SQLException {
        batch.delete(0,batch.length());
    }

    @Override
    public int[] executeBatch() throws SQLException {
        String[] strings = batch.toString().split(";");
        int[] arr_int = new int[strings.length];
        int index = 0;
        try {
            for(String item : strings){
                arr_int[index] = executeUpdate(item);
                ++index;
            }
            batch.delete(0,batch.length());
            return arr_int;
        }catch (SQLException e){
            batch.delete(0,batch.length());
            throw new BatchUpdateException(e.getMessage(),Arrays.copyOf(arr_int,index));
        }
    }

    @Override
    public Connection getConnection() throws SQLException {
        return connection;
    }


    @Override
    public boolean getMoreResults(int current) throws SQLException{
        switch (current){
            case Statement.CLOSE_ALL_RESULTS:
                while (!resultSets.isEmpty()){
                    ResultSet resultSet_ = resultSets.pollLast();
                    if(resultSet_ != null){
                        resultSet_.close();
                    }
                }
                break;
            case Statement.CLOSE_CURRENT_RESULT:
                if(resultSet != null){
                    resultSet.close();
                }
                break;
            case Statement.KEEP_CURRENT_RESULT:
                break;
            default:
                throw new SQLException("The argument supplied is not one of the following:\n" +
                        "Statement.CLOSE_CURRENT_RESULT,\n" +
                        "Statement.KEEP_CURRENT_RESULT or\n" +
                        " Statement.CLOSE_ALL_RESULTS");
        }
        return false;
    }

    @Override
    public ResultSet getGeneratedKeys() throws SQLException {
        Driver.unused();
        return null;
    }

    @Override
    public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException{
        return executeUpdate(sql);
    }

    @Override
    public int executeUpdate(String sql, int[] columnIndexes) throws SQLException{
        return executeUpdate(sql);
    }

    @Override
    public int executeUpdate(String sql, String[] columnNames) throws SQLException{
        return executeUpdate(sql);
    }

    @Override
    public boolean execute(String sql, int autoGeneratedKeys) throws SQLException{
        return execute(sql);
    }

    @Override
    public boolean execute(String sql, int[] columnIndexes) throws SQLException{
        return execute(sql);
    }

    @Override
    public boolean execute(String sql, String[] columnNames) throws SQLException{
        return execute(sql);
    }

    @Override
    public int getResultSetHoldability() throws SQLException {
        Driver.unused();
        return 0;
    }

    @Override
    public boolean isClosed() throws SQLException {
        return isClosed;
    }

    @Override
    public void setPoolable(boolean b) throws SQLException {
        Driver.unused();
    }

    @Override
    public boolean isPoolable() throws SQLException {
        Driver.unused();
        return false;
    }

    @Override
    public void closeOnCompletion() throws SQLException {
        Driver.unused();
    }

    @Override
    public boolean isCloseOnCompletion() throws SQLException {
        Driver.unused();
        return false;
    }

    @Override
    public  T unwrap(Class aClass) throws SQLException {
        return aClass.cast(this);
    }

    @Override
    public boolean isWrapperFor(Class aClass) throws SQLException {
        return aClass.isInstance(this);
    }

    protected BasicInt tableInsert(String tableName, String sql) throws IOException{
        if (sql.startsWith("tableInsert")){
            return (BasicInt)connection.run(sql);
        }
        else {
            String values;
            int index = sql.indexOf(";");
            if (index == -1) {
                values = sql.substring(sql.indexOf("values") + "values".length());
            } else {
                values = sql.substring(sql.indexOf("values") + "values".length(), index);
            }

            String new_sql = MessageFormat.format("tableInsert({0},{1})", tableName, values);
            BasicInt n = (BasicInt) connection.run(new_sql);
            return n;
        }
    }
}