Compare commits

...

79 Commits

Author SHA1 Message Date
Govindas c11980eea5 Improve documentation in README.md 2023-03-30 18:58:59 +03:00
Govindas 4124994b83 Support selecting sql driver at connection time & update dependencies 2023-03-30 15:19:23 +03:00
Govindas cf61d7589b Update README.md 2022-12-08 22:09:39 +02:00
Govindas 4d4af1622e Update README.md 2022-12-08 22:01:07 +02:00
Govindas af25e695f0 Update README.md 2022-12-08 22:00:30 +02:00
Govindas 54e260e56b Small config fix & shade PostgreSQL driver 2022-12-08 21:54:08 +02:00
Govindas 587f303ba5 Add max-connection-lifetime to config 2022-12-08 21:40:28 +02:00
Govindas 8586aeefcd Re-add thread-pool-size config option, fixes #19 2022-12-08 21:33:09 +02:00
Govindas eb89699ed5 Fix no vars if execute is last line & fix code cleanup 2022-12-08 19:58:06 +02:00
Govindas 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
Govindas dbce1d33f9 Revert minimizeJar due to bug & fix sync queries in non-main thread 2022-12-08 17:59:45 +02:00
Govindas 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
rigbot bf5429634a Minimize Jar [pom.xl]
Added minimize jar to configuration. Greatly reduces exported jar file size.
2022-08-18 09:45:40 +00:00
rigbot abd2d6fe03 Update 'pom.xml' 2022-08-18 09:44:50 +00:00
rigbot 2cc46fdae5 Unnecessary Import [EffExecuteStatement.java]
Removed unnecessary import.
2022-08-18 09:43:54 +00:00
rigbot 4fee9f2898 Update 'pom.xml' 2022-08-18 09:41:44 +00:00
rigbot 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
Govindas f51db586ef Document supported Minecraft server versions 2022-08-12 08:38:18 +00:00
Govindas 3466a04ec8 Fix null pointer exception 2022-03-21 17:28:59 +02:00
Govindas d0191007a5 Fix mistake in readme 2022-03-20 11:30:51 +01:00
Govindas b1df041ccb Update 'README.md' 2022-03-20 11:29:17 +01:00
Govindas b9e14652ea Update 'README.md' 2022-03-20 11:28:55 +01:00
Govindas 0098450441 Update 'README.md' 2022-03-20 11:28:25 +01:00
Govindas 9c41039217 Update 'README.md' 2022-03-20 11:27:35 +01:00
Govindas 46a72639af Add config to readme 2022-03-20 11:26:33 +01:00
Govindas cbd1565896 Add installation to readme 2022-03-20 11:21:59 +01:00
Govindas 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
Govindas 32f40af484 Fix blob column types & Use Java 9 2022-03-20 11:57:25 +02:00
Govindas 2a24a974ef Update dependency, performance improvement 2022-01-28 15:06:16 +02:00
Govindas d80df616c3 Update dependencies 2022-01-23 11:39:02 +02:00
Govindas 0b3b183a14 Fix compilation 2021-10-21 11:00:14 +03:00
Govindas 70fa7b7105 1.3.3 2021-10-21 11:00:12 +03:00
Govindas c85d579645 fix expressions in queries, thanks to TPGamesNL 2021-10-21 11:00:12 +03:00
Govindas 1e039c1bfe fix skript 2.6 support 2021-10-21 11:00:11 +03:00
Govindas 2a4d7f6a6d add null check on locals 2021-10-21 11:00:11 +03:00
Govindas 2dcd4edf58 reformat code (thanks TPGamesNL) 2021-10-21 11:00:11 +03:00
Govindas 9b694206d3 Update README.md 2021-10-21 11:00:10 +03:00
Govindas d8cb6fedb5 finish experimental (disabled) on complete of sql query event 2021-10-21 11:00:10 +03:00
Govindas 2eda1418cf Add Java 8 support 2021-10-21 11:00:09 +03:00
Govindas 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
Govindas 66459c6190 Merge pull request #10 from ham1255/master
Fixed Govindas weirdness on project files (maven fix)
2021-10-21 11:00:08 +03:00
Govindas 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
Govindas 4da64624d8 Fix pom.xml & update HikariCP 2021-10-21 11:00:07 +03:00
Govindas 0c16b98553 make sql auto sync if not main thread 2021-10-21 11:00:07 +03:00
Govindas 9e7c5fcbee Preparations for quicker queries (no 50ms delay) feature 2021-10-21 11:00:07 +03:00
Govindas 8b3d26cf78 Update README.md 2021-10-21 11:00:07 +03:00
Govindas 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
Govindas 05554a0043 1.2.1 2021-10-21 11:00:06 +03:00
Govindas f6a43f0b53 Make config more informative 2021-10-21 11:00:06 +03:00
Govindas 5803be55b7 Support missing config options 2021-10-21 11:00:06 +03:00
Govindas d57b921b4f Shade MariaDB driver & add configurable driver class to config 2021-10-21 11:00:06 +03:00
Govindas 4fd654c743 bump HikariCP version 2021-10-21 11:00:06 +03:00
Govindas 233d21a30b Fix memory leak introduced by last commit 2021-10-21 11:00:06 +03:00
Govindas 8e760c4d8b Merge branch 'master' of https://github.com/Govindass/skript-db 2021-10-21 11:00:06 +03:00
Govindas fe2231ae23 Update README.md 2021-10-21 11:00:06 +03:00
Govindas a132912f6b Switch from gradle to maven (preference) and optimize code 2021-10-21 11:00:05 +03:00
Govindas d4f9a394fd Add synchronous syntax to readme 2021-10-21 11:00:05 +03:00
Govindas a63a4c6d6f Fix local variables in sql queries within the same tick & first startup errors 2021-10-21 11:00:05 +03:00
Govindas 11063c166d Bump HikariCP 3.4.3 -> 3.4.5 2021-10-21 11:00:05 +03:00
Govindas b5f2d56263 Remove debug message 2021-10-21 11:00:05 +03:00
Govindas 8b5121d5fb Make thread pool configurable 2021-10-21 11:00:05 +03:00
Govindas b435696385 Add synchronous support on current thread 2021-10-21 11:00:05 +03:00
Govindas 4d795edf01 Merge FranKusmiruk's Pull Request 2021-10-21 11:00:05 +03:00
Govindas 2570408906 add gradle fatJar task 2021-10-21 11:00:05 +03:00
Govindas 8d94a85779 Update HikariCP, should fix a lot of issues 2021-10-21 11:00:05 +03:00
Govindas 6d0ec81e2e Remove unused class 2021-10-21 11:00:05 +03:00
Govindas c4379d45b7 add synchronous 2021-10-21 11:00:04 +03:00
Govindas 434ea07410 Delete EffSyncExecuteStatement.java 2021-10-21 11:00:04 +03:00
Govindas 8ca308802f Delete EffExecuteStatement.java 2021-10-21 11:00:04 +03:00
Govindas 2ce1dd924e Delete plugin.yml 2021-10-21 11:00:02 +03:00
Govindas 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
23 changed files with 821 additions and 788 deletions

View File

@ -1,13 +0,0 @@
# 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,10 +1,9 @@
## OSX
*.DS_Store
## Gradle
.gradle
build/
## IntelliJ
.idea/
*.iml
out/
target
compile/

View File

@ -3,19 +3,42 @@
> 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! 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
```
[the] data(base|[ ]source) [(of|at)] %string%
[the] data(base|[ ]source) [(of|at)] %string% [with [a] [max[imum]] [connection] life[ ]time of %timespan%] [[(using|with)] [a] driver %-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"
```
---
@ -29,9 +52,11 @@ 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
```
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
@ -55,6 +80,7 @@ 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%
@ -68,5 +94,23 @@ 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"
```

View File

@ -1,45 +0,0 @@
group 'com.btk5h.skript-db'
version '0.1.1'
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.1'
}
}
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://maven.njol.ch/repo/'
}
}
dependencies {
shadow 'org.spigotmc:spigot-api:1.11-R0.1-SNAPSHOT'
shadow 'ch.njol:skript:2.2-SNAPSHOT'
compile 'com.zaxxer:HikariCP:2.6.2'
}
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')
}

Binary file not shown.

View File

@ -1,6 +0,0 @@
#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-3.3-all.zip

172
gradlew vendored
View File

@ -1,172 +0,0 @@
#!/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
View File

@ -1,84 +0,0 @@
@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 Normal file
View File

@ -0,0 +1,115 @@
<?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>

View File

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

View File

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

View File

@ -25,9 +25,11 @@
package com.btk5h.skriptdb;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.IOException;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
import javax.sql.rowset.RowSetFactory;
@ -49,6 +51,7 @@ public final class SkriptDB extends JavaPlugin {
private static SkriptAddon addonInstance;
private static RowSetFactory rowSetFactory;
protected FileConfiguration config;
public SkriptDB() {
if (instance == null) {
@ -58,16 +61,55 @@ public final class SkriptDB extends JavaPlugin {
}
}
private void setupConfig() throws IOException {
//don't check if it exists, because mkdir already does that
File file = new File("plugins/skript-db/config.yml");
if (file.getParentFile().mkdirs()) {
BufferedWriter out = null;
try {
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("plugins/skript-db/config.yml", false), StandardCharsets.UTF_8));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
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("# 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");
} catch (IOException e) {
e.printStackTrace();
}
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void onEnable() {
try {
rowSetFactory = RowSetProvider.newFactory();
getAddonInstance().loadClasses("com.btk5h.skriptdb.skript");
} catch (SQLException e) {
throw new RuntimeException(e);
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,25 +1,17 @@
package com.btk5h.skriptdb;
import org.bukkit.event.Event;
import ch.njol.skript.Skript;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.VariableString;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Optional;
import java.util.Set;
import ch.njol.skript.Skript;
import ch.njol.skript.effects.Delay;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.Variable;
import ch.njol.skript.lang.VariableString;
public class SkriptUtil {
private static final Field STRING;
private static final Field SIMPLE;
private static final Field DELAYED;
private static final Field EXPR;
private static final Field VARIABLE_NAME;
static {
Field _FIELD = null;
@ -32,25 +24,6 @@ public class SkriptUtil {
}
STRING = _FIELD;
try {
_FIELD = VariableString.class.getDeclaredField("simple");
_FIELD.setAccessible(true);
} catch (NoSuchFieldException e) {
Skript.error("Skript's 'simple' field could not be resolved.");
e.printStackTrace();
}
SIMPLE = _FIELD;
try {
_FIELD = Delay.class.getDeclaredField("delayed");
_FIELD.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
Skript.warning("Skript's 'delayed' method could not be resolved. Some Skript warnings may " +
"not be available.");
}
DELAYED = _FIELD;
try {
Optional<Class<?>> expressionInfo = Arrays.stream(VariableString.class.getDeclaredClasses())
.filter(cls -> cls.getSimpleName().equals("ExpressionInfo"))
@ -67,33 +40,6 @@ public class SkriptUtil {
Skript.error("Skript's 'expr' field could not be resolved.");
}
EXPR = _FIELD;
try {
_FIELD = Variable.class.getDeclaredField("name");
_FIELD.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
Skript.error("Skript's 'variable name' method could not be resolved.");
}
VARIABLE_NAME = _FIELD;
}
@SuppressWarnings("unchecked")
public static void delay(Event e) {
if (DELAYED != null) {
try {
((Set<Event>) DELAYED.get(null)).add(e);
} catch (IllegalAccessException ignored) {
}
}
}
public static String getSimpleString(VariableString vs) {
try {
return (String) SIMPLE.get(vs);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public static Object[] getTemplateString(VariableString vs) {
@ -112,12 +58,4 @@ public class SkriptUtil {
}
}
public static VariableString getVariableName(Variable<?> var) {
try {
return (VariableString) VARIABLE_NAME.get(var);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,35 @@
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

@ -1,36 +1,31 @@
package com.btk5h.skriptdb.skript;
import ch.njol.skript.Skript;
import ch.njol.skript.effects.Delay;
import ch.njol.skript.lang.*;
import ch.njol.skript.variables.Variables;
import ch.njol.util.Kleenean;
import ch.njol.util.Pair;
import com.btk5h.skriptdb.SkriptDB;
import com.btk5h.skriptdb.SkriptUtil;
import com.zaxxer.hikari.HikariDataSource;
import org.bukkit.Bukkit;
import org.bukkit.event.Event;
import org.eclipse.jdt.annotation.Nullable;
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;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.sql.rowset.CachedRowSet;
import ch.njol.skript.Skript;
import ch.njol.skript.effects.Delay;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.SkriptParser;
import ch.njol.skript.lang.TriggerItem;
import ch.njol.skript.lang.Variable;
import ch.njol.skript.lang.VariableString;
import ch.njol.skript.variables.Variables;
import ch.njol.util.Kleenean;
/**
* Executes a statement on a database and optionally stores the result in a variable. Expressions
* embedded in the query will be escaped to avoid SQL injection.
@ -42,224 +37,314 @@ import ch.njol.util.Kleenean;
* variable in the form `{test::<column name>::<row number>}`
*
* @name Execute Statement
* @pattern execute %string% (in|on) %datasource% [and store [[the] (output|result)[s]] (to|in)
* @pattern [synchronously] execute %string% (in|on) %datasource% [and store [[the] (output|result)[s]] (to|in)
* [the] [var[iable]] %-objects%]
* @example execute "select * from table" in {sql} and store the result in {output::*}
* @example execute "select * from %{table variable}%" in {sql} and store the result in {output::*}
* @since 0.1.0
*/
public class EffExecuteStatement extends Delay {
static {
Skript.registerEffect(EffExecuteStatement.class,
"execute %string% (in|on) %datasource% " +
"[and store [[the] (output|result)[s]] (to|in) [the] [var[iable]] %-objects%]");
}
public class EffExecuteStatement extends Effect {
private static final ExecutorService threadPool = Executors.newFixedThreadPool(SkriptDB.getInstance().getConfig().getInt("thread-pool-size", 10));
static String lastError;
static String lastError;
private static final ExecutorService threadPool =
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
private Expression<String> query;
private Expression<HikariDataSource> dataSource;
private VariableString var;
private boolean isLocal;
private boolean isList;
@Override
protected void execute(Event e) {
CompletableFuture<String> sql =
CompletableFuture.supplyAsync(() -> executeStatement(e), threadPool);
sql.whenComplete((res, err) -> {
if (err != null) {
err.printStackTrace();
}
Bukkit.getScheduler().runTask(SkriptDB.getInstance(), () -> {
lastError = res;
if (getNext() != null) {
TriggerItem.walk(getNext(), e);
}
});
});
}
@Override
protected TriggerItem walk(Event e) {
debug(e, true);
SkriptUtil.delay(e);
execute(e);
return null;
}
private String executeStatement(Event e) {
HikariDataSource ds = dataSource.getSingle(e);
if (ds == null) {
return "Data source is not set";
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%]");
}
try (Connection conn = ds.getConnection();
PreparedStatement stmt = createStatement(e, conn)) {
private Expression<String> query;
private Expression<HikariDataSource> dataSource;
private VariableString var;
private boolean isLocal;
private boolean isList;
private boolean quickly;
private boolean isSync = false;
boolean hasResultSet = stmt.execute();
if (var != null) {
String baseVariable = var.toString(e)
.toLowerCase(Locale.ENGLISH);
if (isList) {
baseVariable = baseVariable.substring(0, baseVariable.length() - 1);
}
if (hasResultSet) {
CachedRowSet crs = SkriptDB.getRowSetFactory().createCachedRowSet();
crs.populate(stmt.getResultSet());
if (isList) {
populateVariable(e, crs, baseVariable);
} else {
crs.last();
setVariable(e, baseVariable, crs.getRow());
}
} else if (!isList) {
setVariable(e, baseVariable, stmt.getUpdateCount());
}
}
} catch (SQLException ex) {
return ex.getMessage();
}
return null;
}
private PreparedStatement createStatement(Event e, Connection conn) throws SQLException {
if (!(query instanceof VariableString)) {
return conn.prepareStatement(query.getSingle(e));
}
if (((VariableString) query).isSimple()) {
return conn.prepareStatement(SkriptUtil.getSimpleString(((VariableString) query)));
}
StringBuilder sb = new StringBuilder();
List<Object> parameters = new ArrayList<>();
Object[] objects = SkriptUtil.getTemplateString(((VariableString) query));
for (int i = 0; i < objects.length; i++) {
Object o = objects[i];
if (o instanceof String) {
sb.append(o);
} else {
Expression<?> expr = SkriptUtil.getExpressionFromInfo(o);
String before = getString(objects, i - 1);
String after = getString(objects, i + 1);
boolean standaloneString = false;
if (before != null && after != null) {
if (before.endsWith("'") && after.endsWith("'")) {
standaloneString = true;
}
}
Object expressionValue = expr.getSingle(e);
if (expr instanceof ExprUnsafe) {
sb.append(expressionValue);
if (standaloneString && expressionValue instanceof String) {
String rawExpression = ((ExprUnsafe) expr).getRawExpression();
Skript.warning(
String.format("Unsafe may have been used unnecessarily. Try replacing 'unsafe %1$s' with %1$s",
rawExpression));
}
private void continueScriptExecution(Event e, Object populatedVariables) {
lastError = null;
if (populatedVariables instanceof String) {
lastError = (String) populatedVariables;
} else {
parameters.add(expressionValue);
sb.append('?');
if (standaloneString) {
Skript.warning("Do not surround expressions with quotes!");
}
if (getNext() != null) {
((Map<String, Object>) populatedVariables).forEach((name, value) -> setVariable(e, name, value));
}
}
}
TriggerItem.walk(getNext(), e);
}
PreparedStatement stmt = conn.prepareStatement(sb.toString());
@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);
for (int i = 0; i < parameters.size(); i++) {
stmt.setObject(i + 1, parameters.get(i));
//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));
}
TriggerItem.walk(getNext(), e);
Variables.removeLocals(e);
}
}
return stmt;
}
private String getString(Object[] objects, int index) {
if (index < 0 || index >= objects.length) {
return null;
@Override
protected TriggerItem walk(Event e) {
debug(e, true);
if (!quickly || !isSync) {
Delay.addDelayedEvent(e);
}
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) {
return (String) object;
StringBuilder sb = new StringBuilder();
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) {
Variables.setVariable(name.toLowerCase(Locale.ENGLISH), obj, e, isLocal);
}
boolean hasResultSet = stmt.execute();
private void populateVariable(Event e, CachedRowSet crs, String baseVariable)
throws SQLException {
ResultSetMetaData meta = crs.getMetaData();
int columnCount = meta.getColumnCount();
if (baseVariable != null) {
if (isList) {
baseVariable = baseVariable.substring(0, baseVariable.length() - 1);
}
for (int i = 1; i <= columnCount; i++) {
String label = meta.getColumnLabel(i);
setVariable(e, baseVariable + label, label);
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;
}
int rowNumber = 1;
while (crs.next()) {
for (int i = 1; i <= columnCount; i++) {
setVariable(e, baseVariable + meta.getColumnLabel(i).toLowerCase(Locale.ENGLISH)
+ Variable.SEPARATOR + rowNumber, crs.getObject(i));
}
rowNumber++;
}
}
private PreparedStatement createStatement(Connection conn, Pair<String, List<Object>> query) throws SQLException {
PreparedStatement stmt = conn.prepareStatement(query.getFirst());
List<Object> parameters = query.getSecond();
@Override
public String toString(@Nullable Event e, boolean debug) {
return "execute " + query.toString(e, debug) + " in " + dataSource.toString(e, debug);
}
if (parameters != null) {
for (int i = 0; i < parameters.size(); i++) {
stmt.setObject(i + 1, parameters.get(i));
}
}
@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;
return stmt;
}
dataSource = (Expression<HikariDataSource>) exprs[1];
Expression<?> expr = exprs[2];
if (expr instanceof Variable) {
Variable<?> varExpr = (Variable<?>) expr;
var = SkriptUtil.getVariableName(varExpr);
isLocal = varExpr.isLocal();
isList = varExpr.isList();
} else if (expr != null) {
Skript.error(expr + " is not a variable");
return false;
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;
}
return true;
}
}
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;
}
}

View File

@ -0,0 +1,29 @@
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,13 +1,12 @@
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.
@ -18,33 +17,33 @@ import ch.njol.util.Kleenean;
* @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,12 +1,5 @@
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;
@ -14,84 +7,104 @@ 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%]"
* @pattern [the] data(base|[ ]source) [(of|at)] %string% [with [a] [max[imum]] [connection] life[ ]time of %timespan%] [[(using|with)] [a] driver %-string%]"
* @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> {
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%]");
}
private static final Map<String, HikariDataSource> connectionCache = new HashMap<>();
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;
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%]");
}
if (!jdbcUrl.startsWith("jdbc:")) {
jdbcUrl = "jdbc:" + jdbcUrl;
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 (connectionCache.containsKey(jdbcUrl)) {
return new HikariDataSource[]{connectionCache.get(jdbcUrl)};
@Override
public boolean isSingle() {
return true;
}
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl(jdbcUrl);
if (maxLifetime != null) {
Timespan l = maxLifetime.getSingle(e);
if (l != null) {
ds.setMaxLifetime(l.getMilliSeconds());
}
@Override
public Class<? extends HikariDataSource> getReturnType() {
return HikariDataSource.class;
}
connectionCache.put(jdbcUrl, ds);
@Override
public String toString(Event e, boolean debug) {
return "datasource " + url.toString(e, debug);
}
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;
}
@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;
}
}

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;
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.
@ -20,44 +19,44 @@ import ch.njol.util.Kleenean;
* @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,72 +1,64 @@
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();
}
@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;
}
})
.serializer(new Serializer<HikariDataSource>() {
@Override
public Fields serialize(HikariDataSource o) {
Fields fields = new Fields();
fields.putObject("jdbcurl", o.getJdbcUrl());
return fields;
}
@Override
public void deserialize(HikariDataSource o, Fields f) throws StreamCorruptedException,
NotSerializableException {
}
@Override
public void deserialize(HikariDataSource o, Fields f) {
}
@Override
protected HikariDataSource deserialize(Fields fields) throws StreamCorruptedException,
NotSerializableException {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl((String) fields.getObject("jdbcurl"));
return ds;
}
@Override
protected HikariDataSource deserialize(Fields fields) throws StreamCorruptedException {
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
public boolean canBeInstantiated(Class<? extends HikariDataSource> c) {
return false;
}
}));
}
@Override
protected boolean canBeInstantiated() {
return false;
}
}));
}
}

View File

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

Binary file not shown.