From a22f50d6c0cc2158533ba5e33d6bf33f826aed7b Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 10 Jul 2016 00:02:05 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20jdbc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/com/annimon/ownlang/lib/modules/jdbc.java | 783 ++++++++++++++++++ 1 file changed, 783 insertions(+) create mode 100644 src/com/annimon/ownlang/lib/modules/jdbc.java diff --git a/src/com/annimon/ownlang/lib/modules/jdbc.java b/src/com/annimon/ownlang/lib/modules/jdbc.java new file mode 100644 index 0000000..aa2c005 --- /dev/null +++ b/src/com/annimon/ownlang/lib/modules/jdbc.java @@ -0,0 +1,783 @@ +package com.annimon.ownlang.lib.modules; + +import com.annimon.ownlang.exceptions.ArgumentsMismatchException; +import com.annimon.ownlang.lib.Arguments; +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.FunctionValue; +import com.annimon.ownlang.lib.Functions; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.StringValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; +import java.io.IOException; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Connection; +import java.sql.Date; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * + * @author aNNiMON + */ +public final class jdbc implements Module { + + @Override + public void init() { + Functions.set("getConnection", getConnectionFunction()); + Functions.set("sqlite", getConnectionFunction("jdbc:sqlite:")); + Functions.set("mysql", getConnectionFunction("jdbc:", () -> Class.forName("com.mysql.jdbc.Driver"))); + } + + private static com.annimon.ownlang.lib.Function getConnectionFunction() { + return getConnectionFunction("", null); + } + + private static com.annimon.ownlang.lib.Function getConnectionFunction(String connectionPrefix) { + return getConnectionFunction(connectionPrefix, null); + } + + private static com.annimon.ownlang.lib.Function getConnectionFunction(String connectionPrefix, ThrowableRunnable preAction) { + return (args) -> { + try { + if (preAction != null) { + preAction.run(); + } + switch (args.length) { + case 1: + return new ConnectionValue(getConnection(connectionPrefix + args[0].asString())); + case 2: + Class.forName(args[1].asString()); + return new ConnectionValue(getConnection(connectionPrefix + args[0].asString())); + case 3: { + final String url = connectionPrefix + args[0].asString(); + return new ConnectionValue(getConnection(url, args[1].asString(), args[2].asString())); + } + case 4: { + Class.forName(args[3].asString()); + String url = connectionPrefix + args[0].asString(); + return new ConnectionValue(getConnection(url, args[1].asString(), args[2].asString())); + } + default: + throw new ArgumentsMismatchException("Wrong number of arguments"); + } + } catch (Exception ex) { + throw new RuntimeException(ex); + } + }; + } + + private static Value getConnection(Value... args) { + try { + switch (args.length) { + case 1: + return new ConnectionValue(getConnection(args[0].asString())); + case 3: + return new ConnectionValue(getConnection(args[0].asString(), args[1].asString(), args[2].asString())); + default: + throw new ArgumentsMismatchException("Wrong number of arguments"); + } + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + } + + private static Connection getConnection(String url) throws SQLException { + return DriverManager.getConnection(url); + } + + private static Connection getConnection(String url, String user, String password) throws SQLException { + return DriverManager.getConnection(url, user, password); + } + + // + private static class ConnectionValue extends MapValue { + + private final Connection connection; + + public ConnectionValue(Connection connection) { + super(20); + this.connection = connection; + init(); + } + + private void init() { + set(new StringValue("createStatement"), new FunctionValue(this::createStatement)); + set(new StringValue("prepareStatement"), new FunctionValue(this::prepareStatement)); + set(new StringValue("close"), new FunctionValue(this::close)); + + set(new StringValue("clearWarnings"), voidFunction(connection::clearWarnings)); + set(new StringValue("close"), voidFunction(connection::close)); + set(new StringValue("commit"), voidFunction(connection::commit)); + set(new StringValue("rollback"), voidFunction(connection::rollback)); + + set(new StringValue("setHoldability"), voidIntFunction(connection::setHoldability)); + set(new StringValue("setTransactionIsolation"), voidIntFunction(connection::setTransactionIsolation)); + + set(new StringValue("getAutoCommit"), booleanFunction(connection::getAutoCommit)); + set(new StringValue("isClosed"), booleanFunction(connection::isClosed)); + set(new StringValue("isReadOnly"), booleanFunction(connection::isReadOnly)); + + set(new StringValue("getHoldability"), intFunction(connection::getHoldability)); + set(new StringValue("getNetworkTimeout"), intFunction(connection::getNetworkTimeout)); + set(new StringValue("getTransactionIsolation"), intFunction(connection::getTransactionIsolation)); + set(new StringValue("getUpdateCount"), intFunction(connection::getHoldability)); + + set(new StringValue("getCatalog"), stringFunction(connection::getCatalog)); + set(new StringValue("getSchema"), stringFunction(connection::getSchema)); + } + + private Value createStatement(Value... args) { + try { + switch (args.length) { + case 0: + return new StatementValue(connection.createStatement()); + case 2: + return new StatementValue(connection.createStatement(args[0].asInt(), args[1].asInt())); + case 3: + return new StatementValue(connection.createStatement(args[0].asInt(), args[1].asInt(), args[2].asInt())); + default: + throw new ArgumentsMismatchException("Wrong number of arguments"); + } + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + } + + private Value prepareStatement(Value... args) { + Arguments.checkRange(1, 4, args.length); + try { + final String sql = args[0].asString(); + switch (args.length) { + case 1: + return new StatementValue(connection.prepareStatement(sql)); + case 2: + final PreparedStatement ps = columnData(args[1], + (int autogeneratedKeys) -> connection.prepareStatement(sql, autogeneratedKeys), + (int[] columnIndices) -> connection.prepareStatement(sql, columnIndices), + (String[] columnNames) -> connection.prepareStatement(sql, columnNames)); + return new StatementValue(ps); + case 3: + return new StatementValue(connection.prepareStatement(sql, args[1].asInt(), args[2].asInt())); + case 4: + return new StatementValue(connection.prepareStatement(sql, args[1].asInt(), args[2].asInt(), args[3].asInt())); + default: + throw new ArgumentsMismatchException("Wrong number of arguments"); + } + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + } + + private Value close(Value... args) { + try { + if (connection != null) { + connection.close(); + } + } catch (SQLException sqlex) { + // skip + } + return NumberValue.ZERO; + } + } + + private static class StatementValue extends MapValue { + + private final Statement statement; + private final PreparedStatement ps; + + public StatementValue(Statement statement) { + super(50); + this.statement = statement; + if (statement instanceof PreparedStatement) { + ps = (PreparedStatement) statement; + } else { + ps = null; + } + init(); + } + + private void init() { + set(new StringValue("execute"), new FunctionValue(this::execute)); + set(new StringValue("executeQuery"), new FunctionValue(this::executeQuery)); + set(new StringValue("executeUpdate"), new FunctionValue(this::executeUpdate)); + set(new StringValue("executeLargeUpdate"), new FunctionValue(this::executeLargeUpdate)); + + set(new StringValue("cancel"), voidFunction(statement::cancel)); + set(new StringValue("clearBatch"), voidFunction(statement::clearBatch)); + set(new StringValue("clearWarnings"), voidFunction(statement::clearWarnings)); + set(new StringValue("close"), voidFunction(statement::close)); + set(new StringValue("closeOnCompletion"), voidFunction(statement::closeOnCompletion)); + + set(new StringValue("setFetchDirection"), voidIntFunction(statement::setFetchDirection)); + set(new StringValue("setFetchSize"), voidIntFunction(statement::setFetchSize)); + set(new StringValue("setMaxFieldSize"), voidIntFunction(statement::setMaxFieldSize)); + set(new StringValue("setMaxRows"), voidIntFunction(statement::setMaxRows)); + set(new StringValue("setQueryTimeout"), voidIntFunction(statement::setQueryTimeout)); + + set(new StringValue("getMoreResults"), booleanFunction(statement::getMoreResults)); + set(new StringValue("isCloseOnCompletion"), booleanFunction(statement::isCloseOnCompletion)); + set(new StringValue("isClosed"), booleanFunction(statement::isClosed)); + set(new StringValue("isPoolable"), booleanFunction(statement::isPoolable)); + + set(new StringValue("getFetchDirection"), intFunction(statement::getFetchDirection)); + set(new StringValue("getFetchSize"), intFunction(statement::getFetchSize)); + set(new StringValue("getMaxFieldSize"), intFunction(statement::getMaxFieldSize)); + set(new StringValue("getMaxRows"), intFunction(statement::getMaxRows)); + set(new StringValue("getQueryTimeout"), intFunction(statement::getQueryTimeout)); + set(new StringValue("getResultSetConcurrency"), intFunction(statement::getResultSetConcurrency)); + set(new StringValue("getResultSetHoldability"), intFunction(statement::getResultSetHoldability)); + set(new StringValue("getResultSetType"), intFunction(statement::getResultSetType)); + set(new StringValue("getUpdateCount"), intFunction(statement::getUpdateCount)); + + set(new StringValue("addBatch"), updateData(statement::addBatch, (args) -> args[0].asString())); + set(new StringValue("setCursorName"), updateData(statement::setCursorName, (args) -> args[0].asString())); + set(new StringValue("setEscapeProcessing"), updateData(statement::setEscapeProcessing, (args) -> args[0].asInt() != 0)); + set(new StringValue("setLargeMaxRows"), updateData(statement::setLargeMaxRows, (args) -> getNumber(args[0]).longValue())); + set(new StringValue("setPoolable"), updateData(statement::setPoolable, (args) -> args[0].asInt() != 0)); + + set(new StringValue("getResultSet"), objectFunction(statement::getResultSet, ResultSetValue::new)); + + if (ps != null) { + set(new StringValue("setBigDecimal"), updateData(ps::setBigDecimal, (args) -> new BigDecimal(args[1].asString()))); + set(new StringValue("setBoolean"), updateData(ps::setBoolean, (args) -> args[1].asInt() != 0)); + set(new StringValue("setByte"), updateData(ps::setByte, (args) -> getNumber(args[1]).byteValue())); + set(new StringValue("setBytes"), updateData(ps::setBytes, (args) -> valueToByteArray(args[1]))); + set(new StringValue("setDate"), updateData(ps::setDate, (args) -> new Date(getNumber(args[1]).longValue()))); + set(new StringValue("setDouble"), updateData(ps::setDouble, (args) -> getNumber(args[1]).doubleValue())); + set(new StringValue("setFloat"), updateData(ps::setFloat, (args) -> getNumber(args[1]).floatValue())); + set(new StringValue("setInt"), updateData(ps::setInt, (args) -> args[1].asInt())); + set(new StringValue("setLong"), updateData(ps::setLong, (args) -> getNumber(args[1]).longValue())); + set(new StringValue("setNString"), updateData(ps::setNString, (args) -> args[1].asString())); + set(new StringValue("setNull"), updateData(ps::setNull, (args) -> args[1].asInt())); + set(new StringValue("setShort"), updateData(ps::setShort, (args) -> getNumber(args[1]).shortValue())); + set(new StringValue("setString"), updateData(ps::setString, (args) -> args[1].asString())); + set(new StringValue("setTime"), updateData(ps::setTime, (args) -> new Time(getNumber(args[1]).longValue()))); + set(new StringValue("setTimestamp"), updateData(ps::setTimestamp, (args) -> new Timestamp(getNumber(args[1]).longValue()))); + set(new StringValue("setURL"), updateData(ps::setURL, (args) -> { + try { + return new URL(args[1].asString()); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + })); + } + } + + private Value execute(Value... args) { + Arguments.checkOrOr(1, 2, args.length); + try { + final String sql = args[0].asString(); + if (args.length == 1) return NumberValue.fromBoolean(statement.execute(sql)); + + boolean result = columnData(args[1], + (int autogeneratedKeys) -> statement.execute(sql, autogeneratedKeys), + (int[] columnIndices) -> statement.execute(sql, columnIndices), + (String[] columnNames) -> statement.execute(sql, columnNames)); + return NumberValue.fromBoolean(result); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + } + + private Value executeQuery(Value... args) { + Arguments.check(1, args.length); + try { + return new ResultSetValue(statement.executeQuery(args[0].asString())); + } catch (SQLException sqlex) { + return NumberValue.ZERO; + } + } + + private Value executeUpdate(Value... args) { + Arguments.checkOrOr(1, 2, args.length); + try { + final String sql = args[0].asString(); + if (args.length == 1) return NumberValue.of(statement.executeUpdate(sql)); + + int rowCount = columnData(args[1], + (int autogeneratedKeys) -> statement.executeUpdate(sql, autogeneratedKeys), + (int[] columnIndices) -> statement.executeUpdate(sql, columnIndices), + (String[] columnNames) -> statement.executeUpdate(sql, columnNames)); + return NumberValue.of(rowCount); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + } + + private Value executeLargeUpdate(Value... args) { + Arguments.checkOrOr(1, 2, args.length); + try { + final String sql = args[0].asString(); + if (args.length == 1) return NumberValue.of(statement.executeLargeUpdate(sql)); + + long rowCount = columnData(args[1], + (int autogeneratedKeys) -> statement.executeLargeUpdate(sql, autogeneratedKeys), + (int[] columnIndices) -> statement.executeLargeUpdate(sql, columnIndices), + (String[] columnNames) -> statement.executeLargeUpdate(sql, columnNames)); + return NumberValue.of(rowCount); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + } + } + + private static class ResultSetValue extends MapValue { + + private final ResultSet rs; + + public ResultSetValue(ResultSet rs) { + super(70); + this.rs = rs; + init(); + } + + private void init() { + set(new StringValue("findColumn"), new FunctionValue(this::findColumn)); + + set(new StringValue("afterLast"), voidFunction(rs::afterLast)); + set(new StringValue("beforeFirst"), voidFunction(rs::beforeFirst)); + set(new StringValue("cancelRowUpdates"), voidFunction(rs::cancelRowUpdates)); + set(new StringValue("clearWarnings"), voidFunction(rs::clearWarnings)); + set(new StringValue("close"), voidFunction(rs::close)); + set(new StringValue("deleteRow"), voidFunction(rs::deleteRow)); + set(new StringValue("insertRow"), voidFunction(rs::insertRow)); + set(new StringValue("moveToCurrentRow"), voidFunction(rs::moveToCurrentRow)); + set(new StringValue("moveToInsertRow"), voidFunction(rs::moveToInsertRow)); + set(new StringValue("refreshRow"), voidFunction(rs::refreshRow)); + set(new StringValue("updateRow"), voidFunction(rs::updateRow)); + + set(new StringValue("absolute"), voidIntFunction(rs::absolute)); + set(new StringValue("relative"), voidIntFunction(rs::relative)); + set(new StringValue("setFetchDirection"), voidIntFunction(rs::setFetchDirection)); + set(new StringValue("setFetchSize"), voidIntFunction(rs::setFetchSize)); + + set(new StringValue("first"), booleanFunction(rs::first)); + set(new StringValue("isAfterLast"), booleanFunction(rs::isAfterLast)); + set(new StringValue("isBeforeFirst"), booleanFunction(rs::isBeforeFirst)); + set(new StringValue("isClosed"), booleanFunction(rs::isClosed)); + set(new StringValue("isFirst"), booleanFunction(rs::isFirst)); + set(new StringValue("isLast"), booleanFunction(rs::isLast)); + set(new StringValue("last"), booleanFunction(rs::last)); + set(new StringValue("next"), booleanFunction(rs::next)); + set(new StringValue("previous"), booleanFunction(rs::previous)); + set(new StringValue("rowDeleted"), booleanFunction(rs::rowDeleted)); + set(new StringValue("rowInserted"), booleanFunction(rs::rowInserted)); + set(new StringValue("rowUpdated"), booleanFunction(rs::rowUpdated)); + set(new StringValue("wasNull"), booleanFunction(rs::wasNull)); + + set(new StringValue("getConcurrency"), intFunction(rs::getConcurrency)); + set(new StringValue("getFetchDirection"), intFunction(rs::getFetchDirection)); + set(new StringValue("getFetchSize"), intFunction(rs::getFetchSize)); + set(new StringValue("getHoldability"), intFunction(rs::getHoldability)); + set(new StringValue("getRow"), intFunction(rs::getRow)); + set(new StringValue("getType"), intFunction(rs::getType)); + + set(new StringValue("getCursorName"), stringFunction(rs::getCursorName)); + set(new StringValue("getStatement"), objectFunction(rs::getStatement, StatementValue::new)); + + // Results + set(new StringValue("getArray"), getObjectResult(rs::getArray, rs::getArray, jdbc::arrayToResultSetValue)); + set(new StringValue("getBigDecimal"), getObjectResult(rs::getBigDecimal, rs::getBigDecimal, (bd) -> new StringValue(bd.toString()))); + set(new StringValue("getBoolean"), getBooleanResult(rs::getBoolean, rs::getBoolean)); + set(new StringValue("getByte"), getNumberResult(rs::getByte, rs::getByte)); + set(new StringValue("getBytes"), getObjectResult(rs::getBytes, rs::getBytes, (bytes) -> ArrayValue.of(bytes))); + set(new StringValue("getDate"), getObjectResult(rs::getDate, rs::getDate, (date) -> NumberValue.of(date.getTime()))); + set(new StringValue("getDouble"), getNumberResult(rs::getDouble, rs::getDouble)); + set(new StringValue("getFloat"), getNumberResult(rs::getFloat, rs::getFloat)); + set(new StringValue("getInt"), getNumberResult(rs::getInt, rs::getInt)); + set(new StringValue("getLong"), getNumberResult(rs::getLong, rs::getLong)); + set(new StringValue("getNString"), getStringResult(rs::getNString, rs::getNString)); + set(new StringValue("getRowId"), getObjectResult(rs::getRowId, rs::getRowId, (rowid) -> ArrayValue.of(rowid.getBytes()))); + set(new StringValue("getShort"), getNumberResult(rs::getShort, rs::getShort)); + set(new StringValue("getString"), getStringResult(rs::getString, rs::getString)); + set(new StringValue("getTime"), getObjectResult(rs::getTime, rs::getTime, (time) -> NumberValue.of(time.getTime()))); + set(new StringValue("getTimestamp"), getObjectResult(rs::getTimestamp, rs::getTimestamp, (timestamp) -> NumberValue.of(timestamp.getTime()))); + set(new StringValue("getURL"), getObjectResult(rs::getURL, rs::getURL, (url) -> new StringValue(url.toExternalForm()))); + + // Update + set(new StringValue("updateNull"), new FunctionValue(this::updateNull)); + set(new StringValue("updateBigDecimal"), updateData(rs::updateBigDecimal, rs::updateBigDecimal, (args) -> new BigDecimal(args[1].asString()))); + set(new StringValue("updateBoolean"), updateData(rs::updateBoolean, rs::updateBoolean, (args) -> args[1].asInt() != 0)); + set(new StringValue("updateByte"), updateData(rs::updateByte, rs::updateByte, (args) -> getNumber(args[1]).byteValue())); + set(new StringValue("updateBytes"), updateData(rs::updateBytes, rs::updateBytes, (args) -> valueToByteArray(args[1]))); + set(new StringValue("updateDate"), updateData(rs::updateDate, rs::updateDate, (args) -> new Date(getNumber(args[1]).longValue()))); + set(new StringValue("updateDouble"), updateData(rs::updateDouble, rs::updateDouble, (args) -> getNumber(args[1]).doubleValue())); + set(new StringValue("updateFloat"), updateData(rs::updateFloat, rs::updateFloat, (args) -> getNumber(args[1]).floatValue())); + set(new StringValue("updateInt"), updateData(rs::updateInt, rs::updateInt, (args) -> getNumber(args[1]).intValue())); + set(new StringValue("updateLong"), updateData(rs::updateLong, rs::updateLong, (args) -> getNumber(args[1]).longValue())); + set(new StringValue("updateNString"), updateData(rs::updateNString, rs::updateNString, (args) -> args[1].asString())); + set(new StringValue("updateShort"), updateData(rs::updateShort, rs::updateShort, (args) -> getNumber(args[1]).shortValue())); + set(new StringValue("updateString"), updateData(rs::updateString, rs::updateString, (args) -> args[1].asString())); + set(new StringValue("updateTime"), updateData(rs::updateTime, rs::updateTime, (args) -> new Time(getNumber(args[1]).longValue()))); + set(new StringValue("updateTimestamp"), updateData(rs::updateTimestamp, rs::updateTimestamp, (args) -> new Timestamp(getNumber(args[1]).longValue()))); + } + + private Value findColumn(Value... args) { + try { + return NumberValue.of(rs.findColumn(args[0].asString())); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + } + + private Value updateNull(Value... args) { + Arguments.check(1, args.length); + try { + if (args[0].type() == Types.NUMBER) { + rs.updateNull(args[0].asInt()); + } + rs.updateNull(args[0].asString()); + return NumberValue.ONE; + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + } + } +// + + private static Number getNumber(Value value) { + if (value.type() == Types.NUMBER) return ((NumberValue) value).raw(); + return value.asInt(); + } + + private static T columnData(Value value, AutogeneratedKeys autogeneratedKeysFunction, + ColumnIndices columnIndicesFunction, ColumnNames columnNamesFunction) { + try { + if (value.type() != Types.ARRAY) { + return autogeneratedKeysFunction.apply(value.asInt()); + } + + final ArrayValue array = (ArrayValue) value; + final int size = array.size(); + final boolean isIntArray = (size > 0) && (array.get(0).type() == Types.NUMBER); + int index = 0; + + if (isIntArray) { + final int[] columnIndices = new int[size]; + for (Value v : array) { + columnIndices[index++] = v.asInt(); + } + return columnIndicesFunction.apply(columnIndices); + } + + final String[] columnNames = new String[size]; + for (Value v : array) { + columnNames[index++] = v.asString(); + } + return columnNamesFunction.apply(columnNames); + + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + } + + private interface AutogeneratedKeys { + + T apply(int autogeneratedKeys) throws SQLException; + } + + private interface ColumnIndices { + + T apply(int[] columnIndices) throws SQLException; + } + + private interface ColumnNames { + + T apply(String[] columnNames) throws SQLException; + } + + private static FunctionValue voidFunction(VoidResult result) { + return new FunctionValue((args) -> { + try { + result.execute(); + return NumberValue.ONE; + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static FunctionValue voidIntFunction(VoidResultInt result) { + return new FunctionValue((args) -> { + Arguments.check(1, args.length); + try { + result.execute(args[0].asInt()); + return NumberValue.ONE; + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static FunctionValue booleanFunction(BooleanResult result) { + return new FunctionValue((args) -> { + try { + return NumberValue.fromBoolean(result.get()); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static Value intFunction(IntResult numberResult) { + return new FunctionValue((args) -> { + try { + return NumberValue.of(numberResult.get()); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static Value stringFunction(StringResult stringResult) { + return new FunctionValue((args) -> { + try { + return new StringValue(stringResult.get()); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static Value objectFunction(ObjectResult objectResult, Function converter) { + return new FunctionValue((args) -> { + try { + return converter.apply(objectResult.get()); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + + private static Value getBooleanResult(BooleanColumnResultInt numberResult, BooleanColumnResultString stringResult) { + return new FunctionValue((args) -> { + Arguments.check(1, args.length); + try { + if (args[0].type() == Types.NUMBER) { + return NumberValue.fromBoolean(numberResult.get(args[0].asInt())); + } + return NumberValue.fromBoolean(stringResult.get(args[0].asString())); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static Value getNumberResult(NumberColumnResultInt numberResult, NumberColumnResultIntString stringResult) { + return new FunctionValue((args) -> { + Arguments.check(1, args.length); + try { + if (args[0].type() == Types.NUMBER) { + return NumberValue.of(numberResult.get(args[0].asInt())); + } + return NumberValue.of(stringResult.get(args[0].asString())); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static Value getStringResult(StringColumnResultInt numberResult, StringColumnResultIntString stringResult) { + return new FunctionValue((args) -> { + Arguments.check(1, args.length); + try { + if (args[0].type() == Types.NUMBER) { + return new StringValue(numberResult.get(args[0].asInt())); + } + return new StringValue(stringResult.get(args[0].asString())); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static Value getObjectResult(ObjectColumnResultInt numberResult, ObjectColumnResultString stringResult, + Function converter) { + return new FunctionValue((args) -> { + Arguments.check(1, args.length); + try { + if (args[0].type() == Types.NUMBER) { + return converter.apply(numberResult.get(args[0].asInt())); + } + return converter.apply(stringResult.get(args[0].asString())); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static Value getObjectResult(ObjectColumnResultInt numberResult, ObjectColumnResultString stringResult, + BiFunction converter) { + return new FunctionValue((args) -> { + Arguments.checkAtLeast(1, args.length); + try { + if (args[0].type() == Types.NUMBER) { + return converter.apply(numberResult.get(args[0].asInt()), args); + } + return converter.apply(stringResult.get(args[0].asString()), args); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static Value updateData(VoidResultObject result, Function converter) { + return new FunctionValue((args) -> { + Arguments.checkAtLeast(2, args.length); + try { + result.execute(converter.apply(args)); + return NumberValue.ONE; + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static Value updateData(VoidColumnResultIntObject numberResult, Function converter) { + return new FunctionValue((args) -> { + Arguments.checkAtLeast(2, args.length); + try { + numberResult.execute(args[0].asInt(), converter.apply(args)); + return NumberValue.ONE; + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static Value updateData(VoidColumnResultIntObject numberResult, VoidColumnResultStringObject stringResult, + Function converter) { + return new FunctionValue((args) -> { + Arguments.checkAtLeast(2, args.length); + try { + if (args[0].type() == Types.NUMBER) { + numberResult.execute(args[0].asInt(), converter.apply(args)); + } else { + stringResult.execute(args[0].asString(), converter.apply(args)); + } + return NumberValue.ONE; + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + }); + } + + private static Value arrayToResultSetValue(Array array, Value[] args) { + try { + final ResultSet result; + switch (args.length) { + case 1: + // column + result = array.getResultSet(); + break; + case 3: + // column, index, count + long index = (args[1].type() == Types.NUMBER) ? ((NumberValue) args[1]).asLong() : args[1].asInt(); + result = array.getResultSet(index, args[2].asInt()); + break; + default: + throw new ArgumentsMismatchException("Wrong number of arguments"); + } + return new ResultSetValue(result); + } catch (SQLException sqlex) { + throw new RuntimeException(sqlex); + } + } + + private static byte[] valueToByteArray(Value value) { + if (value.type() != Types.ARRAY) { + return new byte[0]; + } + final ArrayValue array = (ArrayValue) value; + final int size = array.size(); + final byte[] result = new byte[size]; + for (int i = 0; i < size; i++) { + result[i] = getNumber(array.get(i)).byteValue(); + } + return result; + } + + private interface ThrowableRunnable { + void run() throws Exception; + } + + private interface VoidResult { + void execute() throws SQLException; + } + + private interface VoidResultInt { + void execute(int index) throws SQLException; + } + + private interface VoidResultObject { + void execute(T data) throws SQLException; + } + + private interface BooleanResult { + boolean get() throws SQLException; + } + + private interface IntResult { + int get() throws SQLException; + } + + private interface StringResult { + String get() throws SQLException; + } + + private interface ObjectResult { + T get() throws SQLException; + } + + private interface BooleanColumnResultInt { + boolean get(int columnIndex) throws SQLException; + } + + private interface BooleanColumnResultString { + boolean get(String columnName) throws SQLException; + } + + private interface NumberColumnResultInt { + Number get(int columnIndex) throws SQLException; + } + + private interface NumberColumnResultIntString { + Number get(String columnName) throws SQLException; + } + + private interface StringColumnResultInt { + String get(int columnIndex) throws SQLException; + } + + private interface StringColumnResultIntString { + String get(String columnName) throws SQLException; + } + + private interface ObjectColumnResultInt { + T get(int columnIndex) throws SQLException; + } + + private interface ObjectColumnResultString { + T get(String columnName) throws SQLException; + } + + private interface VoidColumnResultIntObject { + void execute(int columnIndex, T value) throws SQLException; + } + + private interface VoidColumnResultStringObject { + void execute(String columnName, T value) throws SQLException; + } +}