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 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.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 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::::}`
*
* @name Execute Statement
* @pattern [synchronously] 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 * from %{table variable}%" in {sql} and store the result in {output::*}
* @since 0.1.0
*/
public class EffExecuteStatement extends Effect {
static {
Skript.registerEffect(EffExecuteStatement.class,
"execute %string% (in|on) %datasource% " +
"[and store [[the] (output|result)[s]] (to|in) [the] [var[iable]] %-objects%]", "synchronously execute %string% (in|on) %datasource% " +
"[and store [[the] (output|result)[s]] (to|in) [the] [var[iable]] %-objects%]");
}
static String lastError;
private static final ExecutorService threadPool =
Executors.newFixedThreadPool(SkriptDB.getInstance().getConfig().getInt("thread-pool-size"));
private Expression query;
private Expression dataSource;
private VariableString var;
private boolean isLocal;
private boolean isList;
private boolean isSync;
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 (isSync) {
Object populatedVariables = executeStatement(ds, baseVariable, query);
continueScriptExecution(e, populatedVariables);
} else {
Object locals = Variables.removeLocals(e);
//execute SQL statement
CompletableFuture