Compare commits

..

20 Commits

Author SHA1 Message Date
Govindass
3e7cf7a6dd Bump HikariCP 3.4.3 -> 3.4.5 2020-06-24 08:19:09 +03:00
Govindass
185a324e8c Remove debug message 2020-05-20 14:18:23 +03:00
Govindass
e75941d75a Make thread pool configurable 2020-05-18 15:34:43 +03:00
Govindass
efa37217c2 Add synchronous support on current thread 2020-05-18 13:20:01 +03:00
Govindass
0d8d834929 Merge FranKusmiruk's Pull Request 2020-05-02 20:25:36 +03:00
Govindass
b2a53078a6 add gradle fatJar task 2020-05-02 20:02:25 +03:00
Govindas
83f71e147b
Update HikariCP, should fix a lot of issues 2020-05-02 19:48:35 +03:00
Govindass
e610fc1357 Remove unused class 2020-05-02 19:30:48 +03:00
Govindass
aa1a1d14c7
add synchronous 2019-09-30 15:28:32 +03:00
Govindass
3a1ec76a0b
Delete EffSyncExecuteStatement.java 2019-09-30 15:27:26 +03:00
Govindass
2eb691cd69
Delete EffExecuteStatement.java 2019-09-30 15:27:16 +03:00
Govindass
6dbd2effb3
Delete plugin.yml 2019-09-30 15:26:48 +03:00
Govindass
88b76f1b5b
Add files via upload 2019-09-30 15:25:37 +03:00
Bryan Terce
b692047878
0.2.1 Hotfix 2019-06-26 01:56:29 -07:00
Bryan Terce
3e57cae866
Fix sync execute logic (fixes #16) 2019-06-26 01:55:08 -07:00
Bryan Terce
1e95b818eb
0.2.0 Update 2019-06-22 12:12:27 -07:00
Bryan Terce
688ea9d46b
Manually fix README 2019-06-22 12:11:29 -07:00
Bryan Terce
dd6d574479
Update README 2019-06-22 12:09:59 -07:00
Bryan Terce
1f6091eb95
Add synchronous execution flag 2019-06-22 12:09:17 -07:00
Bryan Terce
74d4918f44
Fix lifespan syntax 2019-06-22 12:05:22 -07:00
22 changed files with 767 additions and 784 deletions

13
.editorconfig Normal file
View File

@ -0,0 +1,13 @@
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.java]
continuation_indent_size = 4

7
.gitignore vendored
View File

@ -1,9 +1,10 @@
## OSX
*.DS_Store
## Gradle
.gradle
build/
## IntelliJ
.idea/
*.iml
out/
target
compile/

View File

@ -3,42 +3,19 @@
> Sensible SQL support for Skript.
---
### Difference from original skript-db
- Fixed local variables disappearance in newer Skript versions (very hacky fix, but it works, so that's good!)
- Uses newer versions of dependencies (Increased performance and security)
- Replaced `synchronously execute` with `quickly execute`, which allows to speed up queries by 50ms with some risk
- If a sql query is detected to be running on non-main thread, it becomes synchronous automatically
- SQL Driver is configurable both in config and in database connection, comes with shaded MariaDB and PostgreSQL drivers
- A few variable type related bugs fixed
- Uses Java 11 instead of Java 8
### 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`
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.
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.
The url format for your database may vary! The example provided uses a MySQL database.
#### Syntax
```
[the] data(base|[ ]source) [(of|at)] %string% [with [a] [max[imum]] [connection] life[ ]time of %timespan%] [[(using|with)] [a] driver %-string%]
[the] data(base|[ ]source) [(of|at)] %string%
```
#### Examples
```
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"
# Extra parameters:
set {sql} to the database "postgresql://localhost:3306/mydatabase?user=admin&password=12345&ssl=false" with a maximum connection lifetime of 30 minutes
set {sql} to the database "postgresql://localhost:3306/mydatabase?user=admin&password=12345&ssl=false" with a maximum connection lifetime of 30 minutes using driver "org.postgresql.Driver"
```
---
@ -52,11 +29,9 @@ 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
variable in the form `{test::<column name>::<row number>}`
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
```
[quickly] execute %string% (in|on) %datasource% [and store [[the] (output|result)[s]] (to|in) [the] [var[iable]] %-objects%]
execute %string% (in|on) %datasource% [and store [[the] (output|result)[s]] (to|in) [the] [var[iable]] %-objects%]
```
#### Examples
@ -80,7 +55,6 @@ Stores the error from the last executed statement, if there was one.
### Expression `Unsafe Expression` => `text`
Opts out of automatic SQL injection protection for a specific expression in a statement.
Note: If using PostgreSQL, this will always be needed, due to skript-db not supporting SQL injection protection for PostgreSQL currently.
#### Syntax
```
unsafe %text%
@ -94,23 +68,5 @@ execute "select %unsafe {columns variable}% from %{table variable}%" 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"
```

56
build.gradle Normal file
View File

@ -0,0 +1,56 @@
group 'com.btk5h.skript-db'
version '1.1.0'
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:4.0.2'
}
}
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'java'
sourceCompatibility = 1.8
repositories {
mavenCentral()
maven {
url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/'
}
maven {
url 'https://oss.sonatype.org/content/groups/public/'
}
maven {
url 'http://jitpack.io/'
}
}
dependencies {
shadow 'org.spigotmc:spigot-api:1.13.2-R0.1-SNAPSHOT'
shadow 'com.github.SkriptLang:Skript:2.3.6'
compile 'com.zaxxer:HikariCP:3.4.5'
}
task buildReadme(type: Javadoc) {
source = sourceSets.main.allJava
classpath = sourceSets.main.compileClasspath
destinationDir = projectDir
options.docletpath = [file('tools/skriptdoclet.jar')]
options.doclet = 'com.btk5h.skriptdoclet.SkriptDoclet'
options.addStringOption('file', 'README.md')
options.addStringOption('markdown', '-quiet')
}
task fatJar(type: Jar) {
manifest {
attributes 'Implementation-Title': 'Gradle Jar File Example',
'Implementation-Version': version,
'Main-Class': 'com.mkyong.DateUtils'
}
baseName = project.name + '-all'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,6 @@
#Thu Aug 24 18:49:02 PDT 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip

172
gradlew vendored Executable file
View File

@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save ( ) {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
gradlew.bat vendored Normal file
View File

@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

115
pom.xml
View File

@ -1,115 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.btk5h</groupId>
<artifactId>skript-db</artifactId>
<version>1.4.0</version>
<packaging>jar</packaging>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/org/spigotmc/spigot-api/</url>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
<repository>
<id>mvnrepository</id>
<url>https://mvnrepository.com</url>
</repository>
<repository>
<id>PaperMC</id>
<url>https://repo.destroystokyo.com/repository/maven-public/</url>
</repository>
<repository>
<id>sk89q</id>
<url>https://maven.sk89q.com/repo</url>
</repository>
</repositories>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>11</source>
<target>11</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.3</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<outputDirectory>${project.basedir}/compile</outputDirectory>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.0.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.16.5-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.SkriptLang</groupId>
<artifactId>Skript</artifactId>
<version>2.6.1</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client -->
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>3.1.2</version>
<scope>compile</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.postgresql/postgresql -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.6.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

2
settings.gradle Normal file
View File

@ -0,0 +1,2 @@
rootProject.name = 'skript-db'

View File

@ -1,3 +1,3 @@
Manifest-Version: 1.0
Main-Class: com.btk5h.skriptdb.SkriptDB
Manifest-Version: 1.0
Main-Class: com.btk5h.skriptdb.SkriptDB

View File

@ -74,14 +74,9 @@ public final class SkriptDB extends JavaPlugin {
try {
if (out == null) return;
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("# How many connections can be awaited for simultaneously, may be useful to increase if mysql 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("thread-pool-size: " + (Runtime.getRuntime().availableProcessors() + 1) + "\n");
} catch (IOException e) {
e.printStackTrace();
}
@ -96,20 +91,19 @@ public final class SkriptDB extends JavaPlugin {
@Override
public void onEnable() {
try {
rowSetFactory = RowSetProvider.newFactory();
getAddonInstance().loadClasses("com.btk5h.skriptdb.skript");
} catch (SQLException e) {
throw new RuntimeException(e);
} catch (IOException e) {
e.printStackTrace();
}
try {
setupConfig();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
rowSetFactory = RowSetProvider.newFactory();
getAddonInstance().loadClasses("com.btk5h.skriptdb.skript");
} catch (SQLException e) {
throw new RuntimeException(e);
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@ -1,35 +0,0 @@
package com.btk5h.skriptdb.events;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
public class SQLQueryCompleteEvent extends Event {
private final static HandlerList HANDLERS = new HandlerList();
private final String argument;
public SQLQueryCompleteEvent(String argument) {
super(true);
this.argument = argument;
// this.variables = variables;
}
public static HandlerList getHandlerList() {
return HANDLERS;
}
@Override
public String getEventName() {
return super.getEventName();
}
@Override
public HandlerList getHandlers() {
return HANDLERS;
}
public String getQuery() {
return argument;
}
// public String getVariables() {return;}
}

View File

@ -14,9 +14,6 @@ import org.bukkit.event.Event;
import javax.sql.DataSource;
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.PreparedStatement;
import java.sql.ResultSetMetaData;
@ -43,308 +40,253 @@ import java.util.concurrent.Executors;
* @example execute "select * from %{table variable}%" in {sql} and store the result in {output::*}
* @since 0.1.0
*/
public class EffExecuteStatement extends Effect {
private static final ExecutorService threadPool = Executors.newFixedThreadPool(SkriptDB.getInstance().getConfig().getInt("thread-pool-size", 10));
static String lastError;
public class EffExecuteStatement extends Delay {
static {
Skript.registerEffect(EffExecuteStatement.class,
"execute %string% (in|on) %datasource% " +
"[and store [[the] (output|result)[s]] (to|in) [the] [var[iable]] %-objects%]", "synchronously execute %string% (in|on) %datasource% " +
"[and store [[the] (output|result)[s]] (to|in) [the] [var[iable]] %-objects%]");
}
static {
Skript.registerEffect(EffExecuteStatement.class,
"execute %string% (in|on) %datasource% " +
"[and store [[the] (output|result)[s]] (to|in) [the] [var[iable]] %-objects%]", "quickly execute %string% (in|on) %datasource% " +
"[and store [[the] (output|result)[s]] (to|in) [the] [var[iable]] %-objects%]");
static String lastError;
private static final ExecutorService threadPool =
Executors.newFixedThreadPool(SkriptDB.getInstance().getConfig().getInt("thread-pool-size"));
private Expression<String> query;
private Expression<HikariDataSource> dataSource;
private VariableString var;
private boolean isLocal;
private boolean isList;
private Map<String, Object> doLater = new HashMap<>();
private boolean isSync;
private void continueScriptExecution(Event e, String res) {
lastError = res;
if (getNext() != null) {
doLater.forEach((name, value) -> setVariable(e, name, value));
doLater.clear();
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;
private Expression<String> query;
private Expression<HikariDataSource> dataSource;
private VariableString var;
private boolean isLocal;
private boolean isList;
private boolean quickly;
private boolean isSync = false;
if (ds == null)
return;
if (isSync) {
String result = executeStatement(ds, baseVariable, query);
continueScriptExecution(e, result);
} else {
Object locals = Variables.removeLocals(e);
CompletableFuture<String> sql =
CompletableFuture.supplyAsync(() -> executeStatement(ds, baseVariable, query), threadPool);
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));
}
sql.whenComplete((res, err) -> {
if (err != null) {
err.printStackTrace();
}
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;
Object locals = Variables.removeLocals(e);
Bukkit.getScheduler().runTask(SkriptDB.getInstance(), () -> {
lastError = res;
//execute SQL statement
if (Bukkit.isPrimaryThread()) {
CompletableFuture<Object> sql = CompletableFuture.supplyAsync(() -> executeStatement(ds, baseVariable, query), threadPool);
sql.whenComplete((res, err) -> {
if (err != null) {
err.printStackTrace();
}
//handle last error syntax data
lastError = null;
if (res instanceof String) {
lastError = (String) res;
}
//if local variables are present
//bring back local variables
//populate SQL data into variables
if (!quickly) {
Bukkit.getScheduler().runTask(SkriptDB.getInstance(), () -> {
if (locals != null && getNext() != null) {
Variables.setLocalVariables(e, locals);
}
if (!(res instanceof String)) {
((Map<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));
}
if (getNext() != null) {
if (locals != null)
Variables.setLocalVariables(e, locals);
doLater.forEach((name, value) -> setVariable(e, name, value));
doLater.clear();
TriggerItem.walk(getNext(), e);
Variables.removeLocals(e);
}
}
});
});
}
}
@Override
protected TriggerItem walk(Event e) {
debug(e, true);
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);
}
@Override
protected TriggerItem walk(Event e) {
debug(e, true);
if (!quickly || !isSync) {
Delay.addDelayedEvent(e);
}
execute(e);
return null;
}
StringBuilder sb = new StringBuilder();
List<Object> parameters = new ArrayList<>();
Object[] objects = SkriptUtil.getTemplateString(q);
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);
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;
}
}
StringBuilder sb = new StringBuilder();
List<Object> parameters = new ArrayList<>();
Object[] objects = SkriptUtil.getTemplateString(q);
Object expressionValue = expr.getSingle(e);
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);
if (expr instanceof ExprUnsafe) {
sb.append(expressionValue);
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);
}
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)) {
boolean hasResultSet = stmt.execute();
if (baseVariable != null) {
if (isList) {
baseVariable = baseVariable.substring(0, baseVariable.length() - 1);
}
if (hasResultSet) {
CachedRowSet crs = SkriptDB.getRowSetFactory().createCachedRowSet();
crs.populate(stmt.getResultSet());
if (isList) {
ResultSetMetaData meta = crs.getMetaData();
int columnCount = meta.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
String label = meta.getColumnLabel(i);
variableList.put(baseVariable + label, label);
}
int rowNumber = 1;
try {
while (crs.next()) {
for (int i = 1; i <= columnCount; i++) {
variableList.put(baseVariable + meta.getColumnLabel(i).toLowerCase(Locale.ENGLISH)
+ Variable.SEPARATOR + rowNumber, crs.getObject(i));
}
rowNumber++;
}
} catch (SQLException ex) {
return ex.getMessage();
}
} else {
crs.last();
variableList.put(baseVariable, crs.getRow());
}
} else if (!isList) {
//if no results are returned and the specified variable isn't a list variable, put the affected rows count in the variable
variableList.put(baseVariable, stmt.getUpdateCount());
}
}
} 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;
}
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;
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 {
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;
parameters.add(expressionValue);
sb.append('?');
if (standaloneString) {
Skript.warning("Do not surround expressions with quotes!");
}
}
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 new Pair<>(sb.toString(), parameters);
}
private String executeStatement(DataSource ds, String baseVariable, Pair<String, List<Object>> query) {
if (ds == null) {
return "Data source is not set";
}
try (Connection conn = ds.getConnection();
PreparedStatement stmt = createStatement(conn, query)) {
boolean hasResultSet = stmt.execute();
if (baseVariable != null) {
if (isList) {
baseVariable = baseVariable.substring(0, baseVariable.length() - 1);
}
if (hasResultSet) {
CachedRowSet crs = SkriptDB.getRowSetFactory().createCachedRowSet();
crs.populate(stmt.getResultSet());
if (isList) {
populateVariable(crs, baseVariable);
} else {
crs.last();
doLater.put(baseVariable, crs.getRow());
}
} else if (!isList) {
doLater.put(baseVariable, stmt.getUpdateCount());
}
}
} catch (SQLException ex) {
return ex.getMessage();
}
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();
if (parameters != null) {
for (int i = 0; i < parameters.size(); i++) {
stmt.setObject(i + 1, parameters.get(i));
}
}
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) {
Variables.setVariable(name.toLowerCase(Locale.ENGLISH), obj, e, isLocal);
}
private void populateVariable(CachedRowSet crs, String baseVariable)
throws SQLException {
ResultSetMetaData meta = crs.getMetaData();
int columnCount = meta.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
String label = meta.getColumnLabel(i);
doLater.put(baseVariable + label, label);
}
int rowNumber = 1;
while (crs.next()) {
for (int i = 1; i <= columnCount; i++) {
doLater.put(baseVariable + meta.getColumnLabel(i).toLowerCase(Locale.ENGLISH)
+ Variable.SEPARATOR + rowNumber, crs.getObject(i));
}
rowNumber++;
}
}
@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;
}
}

View File

@ -1,29 +0,0 @@
package com.btk5h.skriptdb.skript;
import ch.njol.skript.Skript;
import ch.njol.skript.lang.Literal;
import ch.njol.skript.lang.SkriptEvent;
import ch.njol.skript.lang.SkriptParser;
import com.btk5h.skriptdb.events.SQLQueryCompleteEvent;
import org.bukkit.event.Event;
public class EvtSQLQueryComplete extends SkriptEvent {
static {
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
public String toString(Event event, boolean debug) {
return "complete of sql query";
}
}

View File

@ -1,12 +1,13 @@
package com.btk5h.skriptdb.skript;
import org.bukkit.event.Event;
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.util.Kleenean;
import org.bukkit.event.Event;
/**
* Stores the error from the last executed statement, if there was one.
@ -17,33 +18,33 @@ import org.bukkit.event.Event;
* @since 0.1.0
*/
public class ExprDBError extends SimpleExpression<String> {
static {
Skript.registerExpression(ExprDBError.class, String.class,
ExpressionType.SIMPLE, "[the] [last] (sql|db|data(base|[ ]source)) error");
}
static {
Skript.registerExpression(ExprDBError.class, String.class,
ExpressionType.SIMPLE, "[the] [last] (sql|db|data(base|[ ]source)) error");
}
@Override
protected String[] get(Event e) {
return new String[]{EffExecuteStatement.lastError};
}
@Override
protected String[] get(Event e) {
return new String[]{EffExecuteStatement.lastError};
}
@Override
public boolean isSingle() {
return true;
}
@Override
public boolean isSingle() {
return true;
}
@Override
public Class<? extends String> getReturnType() {
return String.class;
}
@Override
public Class<? extends String> getReturnType() {
return String.class;
}
@Override
public String toString(Event e, boolean debug) {
return "last database error";
}
@Override
public String toString(Event e, boolean debug) {
return "last database error";
}
@Override
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) {
return true;
}
@Override
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) {
return true;
}
}

View File

@ -1,5 +1,12 @@
package com.btk5h.skriptdb.skript;
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.lang.Expression;
import ch.njol.skript.lang.ExpressionType;
@ -7,104 +14,84 @@ import ch.njol.skript.lang.SkriptParser;
import ch.njol.skript.lang.util.SimpleExpression;
import ch.njol.skript.util.Timespan;
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
* `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.
*
* @name Data Source
* @index -1
* @pattern [the] data(base|[ ]source) [(of|at)] %string% [with [a] [max[imum]] [connection] life[ ]time of %timespan%] [[(using|with)] [a] driver %-string%]"
* @pattern [the] data(base|[ ]source) [(of|at)] %string% [with [a] [max[imum]] [connection] life[ ]time of %timespan%]"
* @return datasource
* @example set {sql} to the database "mysql://localhost:3306/mydatabase?user=admin&password=12345&useSSL=false"
* @since 0.1.0
*/
public class ExprDataSource extends SimpleExpression<HikariDataSource> {
private static final Map<String, HikariDataSource> connectionCache = new HashMap<>();
static {
Skript.registerExpression(ExprDataSource.class, HikariDataSource.class,
ExpressionType.COMBINED, "[the] data(base|[ ]source) [(of|at)] %string% " +
"[with [a] [max[imum]] [connection] life[ ]time of %-timespan%]");
}
static {
Skript.registerExpression(ExprDataSource.class, HikariDataSource.class,
ExpressionType.COMBINED, "[the] data(base|[ ]source) [(of|at)] %string% " +
"[with [a] [max[imum]] [connection] life[ ]time of %-timespan%] " + "[[(using|with)] [a] driver %-string%]");
private static Map<String, HikariDataSource> connectionCache = new HashMap<>();
private Expression<String> url;
private Expression<Timespan> maxLifetime;
@Override
protected HikariDataSource[] get(Event e) {
String jdbcUrl = url.getSingle(e);
if (jdbcUrl == null) {
return null;
}
private Expression<String> url;
private Expression<Timespan> maxLifetime;
private Expression<String> driver;
@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 (driver != null && driver.getSingle(e) != null) {
ds.setDriverClassName(driver.getSingle(e));
} else 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 (!jdbcUrl.startsWith("jdbc:")) {
jdbcUrl = "jdbc:" + jdbcUrl;
}
@Override
public boolean isSingle() {
return true;
if (connectionCache.containsKey(jdbcUrl)) {
return new HikariDataSource[]{connectionCache.get(jdbcUrl)};
}
@Override
public Class<? extends HikariDataSource> getReturnType() {
return HikariDataSource.class;
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl(jdbcUrl);
if (maxLifetime != null) {
Timespan l = maxLifetime.getSingle(e);
if (l != null) {
ds.setMaxLifetime(l.getMilliSeconds());
}
}
@Override
public String toString(Event e, boolean debug) {
return "datasource " + url.toString(e, debug);
}
connectionCache.put(jdbcUrl, ds);
@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];
driver = (Expression<String>) exprs[2];
return true;
}
return new HikariDataSource[]{ds};
}
@Override
public boolean isSingle() {
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

@ -1,60 +0,0 @@
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,12 +1,13 @@
package com.btk5h.skriptdb.skript;
import org.bukkit.event.Event;
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.util.Kleenean;
import org.bukkit.event.Event;
/**
* Opts out of automatic SQL injection protection for a specific expression in a statement.
@ -19,44 +20,44 @@ import org.bukkit.event.Event;
* @since 0.1.0
*/
public class ExprUnsafe extends SimpleExpression<String> {
static {
Skript.registerExpression(ExprUnsafe.class, String.class, ExpressionType.COMBINED,
"unsafe %string%");
}
static {
Skript.registerExpression(ExprUnsafe.class, String.class, ExpressionType.COMBINED,
"unsafe %string%");
}
private Expression<String> stringExpression;
private String rawExpression;
private Expression<String> stringExpression;
private String rawExpression;
public String getRawExpression() {
return rawExpression;
}
public String getRawExpression() {
return rawExpression;
}
@Override
protected String[] get(Event e) {
return stringExpression.getArray(e);
}
@Override
protected String[] get(Event e) {
return stringExpression.getArray(e);
}
@Override
public boolean isSingle() {
return true;
}
@Override
public boolean isSingle() {
return true;
}
@Override
public Class<? extends String> getReturnType() {
return String.class;
}
@Override
public Class<? extends String> getReturnType() {
return String.class;
}
@Override
public String toString(Event e, boolean debug) {
return "unsafe " + stringExpression.toString(e, debug);
}
@Override
public String toString(Event e, boolean debug) {
return "unsafe " + stringExpression.toString(e, debug);
}
@SuppressWarnings("unchecked")
@Override
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed,
SkriptParser.ParseResult parseResult) {
stringExpression = (Expression<String>) exprs[0];
rawExpression = parseResult.expr.substring("unsafe".length()).trim();
return true;
}
@SuppressWarnings("unchecked")
@Override
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed,
SkriptParser.ParseResult parseResult) {
stringExpression = (Expression<String>) exprs[0];
rawExpression = parseResult.expr.substring("unsafe".length()).trim();
return true;
}
}

View File

@ -1,64 +1,72 @@
package com.btk5h.skriptdb.skript;
import com.zaxxer.hikari.HikariDataSource;
import java.io.NotSerializableException;
import java.io.StreamCorruptedException;
import ch.njol.skript.classes.ClassInfo;
import ch.njol.skript.classes.Parser;
import ch.njol.skript.classes.Serializer;
import ch.njol.skript.lang.ParseContext;
import ch.njol.skript.registrations.Classes;
import ch.njol.yggdrasil.Fields;
import com.zaxxer.hikari.HikariDataSource;
import java.io.StreamCorruptedException;
public class Types {
static {
Classes.registerClass(new ClassInfo<>(HikariDataSource.class, "datasource")
.user("datasources?")
.parser(new Parser<HikariDataSource>() {
@Override
public HikariDataSource parse(String s, ParseContext context) {
return null;
}
static {
Classes.registerClass(new ClassInfo<>(HikariDataSource.class, "datasource")
.user("datasources?")
.parser(new Parser<HikariDataSource>() {
@Override
public HikariDataSource parse(String s, ParseContext context) {
return null;
}
@Override
public String toString(HikariDataSource o, int flags) {
return o.getJdbcUrl();
}
@Override
public String toString(HikariDataSource o, int flags) {
return o.getJdbcUrl();
}
@Override
public String toVariableNameString(HikariDataSource o) {
return o.getJdbcUrl();
}
@Override
public String toVariableNameString(HikariDataSource o) {
return o.getJdbcUrl();
}
})
.serializer(new Serializer<HikariDataSource>() {
@Override
public Fields serialize(HikariDataSource o) {
Fields fields = new Fields();
fields.putObject("jdbcurl", o.getJdbcUrl());
return fields;
}
@Override
public String getVariableNamePattern() {
return "jdbc:.+";
}
})
.serializer(new Serializer<HikariDataSource>() {
@Override
public Fields serialize(HikariDataSource o) throws NotSerializableException {
Fields fields = new Fields();
fields.putObject("jdbcurl", o.getJdbcUrl());
return fields;
}
@Override
public void deserialize(HikariDataSource o, Fields f) {
}
@Override
public void deserialize(HikariDataSource o, Fields f) throws StreamCorruptedException,
NotSerializableException {
}
@Override
protected HikariDataSource deserialize(Fields fields) throws StreamCorruptedException {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl((String) fields.getObject("jdbcurl"));
return ds;
}
@Override
protected HikariDataSource deserialize(Fields fields) throws StreamCorruptedException,
NotSerializableException {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl((String) fields.getObject("jdbcurl"));
return ds;
}
@Override
public boolean mustSyncDeserialization() {
return false;
}
@Override
public boolean mustSyncDeserialization() {
return false;
}
@Override
protected boolean canBeInstantiated() {
return false;
}
}));
}
@Override
protected boolean canBeInstantiated() {
return false;
}
}));
}
}

View File

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

BIN
tools/skriptdoclet.jar Normal file

Binary file not shown.