77 Commits
1.3.1 ... 1.3.9

Author SHA1 Message Date
cf61d7589b Update README.md 2022-12-08 22:09:39 +02:00
4d4af1622e Update README.md 2022-12-08 22:01:07 +02:00
af25e695f0 Update README.md 2022-12-08 22:00:30 +02:00
54e260e56b Small config fix & shade PostgreSQL driver 2022-12-08 21:54:08 +02:00
587f303ba5 Add max-connection-lifetime to config 2022-12-08 21:40:28 +02:00
8586aeefcd Re-add thread-pool-size config option, fixes #19 2022-12-08 21:33:09 +02:00
eb89699ed5 Fix no vars if execute is last line & fix code cleanup 2022-12-08 19:58:06 +02:00
f92b16a09f Code cleanup, better skript 2.6.1 support, update mariadb driver, possible bugfix, add example to README 2022-12-08 18:48:53 +02:00
dbce1d33f9 Revert minimizeJar due to bug & fix sync queries in non-main thread 2022-12-08 17:59:45 +02:00
a97ba3aee8 Merge pull request 'Small Improvements' (#20) from rigbot/skript-db:master into master
Reviewed-on: #20
2022-08-18 09:48:59 +00:00
bf5429634a Minimize Jar [pom.xl]
Added minimize jar to configuration. Greatly reduces exported jar file size.
2022-08-18 09:45:40 +00:00
abd2d6fe03 Update 'pom.xml' 2022-08-18 09:44:50 +00:00
2cc46fdae5 Unnecessary Import [EffExecuteStatement.java]
Removed unnecessary import.
2022-08-18 09:43:54 +00:00
4fee9f2898 Update 'pom.xml' 2022-08-18 09:41:44 +00:00
bbbfb83518 Small Changes [pom.xml]
Added minimize jar to configuration. Makes exported jar file size substantially smaller.
2022-08-18 09:41:10 +00:00
f51db586ef Document supported Minecraft server versions 2022-08-12 08:38:18 +00:00
3466a04ec8 Fix null pointer exception 2022-03-21 17:28:59 +02:00
d0191007a5 Fix mistake in readme 2022-03-20 11:30:51 +01:00
b1df041ccb Update 'README.md' 2022-03-20 11:29:17 +01:00
b9e14652ea Update 'README.md' 2022-03-20 11:28:55 +01:00
0098450441 Update 'README.md' 2022-03-20 11:28:25 +01:00
9c41039217 Update 'README.md' 2022-03-20 11:27:35 +01:00
46a72639af Add config to readme 2022-03-20 11:26:33 +01:00
cbd1565896 Add installation to readme 2022-03-20 11:21:59 +01:00
0332ad9334 If we're moving to Java 9, let's move to Java 11 as it is LTS 2022-03-20 12:08:41 +02:00
32f40af484 Fix blob column types & Use Java 9 2022-03-20 11:57:25 +02:00
2a24a974ef Update dependency, performance improvement 2022-01-28 15:06:16 +02:00
d80df616c3 Update dependencies 2022-01-23 11:39:02 +02:00
0b3b183a14 Fix compilation 2021-10-21 11:00:14 +03:00
70fa7b7105 1.3.3 2021-10-21 11:00:12 +03:00
c85d579645 fix expressions in queries, thanks to TPGamesNL 2021-10-21 11:00:12 +03:00
1e039c1bfe fix skript 2.6 support 2021-10-21 11:00:11 +03:00
2a4d7f6a6d add null check on locals 2021-10-21 11:00:11 +03:00
2dcd4edf58 reformat code (thanks TPGamesNL) 2021-10-21 11:00:11 +03:00
9b694206d3 Update README.md 2021-10-21 11:00:10 +03:00
d8cb6fedb5 finish experimental (disabled) on complete of sql query event 2021-10-21 11:00:10 +03:00
2eda1418cf Add Java 8 support 2021-10-21 11:00:09 +03:00
83d2795519 set api version to 1.13
I think this will help with performance in 1.13+ version, but will keep working in 1.9+
2021-10-21 11:00:09 +03:00
66459c6190 Merge pull request #10 from ham1255/master
Fixed Govindas weirdness on project files (maven fix)
2021-10-21 11:00:08 +03:00
b1ef437b96 partially fixed pom.xml 2021-10-21 11:00:08 +03:00
mohammed jasem alaajel
35cfb0b4bb Fixed Govindas weirdness on project files 2021-10-21 11:00:08 +03:00
4da64624d8 Fix pom.xml & update HikariCP 2021-10-21 11:00:07 +03:00
0c16b98553 make sql auto sync if not main thread 2021-10-21 11:00:07 +03:00
9e7c5fcbee Preparations for quicker queries (no 50ms delay) feature 2021-10-21 11:00:07 +03:00
8b3d26cf78 Update README.md 2021-10-21 11:00:07 +03:00
5a22864650 remove thread pool config option & Use CachedThreadPool to make sure all threads are used properly
CachedThreadPool automatically creates threads on demand and automatically deletes unused threads after 60 seconds of no usage, so we can remove config option
2021-10-21 11:00:06 +03:00
05554a0043 1.2.1 2021-10-21 11:00:06 +03:00
f6a43f0b53 Make config more informative 2021-10-21 11:00:06 +03:00
5803be55b7 Support missing config options 2021-10-21 11:00:06 +03:00
d57b921b4f Shade MariaDB driver & add configurable driver class to config 2021-10-21 11:00:06 +03:00
4fd654c743 bump HikariCP version 2021-10-21 11:00:06 +03:00
233d21a30b Fix memory leak introduced by last commit 2021-10-21 11:00:06 +03:00
8e760c4d8b Merge branch 'master' of https://github.com/Govindass/skript-db 2021-10-21 11:00:06 +03:00
fe2231ae23 Update README.md 2021-10-21 11:00:06 +03:00
a132912f6b Switch from gradle to maven (preference) and optimize code 2021-10-21 11:00:05 +03:00
d4f9a394fd Add synchronous syntax to readme 2021-10-21 11:00:05 +03:00
a63a4c6d6f Fix local variables in sql queries within the same tick & first startup errors 2021-10-21 11:00:05 +03:00
11063c166d Bump HikariCP 3.4.3 -> 3.4.5 2021-10-21 11:00:05 +03:00
b5f2d56263 Remove debug message 2021-10-21 11:00:05 +03:00
8b5121d5fb Make thread pool configurable 2021-10-21 11:00:05 +03:00
b435696385 Add synchronous support on current thread 2021-10-21 11:00:05 +03:00
4d795edf01 Merge FranKusmiruk's Pull Request 2021-10-21 11:00:05 +03:00
2570408906 add gradle fatJar task 2021-10-21 11:00:05 +03:00
8d94a85779 Update HikariCP, should fix a lot of issues 2021-10-21 11:00:05 +03:00
6d0ec81e2e Remove unused class 2021-10-21 11:00:05 +03:00
c4379d45b7 add synchronous 2021-10-21 11:00:04 +03:00
434ea07410 Delete EffSyncExecuteStatement.java 2021-10-21 11:00:04 +03:00
8ca308802f Delete EffExecuteStatement.java 2021-10-21 11:00:04 +03:00
2ce1dd924e Delete plugin.yml 2021-10-21 11:00:02 +03:00
b20740b020 Add files via upload 2021-10-21 10:59:57 +03:00
Bryan Terce
7906dd8eb1 0.2.1 Hotfix 2019-06-26 01:56:29 -07:00
Bryan Terce
33f4d5ff54 Fix sync execute logic (fixes #16) 2019-06-26 01:55:08 -07:00
Bryan Terce
9a20da02a0 0.2.0 Update 2019-06-22 12:12:27 -07:00
Bryan Terce
f13bf78007 Manually fix README 2019-06-22 12:11:29 -07:00
Bryan Terce
55081e5d28 Update README 2019-06-22 12:09:59 -07:00
Bryan Terce
d88e431f07 Add synchronous execution flag 2019-06-22 12:09:17 -07:00
Bryan Terce
e6606a012a Fix lifespan syntax 2019-06-22 12:05:22 -07:00
12 changed files with 598 additions and 473 deletions

View File

@@ -5,15 +5,25 @@
### Difference from original skript-db ### Difference from original skript-db
- Fixed local variables disappearance in newer Skript versions (very hacky fix, but it works, so that's good!) - Fixed local variables disappearance in newer Skript versions (very hacky fix, but it works, so that's good!)
- Thread-pool size is now automatically increasing on demand to use of CachedThreadPool, instead of a fixed hard-coded number - Uses newer versions of dependencies (Increased performance and security)
- Uses a newer version of HikariCP - Replaced `synchronously execute` with `quickly execute`, which allows to speed up queries by 50ms with some risk
- Only meant to be used by newer Minecraft versions (1.8 is not supported) - If a sql query is detected to be running on non-main thread, it becomes synchronous automatically
- SQL Driver is configurable, comes with shaded MariaDB and PostgreSQL drivers
- A few variable type related bugs fixed
- Uses Java 11 instead of Java 8
- Configurable max-connection-lifetime
### Installation
1. Use 1.8+ Minecraft server version.
2. Use Skript 2.5+ (1.8 Skript fork is needed if you're using 1.8)
3. Use Java 11+ (If you use 1.8, a spigot fork is needed to support Java 11+)
4. Put skript-db in plugins folder and restart the server
### Expression `Data Source` => `datasource` ### Expression `Data Source` => `datasource`
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.
The url format for your database may vary! The example provided uses a MySQL database. The url format for your database may vary depending on database you are using.
MariaDB/PostgreSQL users: make sure to check `config.yml` to use the correct driver.
#### Syntax #### Syntax
``` ```
[the] data(base|[ ]source) [(of|at)] %string% [the] data(base|[ ]source) [(of|at)] %string%
@@ -22,6 +32,9 @@ Stores the connection information for a data source. This should be saved to a v
#### Examples #### Examples
``` ```
set {sql} to the database "mysql://localhost:3306/mydatabase?user=admin&password=12345&useSSL=false" set {sql} to the database "mysql://localhost:3306/mydatabase?user=admin&password=12345&useSSL=false"
set {sql} to the database "mariadb://localhost:3306/mydatabase?user=admin&password=12345&useSSL=false"
set {sql} to the database "postgresql://localhost:3306/mydatabase?user=admin&password=12345&ssl=false"
set {sql} to the database "sqlite:database.db"
``` ```
--- ---
@@ -36,10 +49,10 @@ Executes a statement on a database and optionally stores the result in a variabl
If a list variable, such as `{test::*}`, is passed, the query result will be mapped to the list If a list variable, such as `{test::*}`, is passed, the query result will be mapped to the list
variable in the form `{test::<column name>::<row number>}` variable in the form `{test::<column name>::<row number>}`
If `synchronously` is specified, the SQL query will be done on the current thread. If `quickly` is specified, the SQL query will be done without jumping back to main thread, which speeds it up by 50ms, however that makes code after it to also be on separate thread, you can jump back to main thread by adding `wait a tick`
#### Syntax #### Syntax
``` ```
[synchronously] 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%]
``` ```
#### Examples #### Examples
@@ -76,5 +89,23 @@ execute "select %unsafe {columns variable}% from %{table variable}%" in {sql}
execute unsafe {fully dynamic query} in {sql} execute unsafe {fully dynamic query} in {sql}
``` ```
#### FAQ: How to return sql data in a function?
You can't because functions don't allow delays, but you can use skript-reflect sections for this:
```
on load:
create new section stored in {-section::getPlayersFromDatabase}:
execute "SELECT uuid FROM table" in {-sql} and store the result in {_result::*}
return {_result::uuid::*}
command /showplayers [<text>]:
trigger:
run section {-section::getPlayersFromDatabase} async and store result in {_uuids::*} and wait
send "%{_uuids::*}%"
```
--- ---
### Configuration
plugins/skript-db/config.yml
```
# Only change this if you wish to use a different driver than Java's default, like MariaDB driver.
# If you use MariaDB, its driver is shaded together with skript-db, so you can just specify: "org.mariadb.jdbc.Driver"
sql-driver-class-name: "default"
```

23
pom.xml
View File

@@ -6,7 +6,7 @@
<groupId>com.btk5h</groupId> <groupId>com.btk5h</groupId>
<artifactId>skript-db</artifactId> <artifactId>skript-db</artifactId>
<version>1.3.1</version> <version>1.3.9</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<repositories> <repositories>
@@ -19,8 +19,8 @@
<url>https://jitpack.io</url> <url>https://jitpack.io</url>
</repository> </repository>
<repository> <repository>
<id>commons-pool2</id> <id>mvnrepository</id>
<url>https://mvnrepository.com/artifact/org.apache.commons/commons-pool2</url> <url>https://mvnrepository.com</url>
</repository> </repository>
<repository> <repository>
<id>PaperMC</id> <id>PaperMC</id>
@@ -45,8 +45,8 @@
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version> <version>3.6.1</version>
<configuration> <configuration>
<source>8</source> <source>11</source>
<target>8</target> <target>11</target>
<encoding>UTF-8</encoding> <encoding>UTF-8</encoding>
</configuration> </configuration>
</plugin> </plugin>
@@ -81,7 +81,7 @@
<dependency> <dependency>
<groupId>com.zaxxer</groupId> <groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId> <artifactId>HikariCP</artifactId>
<version>4.0.3</version> <version>5.0.1</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
@@ -93,14 +93,21 @@
<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.1</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 -->
<dependency> <dependency>
<groupId>org.mariadb.jdbc</groupId> <groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId> <artifactId>mariadb-java-client</artifactId>
<version>2.7.2</version> <version>3.0.9</version>
<scope>compile</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.postgresql/postgresql -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.5.1</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>

View File

@@ -73,8 +73,14 @@ public final class SkriptDB extends JavaPlugin {
} }
try { try {
if (out == null) return; if (out == null) return;
out.write("# Only change this if you wish to use a different driver than Java's default, like MariaDB driver.\n");
out.write("# If you use MariaDB, its driver is shaded together with skript-db, so you can just specify:" + "\"org.mariadb.jdbc.Driver\"" + ".\n"); out.write("# How many connections can be awaited for simultaneously, may be useful to increase if SQL database is hosted on a separate machine to account for ping.\n");
out.write("# If it is hosted within the same machine, set it to the count of cores your processor has or the count of threads your processor can process at once.\n");
out.write("thread-pool-size: " + (Runtime.getRuntime().availableProcessors() + 2) + "\n");
out.write("# How long SQL connections should be kept alive in HikariCP. Default: 1800000 (30 minutes)");
out.write("max-connection-lifetime: 1800000");
out.write("# Only change this if you wish to use a different driver than Java's default, like MariaDB/PostgreSQL driver.\n");
out.write("# If you use MariaDB or PostgreSQL, its driver is shaded together with skript-db, so you can just specify:" + "\"org.mariadb.jdbc.Driver\"" + " or " + "\"org.postgresql.Driver\"" + ".\n");
out.write("sql-driver-class-name: " + "\"default\"" + "\n"); out.write("sql-driver-class-name: " + "\"default\"" + "\n");
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();

View File

@@ -5,14 +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;
private Object variables;
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();
@@ -22,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 getArgument() { public String getQuery() {
return argument; return argument;
} }
// public String getVariables() {return;} // public String getVariables() {return;}
} }

View File

@@ -8,13 +8,15 @@ 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;
import javax.sql.DataSource; import javax.sql.DataSource;
import javax.sql.rowset.CachedRowSet; import javax.sql.rowset.CachedRowSet;
import javax.sql.rowset.serial.SerialBlob;
import javax.sql.rowset.serial.SerialException;
import java.io.IOException;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSetMetaData; import java.sql.ResultSetMetaData;
@@ -42,283 +44,307 @@ 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 = Executors.newFixedThreadPool(SkriptDB.getInstance().getConfig().getInt("thread-pool-size", 10));
Skript.registerEffect(EffExecuteStatement.class, static String lastError;
"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; 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 (!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
SQLQueryCompleteEvent event = new SQLQueryCompleteEvent("something");
((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 quickly;
private boolean isSync = false;
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;
Object locals = Variables.removeLocals(e);
boolean hasResultSet = stmt.execute(); //execute SQL statement
if (Bukkit.isPrimaryThread()) {
if (baseVariable != null) { CompletableFuture<Object> sql = CompletableFuture.supplyAsync(() -> executeStatement(ds, baseVariable, query), threadPool);
if (isList) { sql.whenComplete((res, err) -> {
baseVariable = baseVariable.substring(0, baseVariable.length() - 1); if (err != null) {
} err.printStackTrace();
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) { //handle last error syntax data
return ex.getMessage(); lastError = null;
} if (res instanceof String) {
} else { lastError = (String) res;
crs.last(); }
variableList.put(baseVariable, crs.getRow()); //if local variables are present
} //bring back local variables
} else if (!isList) { //populate SQL data into variables
//if no results are returned and the specified variable isn't a list variable, put the affected rows count in the variable if (!quickly) {
variableList.put(baseVariable, stmt.getUpdateCount()); 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);
});
} 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);
}
});
// sync executed SQL query, same as above, just sync
} else {
isSync = true;
Object resources = executeStatement(ds, baseVariable, query);
//handle last error syntax data
lastError = null;
if (resources instanceof String) {
lastError = (String) resources;
}
//if local variables are present
//bring back local variables
//populate SQL data into variables
if (locals != null && getNext() != null) {
Variables.setLocalVariables(e, locals);
}
if (!(resources instanceof String)) {
((Map<String, Object>) resources).forEach((name, value) -> setVariable(e, name, value));
}
TriggerItem.walk(getNext(), e);
Variables.removeLocals(e);
} }
}
} 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));
}
} }
return stmt; @Override
} protected TriggerItem walk(Event e) {
debug(e, true);
private String getString(Object[] objects, int index) { if (!quickly || !isSync) {
if (index < 0 || index >= objects.length) { Delay.addDelayedEvent(e);
return null; }
execute(e);
return null;
} }
Object object = objects[index]; 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);
}
if (object instanceof String) { StringBuilder sb = new StringBuilder();
return (String) object; List<Object> parameters = new ArrayList<>();
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;
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;
}
}
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);
} }
return null; 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)) {
private void setVariable(Event e, String name, Object obj) { boolean hasResultSet = stmt.execute();
Variables.setVariable(name.toLowerCase(Locale.ENGLISH), obj, e, isLocal);
}
@Override if (baseVariable != null) {
public String toString(Event e, boolean debug) { if (isList) {
return "execute " + query.toString(e, debug) + " in " + dataSource.toString(e, debug); baseVariable = baseVariable.substring(0, baseVariable.length() - 1);
} }
@SuppressWarnings("unchecked") if (hasResultSet) {
@Override CachedRowSet crs = SkriptDB.getRowSetFactory().createCachedRowSet();
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, crs.populate(stmt.getResultSet());
SkriptParser.ParseResult parseResult) {
Expression<String> statementExpr = (Expression<String>) exprs[0]; if (isList) {
if (statementExpr instanceof VariableString || statementExpr instanceof ExprUnsafe) { ResultSetMetaData meta = crs.getMetaData();
query = statementExpr; int columnCount = meta.getColumnCount();
} else {
Skript.error("Database statements must be string literals. If you must use an expression, " + for (int i = 1; i <= columnCount; i++) {
"you may use \"%unsafe (your expression)%\", but keep in mind, you may be vulnerable " + String label = meta.getColumnLabel(i);
"to SQL injection attacks!"); variableList.put(baseVariable + label, label);
return false; }
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;
} }
dataSource = (Expression<HikariDataSource>) exprs[1];
Expression<?> expr = exprs[2]; private PreparedStatement createStatement(Connection conn, Pair<String, List<Object>> query) throws SQLException {
isSync = matchedPattern == 1; PreparedStatement stmt = conn.prepareStatement(query.getFirst());
if (expr instanceof Variable) { List<Object> parameters = query.getSecond();
Variable<?> varExpr = (Variable<?>) expr;
var = varExpr.getName(); if (parameters != null) {
isLocal = varExpr.isLocal(); for (int i = 0; i < parameters.size(); i++) {
isList = varExpr.isList(); stmt.setObject(i + 1, parameters.get(i));
} else if (expr != null) { }
Skript.error(expr + " is not a variable"); }
return false;
return stmt;
}
private String getString(Object[] objects, int index) {
if (index < 0 || index >= objects.length) {
return null;
}
Object object = objects[index];
if (object instanceof String) {
return (String) object;
}
return null;
}
private void setVariable(Event e, String name, Object obj) {
//fix mediumblob and similar column types, so they return a String correctly
if (obj != null) {
if (obj.getClass().getName().equals("[B")) {
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)
} else if (obj instanceof SerialBlob) {
try {
obj = new String(((SerialBlob) obj).getBinaryStream().readAllBytes());
} catch (IOException | SerialException ex) {
ex.printStackTrace();
}
}
}
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];
quickly = 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;
}
} }

View File

@@ -6,24 +6,24 @@ import ch.njol.skript.lang.SkriptEvent;
import ch.njol.skript.lang.SkriptParser; import ch.njol.skript.lang.SkriptParser;
import com.btk5h.skriptdb.events.SQLQueryCompleteEvent; import com.btk5h.skriptdb.events.SQLQueryCompleteEvent;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.eclipse.jdt.annotation.Nullable;
public class EvtSQLQueryComplete extends SkriptEvent { 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
public boolean check(Event event) {
return (event instanceof SQLQueryCompleteEvent);
}
@Override @Override
public String toString(@Nullable Event event, boolean debug) { public boolean init(final Literal<?>[] literals, final int i, final SkriptParser.ParseResult parseResult) {
return true;
}
@Override
public boolean check(Event event) {
return (event instanceof SQLQueryCompleteEvent);
}
@Override
public String toString(Event event, boolean debug) {
return "complete of sql query"; return "complete of sql query";
} }
} }

View File

@@ -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;
} }
} }

View File

@@ -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,76 @@ 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();
ds.setMaximumPoolSize(SkriptDB.getInstance().getConfig().getInt("thread-pool-size", 10));
// 30 minutes by default
ds.setMaxLifetime(SkriptDB.getInstance().getConfig().getInt("max-connection-lifetime", 1800000));
//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;
}
} }

View File

@@ -0,0 +1,60 @@
package com.btk5h.skriptdb.skript;
import ch.njol.skript.ScriptLoader;
import ch.njol.skript.Skript;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.ExpressionType;
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.
*
* @name Last Data Source Error
* @pattern [the] [last] (sql|db|data(base|[ ]source)) error
* @return text
* @since 0.1.0
*/
public class ExprSQLQuery extends SimpleExpression<String> {
static {
Skript.registerExpression(ExprSQLQuery.class, String.class,
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
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;
}
}

View File

@@ -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;
} }
} }

View File

@@ -11,58 +11,54 @@ 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 })
public String getVariableNamePattern() { .serializer(new Serializer<HikariDataSource>() {
return "jdbc:.+"; @Override
} public Fields serialize(HikariDataSource o) {
}) Fields fields = new Fields();
.serializer(new Serializer<HikariDataSource>() { fields.putObject("jdbcurl", o.getJdbcUrl());
@Override return fields;
public Fields serialize(HikariDataSource o) { }
Fields fields = new Fields();
fields.putObject("jdbcurl", o.getJdbcUrl());
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;
} }
})); }));
} }
} }

View File

@@ -1,6 +1,6 @@
name: skript-db name: skript-db
version: 1.3.1 version: 1.3.9
main: com.btk5h.skriptdb.SkriptDB main: com.btk5h.skriptdb.SkriptDB
depend: [Skript] depend: [Skript]
authors: [btk5h, FranKusmiruk, Govindas] authors: [btk5h, FranKusmiruk, Govindas, TPGamesNL]
api-version: 1.13 api-version: 1.13