Compare commits

..

79 Commits

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

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-5.2.1-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

@ -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,9 +74,14 @@ 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 mysql 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 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() + 1) + "\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();
}
@ -91,19 +96,20 @@ 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

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

@ -14,6 +14,9 @@ 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;
@ -40,253 +43,308 @@ 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 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%]");
}
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(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);
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%]");
}
}
@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 (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 Expression<String> query;
private Expression<HikariDataSource> dataSource;
private VariableString var;
private boolean isLocal;
private boolean isList;
private boolean quickly;
private boolean isSync = false;
sql.whenComplete((res, err) -> {
if (err != null) {
err.printStackTrace();
private void continueScriptExecution(Event e, Object populatedVariables) {
lastError = null;
if (populatedVariables instanceof String) {
lastError = (String) populatedVariables;
} else {
if (getNext() != null) {
((Map<String, Object>) populatedVariables).forEach((name, value) -> setVariable(e, name, value));
}
}
TriggerItem.walk(getNext(), e);
}
Bukkit.getScheduler().runTask(SkriptDB.getInstance(), () -> {
lastError = res;
@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);
if (getNext() != null) {
if (locals != null)
Variables.setLocalVariables(e, locals);
doLater.forEach((name, value) -> setVariable(e, name, value));
doLater.clear();
//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);
}
});
});
}
}
@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);
}
}
StringBuilder sb = new StringBuilder();
List<Object> parameters = new ArrayList<>();
Object[] objects = SkriptUtil.getTemplateString(q);
@Override
protected TriggerItem walk(Event e) {
debug(e, true);
if (!quickly || !isSync) {
Delay.addDelayedEvent(e);
}
execute(e);
return 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;
}
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);
}
Object expressionValue = expr.getSingle(e);
StringBuilder sb = new StringBuilder();
List<Object> parameters = new ArrayList<>();
Object[] objects = SkriptUtil.getTemplateString(q);
if (expr instanceof ExprUnsafe) {
sb.append(expressionValue);
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 (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));
}
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;
} else {
parameters.add(expressionValue);
sb.append('?');
if (standaloneString) {
Skript.warning("Do not surround expressions with quotes!");
}
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 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);
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;
}
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 true;
}
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

@ -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
protected boolean canBeInstantiated() {
return false;
}
}));
}
@Override
protected boolean canBeInstantiated() {
return false;
}
}));
}
}

View File

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

Binary file not shown.