From 4de7dff0ad6ab8f419d5e25dc4e61fe744549c70 Mon Sep 17 00:00:00 2001 From: szumielxd <43210079+szumielxd@users.noreply.github.com> Date: Wed, 17 Jul 2024 20:26:07 +0200 Subject: [PATCH 1/7] Added missing Skript repository --- pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pom.xml b/pom.xml index 78da008..eb6276e 100644 --- a/pom.xml +++ b/pom.xml @@ -30,6 +30,10 @@ sk89q https://maven.sk89q.com/repo + + skript + https://repo.skriptlang.org/releases + From 52d111dbfa1c8b3bcbeb2e6b23097acd5b3dedbb Mon Sep 17 00:00:00 2001 From: szumielxd <43210079+szumielxd@users.noreply.github.com> Date: Wed, 17 Jul 2024 20:36:23 +0200 Subject: [PATCH 2/7] Ignore Eclipse internal files --- .gitignore | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.gitignore b/.gitignore index a942888..25d3b8a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,19 @@ out/ target compile/ + +# eclipse specific git ignore +*.pydevproject +.project +.metadata +bin/** +tmp/** +tmp/**/* +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath From 110f3982adc8ae5eec73e1415763862040dab1e6 Mon Sep 17 00:00:00 2001 From: szumielxd <43210079+szumielxd@users.noreply.github.com> Date: Thu, 18 Jul 2024 00:45:02 +0200 Subject: [PATCH 3/7] Added option to provide PreparedStatement-like query argument binding --- .../skriptdb/skript/EffExecuteStatement.java | 228 +++++++++--------- 1 file changed, 120 insertions(+), 108 deletions(-) diff --git a/src/main/java/com/btk5h/skriptdb/skript/EffExecuteStatement.java b/src/main/java/com/btk5h/skriptdb/skript/EffExecuteStatement.java index 6a74670..9a935b2 100644 --- a/src/main/java/com/btk5h/skriptdb/skript/EffExecuteStatement.java +++ b/src/main/java/com/btk5h/skriptdb/skript/EffExecuteStatement.java @@ -25,6 +25,7 @@ import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.regex.Pattern; /** * Executes a statement on a database and optionally stores the result in a variable. Expressions @@ -45,17 +46,20 @@ import java.util.concurrent.Executors; */ public class EffExecuteStatement extends Effect { private static final ExecutorService threadPool = Executors.newFixedThreadPool(SkriptDB.getInstance().getConfig().getInt("thread-pool-size", 10)); + private static final Pattern ARGUMENT_PLACEHOLDER = Pattern.compile("(? query; private Expression dataSource; + private Expression queryArguments; private VariableString var; private boolean isLocal; private boolean isList; @@ -157,142 +161,144 @@ public class EffExecuteStatement extends Effect { } private Pair> parseQuery(Event e) { - if (!(query instanceof VariableString)) { - return new Pair<>(query.getSingle(e), null); + if (queryArguments != null) { + Object[] args = queryArguments.getArray(e); + String queryString = query.getSingle(e); + int queryArgCount = (int) ARGUMENT_PLACEHOLDER.matcher(queryString).results().count(); + if (queryArgCount != args.length) { + Skript.warning("Your query has %d question marks, but you provided %d arguments."); + args = Arrays.copyOf(args, queryArgCount); + } + return new Pair<>(query.getSingle(e), List.of(args)); + } else if (query instanceof VariableString && !((VariableString) query).isSimple()) { + return parseVariableQuery(e, (VariableString) query); } - VariableString q = (VariableString) query; - if (q.isSimple()) { - return new Pair<>(q.toString(e), null); - } - + return new Pair<>(query.getSingle(e), null); + } + + private Pair> parseVariableQuery(Event e, VariableString varQuery) { StringBuilder sb = new StringBuilder(); - List parameters = new ArrayList<>(); - Object[] objects = SkriptUtil.getTemplateString(q); - + List parameters = new LinkedList<>(); + Object[] objects = SkriptUtil.getTemplateString(varQuery); + for (int i = 0; i < objects.length; i++) { - Object o = objects[i]; - if (o instanceof String) { - sb.append(o); + if (objects[i] instanceof String) { + sb.append(objects[i]); } else { - Expression expr; - if (o instanceof Expression) - expr = (Expression) o; - else - expr = SkriptUtil.getExpressionFromInfo(o); - - String before = getString(objects, i - 1); - String after = getString(objects, i + 1); - boolean standaloneString = false; - - if (before != null && after != null) { - if (before.endsWith("'") && after.endsWith("'")) { - standaloneString = true; - } - } - + Expression expr = objects[i] instanceof Expression ? (Expression) objects[i] : SkriptUtil.getExpressionFromInfo(objects[i]); + boolean standaloneString = isStandaloneString(objects, i); Object expressionValue = expr.getSingle(e); - if (expr instanceof ExprUnsafe) { - sb.append(expressionValue); - - if (standaloneString && expressionValue instanceof String) { - String rawExpression = ((ExprUnsafe) expr).getRawExpression(); - Skript.warning( - String.format("Unsafe may have been used unnecessarily. Try replacing 'unsafe %1$s' with %1$s", - rawExpression)); - } - } else { - parameters.add(expressionValue); - sb.append('?'); - - if (standaloneString) { - Skript.warning("Do not surround expressions with quotes!"); - } + Pair toAppend = parseExpressionQuery(expr, expressionValue, standaloneString); + sb.append(toAppend.getFirst()); + if (toAppend.getSecond() != null) { + parameters.add(toAppend.getSecond()); } } } return new Pair<>(sb.toString(), parameters); } + + private Pair parseExpressionQuery(Expression expr, Object expressionValue, boolean standaloneString) { + if (expr instanceof ExprUnsafe) { + if (standaloneString && expressionValue instanceof String) { + Skript.warning( + String.format("Unsafe may have been used unnecessarily. Try replacing 'unsafe %1$s' with %1$s", + ((ExprUnsafe) expr).getRawExpression())); + } + return new Pair<>((String) expressionValue, null); + } else { + if (standaloneString) { + Skript.warning("Do not surround expressions with quotes!"); + } + return new Pair<>("?", expressionValue); + } + } private Object executeStatement(DataSource ds, String baseVariable, Pair> query) { if (ds == null) { return "Data source is not set"; } - Map variableList = new HashMap<>(); - try (Connection conn = ds.getConnection(); - PreparedStatement stmt = createStatement(conn, query)) { + try (Connection conn = ds.getConnection()) { + try (PreparedStatement stmt = createStatement(conn, query)) { + boolean hasResultSet = stmt.execute(); - 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) { - ResultSetMetaData meta = crs.getMetaData(); - int columnCount = meta.getColumnCount(); - - for (int i = 1; i <= columnCount; i++) { - String label = meta.getColumnLabel(i); - variableList.put(baseVariable + label, label); - } - - int rowNumber = 1; - try { - while (crs.next()) { - for (int i = 1; i <= columnCount; i++) { - variableList.put(baseVariable + meta.getColumnLabel(i).toLowerCase(Locale.ENGLISH) - + Variable.SEPARATOR + rowNumber, crs.getObject(i)); - } - rowNumber++; - } - } catch (SQLException ex) { - return ex.getMessage(); - } - } else { - crs.last(); - variableList.put(baseVariable, crs.getRow()); - } - } else if (!isList) { - //if no results are returned and the specified variable isn't a list variable, put the affected rows count in the variable - variableList.put(baseVariable, stmt.getUpdateCount()); + if (baseVariable != null) { + return processBaseVariable(baseVariable, stmt, hasResultSet); } + return Map.of(); } } catch (SQLException ex) { return ex.getMessage(); } + } + + private Object processBaseVariable(String baseVariable, PreparedStatement stmt, boolean hasResultSet) throws SQLException { + Map variableList = new HashMap<>(); + if (isList) { + baseVariable = baseVariable.substring(0, baseVariable.length() - 1); + } + + if (hasResultSet) { + CachedRowSet crs = SkriptDB.getRowSetFactory().createCachedRowSet(); + crs.populate(stmt.getResultSet()); + + if (isList) { + return fetchQueryResultSet(crs, baseVariable); + } else { + crs.last(); + variableList.put(baseVariable, crs.getRow()); + } + } else if (!isList) { + //if no results are returned and the specified variable isn't a list variable, put the affected rows count in the variable + return Map.of(baseVariable, stmt.getUpdateCount()); + } + return Map.of(); + } + + private Map fetchQueryResultSet(CachedRowSet crs, String baseVariable) throws SQLException { + Map variableList = new HashMap<>(); + ResultSetMetaData meta = crs.getMetaData(); + int columnCount = meta.getColumnCount(); + + for (int i = 1; i <= columnCount; i++) { + String label = meta.getColumnLabel(i); + variableList.put(baseVariable + label, label); + } + + int rowNumber = 1; + while (crs.next()) { + for (int i = 1; i <= columnCount; i++) { + variableList.put(baseVariable + meta.getColumnLabel(i).toLowerCase(Locale.ENGLISH) + + Variable.SEPARATOR + rowNumber, crs.getObject(i)); + } + rowNumber++; + } return variableList; } 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)); + if (query.getSecond() != null) { + Iterator iter = query.getSecond().iterator(); + for (int i = 1; iter.hasNext(); i++) { + stmt.setObject(i, iter.next()); } } return stmt; } + + private boolean isStandaloneString(Object[] objects, int index) { + String before = getString(objects, index - 1); + String after = getString(objects, index + 1); + return before != null && before.endsWith("'") && after != null && after.endsWith("'"); + } private String getString(Object[] objects, int index) { - if (index < 0 || index >= objects.length) { - return null; + if (index >= 0 && index < objects.length && objects[index] instanceof String) { + return (String) objects[index]; } - - Object object = objects[index]; - - if (object instanceof String) { - return (String) object; - } - return null; } @@ -334,15 +340,21 @@ public class EffExecuteStatement extends Effect { return false; } dataSource = (Expression) exprs[1]; - Expression expr = exprs[2]; + if (exprs[2] != null) { + if (query instanceof VariableString && !((VariableString) query).isSimple()) { + Skript.warning("Your query string contains expresions, but you've also provided query arguments. Consider using `unsafe` keyword before your query."); + } + queryArguments = (Expression) exprs[2]; + } + Expression resultHolder = exprs[3]; quickly = matchedPattern == 1; - if (expr instanceof Variable) { - Variable varExpr = (Variable) expr; + if (resultHolder instanceof Variable) { + Variable varExpr = (Variable) resultHolder; var = varExpr.getName(); isLocal = varExpr.isLocal(); isList = varExpr.isList(); - } else if (expr != null) { - Skript.error(expr + " is not a variable"); + } else if (resultHolder != null) { + Skript.error(resultHolder + " is not a variable"); return false; } return true; From 2dc1f70f4fa2511d2d496638739a6020d28e4272 Mon Sep 17 00:00:00 2001 From: szumielxd <43210079+szumielxd@users.noreply.github.com> Date: Wed, 24 Jul 2024 14:08:08 +0200 Subject: [PATCH 4/7] Removed some duplicated code and improved readability --- .../skriptdb/skript/EffExecuteStatement.java | 153 +++++++++--------- 1 file changed, 73 insertions(+), 80 deletions(-) diff --git a/src/main/java/com/btk5h/skriptdb/skript/EffExecuteStatement.java b/src/main/java/com/btk5h/skriptdb/skript/EffExecuteStatement.java index 9a935b2..70ca721 100644 --- a/src/main/java/com/btk5h/skriptdb/skript/EffExecuteStatement.java +++ b/src/main/java/com/btk5h/skriptdb/skript/EffExecuteStatement.java @@ -51,104 +51,79 @@ public class EffExecuteStatement extends Effect { static { Skript.registerEffect(EffExecuteStatement.class, - "execute %string% (in|on) %datasource% " + - "[with arg[ument][s] %-objects%] [and store [[the] (output|result)[s]] (to|in) [the] [var[iable]] %-objects%]", - "quickly execute %string% (in|on) %datasource% " + + "[quickly:quickly] execute %string% (in|on) %datasource% " + "[with arg[ument][s] %-objects%] [and store [[the] (output|result)[s]] (to|in) [the] [var[iable]] %-objects%]"); } private Expression query; private Expression dataSource; private Expression queryArguments; - private VariableString var; + private VariableString resultVariableName; private boolean isLocal; private boolean isList; private boolean quickly; private boolean isSync = false; - private void continueScriptExecution(Event e, Object populatedVariables) { - lastError = null; - if (populatedVariables instanceof String) { - lastError = (String) populatedVariables; - } else { - - if (getNext() != null) { - ((Map) populatedVariables).forEach((name, value) -> setVariable(e, name, value)); - } - } - TriggerItem.walk(getNext(), e); - } - @Override protected void execute(Event e) { DataSource ds = dataSource.getSingle(e); - Pair> query = parseQuery(e); - String baseVariable = var != null ? var.toString(e).toLowerCase(Locale.ENGLISH) : null; //if data source isn't set - if (ds == null) return; + if (ds == null) { + return; + } + Pair> parsedQuery = parseQuery(e); + String baseVariable = resultVariableName != null ? resultVariableName.toString(e).toLowerCase(Locale.ENGLISH) : null; + Object locals = Variables.removeLocals(e); //execute SQL statement if (Bukkit.isPrimaryThread()) { - CompletableFuture sql = CompletableFuture.supplyAsync(() -> executeStatement(ds, baseVariable, query), threadPool); - sql.whenComplete((res, err) -> { - if (err != null) { - err.printStackTrace(); - } - //handle last error syntax data - lastError = null; - if (res instanceof String) { - lastError = (String) res; - } - //if local variables are present - //bring back local variables - //populate SQL data into variables - if (!quickly) { - Bukkit.getScheduler().runTask(SkriptDB.getInstance(), () -> { - if (locals != null && getNext() != null) { - Variables.setLocalVariables(e, locals); - } - if (!(res instanceof String)) { - ((Map) res).forEach((name, value) -> setVariable(e, name, value)); - } - TriggerItem.walk(getNext(), e); - //the line below is required to prevent memory leaks - Variables.removeLocals(e); - }); - } else { - if (locals != null && getNext() != null) { - Variables.setLocalVariables(e, locals); - } - if (!(res instanceof String)) { - ((Map) res).forEach((name, value) -> setVariable(e, name, value)); - } - TriggerItem.walk(getNext(), e); - //the line below is required to prevent memory leaks - Variables.removeLocals(e); - } - }); + CompletableFuture.supplyAsync(() -> executeStatement(ds, baseVariable, parsedQuery), threadPool) + .whenComplete((resources, err) -> { + //handle last error syntax data + resetLastSQLError(); + if (err instanceof SkriptDBQueryException) { + setLastSQLError(err.getMessage()); + } + //if local variables are present + //bring back local variables + //populate SQL data into variables + if (!quickly) { + Bukkit.getScheduler().runTask(SkriptDB.getInstance(), + () -> postExecution(e, locals, resources)); + } else { + postExecution(e, locals, resources); + } + }); // sync executed SQL query, same as above, just sync } else { isSync = true; - Object resources = executeStatement(ds, baseVariable, query); - //handle last error syntax data - lastError = null; - if (resources instanceof String) { - lastError = (String) resources; - } + Map resources = null; + try { + resources = executeStatement(ds, baseVariable, parsedQuery); + resetLastSQLError(); + } catch (SkriptDBQueryException err) { + //handle last error syntax data + setLastSQLError(err.getMessage()); + } //if local variables are present //bring back local variables //populate SQL data into variables - if (locals != null && getNext() != null) { - Variables.setLocalVariables(e, locals); - } - if (!(resources instanceof String)) { - ((Map) resources).forEach((name, value) -> setVariable(e, name, value)); - } - TriggerItem.walk(getNext(), e); - Variables.removeLocals(e); + postExecution(e, locals, resources); } } + + private void postExecution(Event e, Object locals, Map resources) { + if (locals != null && getNext() != null) { + Variables.setLocalVariables(e, locals); + } + if (resources != null) { + resources.forEach((name, value) -> setVariable(e, name, value)); + } + TriggerItem.walk(getNext(), e); + //the line below is required to prevent memory leaks + Variables.removeLocals(e); + } @Override protected TriggerItem walk(Event e) { @@ -215,9 +190,9 @@ public class EffExecuteStatement extends Effect { } } - private Object executeStatement(DataSource ds, String baseVariable, Pair> query) { + private Map executeStatement(DataSource ds, String baseVariable, Pair> query) throws SkriptDBQueryException { if (ds == null) { - return "Data source is not set"; + throw new SkriptDBQueryException("Data source is not set"); } try (Connection conn = ds.getConnection()) { try (PreparedStatement stmt = createStatement(conn, query)) { @@ -229,11 +204,11 @@ public class EffExecuteStatement extends Effect { return Map.of(); } } catch (SQLException ex) { - return ex.getMessage(); + throw new SkriptDBQueryException(ex.getMessage()); } } - private Object processBaseVariable(String baseVariable, PreparedStatement stmt, boolean hasResultSet) throws SQLException { + private Map processBaseVariable(String baseVariable, PreparedStatement stmt, boolean hasResultSet) throws SQLException { Map variableList = new HashMap<>(); if (isList) { baseVariable = baseVariable.substring(0, baseVariable.length() - 1); @@ -306,7 +281,7 @@ public class EffExecuteStatement extends Effect { //fix mediumblob and similar column types, so they return a String correctly if (obj != null) { - if (obj.getClass().getName().equals("[B")) { + if (obj instanceof byte[]) { obj = new String((byte[]) obj); //in some servers instead of being byte array, it appears as SerialBlob (depends on mc version, 1.12.2 is bvte array, 1.16.5 SerialBlob) @@ -320,6 +295,14 @@ public class EffExecuteStatement extends Effect { } Variables.setVariable(name.toLowerCase(Locale.ENGLISH), obj, e, isLocal); } + + private static void resetLastSQLError() { + lastError = null; + } + + private static void setLastSQLError(String error) { + lastError = error; + } @Override public String toString(Event e, boolean debug) { @@ -328,8 +311,7 @@ public class EffExecuteStatement extends Effect { @SuppressWarnings("unchecked") @Override - public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, - SkriptParser.ParseResult parseResult) { + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { Expression statementExpr = (Expression) exprs[0]; if (statementExpr instanceof VariableString || statementExpr instanceof ExprUnsafe) { query = statementExpr; @@ -347,10 +329,10 @@ public class EffExecuteStatement extends Effect { queryArguments = (Expression) exprs[2]; } Expression resultHolder = exprs[3]; - quickly = matchedPattern == 1; + quickly = parseResult.hasTag("quickly"); if (resultHolder instanceof Variable) { Variable varExpr = (Variable) resultHolder; - var = varExpr.getName(); + resultVariableName = varExpr.getName(); isLocal = varExpr.isLocal(); isList = varExpr.isList(); } else if (resultHolder != null) { @@ -359,4 +341,15 @@ public class EffExecuteStatement extends Effect { } return true; } + + public static class SkriptDBQueryException extends RuntimeException { + + private static final long serialVersionUID = -1869895286406538884L; + + public SkriptDBQueryException(String message) { + super(message); + } + + } + } \ No newline at end of file From 41881dbb7d54a3347e75154e62933fc8548be0e9 Mon Sep 17 00:00:00 2001 From: szumielxd <43210079+szumielxd@users.noreply.github.com> Date: Wed, 24 Jul 2024 14:31:49 +0200 Subject: [PATCH 5/7] Fixed missing 'string' field in Skript 2.8 --- .../java/com/btk5h/skriptdb/SkriptUtil.java | 98 +++++++++++-------- 1 file changed, 58 insertions(+), 40 deletions(-) diff --git a/src/main/java/com/btk5h/skriptdb/SkriptUtil.java b/src/main/java/com/btk5h/skriptdb/SkriptUtil.java index 86075cb..c95da2e 100644 --- a/src/main/java/com/btk5h/skriptdb/SkriptUtil.java +++ b/src/main/java/com/btk5h/skriptdb/SkriptUtil.java @@ -10,52 +10,70 @@ import java.util.Optional; public class SkriptUtil { - private static final Field STRING; - private static final Field EXPR; + private static final Field STRING; + private static final Field EXPR; - static { - Field _FIELD = null; - try { - _FIELD = VariableString.class.getDeclaredField("string"); - _FIELD.setAccessible(true); - } catch (NoSuchFieldException e) { - Skript.error("Skript's 'string' field could not be resolved."); - e.printStackTrace(); - } - STRING = _FIELD; + static { + STRING = tryGetOldStringField() + .or(() -> tryGetNewStringField()) + .orElseGet(() -> { + Skript.error("Skript's 'string' field could not be resolved."); + return null; + }); - try { - Optional> expressionInfo = Arrays.stream(VariableString.class.getDeclaredClasses()) - .filter(cls -> cls.getSimpleName().equals("ExpressionInfo")) - .findFirst(); - if (expressionInfo.isPresent()) { - Class expressionInfoClass = expressionInfo.get(); - _FIELD = expressionInfoClass.getDeclaredField("expr"); - _FIELD.setAccessible(true); - } else { - Skript.error("Skript's 'ExpressionInfo' class could not be resolved."); - } - } catch (NoSuchFieldException e) { - e.printStackTrace(); - Skript.error("Skript's 'expr' field could not be resolved."); + Field f = null; + try { + Optional> expressionInfo = Arrays.stream(VariableString.class.getDeclaredClasses()) + .filter(cls -> cls.getSimpleName().equals("ExpressionInfo")) + .findFirst(); + if (expressionInfo.isPresent()) { + Class expressionInfoClass = expressionInfo.get(); + f = expressionInfoClass.getDeclaredField("expr"); + f.setAccessible(true); + } else { + Skript.error("Skript's 'ExpressionInfo' class could not be resolved."); + } + } catch (NoSuchFieldException e) { + e.printStackTrace(); + Skript.error("Skript's 'expr' field could not be resolved."); + } + EXPR = f; } - EXPR = _FIELD; - } - public static Object[] getTemplateString(VariableString vs) { - try { - return (Object[]) STRING.get(vs); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); + public static Object[] getTemplateString(VariableString vs) { + try { + return (Object[]) STRING.get(vs); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } } - } - public static Expression getExpressionFromInfo(Object o) { - try { - return (Expression) EXPR.get(o); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); + public static Expression getExpressionFromInfo(Object o) { + try { + return (Expression) EXPR.get(o); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private static Optional tryGetOldStringField() { + try { + Field f = VariableString.class.getDeclaredField("string"); + f.setAccessible(true); + return Optional.of(f); + } catch (NoSuchFieldException e) { + return Optional.empty(); + } + } + + private static Optional tryGetNewStringField() { + try { + Field f = VariableString.class.getDeclaredField("strings"); + f.setAccessible(true); + return Optional.of(f); + } catch (NoSuchFieldException e) { + return Optional.empty(); + } } - } } From 0670f88a4e0b23923f2e3cae9460d051d2796de6 Mon Sep 17 00:00:00 2001 From: szumielxd <43210079+szumielxd@users.noreply.github.com> Date: Wed, 24 Jul 2024 16:01:44 +0200 Subject: [PATCH 6/7] Code cleanup --- .../java/com/btk5h/skriptdb/SkriptUtil.java | 23 +++++++++++++++---- .../events/SQLQueryCompleteEvent.java | 20 ++++++---------- .../btk5h/skriptdb/skript/ExprDataSource.java | 8 +++---- .../btk5h/skriptdb/skript/ExprSQLQuery.java | 10 ++++---- .../com/btk5h/skriptdb/skript/ExprUnsafe.java | 3 +-- 5 files changed, 37 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/btk5h/skriptdb/SkriptUtil.java b/src/main/java/com/btk5h/skriptdb/SkriptUtil.java index c95da2e..f6bbf94 100644 --- a/src/main/java/com/btk5h/skriptdb/SkriptUtil.java +++ b/src/main/java/com/btk5h/skriptdb/SkriptUtil.java @@ -1,13 +1,17 @@ package com.btk5h.skriptdb; -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 org.bukkit.event.Event; + +import ch.njol.skript.ScriptLoader; +import ch.njol.skript.Skript; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.VariableString; +import ch.njol.skript.lang.parser.ParserInstance; + public class SkriptUtil { private static final Field STRING; @@ -56,6 +60,17 @@ public class SkriptUtil { } } + @SuppressWarnings("deprecation") + public static boolean isCurrentEvent(Class event) { + try { + Class.forName("ch.njol.skript.lang.parser.ParserInstance"); + return ParserInstance.get().isCurrentEvent(event); + } catch (ClassNotFoundException e) { + return ScriptLoader.isCurrentEvent(event); + } + } + + private static Optional tryGetOldStringField() { try { Field f = VariableString.class.getDeclaredField("string"); diff --git a/src/main/java/com/btk5h/skriptdb/events/SQLQueryCompleteEvent.java b/src/main/java/com/btk5h/skriptdb/events/SQLQueryCompleteEvent.java index 969582f..511bd28 100644 --- a/src/main/java/com/btk5h/skriptdb/events/SQLQueryCompleteEvent.java +++ b/src/main/java/com/btk5h/skriptdb/events/SQLQueryCompleteEvent.java @@ -4,32 +4,26 @@ import org.bukkit.event.Event; import org.bukkit.event.HandlerList; public class SQLQueryCompleteEvent extends Event { - private final static HandlerList HANDLERS = new HandlerList(); + + private static final HandlerList HANDLERS = new HandlerList(); + private final String argument; public SQLQueryCompleteEvent(String argument) { super(true); this.argument = argument; - // this.variables = variables; - } - - public static HandlerList getHandlerList() { - return HANDLERS; - } - - @Override - public String getEventName() { - return super.getEventName(); } @Override public HandlerList getHandlers() { - return HANDLERS; + return getHandlerList(); } public String getQuery() { return argument; } - // public String getVariables() {return;} + public static HandlerList getHandlerList() { + return HANDLERS; + } } diff --git a/src/main/java/com/btk5h/skriptdb/skript/ExprDataSource.java b/src/main/java/com/btk5h/skriptdb/skript/ExprDataSource.java index 945db22..22dec95 100644 --- a/src/main/java/com/btk5h/skriptdb/skript/ExprDataSource.java +++ b/src/main/java/com/btk5h/skriptdb/skript/ExprDataSource.java @@ -32,8 +32,9 @@ public class ExprDataSource extends SimpleExpression { static { Skript.registerExpression(ExprDataSource.class, HikariDataSource.class, - ExpressionType.COMBINED, "[the] data(base|[ ]source) [(of|at)] %string% " + - "[with [a] [max[imum]] [connection] life[ ]time of %-timespan%] " + "[[(using|with)] [a] driver %-string%]"); + ExpressionType.COMBINED, "[the] data(base|[ ]source) [(of|at)] %string% " + + "[with [a] [max[imum]] [connection] life[ ]time of %-timespan%] " + + "[[(using|with)] [a] driver %-string%]"); } private Expression url; @@ -100,8 +101,7 @@ public class ExprDataSource extends SimpleExpression { @SuppressWarnings("unchecked") @Override - public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, - SkriptParser.ParseResult parseResult) { + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { url = (Expression) exprs[0]; maxLifetime = (Expression) exprs[1]; driver = (Expression) exprs[2]; diff --git a/src/main/java/com/btk5h/skriptdb/skript/ExprSQLQuery.java b/src/main/java/com/btk5h/skriptdb/skript/ExprSQLQuery.java index b44e47b..96be03d 100644 --- a/src/main/java/com/btk5h/skriptdb/skript/ExprSQLQuery.java +++ b/src/main/java/com/btk5h/skriptdb/skript/ExprSQLQuery.java @@ -1,6 +1,10 @@ package com.btk5h.skriptdb.skript; -import ch.njol.skript.ScriptLoader; +import org.bukkit.event.Event; + +import com.btk5h.skriptdb.SkriptUtil; +import com.btk5h.skriptdb.events.SQLQueryCompleteEvent; + import ch.njol.skript.Skript; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionType; @@ -8,8 +12,6 @@ import ch.njol.skript.lang.SkriptParser; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.skript.log.ErrorQuality; import ch.njol.util.Kleenean; -import com.btk5h.skriptdb.events.SQLQueryCompleteEvent; -import org.bukkit.event.Event; /** * Stores the error from the last executed statement, if there was one. @@ -51,7 +53,7 @@ public class ExprSQLQuery extends SimpleExpression { @Override public boolean init(final Expression[] expressions, final int matchedPattern, final Kleenean isDelayed, final SkriptParser.ParseResult parseResult) { - if (!ScriptLoader.isCurrentEvent(SQLQueryCompleteEvent.class)) { + if (!SkriptUtil.isCurrentEvent(SQLQueryCompleteEvent.class)) { Skript.error("Cannot use 'sql query' outside of a complete of sql query event", ErrorQuality.SEMANTIC_ERROR); return false; } diff --git a/src/main/java/com/btk5h/skriptdb/skript/ExprUnsafe.java b/src/main/java/com/btk5h/skriptdb/skript/ExprUnsafe.java index 44602ab..70df358 100644 --- a/src/main/java/com/btk5h/skriptdb/skript/ExprUnsafe.java +++ b/src/main/java/com/btk5h/skriptdb/skript/ExprUnsafe.java @@ -53,8 +53,7 @@ public class ExprUnsafe extends SimpleExpression { @SuppressWarnings("unchecked") @Override - public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, - SkriptParser.ParseResult parseResult) { + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { stringExpression = (Expression) exprs[0]; rawExpression = parseResult.expr.substring("unsafe".length()).trim(); return true; From 99be47d4d301b48cc6fdd795d89f42d1f0fb2c84 Mon Sep 17 00:00:00 2001 From: szumielxd <43210079+szumielxd@users.noreply.github.com> Date: Thu, 30 Jan 2025 02:41:32 +0100 Subject: [PATCH 7/7] Added additional arguments info in prepared statement warning --- .../skriptdb/skript/EffExecuteStatement.java | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/btk5h/skriptdb/skript/EffExecuteStatement.java b/src/main/java/com/btk5h/skriptdb/skript/EffExecuteStatement.java index 70ca721..ad6bfb5 100644 --- a/src/main/java/com/btk5h/skriptdb/skript/EffExecuteStatement.java +++ b/src/main/java/com/btk5h/skriptdb/skript/EffExecuteStatement.java @@ -21,8 +21,10 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSetMetaData; import java.sql.SQLException; +import java.sql.Statement; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.regex.Pattern; @@ -52,7 +54,7 @@ public class EffExecuteStatement extends Effect { static { Skript.registerEffect(EffExecuteStatement.class, "[quickly:quickly] execute %string% (in|on) %datasource% " + - "[with arg[ument][s] %-objects%] [and store [[the] (output|result)[s]] (to|in) [the] [var[iable]] %-objects%]"); + "[with arg[ument][s] %-objects%] [and store [[the] [keys:generated keys] (output|result)[s]] (to|in) [the] [var[iable]] %-objects%]"); } private Expression query; @@ -62,6 +64,7 @@ public class EffExecuteStatement extends Effect { private boolean isLocal; private boolean isList; private boolean quickly; + private boolean generatedKeys; private boolean isSync = false; @Override @@ -82,8 +85,8 @@ public class EffExecuteStatement extends Effect { .whenComplete((resources, err) -> { //handle last error syntax data resetLastSQLError(); - if (err instanceof SkriptDBQueryException) { - setLastSQLError(err.getMessage()); + if (err instanceof CompletionException && err.getCause() instanceof SkriptDBQueryException) { + setLastSQLError(err.getCause().getMessage()); } //if local variables are present //bring back local variables @@ -99,9 +102,9 @@ public class EffExecuteStatement extends Effect { } else { isSync = true; Map resources = null; - try { + resetLastSQLError(); + try { resources = executeStatement(ds, baseVariable, parsedQuery); - resetLastSQLError(); } catch (SkriptDBQueryException err) { //handle last error syntax data setLastSQLError(err.getMessage()); @@ -141,10 +144,16 @@ public class EffExecuteStatement extends Effect { String queryString = query.getSingle(e); int queryArgCount = (int) ARGUMENT_PLACEHOLDER.matcher(queryString).results().count(); if (queryArgCount != args.length) { - Skript.warning("Your query has %d question marks, but you provided %d arguments."); + Skript.warning(String.format("Your query has %d question marks, but you provided %d arguments. (%s) [%s]", + queryArgCount, + args.length, + queryArguments.toString(e, true), + Optional.ofNullable(getTrigger()) + .map(Trigger::getDebugLabel) + .orElse("unknown"))); args = Arrays.copyOf(args, queryArgCount); } - return new Pair<>(query.getSingle(e), List.of(args)); + return new Pair<>(query.getSingle(e), Arrays.asList(args)); } else if (query instanceof VariableString && !((VariableString) query).isSimple()) { return parseVariableQuery(e, (VariableString) query); } @@ -209,20 +218,19 @@ public class EffExecuteStatement extends Effect { } private Map processBaseVariable(String baseVariable, PreparedStatement stmt, boolean hasResultSet) throws SQLException { - Map variableList = new HashMap<>(); if (isList) { baseVariable = baseVariable.substring(0, baseVariable.length() - 1); } if (hasResultSet) { CachedRowSet crs = SkriptDB.getRowSetFactory().createCachedRowSet(); - crs.populate(stmt.getResultSet()); + crs.populate(generatedKeys ? stmt.getGeneratedKeys() : stmt.getResultSet()); if (isList) { return fetchQueryResultSet(crs, baseVariable); } else { crs.last(); - variableList.put(baseVariable, crs.getRow()); + return Map.of(baseVariable, crs.getRow()); } } else if (!isList) { //if no results are returned and the specified variable isn't a list variable, put the affected rows count in the variable @@ -253,7 +261,9 @@ public class EffExecuteStatement extends Effect { } private PreparedStatement createStatement(Connection conn, Pair> query) throws SQLException { - PreparedStatement stmt = conn.prepareStatement(query.getFirst()); + PreparedStatement stmt = generatedKeys ? + conn.prepareStatement(query.getFirst(), Statement.RETURN_GENERATED_KEYS) + : conn.prepareStatement(query.getFirst(), Statement.NO_GENERATED_KEYS); if (query.getSecond() != null) { Iterator iter = query.getSecond().iterator(); for (int i = 1; iter.hasNext(); i++) { @@ -328,6 +338,7 @@ public class EffExecuteStatement extends Effect { } queryArguments = (Expression) exprs[2]; } + ; Expression resultHolder = exprs[3]; quickly = parseResult.hasTag("quickly"); if (resultHolder instanceof Variable) { @@ -335,6 +346,7 @@ public class EffExecuteStatement extends Effect { resultVariableName = varExpr.getName(); isLocal = varExpr.isLocal(); isList = varExpr.isList(); + generatedKeys = parseResult.hasTag("keys"); } else if (resultHolder != null) { Skript.error(resultHolder + " is not a variable"); return false;