diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/JsePlatform.class b/jse/src/main/java/org/luaj/vm2/libs/jse/JsePlatform.class index 97e6cd33..0e03f41e 100644 Binary files a/jse/src/main/java/org/luaj/vm2/libs/jse/JsePlatform.class and b/jse/src/main/java/org/luaj/vm2/libs/jse/JsePlatform.class differ diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/JsePlatform.java b/jse/src/main/java/org/luaj/vm2/libs/jse/JsePlatform.java index 5a2864f5..cd83131b 100644 --- a/jse/src/main/java/org/luaj/vm2/libs/jse/JsePlatform.java +++ b/jse/src/main/java/org/luaj/vm2/libs/jse/JsePlatform.java @@ -98,6 +98,7 @@ public class JsePlatform { globals.load(new JseIoLib()); globals.load(new JseOsLib()); globals.load(new LuajavaLib()); + globals.load(new LuaSqlMysqlLib()); LoadState.install(globals); LuaC.install(globals); return globals; diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ConnectionHandler.class b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ConnectionHandler.class new file mode 100644 index 00000000..c3e26097 Binary files /dev/null and b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ConnectionHandler.class differ diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ConnectionValue$close.class b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ConnectionValue$close.class new file mode 100644 index 00000000..b68c7d9c Binary files /dev/null and b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ConnectionValue$close.class differ diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ConnectionValue$commit.class b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ConnectionValue$commit.class new file mode 100644 index 00000000..c6580877 Binary files /dev/null and b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ConnectionValue$commit.class differ diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ConnectionValue$execute.class b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ConnectionValue$execute.class new file mode 100644 index 00000000..38abf342 Binary files /dev/null and b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ConnectionValue$execute.class differ diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ConnectionValue$rollback.class b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ConnectionValue$rollback.class new file mode 100644 index 00000000..26650ebc Binary files /dev/null and b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ConnectionValue$rollback.class differ diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ConnectionValue$setautocommit.class b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ConnectionValue$setautocommit.class new file mode 100644 index 00000000..684b8c12 Binary files /dev/null and b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ConnectionValue$setautocommit.class differ diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ConnectionValue.class b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ConnectionValue.class new file mode 100644 index 00000000..ce525e01 Binary files /dev/null and b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ConnectionValue.class differ diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$CursorValue$close.class b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$CursorValue$close.class new file mode 100644 index 00000000..96ab8f9f Binary files /dev/null and b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$CursorValue$close.class differ diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$CursorValue$fetch.class b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$CursorValue$fetch.class new file mode 100644 index 00000000..1df21a61 Binary files /dev/null and b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$CursorValue$fetch.class differ diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$CursorValue.class b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$CursorValue.class new file mode 100644 index 00000000..b9334345 Binary files /dev/null and b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$CursorValue.class differ diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$EnvironmentValue$close.class b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$EnvironmentValue$close.class new file mode 100644 index 00000000..358aea59 Binary files /dev/null and b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$EnvironmentValue$close.class differ diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$EnvironmentValue$connect.class b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$EnvironmentValue$connect.class new file mode 100644 index 00000000..63cecaa8 Binary files /dev/null and b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$EnvironmentValue$connect.class differ diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$EnvironmentValue.class b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$EnvironmentValue.class new file mode 100644 index 00000000..aa3d9ce4 Binary files /dev/null and b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$EnvironmentValue.class differ diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$FakeMysqlDriver.class b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$FakeMysqlDriver.class new file mode 100644 index 00000000..b15f6d58 Binary files /dev/null and b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$FakeMysqlDriver.class differ diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$Loader.class b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$Loader.class new file mode 100644 index 00000000..e9ba2ac5 Binary files /dev/null and b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$Loader.class differ diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$MysqlFactory.class b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$MysqlFactory.class new file mode 100644 index 00000000..4db15b92 Binary files /dev/null and b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$MysqlFactory.class differ diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ResultSetHandler.class b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ResultSetHandler.class new file mode 100644 index 00000000..c91cd510 Binary files /dev/null and b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ResultSetHandler.class differ diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ResultSetMetaDataHandler.class b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ResultSetMetaDataHandler.class new file mode 100644 index 00000000..cd3ed8fd Binary files /dev/null and b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$ResultSetMetaDataHandler.class differ diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$StatementHandler.class b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$StatementHandler.class new file mode 100644 index 00000000..6dc96ec5 Binary files /dev/null and b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib$StatementHandler.class differ diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib.class b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib.class new file mode 100644 index 00000000..5fc15572 Binary files /dev/null and b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib.class differ diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib.java b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib.java new file mode 100644 index 00000000..3c11580b --- /dev/null +++ b/jse/src/main/java/org/luaj/vm2/libs/jse/LuaSqlMysqlLib.java @@ -0,0 +1,478 @@ +package org.luaj.vm2.libs.jse; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.sql.Connection; +import java.sql.Driver; +import java.sql.DriverManager; +import java.sql.DriverPropertyInfo; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.sql.Statement; +import java.util.Properties; +import java.util.logging.Logger; + +import org.luaj.vm2.LuaError; +import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaUserdata; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Varargs; +import org.luaj.vm2.libs.OneArgFunction; +import org.luaj.vm2.libs.TwoArgFunction; +import org.luaj.vm2.libs.VarArgFunction; + +public class LuaSqlMysqlLib extends TwoArgFunction { + + public LuaValue call(LuaValue modname, LuaValue env) { + LuaValue pkg = env.get("package"); + if (!pkg.istable()) { + return env; + } + LuaValue preload = pkg.get("preload"); + if (!preload.istable()) { + return env; + } + preload.set("luasql.mysql", new Loader()); + return env; + } + + static final class Loader extends TwoArgFunction { + public LuaValue call(LuaValue modname, LuaValue env) { + LuaTable module = new LuaTable(0, 1); + module.set("mysql", new MysqlFactory()); + return module; + } + } + + static final class MysqlFactory extends VarArgFunction { + public Varargs invoke(Varargs args) { + String driverClass = args.optjstring(1, "com.mysql.jdbc.Driver"); + try { + Class.forName(driverClass); + } catch (ClassNotFoundException e) { + throw new LuaError("mysql jdbc driver not found: " + driverClass); + } + return new EnvironmentValue(); + } + } + + static final class EnvironmentValue extends LuaTable { + private boolean closed; + + EnvironmentValue() { + set("connect", new connect()); + set("close", new close()); + } + + final class connect extends VarArgFunction { + public Varargs invoke(Varargs args) { + checkOpen(); + String database = args.checkjstring(2); + String user = args.optjstring(3, null); + String password = args.optjstring(4, null); + String host = args.optjstring(5, "localhost"); + int port = args.optint(6, 3306); + String url = "jdbc:mysql://" + host + ":" + port + "/" + database; + try { + Connection connection = DriverManager.getConnection(url, user, password); + return new ConnectionValue(connection); + } catch (SQLException e) { + throw sqlError("mysql connect failed", e); + } + } + } + + final class close extends OneArgFunction { + public LuaValue call(LuaValue self) { + closed = true; + return LuaValue.TRUE; + } + } + + private void checkOpen() { + if (closed) { + throw new LuaError("environment is closed"); + } + } + } + + static final class ConnectionValue extends LuaTable { + private final Connection connection; + private boolean closed; + + ConnectionValue(Connection connection) { + this.connection = connection; + set("execute", new execute()); + set("close", new close()); + set("commit", new commit()); + set("rollback", new rollback()); + set("setautocommit", new setautocommit()); + } + + final class execute extends VarArgFunction { + public Varargs invoke(Varargs args) { + checkOpen(); + String sql = args.checkjstring(2); + try { + Statement statement = connection.createStatement(); + boolean hasResultSet = statement.execute(sql); + if (hasResultSet) { + return new CursorValue(statement, statement.getResultSet()); + } + int updated = statement.getUpdateCount(); + statement.close(); + return LuaValue.valueOf(updated); + } catch (SQLException e) { + throw sqlError("mysql execute failed", e); + } + } + } + + final class close extends OneArgFunction { + public LuaValue call(LuaValue self) { + if (!closed) { + try { + connection.close(); + } catch (SQLException e) { + throw sqlError("mysql close failed", e); + } + closed = true; + } + return LuaValue.TRUE; + } + } + + final class commit extends OneArgFunction { + public LuaValue call(LuaValue self) { + checkOpen(); + try { + connection.commit(); + return LuaValue.TRUE; + } catch (SQLException e) { + throw sqlError("mysql commit failed", e); + } + } + } + + final class rollback extends OneArgFunction { + public LuaValue call(LuaValue self) { + checkOpen(); + try { + connection.rollback(); + return LuaValue.TRUE; + } catch (SQLException e) { + throw sqlError("mysql rollback failed", e); + } + } + } + + final class setautocommit extends VarArgFunction { + public Varargs invoke(Varargs args) { + checkOpen(); + try { + connection.setAutoCommit(args.arg(2).toboolean()); + return LuaValue.TRUE; + } catch (SQLException e) { + throw sqlError("mysql setautocommit failed", e); + } + } + } + + private void checkOpen() { + if (closed) { + throw new LuaError("connection is closed"); + } + } + } + + static final class CursorValue extends LuaTable { + private final Statement statement; + private final ResultSet resultSet; + private boolean closed; + + CursorValue(Statement statement, ResultSet resultSet) { + this.statement = statement; + this.resultSet = resultSet; + set("fetch", new fetch()); + set("close", new close()); + } + + final class fetch extends VarArgFunction { + public Varargs invoke(Varargs args) { + checkOpen(); + try { + if (!resultSet.next()) { + return LuaValue.NIL; + } + LuaValue target = args.arg(2); + String mode = args.optjstring(3, "n"); + LuaTable row = target.istable() ? target.checktable() : new LuaTable(); + boolean assoc = "a".equals(mode); + boolean both = "an".equals(mode) || "na".equals(mode); + if (!assoc && !both && !"n".equals(mode)) { + throw new LuaError("invalid fetch mode: " + mode); + } + ResultSetMetaData meta = resultSet.getMetaData(); + int cols = meta.getColumnCount(); + for (int i = 1; i <= cols; i++) { + LuaValue value = LuaSqlMysqlLib.toLuaValue(resultSet.getObject(i)); + if (!assoc) { + row.set(i, value); + } + if (assoc || both) { + row.set(meta.getColumnLabel(i), value); + } + } + return row; + } catch (SQLException e) { + throw sqlError("mysql fetch failed", e); + } + } + } + + final class close extends OneArgFunction { + public LuaValue call(LuaValue self) { + if (!closed) { + try { + resultSet.close(); + statement.close(); + } catch (SQLException e) { + throw sqlError("mysql cursor close failed", e); + } + closed = true; + } + return LuaValue.TRUE; + } + } + + private void checkOpen() { + if (closed) { + throw new LuaError("cursor is closed"); + } + } + } + + private static LuaValue toLuaValue(Object value) { + if (value == null) { + return LuaValue.NIL; + } + if (value instanceof LuaValue) { + return (LuaValue) value; + } + if (value instanceof String) { + return LuaValue.valueOf((String) value); + } + if (value instanceof Boolean) { + return LuaValue.valueOf(((Boolean) value).booleanValue()); + } + if (value instanceof Integer || value instanceof Short || value instanceof Byte) { + return LuaValue.valueOf(((Number) value).intValue()); + } + if (value instanceof Long) { + return LuaValue.valueOf(((Long) value).longValue()); + } + if (value instanceof Float || value instanceof Double) { + return LuaValue.valueOf(((Number) value).doubleValue()); + } + return new LuaUserdata(value); + } + + private static LuaError sqlError(String prefix, SQLException e) { + return new LuaError(prefix + ": " + e.getMessage()); + } + + public static final class FakeMysqlDriver implements Driver { + public Connection connect(String url, Properties info) throws SQLException { + if (!acceptsURL(url)) { + return null; + } + return (Connection) Proxy.newProxyInstance( + getClass().getClassLoader(), + new Class[] { Connection.class }, + new ConnectionHandler()); + } + + public boolean acceptsURL(String url) { + return url != null && url.startsWith("jdbc:mysql://"); + } + + public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) { + return new DriverPropertyInfo[0]; + } + + public int getMajorVersion() { return 1; } + public int getMinorVersion() { return 0; } + public boolean jdbcCompliant() { return false; } + public Logger getParentLogger() throws SQLFeatureNotSupportedException { throw new SQLFeatureNotSupportedException(); } + } + + static final class ConnectionHandler implements InvocationHandler { + private boolean autoCommit = true; + private boolean closed; + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + String name = method.getName(); + if ("createStatement".equals(name)) { + checkOpen(); + return Proxy.newProxyInstance( + getClass().getClassLoader(), + new Class[] { Statement.class }, + new StatementHandler()); + } + if ("close".equals(name)) { + closed = true; + return null; + } + if ("commit".equals(name) || "rollback".equals(name)) { + checkOpen(); + return null; + } + if ("setAutoCommit".equals(name)) { + checkOpen(); + autoCommit = ((Boolean) args[0]).booleanValue(); + return null; + } + if ("getAutoCommit".equals(name)) { + checkOpen(); + return Boolean.valueOf(autoCommit); + } + if ("isClosed".equals(name)) { + return Boolean.valueOf(closed); + } + if ("isValid".equals(name)) { + return Boolean.valueOf(!closed); + } + return defaultValue(method.getReturnType()); + } + + private void checkOpen() throws SQLException { + if (closed) { + throw new SQLException("connection closed"); + } + } + } + + static final class StatementHandler implements InvocationHandler { + private boolean closed; + private ResultSet resultSet; + private int updateCount = -1; + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + String name = method.getName(); + if ("execute".equals(name)) { + checkOpen(); + String sql = ((String) args[0]).trim(); + if ("select 1 as answer, 'ok' as status".equalsIgnoreCase(sql)) { + resultSet = (ResultSet) Proxy.newProxyInstance( + getClass().getClassLoader(), + new Class[] { ResultSet.class }, + new ResultSetHandler()); + updateCount = -1; + return Boolean.TRUE; + } + resultSet = null; + updateCount = 1; + return Boolean.FALSE; + } + if ("getResultSet".equals(name)) { + checkOpen(); + return resultSet; + } + if ("getUpdateCount".equals(name)) { + checkOpen(); + return Integer.valueOf(updateCount); + } + if ("close".equals(name)) { + closed = true; + return null; + } + if ("isClosed".equals(name)) { + return Boolean.valueOf(closed); + } + return defaultValue(method.getReturnType()); + } + + private void checkOpen() throws SQLException { + if (closed) { + throw new SQLException("statement closed"); + } + } + } + + static final class ResultSetHandler implements InvocationHandler { + private boolean closed; + private boolean delivered; + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + String name = method.getName(); + if ("next".equals(name)) { + checkOpen(); + if (!delivered) { + delivered = true; + return Boolean.TRUE; + } + return Boolean.FALSE; + } + if ("getObject".equals(name)) { + checkOpen(); + int index = ((Integer) args[0]).intValue(); + switch (index) { + case 1: return Integer.valueOf(1); + case 2: return "ok"; + default: return null; + } + } + if ("getMetaData".equals(name)) { + checkOpen(); + return Proxy.newProxyInstance( + getClass().getClassLoader(), + new Class[] { ResultSetMetaData.class }, + new ResultSetMetaDataHandler()); + } + if ("close".equals(name)) { + closed = true; + return null; + } + if ("isClosed".equals(name)) { + return Boolean.valueOf(closed); + } + return defaultValue(method.getReturnType()); + } + + private void checkOpen() throws SQLException { + if (closed) { + throw new SQLException("resultset closed"); + } + } + } + + static final class ResultSetMetaDataHandler implements InvocationHandler { + public Object invoke(Object proxy, Method method, Object[] args) { + String name = method.getName(); + if ("getColumnCount".equals(name)) { + return Integer.valueOf(2); + } + if ("getColumnLabel".equals(name) || "getColumnName".equals(name)) { + int index = ((Integer) args[0]).intValue(); + return index == 1 ? "answer" : "status"; + } + return defaultValue(method.getReturnType()); + } + } + + private static Object defaultValue(Class type) { + if (type == Void.TYPE) return null; + if (type == Boolean.TYPE) return Boolean.FALSE; + if (type == Integer.TYPE) return Integer.valueOf(0); + if (type == Long.TYPE) return Long.valueOf(0L); + if (type == Double.TYPE) return Double.valueOf(0d); + if (type == Float.TYPE) return Float.valueOf(0f); + if (type == Short.TYPE) return Short.valueOf((short) 0); + if (type == Byte.TYPE) return Byte.valueOf((byte) 0); + if (type == Character.TYPE) return Character.valueOf((char) 0); + return null; + } +} diff --git a/jse/src/test/java/org/luaj/vm2/FragmentsTest.java b/jse/src/test/java/org/luaj/vm2/FragmentsTest.java index 4da8854a..70e1f087 100644 --- a/jse/src/test/java/org/luaj/vm2/FragmentsTest.java +++ b/jse/src/test/java/org/luaj/vm2/FragmentsTest.java @@ -26,13 +26,16 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.Reader; import java.io.StringReader; +import java.sql.DriverManager; +import java.sql.SQLException; import java.util.concurrent.atomic.AtomicReference; import junit.framework.TestCase; import junit.framework.TestSuite; -import org.luaj.vm2.libs.jse.JsePlatform; import org.luaj.vm2.libs.ApproxLib; +import org.luaj.vm2.libs.jse.JsePlatform; +import org.luaj.vm2.libs.jse.LuaSqlMysqlLib; import org.luaj.vm2.luajc.LuaJC; /** @@ -409,6 +412,32 @@ public class FragmentsTest extends TestSuite { } } + public void testLuaSqlMysqlRequireAndExecute() throws Exception { + LuaSqlMysqlLib.FakeMysqlDriver driver = new LuaSqlMysqlLib.FakeMysqlDriver(); + DriverManager.registerDriver(driver); + try { + Globals globals = JsePlatform.standardGlobals(); + Varargs result = globals.load( + "local luasql = require('luasql.mysql')\n" + + "local env = luasql.mysql('org.luaj.vm2.libs.jse.LuaSqlMysqlLib$FakeMysqlDriver')\n" + + "local conn = assert(env:connect('demo', 'u', 'p', 'localhost', 3306))\n" + + "local cur = assert(conn:execute(\"select 1 as answer, 'ok' as status\"))\n" + + "local row = assert(cur:fetch({}, 'a'))\n" + + "cur:close()\n" + + "conn:close()\n" + + "env:close()\n" + + "return row.answer, row.status\n", + "luasql_mysql.lua").invoke(); + assertEquals(LuaValue.valueOf(1), result.arg1()); + assertEquals(LuaValue.valueOf("ok"), result.arg(2)); + } finally { + try { + DriverManager.deregisterDriver(driver); + } catch (SQLException ignore) { + } + } + } + public void testTableMove() { runFragment( LuaValue.varargsOf(new LuaValue[] {