Alternative method to provide dynamic arguments into sql query #28
							
								
								
									
										16
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -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 | ||||
|  | ||||
							
								
								
									
										4
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								pom.xml
									
									
									
									
									
								
							| @ -30,6 +30,10 @@ | ||||
|             <id>sk89q</id> | ||||
|             <url>https://maven.sk89q.com/repo</url> | ||||
|         </repository> | ||||
|         <repository> | ||||
|             <id>skript</id> | ||||
|             <url>https://repo.skriptlang.org/releases</url> | ||||
|         </repository> | ||||
|     </repositories> | ||||
| 
 | ||||
|     <build> | ||||
|  | ||||
| @ -1,37 +1,39 @@ | ||||
| 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; | ||||
|     private static final Field EXPR; | ||||
| 
 | ||||
|     static { | ||||
|     Field _FIELD = null; | ||||
|     try { | ||||
|       _FIELD = VariableString.class.getDeclaredField("string"); | ||||
|       _FIELD.setAccessible(true); | ||||
|     } catch (NoSuchFieldException e) { | ||||
|         STRING = tryGetOldStringField() | ||||
|                 .or(() -> tryGetNewStringField()) | ||||
|                 .orElseGet(() -> { | ||||
|                     Skript.error("Skript's 'string' field could not be resolved."); | ||||
|       e.printStackTrace(); | ||||
|     } | ||||
|     STRING = _FIELD; | ||||
|                     return null; | ||||
|                 }); | ||||
| 
 | ||||
|         Field f = null; | ||||
|         try { | ||||
|             Optional<Class<?>> 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); | ||||
|                 f = expressionInfoClass.getDeclaredField("expr"); | ||||
|                 f.setAccessible(true); | ||||
|             } else { | ||||
|                 Skript.error("Skript's 'ExpressionInfo' class could not be resolved."); | ||||
|             } | ||||
| @ -39,7 +41,7 @@ public class SkriptUtil { | ||||
|             e.printStackTrace(); | ||||
|             Skript.error("Skript's 'expr' field could not be resolved."); | ||||
|         } | ||||
|     EXPR = _FIELD; | ||||
|         EXPR = f; | ||||
|     } | ||||
| 
 | ||||
|     public static Object[] getTemplateString(VariableString vs) { | ||||
| @ -58,4 +60,35 @@ public class SkriptUtil { | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     @SuppressWarnings("deprecation") | ||||
| 	public static boolean isCurrentEvent(Class<? extends Event> 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<Field> tryGetOldStringField() { | ||||
|         try { | ||||
|             Field f = VariableString.class.getDeclaredField("string"); | ||||
|             f.setAccessible(true); | ||||
|             return Optional.of(f); | ||||
|         } catch (NoSuchFieldException e) { | ||||
|             return Optional.empty(); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     private static Optional<Field> tryGetNewStringField() { | ||||
|         try { | ||||
|             Field f = VariableString.class.getDeclaredField("strings"); | ||||
|             f.setAccessible(true); | ||||
|             return Optional.of(f); | ||||
|         } catch (NoSuchFieldException e) { | ||||
|             return Optional.empty(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -21,10 +21,13 @@ 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; | ||||
| 
 | ||||
| /** | ||||
|  * Executes a statement on a database and optionally stores the result in a variable. Expressions | ||||
| @ -45,106 +48,85 @@ 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("(?<!\\\\)\\?"); | ||||
|     static String lastError; | ||||
| 
 | ||||
|     static { | ||||
|         Skript.registerEffect(EffExecuteStatement.class, | ||||
|                 "execute %string% (in|on) %datasource% " + | ||||
|                         "[and store [[the] (output|result)[s]] (to|in) [the] [var[iable]] %-objects%]", "quickly execute %string% (in|on) %datasource% " + | ||||
|                         "[and store [[the] (output|result)[s]] (to|in) [the] [var[iable]] %-objects%]"); | ||||
|                 "[quickly:quickly] execute %string% (in|on) %datasource% " + | ||||
|                         "[with arg[ument][s] %-objects%] [and store [[the] [keys:generated keys] (output|result)[s]] (to|in) [the] [var[iable]] %-objects%]"); | ||||
|     } | ||||
| 
 | ||||
|     private Expression<String> query; | ||||
|     private Expression<HikariDataSource> dataSource; | ||||
|     private VariableString var; | ||||
|     private Expression<Object> queryArguments; | ||||
|     private VariableString resultVariableName; | ||||
|     private boolean isLocal; | ||||
|     private boolean isList; | ||||
|     private boolean quickly; | ||||
|     private boolean generatedKeys; | ||||
|     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<String, Object>) populatedVariables).forEach((name, value) -> setVariable(e, name, value)); | ||||
|             } | ||||
|         } | ||||
|         TriggerItem.walk(getNext(), e); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void execute(Event e) { | ||||
|         DataSource ds = dataSource.getSingle(e); | ||||
|         Pair<String, List<Object>> 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<String, List<Object>> 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<Object> sql = CompletableFuture.supplyAsync(() -> executeStatement(ds, baseVariable, query), threadPool); | ||||
|             sql.whenComplete((res, err) -> { | ||||
|                 if (err != null) { | ||||
|                     err.printStackTrace(); | ||||
|                 } | ||||
|             CompletableFuture.supplyAsync(() -> executeStatement(ds, baseVariable, parsedQuery), threadPool) | ||||
| 		            .whenComplete((resources, err) -> { | ||||
| 		            	//handle last error syntax data | ||||
|                 lastError = null; | ||||
|                 if (res instanceof String) { | ||||
|                     lastError = (String) res; | ||||
| 		            	resetLastSQLError(); | ||||
| 		            	if (err instanceof CompletionException && err.getCause() instanceof SkriptDBQueryException) { | ||||
| 	            			setLastSQLError(err.getCause().getMessage()); | ||||
| 	            		} | ||||
| 		                //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<String, Object>) res).forEach((name, value) -> setVariable(e, name, value)); | ||||
|                         } | ||||
|                         TriggerItem.walk(getNext(), e); | ||||
|                         //the line below is required to prevent memory leaks | ||||
|                         Variables.removeLocals(e); | ||||
|                     }); | ||||
| 		                    Bukkit.getScheduler().runTask(SkriptDB.getInstance(), | ||||
| 		                    		() -> postExecution(e, locals, resources)); | ||||
| 		                } else { | ||||
|                     if (locals != null && getNext() != null) { | ||||
|                         Variables.setLocalVariables(e, locals); | ||||
|                     } | ||||
|                     if (!(res instanceof String)) { | ||||
|                         ((Map<String, Object>) res).forEach((name, value) -> setVariable(e, name, value)); | ||||
|                     } | ||||
|                     TriggerItem.walk(getNext(), e); | ||||
|                     //the line below is required to prevent memory leaks | ||||
|                     Variables.removeLocals(e); | ||||
| 		                	postExecution(e, locals, resources); | ||||
| 		                } | ||||
| 		            }); | ||||
|             // sync executed SQL query, same as above, just sync | ||||
|         } else { | ||||
|             isSync = true; | ||||
|             Object resources = executeStatement(ds, baseVariable, query); | ||||
|             Map<String, Object> resources = null; | ||||
|             resetLastSQLError(); | ||||
|             try { | ||||
|                 resources = executeStatement(ds, baseVariable, parsedQuery); | ||||
| 			} catch (SkriptDBQueryException err) { | ||||
| 	            //handle last error syntax data | ||||
|             lastError = null; | ||||
|             if (resources instanceof String) { | ||||
|                 lastError = (String) resources; | ||||
| 				setLastSQLError(err.getMessage()); | ||||
| 			} | ||||
|             //if local variables are present | ||||
|             //bring back local variables | ||||
|             //populate SQL data into variables | ||||
| 			postExecution(e, locals, resources); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     private void postExecution(Event e, Object locals, Map<String, Object> resources) { | ||||
|     	if (locals != null && getNext() != null) { | ||||
|             Variables.setLocalVariables(e, locals); | ||||
|         } | ||||
|             if (!(resources instanceof String)) { | ||||
|                 ((Map<String, Object>) resources).forEach((name, value) -> setVariable(e, name, value)); | ||||
|         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) { | ||||
| @ -157,83 +139,108 @@ public class EffExecuteStatement extends Effect { | ||||
|     } | ||||
| 
 | ||||
|     private Pair<String, List<Object>> parseQuery(Event e) { | ||||
|         if (!(query instanceof VariableString)) { | ||||
|         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(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), Arrays.asList(args)); | ||||
|         } else if (query instanceof VariableString && !((VariableString) query).isSimple()) { | ||||
|             return parseVariableQuery(e, (VariableString) query); | ||||
|         } | ||||
|         return new Pair<>(query.getSingle(e), null); | ||||
|     } | ||||
|         VariableString q = (VariableString) query; | ||||
|         if (q.isSimple()) { | ||||
|             return new Pair<>(q.toString(e), null); | ||||
|         } | ||||
|      | ||||
|     private Pair<String, List<Object>> parseVariableQuery(Event e, VariableString varQuery) { | ||||
|         StringBuilder sb = new StringBuilder(); | ||||
|         List<Object> parameters = new ArrayList<>(); | ||||
|         Object[] objects = SkriptUtil.getTemplateString(q); | ||||
|         List<Object> 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<String, Object> toAppend = parseExpressionQuery(expr, expressionValue, standaloneString); | ||||
|                 sb.append(toAppend.getFirst()); | ||||
|                 if (toAppend.getSecond() != null) { | ||||
|                     parameters.add(toAppend.getSecond()); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return new Pair<>(sb.toString(), parameters); | ||||
|     } | ||||
|      | ||||
|     private Object executeStatement(DataSource ds, String baseVariable, Pair<String, List<Object>> query) { | ||||
|         if (ds == null) { | ||||
|             return "Data source is not set"; | ||||
|     private Pair<String, Object> 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); | ||||
|         } | ||||
|     } | ||||
|         Map<String, Object> variableList = new HashMap<>(); | ||||
|         try (Connection conn = ds.getConnection(); | ||||
|              PreparedStatement stmt = createStatement(conn, query)) { | ||||
| 
 | ||||
|     private Map<String, Object> executeStatement(DataSource ds, String baseVariable, Pair<String, List<Object>> query) throws SkriptDBQueryException { | ||||
|         if (ds == null) { | ||||
|         	throw new SkriptDBQueryException("Data source is not set"); | ||||
|         } | ||||
|         try (Connection conn = ds.getConnection()) { | ||||
|             try (PreparedStatement stmt = createStatement(conn, query)) { | ||||
|                 boolean hasResultSet = stmt.execute(); | ||||
| 
 | ||||
|                 if (baseVariable != null) { | ||||
|                     return processBaseVariable(baseVariable, stmt, hasResultSet); | ||||
|                 } | ||||
|                 return Map.of(); | ||||
|             } | ||||
|         } catch (SQLException ex) { | ||||
|             throw new SkriptDBQueryException(ex.getMessage()); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     private Map<String, Object> processBaseVariable(String baseVariable, PreparedStatement stmt, boolean hasResultSet) throws SQLException { | ||||
|         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(); | ||||
|                 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 | ||||
|             return Map.of(baseVariable, stmt.getUpdateCount()); | ||||
|         } | ||||
|         return Map.of(); | ||||
|     } | ||||
|      | ||||
|     private Map<String, Object> fetchQueryResultSet(CachedRowSet crs, String baseVariable) throws SQLException { | ||||
|         Map<String, Object> variableList = new HashMap<>(); | ||||
|         ResultSetMetaData meta = crs.getMetaData(); | ||||
|         int columnCount = meta.getColumnCount(); | ||||
| 
 | ||||
| @ -243,7 +250,6 @@ public class EffExecuteStatement extends Effect { | ||||
|         } | ||||
| 
 | ||||
|         int rowNumber = 1; | ||||
|                         try { | ||||
|         while (crs.next()) { | ||||
|             for (int i = 1; i <= columnCount; i++) { | ||||
|                 variableList.put(baseVariable + meta.getColumnLabel(i).toLowerCase(Locale.ENGLISH) | ||||
| @ -251,48 +257,33 @@ public class EffExecuteStatement extends Effect { | ||||
|             } | ||||
|             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()); | ||||
|                 } | ||||
|             } | ||||
|         } catch (SQLException ex) { | ||||
|             return ex.getMessage(); | ||||
|         } | ||||
|         return variableList; | ||||
|     } | ||||
| 
 | ||||
|     private PreparedStatement createStatement(Connection conn, Pair<String, List<Object>> query) throws SQLException { | ||||
|         PreparedStatement stmt = conn.prepareStatement(query.getFirst()); | ||||
|         List<Object> parameters = query.getSecond(); | ||||
| 
 | ||||
|         if (parameters != null) { | ||||
|             for (int i = 0; i < parameters.size(); i++) { | ||||
|                 stmt.setObject(i + 1, parameters.get(i)); | ||||
|         PreparedStatement stmt = generatedKeys ?  | ||||
|         		conn.prepareStatement(query.getFirst(), Statement.RETURN_GENERATED_KEYS) | ||||
|         		: conn.prepareStatement(query.getFirst(), Statement.NO_GENERATED_KEYS); | ||||
|         if (query.getSecond() != null) { | ||||
|             Iterator<Object> 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; | ||||
|     } | ||||
| 
 | ||||
| @ -300,7 +291,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) | ||||
| @ -315,6 +306,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) { | ||||
|         return "execute " + query.toString(e, debug) + " in " + dataSource.toString(e, debug); | ||||
| @ -322,8 +321,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<String> statementExpr = (Expression<String>) exprs[0]; | ||||
|         if (statementExpr instanceof VariableString || statementExpr instanceof ExprUnsafe) { | ||||
|             query = statementExpr; | ||||
| @ -334,17 +332,36 @@ public class EffExecuteStatement extends Effect { | ||||
|             return false; | ||||
|         } | ||||
|         dataSource = (Expression<HikariDataSource>) exprs[1]; | ||||
|         Expression<?> expr = exprs[2]; | ||||
|         quickly = matchedPattern == 1; | ||||
|         if (expr instanceof Variable) { | ||||
|             Variable<?> varExpr = (Variable<?>) expr; | ||||
|             var = varExpr.getName(); | ||||
|         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<Object>) exprs[2]; | ||||
|         } | ||||
|         ; | ||||
|         Expression<?> resultHolder = exprs[3]; | ||||
|         quickly = parseResult.hasTag("quickly"); | ||||
|         if (resultHolder instanceof Variable) { | ||||
|             Variable<?> varExpr = (Variable<?>) resultHolder; | ||||
|             resultVariableName = varExpr.getName(); | ||||
|             isLocal = varExpr.isLocal(); | ||||
|             isList = varExpr.isList(); | ||||
|         } else if (expr != null) { | ||||
|             Skript.error(expr + " is not a variable"); | ||||
|             generatedKeys = parseResult.hasTag("keys"); | ||||
|         } else if (resultHolder != null) { | ||||
|             Skript.error(resultHolder + " is not a variable"); | ||||
|             return false; | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|      | ||||
|     public static class SkriptDBQueryException extends RuntimeException { | ||||
| 
 | ||||
| 		private static final long serialVersionUID = -1869895286406538884L; | ||||
| 		 | ||||
| 		public SkriptDBQueryException(String message) { | ||||
| 			super(message); | ||||
| 		} | ||||
|     	 | ||||
|     } | ||||
|      | ||||
| } | ||||
| @ -32,8 +32,9 @@ public class ExprDataSource extends SimpleExpression<HikariDataSource> { | ||||
| 
 | ||||
|     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<String> url; | ||||
| @ -100,8 +101,7 @@ public class ExprDataSource extends SimpleExpression<HikariDataSource> { | ||||
| 
 | ||||
|     @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<String>) exprs[0]; | ||||
|         maxLifetime = (Expression<Timespan>) exprs[1]; | ||||
|         driver = (Expression<String>) exprs[2]; | ||||
|  | ||||
| @ -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<String> { | ||||
| 
 | ||||
|     @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; | ||||
|         } | ||||
|  | ||||
| @ -53,8 +53,7 @@ public class ExprUnsafe extends SimpleExpression<String> { | ||||
| 
 | ||||
|     @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<String>) exprs[0]; | ||||
|         rawExpression = parseResult.expr.substring("unsafe".length()).trim(); | ||||
|         return true; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user