forked from Limework/skript-db
		
	reformat code (thanks TPGamesNL)
This commit is contained in:
		
							parent
							
								
									9b694206d3
								
							
						
					
					
						commit
						2dcd4edf58
					
				
							
								
								
									
										2
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								pom.xml
									
									
									
									
									
								
							@ -93,7 +93,7 @@
 | 
				
			|||||||
        <dependency>
 | 
					        <dependency>
 | 
				
			||||||
            <groupId>com.github.SkriptLang</groupId>
 | 
					            <groupId>com.github.SkriptLang</groupId>
 | 
				
			||||||
            <artifactId>Skript</artifactId>
 | 
					            <artifactId>Skript</artifactId>
 | 
				
			||||||
            <version>2.5.3</version>
 | 
					            <version>2.6-alpha1</version>
 | 
				
			||||||
            <scope>provided</scope>
 | 
					            <scope>provided</scope>
 | 
				
			||||||
        </dependency>
 | 
					        </dependency>
 | 
				
			||||||
        <!-- https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client -->
 | 
					        <!-- https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client -->
 | 
				
			||||||
 | 
				
			|||||||
@ -5,13 +5,18 @@ import org.bukkit.event.HandlerList;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
public class SQLQueryCompleteEvent extends Event {
 | 
					public class SQLQueryCompleteEvent extends Event {
 | 
				
			||||||
    private final static HandlerList HANDLERS = new HandlerList();
 | 
					    private final static HandlerList HANDLERS = new HandlerList();
 | 
				
			||||||
    private String argument;
 | 
					    private final String argument;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public SQLQueryCompleteEvent(String argument) {
 | 
					    public SQLQueryCompleteEvent(String argument) {
 | 
				
			||||||
        super(true);
 | 
					        super(true);
 | 
				
			||||||
        this.argument = argument;
 | 
					        this.argument = argument;
 | 
				
			||||||
      //  this.variables = variables;
 | 
					        //  this.variables = variables;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static HandlerList getHandlerList() {
 | 
				
			||||||
 | 
					        return HANDLERS;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public String getEventName() {
 | 
					    public String getEventName() {
 | 
				
			||||||
        return super.getEventName();
 | 
					        return super.getEventName();
 | 
				
			||||||
@ -21,13 +26,10 @@ public class SQLQueryCompleteEvent extends Event {
 | 
				
			|||||||
    public HandlerList getHandlers() {
 | 
					    public HandlerList getHandlers() {
 | 
				
			||||||
        return HANDLERS;
 | 
					        return HANDLERS;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public static HandlerList getHandlerList() {
 | 
					 | 
				
			||||||
        return HANDLERS;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public String getQuery() {
 | 
					    public String getQuery() {
 | 
				
			||||||
        return argument;
 | 
					        return argument;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  //  public String getVariables() {return;}
 | 
					    //  public String getVariables() {return;}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,6 @@ import ch.njol.util.Kleenean;
 | 
				
			|||||||
import ch.njol.util.Pair;
 | 
					import ch.njol.util.Pair;
 | 
				
			||||||
import com.btk5h.skriptdb.SkriptDB;
 | 
					import com.btk5h.skriptdb.SkriptDB;
 | 
				
			||||||
import com.btk5h.skriptdb.SkriptUtil;
 | 
					import com.btk5h.skriptdb.SkriptUtil;
 | 
				
			||||||
import com.btk5h.skriptdb.events.SQLQueryCompleteEvent;
 | 
					 | 
				
			||||||
import com.zaxxer.hikari.HikariDataSource;
 | 
					import com.zaxxer.hikari.HikariDataSource;
 | 
				
			||||||
import org.bukkit.Bukkit;
 | 
					import org.bukkit.Bukkit;
 | 
				
			||||||
import org.bukkit.event.Event;
 | 
					import org.bukkit.event.Event;
 | 
				
			||||||
@ -42,288 +41,291 @@ import java.util.concurrent.Executors;
 | 
				
			|||||||
 * @since 0.1.0
 | 
					 * @since 0.1.0
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class EffExecuteStatement extends Effect {
 | 
					public class EffExecuteStatement extends Effect {
 | 
				
			||||||
  static {
 | 
					    private static final ExecutorService threadPool =
 | 
				
			||||||
    Skript.registerEffect(EffExecuteStatement.class,
 | 
					            Executors.newCachedThreadPool();
 | 
				
			||||||
        "execute %string% (in|on) %datasource% " +
 | 
					    static String lastError;
 | 
				
			||||||
            "[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%]");
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static String lastError;
 | 
					    static {
 | 
				
			||||||
 | 
					        Skript.registerEffect(EffExecuteStatement.class,
 | 
				
			||||||
  private static final ExecutorService threadPool =
 | 
					                "execute %string% (in|on) %datasource% " +
 | 
				
			||||||
      Executors.newCachedThreadPool();
 | 
					                        "[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%]");
 | 
				
			||||||
  private Expression<String> query;
 | 
					 | 
				
			||||||
  private Expression<HikariDataSource> 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<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;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      boolean sync = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      //if current thread is not main thread, then make this query to not have delays
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (!Bukkit.isPrimaryThread()) {
 | 
					 | 
				
			||||||
        sync = true;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      Object locals = Variables.removeLocals(e);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      //execute SQL statement
 | 
					 | 
				
			||||||
      CompletableFuture<Object> sql =
 | 
					 | 
				
			||||||
          CompletableFuture.supplyAsync(() -> executeStatement(ds, baseVariable, query), threadPool);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      //when SQL statement is completed
 | 
					 | 
				
			||||||
    boolean finalSync = sync;
 | 
					 | 
				
			||||||
    sql.whenComplete((res, err) -> {
 | 
					 | 
				
			||||||
        if (err != null) { err.printStackTrace(); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          //handle last error syntax data
 | 
					 | 
				
			||||||
          lastError = null;
 | 
					 | 
				
			||||||
          if (res instanceof String) {
 | 
					 | 
				
			||||||
            lastError = (String) res;
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          if (getNext() != null) {
 | 
					 | 
				
			||||||
            //if local variables are present
 | 
					 | 
				
			||||||
            if (locals != null)
 | 
					 | 
				
			||||||
              //bring back local variables
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            //populate SQL data into variables
 | 
					 | 
				
			||||||
            if (!(res instanceof String)) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
              //also set variables in the sql query complete event
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
              //TEMPORARILY DISABLED, AS THIS WOULD WORSEN PERFORMANCE OF THE QUERIES AND NOT BE USED BY MOST PEOPLE.
 | 
					 | 
				
			||||||
              //I may add config option to enable this later?
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
              //SQLQueryCompleteEvent event = new SQLQueryCompleteEvent(this.query.getSingle(e));
 | 
					 | 
				
			||||||
              //((Map<String, Object>) res).forEach((name, value) -> setVariable(event, name, value));
 | 
					 | 
				
			||||||
              //SkriptDB.getPlugin(SkriptDB.class).getServer().getPluginManager().callEvent(event);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (isSync || finalSync) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
              Variables.setLocalVariables(e, locals);
 | 
					 | 
				
			||||||
              if (!(res instanceof String)) { ((Map<String, Object>) res).forEach((name, value) -> setVariable(e, name, value)); }
 | 
					 | 
				
			||||||
              TriggerItem.walk(getNext(), e);
 | 
					 | 
				
			||||||
              Variables.removeLocals(e);
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
              Bukkit.getScheduler().runTask(SkriptDB.getInstance(), () -> {
 | 
					 | 
				
			||||||
                      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
 | 
					 | 
				
			||||||
                      //no functionality difference notice with it being removed from my test, but the memory gets filled with leaks
 | 
					 | 
				
			||||||
                      //so it must be kept
 | 
					 | 
				
			||||||
                      Variables.removeLocals(e);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Override
 | 
					 | 
				
			||||||
  protected TriggerItem walk(Event e) {
 | 
					 | 
				
			||||||
    debug(e, true);
 | 
					 | 
				
			||||||
    //I think no longer needed as of 1.3.0, uncomment if something breaks
 | 
					 | 
				
			||||||
    if (!isSync) {
 | 
					 | 
				
			||||||
      Delay.addDelayedEvent(e);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    execute(e);
 | 
					 | 
				
			||||||
    return null;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private Pair<String, List<Object>> parseQuery(Event e) {
 | 
					 | 
				
			||||||
    if (!(query instanceof VariableString)) {
 | 
					 | 
				
			||||||
      return new Pair<>(query.getSingle(e), null);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    VariableString q = (VariableString) query;
 | 
					 | 
				
			||||||
    if (q.isSimple()) {
 | 
					 | 
				
			||||||
      return new Pair<>(q.toString(e), null);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    StringBuilder sb = new StringBuilder();
 | 
					    private Expression<String> query;
 | 
				
			||||||
    List<Object> parameters = new ArrayList<>();
 | 
					    private Expression<HikariDataSource> dataSource;
 | 
				
			||||||
    Object[] objects = SkriptUtil.getTemplateString(q);
 | 
					    private VariableString var;
 | 
				
			||||||
 | 
					    private boolean isLocal;
 | 
				
			||||||
 | 
					    private boolean isList;
 | 
				
			||||||
 | 
					    private boolean isSync;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (int i = 0; i < objects.length; i++) {
 | 
					    private void continueScriptExecution(Event e, Object populatedVariables) {
 | 
				
			||||||
      Object o = objects[i];
 | 
					        lastError = null;
 | 
				
			||||||
      if (o instanceof String) {
 | 
					        if (populatedVariables instanceof String) {
 | 
				
			||||||
        sb.append(o);
 | 
					            lastError = (String) populatedVariables;
 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        Expression<?> 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;
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        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 {
 | 
					        } else {
 | 
				
			||||||
          parameters.add(expressionValue);
 | 
					 | 
				
			||||||
          sb.append('?');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if (standaloneString) {
 | 
					            if (getNext() != null) {
 | 
				
			||||||
            Skript.warning("Do not surround expressions with quotes!");
 | 
					                ((Map<String, Object>) populatedVariables).forEach((name, value) -> setVariable(e, name, value));
 | 
				
			||||||
          }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					        TriggerItem.walk(getNext(), e);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return new Pair<>(sb.toString(), parameters);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private Object executeStatement(DataSource ds, String baseVariable, Pair<String, List<Object>> query) {
 | 
					    @Override
 | 
				
			||||||
    if (ds == null) {
 | 
					    protected void execute(Event e) {
 | 
				
			||||||
      return "Data source is not set";
 | 
					        DataSource ds = dataSource.getSingle(e);
 | 
				
			||||||
    }
 | 
					        Pair<String, List<Object>> query = parseQuery(e);
 | 
				
			||||||
    Map<String, Object> variableList = new HashMap<>();
 | 
					        String baseVariable = var != null ? var.toString(e).toLowerCase(Locale.ENGLISH) : null;
 | 
				
			||||||
    try (Connection conn = ds.getConnection();
 | 
					        //if data source isn't set
 | 
				
			||||||
         PreparedStatement stmt = createStatement(conn, query)) {
 | 
					        if (ds == null) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      boolean hasResultSet = stmt.execute();
 | 
					        boolean sync = !Bukkit.isPrimaryThread();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (baseVariable != null) {
 | 
					        //if current thread is not main thread, then make this query to not have delays
 | 
				
			||||||
        if (isList) {
 | 
					 | 
				
			||||||
          baseVariable = baseVariable.substring(0, baseVariable.length() - 1);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (hasResultSet) {
 | 
					        Object locals = Variables.removeLocals(e);
 | 
				
			||||||
          CachedRowSet crs = SkriptDB.getRowSetFactory().createCachedRowSet();
 | 
					 | 
				
			||||||
          crs.populate(stmt.getResultSet());
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if (isList) {
 | 
					        //execute SQL statement
 | 
				
			||||||
              ResultSetMetaData meta = crs.getMetaData();
 | 
					        CompletableFuture<Object> sql =
 | 
				
			||||||
              int columnCount = meta.getColumnCount();
 | 
					                CompletableFuture.supplyAsync(() -> executeStatement(ds, baseVariable, query), threadPool);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              for (int i = 1; i <= columnCount; i++) {
 | 
					        //when SQL statement is completed
 | 
				
			||||||
                String label = meta.getColumnLabel(i);
 | 
					        boolean finalSync = sync;
 | 
				
			||||||
                variableList.put(baseVariable + label, label);
 | 
					        sql.whenComplete((res, err) -> {
 | 
				
			||||||
              }
 | 
					            if (err != null) {
 | 
				
			||||||
 | 
					                err.printStackTrace();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              int rowNumber = 1;
 | 
					            //handle last error syntax data
 | 
				
			||||||
              try {
 | 
					            lastError = null;
 | 
				
			||||||
                while (crs.next()) {
 | 
					            if (res instanceof String) {
 | 
				
			||||||
                  for (int i = 1; i <= columnCount; i++) {
 | 
					                lastError = (String) res;
 | 
				
			||||||
                    variableList.put(baseVariable + meta.getColumnLabel(i).toLowerCase(Locale.ENGLISH)
 | 
					            }
 | 
				
			||||||
                        + Variable.SEPARATOR + rowNumber, crs.getObject(i));
 | 
					
 | 
				
			||||||
                  }
 | 
					            if (getNext() != null) {
 | 
				
			||||||
                  rowNumber++;
 | 
					                //if local variables are present
 | 
				
			||||||
 | 
					                if (locals != null)
 | 
				
			||||||
 | 
					                    //bring back local variables
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    //populate SQL data into variables
 | 
				
			||||||
 | 
					                    if (!(res instanceof String)) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        //also set variables in the sql query complete event
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        //TEMPORARILY DISABLED, AS THIS WOULD WORSEN PERFORMANCE OF THE QUERIES AND NOT BE USED BY MOST PEOPLE.
 | 
				
			||||||
 | 
					                        //I may add config option to enable this later?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        //SQLQueryCompleteEvent event = new SQLQueryCompleteEvent(this.query.getSingle(e));
 | 
				
			||||||
 | 
					                        //((Map<String, Object>) res).forEach((name, value) -> setVariable(event, name, value));
 | 
				
			||||||
 | 
					                        //SkriptDB.getPlugin(SkriptDB.class).getServer().getPluginManager().callEvent(event);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                if (isSync || finalSync) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    Variables.setLocalVariables(e, locals);
 | 
				
			||||||
 | 
					                    if (!(res instanceof String)) {
 | 
				
			||||||
 | 
					                        ((Map<String, Object>) res).forEach((name, value) -> setVariable(e, name, value));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    TriggerItem.walk(getNext(), e);
 | 
				
			||||||
 | 
					                    Variables.removeLocals(e);
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    Bukkit.getScheduler().runTask(SkriptDB.getInstance(), () -> {
 | 
				
			||||||
 | 
					                        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
 | 
				
			||||||
 | 
					                        //no functionality difference notice with it being removed from my test, but the memory gets filled with leaks
 | 
				
			||||||
 | 
					                        //so it must be kept
 | 
				
			||||||
 | 
					                        Variables.removeLocals(e);
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
              } catch (SQLException ex) {
 | 
					            }
 | 
				
			||||||
                return ex.getMessage();
 | 
					        });
 | 
				
			||||||
              }
 | 
					    }
 | 
				
			||||||
          } else {
 | 
					
 | 
				
			||||||
            crs.last();
 | 
					    @Override
 | 
				
			||||||
            variableList.put(baseVariable, crs.getRow());
 | 
					    protected TriggerItem walk(Event e) {
 | 
				
			||||||
          }
 | 
					        debug(e, true);
 | 
				
			||||||
        } else if (!isList) {
 | 
					        //I think no longer needed as of 1.3.0, uncomment if something breaks
 | 
				
			||||||
          //if no results are returned and the specified variable isn't a list variable, put the affected rows count in the variable
 | 
					        if (!isSync) {
 | 
				
			||||||
          variableList.put(baseVariable, stmt.getUpdateCount());
 | 
					            Delay.addDelayedEvent(e);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					        execute(e);
 | 
				
			||||||
    } catch (SQLException ex) {
 | 
					        return null;
 | 
				
			||||||
      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));
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return stmt;
 | 
					    private Pair<String, List<Object>> parseQuery(Event e) {
 | 
				
			||||||
  }
 | 
					        if (!(query instanceof VariableString)) {
 | 
				
			||||||
 | 
					            return new Pair<>(query.getSingle(e), null);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        VariableString q = (VariableString) query;
 | 
				
			||||||
 | 
					        if (q.isSimple()) {
 | 
				
			||||||
 | 
					            return new Pair<>(q.toString(e), null);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private String getString(Object[] objects, int index) {
 | 
					        StringBuilder sb = new StringBuilder();
 | 
				
			||||||
    if (index < 0 || index >= objects.length) {
 | 
					        List<Object> parameters = new ArrayList<>();
 | 
				
			||||||
      return null;
 | 
					        Object[] objects = SkriptUtil.getTemplateString(q);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int i = 0; i < objects.length; i++) {
 | 
				
			||||||
 | 
					            Object o = objects[i];
 | 
				
			||||||
 | 
					            if (o instanceof String) {
 | 
				
			||||||
 | 
					                sb.append(o);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                Expression<?> 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;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                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!");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return new Pair<>(sb.toString(), parameters);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Object object = objects[index];
 | 
					    private Object executeStatement(DataSource ds, String baseVariable, Pair<String, List<Object>> query) {
 | 
				
			||||||
 | 
					        if (ds == null) {
 | 
				
			||||||
 | 
					            return "Data source is not set";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Map<String, Object> variableList = new HashMap<>();
 | 
				
			||||||
 | 
					        try (Connection conn = ds.getConnection();
 | 
				
			||||||
 | 
					             PreparedStatement stmt = createStatement(conn, query)) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (object instanceof String) {
 | 
					            boolean hasResultSet = stmt.execute();
 | 
				
			||||||
      return (String) object;
 | 
					
 | 
				
			||||||
 | 
					            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());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (SQLException ex) {
 | 
				
			||||||
 | 
					            return ex.getMessage();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return variableList;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return null;
 | 
					    private PreparedStatement createStatement(Connection conn, Pair<String, List<Object>> query) throws SQLException {
 | 
				
			||||||
  }
 | 
					        PreparedStatement stmt = conn.prepareStatement(query.getFirst());
 | 
				
			||||||
 | 
					        List<Object> parameters = query.getSecond();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private void setVariable(Event e, String name, Object obj) {
 | 
					        if (parameters != null) {
 | 
				
			||||||
    Variables.setVariable(name.toLowerCase(Locale.ENGLISH), obj, e, isLocal);
 | 
					            for (int i = 0; i < parameters.size(); i++) {
 | 
				
			||||||
  }
 | 
					                stmt.setObject(i + 1, parameters.get(i));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					        return stmt;
 | 
				
			||||||
  public String toString(Event e, boolean debug) {
 | 
					 | 
				
			||||||
    return "execute " + query.toString(e, debug) + " in " + dataSource.toString(e, debug);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @SuppressWarnings("unchecked")
 | 
					 | 
				
			||||||
  @Override
 | 
					 | 
				
			||||||
  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;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      Skript.error("Database statements must be string literals. If you must use an expression, " +
 | 
					 | 
				
			||||||
          "you may use \"%unsafe (your expression)%\", but keep in mind, you may be vulnerable " +
 | 
					 | 
				
			||||||
          "to SQL injection attacks!");
 | 
					 | 
				
			||||||
      return false;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    dataSource = (Expression<HikariDataSource>) exprs[1];
 | 
					
 | 
				
			||||||
    Expression<?> expr = exprs[2];
 | 
					    private String getString(Object[] objects, int index) {
 | 
				
			||||||
    isSync = matchedPattern == 1;
 | 
					        if (index < 0 || index >= objects.length) {
 | 
				
			||||||
    if (expr instanceof Variable) {
 | 
					            return null;
 | 
				
			||||||
      Variable<?> varExpr = (Variable<?>) expr;
 | 
					        }
 | 
				
			||||||
      var = varExpr.getName();
 | 
					
 | 
				
			||||||
      isLocal = varExpr.isLocal();
 | 
					        Object object = objects[index];
 | 
				
			||||||
      isList = varExpr.isList();
 | 
					
 | 
				
			||||||
    } else if (expr != null) {
 | 
					        if (object instanceof String) {
 | 
				
			||||||
      Skript.error(expr + " is not a variable");
 | 
					            return (String) object;
 | 
				
			||||||
      return false;
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void setVariable(Event e, String name, Object obj) {
 | 
				
			||||||
 | 
					        Variables.setVariable(name.toLowerCase(Locale.ENGLISH), obj, e, isLocal);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String toString(Event e, boolean debug) {
 | 
				
			||||||
 | 
					        return "execute " + query.toString(e, debug) + " in " + dataSource.toString(e, debug);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @SuppressWarnings("unchecked")
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    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;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            Skript.error("Database statements must be string literals. If you must use an expression, " +
 | 
				
			||||||
 | 
					                    "you may use \"%unsafe (your expression)%\", but keep in mind, you may be vulnerable " +
 | 
				
			||||||
 | 
					                    "to SQL injection attacks!");
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        dataSource = (Expression<HikariDataSource>) exprs[1];
 | 
				
			||||||
 | 
					        Expression<?> expr = exprs[2];
 | 
				
			||||||
 | 
					        isSync = matchedPattern == 1;
 | 
				
			||||||
 | 
					        if (expr instanceof Variable) {
 | 
				
			||||||
 | 
					            Variable<?> varExpr = (Variable<?>) expr;
 | 
				
			||||||
 | 
					            var = varExpr.getName();
 | 
				
			||||||
 | 
					            isLocal = varExpr.isLocal();
 | 
				
			||||||
 | 
					            isList = varExpr.isList();
 | 
				
			||||||
 | 
					        } else if (expr != null) {
 | 
				
			||||||
 | 
					            Skript.error(expr + " is not a variable");
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -12,15 +12,16 @@ public class EvtSQLQueryComplete extends SkriptEvent {
 | 
				
			|||||||
    static {
 | 
					    static {
 | 
				
			||||||
        Skript.registerEvent("complete of sql query", EvtSQLQueryComplete.class, SQLQueryCompleteEvent.class, "complete of [(sql|database)] query");
 | 
					        Skript.registerEvent("complete of sql query", EvtSQLQueryComplete.class, SQLQueryCompleteEvent.class, "complete of [(sql|database)] query");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        @Override
 | 
					 | 
				
			||||||
        public boolean init(final Literal<?>[] literals, final int i, final SkriptParser.ParseResult parseResult) {
 | 
					 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @Override
 | 
					    @Override
 | 
				
			||||||
        public boolean check(Event event) {
 | 
					    public boolean init(final Literal<?>[] literals, final int i, final SkriptParser.ParseResult parseResult) {
 | 
				
			||||||
            return (event instanceof SQLQueryCompleteEvent);
 | 
					        return true;
 | 
				
			||||||
        }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean check(Event event) {
 | 
				
			||||||
 | 
					        return (event instanceof SQLQueryCompleteEvent);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public String toString(@Nullable Event event, boolean debug) {
 | 
					    public String toString(@Nullable Event event, boolean debug) {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,12 @@
 | 
				
			|||||||
package com.btk5h.skriptdb.skript;
 | 
					package com.btk5h.skriptdb.skript;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.bukkit.event.Event;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import ch.njol.skript.Skript;
 | 
					import ch.njol.skript.Skript;
 | 
				
			||||||
import ch.njol.skript.lang.Expression;
 | 
					import ch.njol.skript.lang.Expression;
 | 
				
			||||||
import ch.njol.skript.lang.ExpressionType;
 | 
					import ch.njol.skript.lang.ExpressionType;
 | 
				
			||||||
import ch.njol.skript.lang.SkriptParser;
 | 
					import ch.njol.skript.lang.SkriptParser;
 | 
				
			||||||
import ch.njol.skript.lang.util.SimpleExpression;
 | 
					import ch.njol.skript.lang.util.SimpleExpression;
 | 
				
			||||||
import ch.njol.util.Kleenean;
 | 
					import ch.njol.util.Kleenean;
 | 
				
			||||||
 | 
					import org.bukkit.event.Event;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Stores the error from the last executed statement, if there was one.
 | 
					 * Stores the error from the last executed statement, if there was one.
 | 
				
			||||||
@ -18,33 +17,33 @@ import ch.njol.util.Kleenean;
 | 
				
			|||||||
 * @since 0.1.0
 | 
					 * @since 0.1.0
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class ExprDBError extends SimpleExpression<String> {
 | 
					public class ExprDBError extends SimpleExpression<String> {
 | 
				
			||||||
  static {
 | 
					    static {
 | 
				
			||||||
    Skript.registerExpression(ExprDBError.class, String.class,
 | 
					        Skript.registerExpression(ExprDBError.class, String.class,
 | 
				
			||||||
        ExpressionType.SIMPLE, "[the] [last] (sql|db|data(base|[ ]source)) error");
 | 
					                ExpressionType.SIMPLE, "[the] [last] (sql|db|data(base|[ ]source)) error");
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					    @Override
 | 
				
			||||||
  protected String[] get(Event e) {
 | 
					    protected String[] get(Event e) {
 | 
				
			||||||
    return new String[]{EffExecuteStatement.lastError};
 | 
					        return new String[]{EffExecuteStatement.lastError};
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					    @Override
 | 
				
			||||||
  public boolean isSingle() {
 | 
					    public boolean isSingle() {
 | 
				
			||||||
    return true;
 | 
					        return true;
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					    @Override
 | 
				
			||||||
  public Class<? extends String> getReturnType() {
 | 
					    public Class<? extends String> getReturnType() {
 | 
				
			||||||
    return String.class;
 | 
					        return String.class;
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					    @Override
 | 
				
			||||||
  public String toString(Event e, boolean debug) {
 | 
					    public String toString(Event e, boolean debug) {
 | 
				
			||||||
    return "last database error";
 | 
					        return "last database error";
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					    @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) {
 | 
				
			||||||
    return true;
 | 
					        return true;
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,5 @@
 | 
				
			|||||||
package com.btk5h.skriptdb.skript;
 | 
					package com.btk5h.skriptdb.skript;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.btk5h.skriptdb.SkriptDB;
 | 
					 | 
				
			||||||
import com.zaxxer.hikari.HikariDataSource;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import org.bukkit.event.Event;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.util.HashMap;
 | 
					 | 
				
			||||||
import java.util.Map;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import ch.njol.skript.Skript;
 | 
					import ch.njol.skript.Skript;
 | 
				
			||||||
import ch.njol.skript.lang.Expression;
 | 
					import ch.njol.skript.lang.Expression;
 | 
				
			||||||
import ch.njol.skript.lang.ExpressionType;
 | 
					import ch.njol.skript.lang.ExpressionType;
 | 
				
			||||||
@ -15,11 +7,17 @@ import ch.njol.skript.lang.SkriptParser;
 | 
				
			|||||||
import ch.njol.skript.lang.util.SimpleExpression;
 | 
					import ch.njol.skript.lang.util.SimpleExpression;
 | 
				
			||||||
import ch.njol.skript.util.Timespan;
 | 
					import ch.njol.skript.util.Timespan;
 | 
				
			||||||
import ch.njol.util.Kleenean;
 | 
					import ch.njol.util.Kleenean;
 | 
				
			||||||
 | 
					import com.btk5h.skriptdb.SkriptDB;
 | 
				
			||||||
 | 
					import com.zaxxer.hikari.HikariDataSource;
 | 
				
			||||||
 | 
					import org.bukkit.event.Event;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Stores the connection information for a data source. This should be saved to a variable in a
 | 
					 * 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.
 | 
					 * `script load` event or manually through an effect command.
 | 
				
			||||||
 *
 | 
					 * <p>
 | 
				
			||||||
 * The url format for your database may vary! The example provided uses a MySQL database.
 | 
					 * The url format for your database may vary! The example provided uses a MySQL database.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @name Data Source
 | 
					 * @name Data Source
 | 
				
			||||||
@ -30,74 +28,74 @@ import ch.njol.util.Kleenean;
 | 
				
			|||||||
 * @since 0.1.0
 | 
					 * @since 0.1.0
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class ExprDataSource extends SimpleExpression<HikariDataSource> {
 | 
					public class ExprDataSource extends SimpleExpression<HikariDataSource> {
 | 
				
			||||||
  static {
 | 
					    private static final Map<String, HikariDataSource> connectionCache = new HashMap<>();
 | 
				
			||||||
    Skript.registerExpression(ExprDataSource.class, HikariDataSource.class,
 | 
					 | 
				
			||||||
        ExpressionType.COMBINED, "[the] data(base|[ ]source) [(of|at)] %string% " +
 | 
					 | 
				
			||||||
            "[with [a] [max[imum]] [connection] life[ ]time of %-timespan%]");
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static Map<String, HikariDataSource> connectionCache = new HashMap<>();
 | 
					    static {
 | 
				
			||||||
 | 
					        Skript.registerExpression(ExprDataSource.class, HikariDataSource.class,
 | 
				
			||||||
  private Expression<String> url;
 | 
					                ExpressionType.COMBINED, "[the] data(base|[ ]source) [(of|at)] %string% " +
 | 
				
			||||||
  private Expression<Timespan> maxLifetime;
 | 
					                        "[with [a] [max[imum]] [connection] life[ ]time of %-timespan%]");
 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Override
 | 
					 | 
				
			||||||
  protected HikariDataSource[] get(Event e) {
 | 
					 | 
				
			||||||
    String jdbcUrl = url.getSingle(e);
 | 
					 | 
				
			||||||
    if (jdbcUrl == null) {
 | 
					 | 
				
			||||||
      return null;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!jdbcUrl.startsWith("jdbc:")) {
 | 
					    private Expression<String> url;
 | 
				
			||||||
      jdbcUrl = "jdbc:" + jdbcUrl;
 | 
					    private Expression<Timespan> maxLifetime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected HikariDataSource[] get(Event e) {
 | 
				
			||||||
 | 
					        String jdbcUrl = url.getSingle(e);
 | 
				
			||||||
 | 
					        if (jdbcUrl == null) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!jdbcUrl.startsWith("jdbc:")) {
 | 
				
			||||||
 | 
					            jdbcUrl = "jdbc:" + jdbcUrl;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (connectionCache.containsKey(jdbcUrl)) {
 | 
				
			||||||
 | 
					            return new HikariDataSource[]{connectionCache.get(jdbcUrl)};
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        HikariDataSource ds = new HikariDataSource();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //allow specifying of own sql driver class name
 | 
				
			||||||
 | 
					        if (!SkriptDB.getInstance().getConfig().getString("sql-driver-class-name", "default").equals("default")) {
 | 
				
			||||||
 | 
					            ds.setDriverClassName(SkriptDB.getInstance().getConfig().getString("sql-driver-class-name"));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        ds.setJdbcUrl(jdbcUrl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (maxLifetime != null) {
 | 
				
			||||||
 | 
					            Timespan l = maxLifetime.getSingle(e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (l != null) {
 | 
				
			||||||
 | 
					                ds.setMaxLifetime(l.getMilliSeconds());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        connectionCache.put(jdbcUrl, ds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return new HikariDataSource[]{ds};
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (connectionCache.containsKey(jdbcUrl)) {
 | 
					    @Override
 | 
				
			||||||
      return new HikariDataSource[]{connectionCache.get(jdbcUrl)};
 | 
					    public boolean isSingle() {
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    HikariDataSource ds = new HikariDataSource();
 | 
					    @Override
 | 
				
			||||||
 | 
					    public Class<? extends HikariDataSource> getReturnType() {
 | 
				
			||||||
    //allow specifying of own sql driver class name
 | 
					        return HikariDataSource.class;
 | 
				
			||||||
    if (!SkriptDB.getInstance().getConfig().getString("sql-driver-class-name", "default").equals("default")) {
 | 
					 | 
				
			||||||
      ds.setDriverClassName(SkriptDB.getInstance().getConfig().getString("sql-driver-class-name"));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    ds.setJdbcUrl(jdbcUrl);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (maxLifetime != null) {
 | 
					 | 
				
			||||||
      Timespan l = maxLifetime.getSingle(e);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (l != null) {
 | 
					 | 
				
			||||||
        ds.setMaxLifetime(l.getMilliSeconds());
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    connectionCache.put(jdbcUrl, ds);
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String toString(Event e, boolean debug) {
 | 
				
			||||||
 | 
					        return "datasource " + url.toString(e, debug);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return new HikariDataSource[]{ds};
 | 
					    @SuppressWarnings("unchecked")
 | 
				
			||||||
  }
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed,
 | 
				
			||||||
  @Override
 | 
					                        SkriptParser.ParseResult parseResult) {
 | 
				
			||||||
  public boolean isSingle() {
 | 
					        url = (Expression<String>) exprs[0];
 | 
				
			||||||
    return true;
 | 
					        maxLifetime = (Expression<Timespan>) exprs[1];
 | 
				
			||||||
  }
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  @Override
 | 
					 | 
				
			||||||
  public Class<? extends HikariDataSource> getReturnType() {
 | 
					 | 
				
			||||||
    return HikariDataSource.class;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Override
 | 
					 | 
				
			||||||
  public String toString(Event e, boolean debug) {
 | 
					 | 
				
			||||||
    return "datasource " + url.toString(e, debug);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @SuppressWarnings("unchecked")
 | 
					 | 
				
			||||||
  @Override
 | 
					 | 
				
			||||||
  public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed,
 | 
					 | 
				
			||||||
                      SkriptParser.ParseResult parseResult) {
 | 
					 | 
				
			||||||
    url = (Expression<String>) exprs[0];
 | 
					 | 
				
			||||||
    maxLifetime = (Expression<Timespan>) exprs[1];
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -20,41 +20,41 @@ import org.bukkit.event.Event;
 | 
				
			|||||||
 * @since 0.1.0
 | 
					 * @since 0.1.0
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class ExprSQLQuery extends SimpleExpression<String> {
 | 
					public class ExprSQLQuery extends SimpleExpression<String> {
 | 
				
			||||||
  static {
 | 
					    static {
 | 
				
			||||||
    Skript.registerExpression(ExprSQLQuery.class, String.class,
 | 
					        Skript.registerExpression(ExprSQLQuery.class, String.class,
 | 
				
			||||||
        ExpressionType.SIMPLE, "sql query");
 | 
					                ExpressionType.SIMPLE, "sql query");
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Override
 | 
					 | 
				
			||||||
  protected String[] get(Event e) {
 | 
					 | 
				
			||||||
    if (e instanceof SQLQueryCompleteEvent){
 | 
					 | 
				
			||||||
      return new String[]{((SQLQueryCompleteEvent) e).getQuery()};
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					 | 
				
			||||||
  public boolean isSingle() {
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					    @Override
 | 
				
			||||||
  public Class<? extends String> getReturnType() {
 | 
					    protected String[] get(Event e) {
 | 
				
			||||||
    return String.class;
 | 
					        if (e instanceof SQLQueryCompleteEvent) {
 | 
				
			||||||
  }
 | 
					            return new String[]{((SQLQueryCompleteEvent) e).getQuery()};
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
  @Override
 | 
					        return null;
 | 
				
			||||||
  public String toString(Event e, boolean debug) {
 | 
					    }
 | 
				
			||||||
    return "sql query";
 | 
					
 | 
				
			||||||
  }
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean isSingle() {
 | 
				
			||||||
  @Override
 | 
					        return true;
 | 
				
			||||||
  public boolean init(final Expression<?>[] expressions, final int matchedPattern, final Kleenean isDelayed, final SkriptParser.ParseResult parseResult) {
 | 
					    }
 | 
				
			||||||
    if (!ScriptLoader.isCurrentEvent(SQLQueryCompleteEvent.class)) {
 | 
					
 | 
				
			||||||
      Skript.error("Cannot use 'sql query' outside of a complete of sql query event", ErrorQuality.SEMANTIC_ERROR);
 | 
					    @Override
 | 
				
			||||||
      return false;
 | 
					    public Class<? extends String> getReturnType() {
 | 
				
			||||||
 | 
					        return String.class;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String toString(Event e, boolean debug) {
 | 
				
			||||||
 | 
					        return "sql query";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean init(final Expression<?>[] expressions, final int matchedPattern, final Kleenean isDelayed, final SkriptParser.ParseResult parseResult) {
 | 
				
			||||||
 | 
					        if (!ScriptLoader.isCurrentEvent(SQLQueryCompleteEvent.class)) {
 | 
				
			||||||
 | 
					            Skript.error("Cannot use 'sql query' outside of a complete of sql query event", ErrorQuality.SEMANTIC_ERROR);
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,12 @@
 | 
				
			|||||||
package com.btk5h.skriptdb.skript;
 | 
					package com.btk5h.skriptdb.skript;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.bukkit.event.Event;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import ch.njol.skript.Skript;
 | 
					import ch.njol.skript.Skript;
 | 
				
			||||||
import ch.njol.skript.lang.Expression;
 | 
					import ch.njol.skript.lang.Expression;
 | 
				
			||||||
import ch.njol.skript.lang.ExpressionType;
 | 
					import ch.njol.skript.lang.ExpressionType;
 | 
				
			||||||
import ch.njol.skript.lang.SkriptParser;
 | 
					import ch.njol.skript.lang.SkriptParser;
 | 
				
			||||||
import ch.njol.skript.lang.util.SimpleExpression;
 | 
					import ch.njol.skript.lang.util.SimpleExpression;
 | 
				
			||||||
import ch.njol.util.Kleenean;
 | 
					import ch.njol.util.Kleenean;
 | 
				
			||||||
 | 
					import org.bukkit.event.Event;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Opts out of automatic SQL injection protection for a specific expression in a statement.
 | 
					 * Opts out of automatic SQL injection protection for a specific expression in a statement.
 | 
				
			||||||
@ -20,44 +19,44 @@ import ch.njol.util.Kleenean;
 | 
				
			|||||||
 * @since 0.1.0
 | 
					 * @since 0.1.0
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class ExprUnsafe extends SimpleExpression<String> {
 | 
					public class ExprUnsafe extends SimpleExpression<String> {
 | 
				
			||||||
  static {
 | 
					    static {
 | 
				
			||||||
    Skript.registerExpression(ExprUnsafe.class, String.class, ExpressionType.COMBINED,
 | 
					        Skript.registerExpression(ExprUnsafe.class, String.class, ExpressionType.COMBINED,
 | 
				
			||||||
        "unsafe %string%");
 | 
					                "unsafe %string%");
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private Expression<String> stringExpression;
 | 
					    private Expression<String> stringExpression;
 | 
				
			||||||
  private String rawExpression;
 | 
					    private String rawExpression;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public String getRawExpression() {
 | 
					    public String getRawExpression() {
 | 
				
			||||||
    return rawExpression;
 | 
					        return rawExpression;
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					    @Override
 | 
				
			||||||
  protected String[] get(Event e) {
 | 
					    protected String[] get(Event e) {
 | 
				
			||||||
    return stringExpression.getArray(e);
 | 
					        return stringExpression.getArray(e);
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					    @Override
 | 
				
			||||||
  public boolean isSingle() {
 | 
					    public boolean isSingle() {
 | 
				
			||||||
    return true;
 | 
					        return true;
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					    @Override
 | 
				
			||||||
  public Class<? extends String> getReturnType() {
 | 
					    public Class<? extends String> getReturnType() {
 | 
				
			||||||
    return String.class;
 | 
					        return String.class;
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					    @Override
 | 
				
			||||||
  public String toString(Event e, boolean debug) {
 | 
					    public String toString(Event e, boolean debug) {
 | 
				
			||||||
    return "unsafe " + stringExpression.toString(e, debug);
 | 
					        return "unsafe " + stringExpression.toString(e, debug);
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @SuppressWarnings("unchecked")
 | 
					    @SuppressWarnings("unchecked")
 | 
				
			||||||
  @Override
 | 
					    @Override
 | 
				
			||||||
  public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed,
 | 
					    public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed,
 | 
				
			||||||
                      SkriptParser.ParseResult parseResult) {
 | 
					                        SkriptParser.ParseResult parseResult) {
 | 
				
			||||||
    stringExpression = (Expression<String>) exprs[0];
 | 
					        stringExpression = (Expression<String>) exprs[0];
 | 
				
			||||||
    rawExpression = parseResult.expr.substring("unsafe".length()).trim();
 | 
					        rawExpression = parseResult.expr.substring("unsafe".length()).trim();
 | 
				
			||||||
    return true;
 | 
					        return true;
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -11,58 +11,58 @@ import com.zaxxer.hikari.HikariDataSource;
 | 
				
			|||||||
import java.io.StreamCorruptedException;
 | 
					import java.io.StreamCorruptedException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class Types {
 | 
					public class Types {
 | 
				
			||||||
  static {
 | 
					    static {
 | 
				
			||||||
    Classes.registerClass(new ClassInfo<>(HikariDataSource.class, "datasource")
 | 
					        Classes.registerClass(new ClassInfo<>(HikariDataSource.class, "datasource")
 | 
				
			||||||
        .user("datasources?")
 | 
					                .user("datasources?")
 | 
				
			||||||
        .parser(new Parser<HikariDataSource>() {
 | 
					                .parser(new Parser<HikariDataSource>() {
 | 
				
			||||||
            @Override
 | 
					                    @Override
 | 
				
			||||||
            public HikariDataSource parse(String s, ParseContext context) {
 | 
					                    public HikariDataSource parse(String s, ParseContext context) {
 | 
				
			||||||
                return null;
 | 
					                        return null;
 | 
				
			||||||
            }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            @Override
 | 
					                    @Override
 | 
				
			||||||
            public String toString(HikariDataSource o, int flags) {
 | 
					                    public String toString(HikariDataSource o, int flags) {
 | 
				
			||||||
                return o.getJdbcUrl();
 | 
					                        return o.getJdbcUrl();
 | 
				
			||||||
            }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            @Override
 | 
					                    @Override
 | 
				
			||||||
            public String toVariableNameString(HikariDataSource o) {
 | 
					                    public String toVariableNameString(HikariDataSource o) {
 | 
				
			||||||
                return o.getJdbcUrl();
 | 
					                        return o.getJdbcUrl();
 | 
				
			||||||
            }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            @Override
 | 
					                    @Override
 | 
				
			||||||
            public String getVariableNamePattern() {
 | 
					                    public String getVariableNamePattern() {
 | 
				
			||||||
                return "jdbc:.+";
 | 
					                        return "jdbc:.+";
 | 
				
			||||||
            }
 | 
					                    }
 | 
				
			||||||
        })
 | 
					                })
 | 
				
			||||||
        .serializer(new Serializer<HikariDataSource>() {
 | 
					                .serializer(new Serializer<HikariDataSource>() {
 | 
				
			||||||
            @Override
 | 
					                    @Override
 | 
				
			||||||
            public Fields serialize(HikariDataSource o) {
 | 
					                    public Fields serialize(HikariDataSource o) {
 | 
				
			||||||
                Fields fields = new Fields();
 | 
					                        Fields fields = new Fields();
 | 
				
			||||||
                fields.putObject("jdbcurl", o.getJdbcUrl());
 | 
					                        fields.putObject("jdbcurl", o.getJdbcUrl());
 | 
				
			||||||
                return fields;
 | 
					                        return fields;
 | 
				
			||||||
            }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            @Override
 | 
					                    @Override
 | 
				
			||||||
            public void deserialize(HikariDataSource o, Fields f) {
 | 
					                    public void deserialize(HikariDataSource o, Fields f) {
 | 
				
			||||||
            }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            @Override
 | 
					                    @Override
 | 
				
			||||||
            protected HikariDataSource deserialize(Fields fields) throws StreamCorruptedException {
 | 
					                    protected HikariDataSource deserialize(Fields fields) throws StreamCorruptedException {
 | 
				
			||||||
                HikariDataSource ds = new HikariDataSource();
 | 
					                        HikariDataSource ds = new HikariDataSource();
 | 
				
			||||||
                ds.setJdbcUrl((String) fields.getObject("jdbcurl"));
 | 
					                        ds.setJdbcUrl((String) fields.getObject("jdbcurl"));
 | 
				
			||||||
                return ds;
 | 
					                        return ds;
 | 
				
			||||||
            }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            @Override
 | 
					                    @Override
 | 
				
			||||||
            public boolean mustSyncDeserialization() {
 | 
					                    public boolean mustSyncDeserialization() {
 | 
				
			||||||
                return false;
 | 
					                        return false;
 | 
				
			||||||
            }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            @Override
 | 
					                    @Override
 | 
				
			||||||
            protected boolean canBeInstantiated() {
 | 
					                    protected boolean canBeInstantiated() {
 | 
				
			||||||
                return false;
 | 
					                        return false;
 | 
				
			||||||
            }
 | 
					                    }
 | 
				
			||||||
        }));
 | 
					                }));
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user