From 4d795edf01a940881a9f7217aa1b161348628a24 Mon Sep 17 00:00:00 2001 From: Govindas Date: Sat, 2 May 2020 20:25:36 +0300 Subject: [PATCH] Merge FranKusmiruk's Pull Request --- README.md | 45 ++-- build.gradle | 10 +- gradle/wrapper/gradle-wrapper.properties | 2 +- .../java/com/btk5h/skriptdb/SkriptUtil.java | 68 +----- .../skriptdb/skript/EffExecuteStatement.java | 198 +++++++++--------- .../java/com/btk5h/skriptdb/skript/Types.java | 2 +- src/main/resources/plugin.yml | 2 +- 7 files changed, 128 insertions(+), 199 deletions(-) diff --git a/README.md b/README.md index b040420..564d0ca 100644 --- a/README.md +++ b/README.md @@ -3,21 +3,35 @@ > Sensible SQL support for Skript. --- +### Expression `Data Source` => `datasource` +Stores the connection information for a data source. This should be saved to a variable in a + `script load` event or manually through an effect command. + + The url format for your database may vary! The example provided uses a MySQL database. +#### Syntax +``` +[the] data(base|[ ]source) [(of|at)] %string% +``` + +#### Examples +``` +set {sql} to the database "mysql://localhost:3306/mydatabase?user=admin&password=12345&useSSL=false" +``` + +--- + ### Effect `Execute Statement` Executes a statement on a database and optionally stores the result in a variable. Expressions embedded in the query will be escaped to avoid SQL injection. -

+ If a single variable, such as `{test}`, is passed, the variable will be set to the number of affected rows. -

+ If a list variable, such as `{test::*}`, is passed, the query result will be mapped to the list variable in the form `{test::::}` - - Specifying `synchronously` will make skript-db execute the query on the event thread, which is useful for async - events. Note that skript-db will ignore this flag if you attempt to run this on the main thread. #### Syntax ``` -[synchronously] execute %string% (in|on) %datasource% [and store [[the] (output|result)[s]] (to|in) [the] [var[iable]] %-objects%] +execute %string% (in|on) %datasource% [and store [[the] (output|result)[s]] (to|in) [the] [var[iable]] %-objects%] ``` #### Examples @@ -25,7 +39,7 @@ Executes a statement on a database and optionally stores the result in a variabl execute "select * from table" in {sql} and store the result in {output::*} ``` ``` -execute "select * where player=%{player}%" in {sql} and store the result in {output::*} +execute "select * from %{table variable}%" in {sql} and store the result in {output::*} ``` --- @@ -56,20 +70,3 @@ execute unsafe {fully dynamic query} in {sql} --- -### Expression `Data Source` => `datasource` -Stores the connection information for a data source. This should be saved to a variable in a - `script load` event or manually through an effect command. - - The url format for your database may vary! The example provided uses a MySQL database. -#### Syntax -``` -[the] data(base|[ ]source) [(of|at)] %string% [with [a] [max[imum]] [connection] life[ ]time of %timespan%]" -``` - -#### Examples -``` -set {sql} to the database "mysql://localhost:3306/mydatabase?user=admin&password=12345&useSSL=false" -``` - ---- - diff --git a/build.gradle b/build.gradle index 84a03bc..097aebd 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,12 @@ group 'com.btk5h.skript-db' -version '0.2.1' +version '0.1.1' buildscript { repositories { jcenter() } dependencies { - classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.1' + classpath 'com.github.jengelman.gradle.plugins:shadow:4.0.2' } } @@ -24,13 +24,13 @@ repositories { url 'https://oss.sonatype.org/content/groups/public/' } maven { - url 'http://maven.njol.ch/repo/' + url 'http://jitpack.io/' } } dependencies { - shadow 'org.spigotmc:spigot-api:1.11-R0.1-SNAPSHOT' - shadow 'ch.njol:skript:2.2-SNAPSHOT' + shadow 'org.spigotmc:spigot-api:1.13.2-R0.1-SNAPSHOT' + shadow 'com.github.SkriptLang:Skript:2.3.6' compile 'com.zaxxer:HikariCP:3.4.3' } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3f744f9..4bcfde0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip diff --git a/src/main/java/com/btk5h/skriptdb/SkriptUtil.java b/src/main/java/com/btk5h/skriptdb/SkriptUtil.java index 290094b..86075cb 100644 --- a/src/main/java/com/btk5h/skriptdb/SkriptUtil.java +++ b/src/main/java/com/btk5h/skriptdb/SkriptUtil.java @@ -1,25 +1,17 @@ package com.btk5h.skriptdb; -import org.bukkit.event.Event; +import ch.njol.skript.Skript; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.VariableString; import java.lang.reflect.Field; import java.util.Arrays; import java.util.Optional; -import java.util.Set; - -import ch.njol.skript.Skript; -import ch.njol.skript.effects.Delay; -import ch.njol.skript.lang.Expression; -import ch.njol.skript.lang.Variable; -import ch.njol.skript.lang.VariableString; public class SkriptUtil { private static final Field STRING; - private static final Field SIMPLE; - private static final Field DELAYED; private static final Field EXPR; - private static final Field VARIABLE_NAME; static { Field _FIELD = null; @@ -32,25 +24,6 @@ public class SkriptUtil { } STRING = _FIELD; - try { - _FIELD = VariableString.class.getDeclaredField("simple"); - _FIELD.setAccessible(true); - } catch (NoSuchFieldException e) { - Skript.error("Skript's 'simple' field could not be resolved."); - e.printStackTrace(); - } - SIMPLE = _FIELD; - - try { - _FIELD = Delay.class.getDeclaredField("delayed"); - _FIELD.setAccessible(true); - } catch (NoSuchFieldException e) { - e.printStackTrace(); - Skript.warning("Skript's 'delayed' method could not be resolved. Some Skript warnings may " + - "not be available."); - } - DELAYED = _FIELD; - try { Optional> expressionInfo = Arrays.stream(VariableString.class.getDeclaredClasses()) .filter(cls -> cls.getSimpleName().equals("ExpressionInfo")) @@ -67,33 +40,6 @@ public class SkriptUtil { Skript.error("Skript's 'expr' field could not be resolved."); } EXPR = _FIELD; - - try { - _FIELD = Variable.class.getDeclaredField("name"); - _FIELD.setAccessible(true); - } catch (NoSuchFieldException e) { - e.printStackTrace(); - Skript.error("Skript's 'variable name' method could not be resolved."); - } - VARIABLE_NAME = _FIELD; - } - - @SuppressWarnings("unchecked") - public static void delay(Event e) { - if (DELAYED != null) { - try { - ((Set) DELAYED.get(null)).add(e); - } catch (IllegalAccessException ignored) { - } - } - } - - public static String getSimpleString(VariableString vs) { - try { - return (String) SIMPLE.get(vs); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } } public static Object[] getTemplateString(VariableString vs) { @@ -112,12 +58,4 @@ public class SkriptUtil { } } - public static VariableString getVariableName(Variable var) { - try { - return (VariableString) VARIABLE_NAME.get(var); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - return null; - } } diff --git a/src/main/java/com/btk5h/skriptdb/skript/EffExecuteStatement.java b/src/main/java/com/btk5h/skriptdb/skript/EffExecuteStatement.java index 079011b..2adc842 100644 --- a/src/main/java/com/btk5h/skriptdb/skript/EffExecuteStatement.java +++ b/src/main/java/com/btk5h/skriptdb/skript/EffExecuteStatement.java @@ -1,36 +1,28 @@ package com.btk5h.skriptdb.skript; +import ch.njol.skript.Skript; +import ch.njol.skript.effects.Delay; +import ch.njol.skript.lang.*; +import ch.njol.skript.variables.Variables; +import ch.njol.util.Kleenean; +import ch.njol.util.Pair; import com.btk5h.skriptdb.SkriptDB; import com.btk5h.skriptdb.SkriptUtil; import com.zaxxer.hikari.HikariDataSource; - import org.bukkit.Bukkit; import org.bukkit.event.Event; -import org.eclipse.jdt.annotation.Nullable; +import javax.sql.DataSource; +import javax.sql.rowset.CachedRowSet; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSetMetaData; import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import javax.sql.rowset.CachedRowSet; - -import ch.njol.skript.Skript; -import ch.njol.skript.effects.Delay; -import ch.njol.skript.lang.Expression; -import ch.njol.skript.lang.SkriptParser; -import ch.njol.skript.lang.TriggerItem; -import ch.njol.skript.lang.Variable; -import ch.njol.skript.lang.VariableString; -import ch.njol.skript.variables.Variables; -import ch.njol.util.Kleenean; - /** * Executes a statement on a database and optionally stores the result in a variable. Expressions * embedded in the query will be escaped to avoid SQL injection. @@ -41,19 +33,18 @@ import ch.njol.util.Kleenean; * If a list variable, such as `{test::*}`, is passed, the query result will be mapped to the list * variable in the form `{test::::}` * - * Specifying `synchronously` will make skript-db execute the query on the event thread, which is useful for async - * events. Note that skript-db will ignore this flag if you attempt to run this on the main thread. - * * @name Execute Statement - * @pattern [synchronously] execute %string% (in|on) %datasource% [and store [[the] (output|result)[s]] (to|in) + * @pattern execute %string% (in|on) %datasource% [and store [[the] (output|result)[s]] (to|in) * [the] [var[iable]] %-objects%] * @example execute "select * from table" in {sql} and store the result in {output::*} - * @example execute "select * where player=%{player}%" in {sql} and store the result in {output::*} + * @example execute "select * from %{table variable}%" in {sql} and store the result in {output::*} * @since 0.1.0 */ public class EffExecuteStatement extends Delay { static { - Skript.registerEffect(EffExecuteStatement.class, "[(1¦synchronously)] execute %string% (in|on) %datasource% [and store [[the] (output|result)[s]] (to|in) [the] [var[iable]] %-objects%]"); + Skript.registerEffect(EffExecuteStatement.class, + "execute %string% (in|on) %datasource% " + + "[and store [[the] (output|result)[s]] (to|in) [the] [var[iable]] %-objects%]"); } static String lastError; @@ -66,99 +57,62 @@ public class EffExecuteStatement extends Delay { private VariableString var; private boolean isLocal; private boolean isList; - private boolean isSync; - - private void continueScriptExecution(Event e, String res) { - lastError = res; - - if (getNext() != null) { - TriggerItem.walk(getNext(), e); - } - } + private Map doLater = new HashMap<>(); @Override protected void execute(Event e) { - boolean isMainThread = Bukkit.isPrimaryThread(); + DataSource ds = dataSource.getSingle(e); + Pair> query = parseQuery(e); + String baseVariable = var != null ? var.toString(e).toLowerCase(Locale.ENGLISH) : null; - if (isSync) { - String result = executeStatement(e); - continueScriptExecution(e, result); - } else { + if (ds == null) + return; - CompletableFuture sql = - CompletableFuture.supplyAsync(() -> executeStatement(e), threadPool); + Object locals = Variables.removeLocals(e); + CompletableFuture sql = + CompletableFuture.supplyAsync(() -> executeStatement(ds, baseVariable, query), threadPool); - sql.whenComplete((res, err) -> { - if (err != null) { - err.printStackTrace(); + sql.whenComplete((res, err) -> { + if (err != null) { + err.printStackTrace(); + } + + Bukkit.getScheduler().runTask(SkriptDB.getInstance(), () -> { + lastError = res; + + if (getNext() != null) { + if (locals != null) + Variables.setLocalVariables(e, locals); + doLater.forEach((name, value) -> setVariable(e, name, value)); + doLater.clear(); + TriggerItem.walk(getNext(), e); + Variables.removeLocals(e); } - - Bukkit.getScheduler().runTask(SkriptDB.getInstance(), () -> continueScriptExecution(e, res)); }); - } + }); } @Override protected TriggerItem walk(Event e) { debug(e, true); - if (!isSync) { - SkriptUtil.delay(e); - } + Delay.addDelayedEvent(e); execute(e); return null; } - private String executeStatement(Event e) { - HikariDataSource ds = dataSource.getSingle(e); - - if (ds == null) { - return "Data source is not set"; - } - - try (Connection conn = ds.getConnection(); - PreparedStatement stmt = createStatement(e, conn)) { - - boolean hasResultSet = stmt.execute(); - - if (var != null) { - String baseVariable = var.toString(e) - .toLowerCase(Locale.ENGLISH); - if (isList) { - baseVariable = baseVariable.substring(0, baseVariable.length() - 1); - } - - if (hasResultSet) { - CachedRowSet crs = SkriptDB.getRowSetFactory().createCachedRowSet(); - crs.populate(stmt.getResultSet()); - - if (isList) { - populateVariable(e, crs, baseVariable); - } else { - crs.last(); - setVariable(e, baseVariable, crs.getRow()); - } - } else if (!isList) { - setVariable(e, baseVariable, stmt.getUpdateCount()); - } - } - } catch (SQLException ex) { - return ex.getMessage(); - } - return null; - } - - private PreparedStatement createStatement(Event e, Connection conn) throws SQLException { + private Pair> parseQuery(Event e) { if (!(query instanceof VariableString)) { - return conn.prepareStatement(query.getSingle(e)); + return new Pair<>(query.getSingle(e), null); } - - if (((VariableString) query).isSimple()) { - return conn.prepareStatement(SkriptUtil.getSimpleString(((VariableString) query))); + VariableString q = (VariableString) query; + if (q.isSimple()) { + return new Pair<>(q.toString(e), null); } StringBuilder sb = new StringBuilder(); List parameters = new ArrayList<>(); - Object[] objects = SkriptUtil.getTemplateString(((VariableString) query)); + Object[] objects = SkriptUtil.getTemplateString(q); + for (int i = 0; i < objects.length; i++) { Object o = objects[i]; if (o instanceof String) { @@ -197,11 +151,52 @@ public class EffExecuteStatement extends Delay { } } } + return new Pair<>(sb.toString(), parameters); + } - PreparedStatement stmt = conn.prepareStatement(sb.toString()); + private String executeStatement(DataSource ds, String baseVariable, Pair> query) { + if (ds == null) { + return "Data source is not set"; + } - for (int i = 0; i < parameters.size(); i++) { - stmt.setObject(i + 1, parameters.get(i)); + try (Connection conn = ds.getConnection(); + PreparedStatement stmt = createStatement(conn, query)) { + + boolean hasResultSet = stmt.execute(); + + if (baseVariable != null) { + if (isList) { + baseVariable = baseVariable.substring(0, baseVariable.length() - 1); + } + + if (hasResultSet) { + CachedRowSet crs = SkriptDB.getRowSetFactory().createCachedRowSet(); + crs.populate(stmt.getResultSet()); + + if (isList) { + populateVariable(crs, baseVariable); + } else { + crs.last(); + doLater.put(baseVariable, crs.getRow()); + } + } else if (!isList) { + doLater.put(baseVariable, stmt.getUpdateCount()); + } + } + } catch (SQLException ex) { + return ex.getMessage(); + } + return null; + } + + private PreparedStatement createStatement(Connection conn, Pair> query) throws SQLException { + PreparedStatement stmt = conn.prepareStatement(query.getFirst()); + List parameters = query.getSecond(); + + if (parameters != null) { + for (int i = 0; i < parameters.size(); i++) { + stmt.setObject(i + 1, parameters.get(i)); + } } return stmt; @@ -225,20 +220,20 @@ public class EffExecuteStatement extends Delay { Variables.setVariable(name.toLowerCase(Locale.ENGLISH), obj, e, isLocal); } - private void populateVariable(Event e, CachedRowSet crs, String baseVariable) + private void populateVariable(CachedRowSet crs, String baseVariable) throws SQLException { ResultSetMetaData meta = crs.getMetaData(); int columnCount = meta.getColumnCount(); for (int i = 1; i <= columnCount; i++) { String label = meta.getColumnLabel(i); - setVariable(e, baseVariable + label, label); + doLater.put(baseVariable + label, label); } int rowNumber = 1; while (crs.next()) { for (int i = 1; i <= columnCount; i++) { - setVariable(e, baseVariable + meta.getColumnLabel(i).toLowerCase(Locale.ENGLISH) + doLater.put(baseVariable + meta.getColumnLabel(i).toLowerCase(Locale.ENGLISH) + Variable.SEPARATOR + rowNumber, crs.getObject(i)); } rowNumber++; @@ -246,7 +241,7 @@ public class EffExecuteStatement extends Delay { } @Override - public String toString(@Nullable Event e, boolean debug) { + public String toString(Event e, boolean debug) { return "execute " + query.toString(e, debug) + " in " + dataSource.toString(e, debug); } @@ -265,10 +260,9 @@ public class EffExecuteStatement extends Delay { } dataSource = (Expression) exprs[1]; Expression expr = exprs[2]; - isSync = parseResult.mark == 1; if (expr instanceof Variable) { Variable varExpr = (Variable) expr; - var = SkriptUtil.getVariableName(varExpr); + var = varExpr.getName(); isLocal = varExpr.isLocal(); isList = varExpr.isList(); } else if (expr != null) { diff --git a/src/main/java/com/btk5h/skriptdb/skript/Types.java b/src/main/java/com/btk5h/skriptdb/skript/Types.java index d26610c..7a76cbe 100644 --- a/src/main/java/com/btk5h/skriptdb/skript/Types.java +++ b/src/main/java/com/btk5h/skriptdb/skript/Types.java @@ -64,7 +64,7 @@ public class Types { } @Override - public boolean canBeInstantiated(Class c) { + protected boolean canBeInstantiated() { return false; } })); diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 1958e68..2df1b4e 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,4 +1,4 @@ name: skript-db -version: 0.3.0-Govindas +version: 0.1.1 main: com.btk5h.skriptdb.SkriptDB depend: [Skript]