Compare commits

..

15 Commits

Author SHA1 Message Date
Govindass
b2a53078a6 add gradle fatJar task 2020-05-02 20:02:25 +03:00
Govindas
83f71e147b
Update HikariCP, should fix a lot of issues 2020-05-02 19:48:35 +03:00
Govindass
e610fc1357 Remove unused class 2020-05-02 19:30:48 +03:00
Govindass
aa1a1d14c7
add synchronous 2019-09-30 15:28:32 +03:00
Govindass
3a1ec76a0b
Delete EffSyncExecuteStatement.java 2019-09-30 15:27:26 +03:00
Govindass
2eb691cd69
Delete EffExecuteStatement.java 2019-09-30 15:27:16 +03:00
Govindass
6dbd2effb3
Delete plugin.yml 2019-09-30 15:26:48 +03:00
Govindass
88b76f1b5b
Add files via upload 2019-09-30 15:25:37 +03:00
Bryan Terce
b692047878
0.2.1 Hotfix 2019-06-26 01:56:29 -07:00
Bryan Terce
3e57cae866
Fix sync execute logic (fixes #16) 2019-06-26 01:55:08 -07:00
Bryan Terce
1e95b818eb
0.2.0 Update 2019-06-22 12:12:27 -07:00
Bryan Terce
688ea9d46b
Manually fix README 2019-06-22 12:11:29 -07:00
Bryan Terce
dd6d574479
Update README 2019-06-22 12:09:59 -07:00
Bryan Terce
1f6091eb95
Add synchronous execution flag 2019-06-22 12:09:17 -07:00
Bryan Terce
74d4918f44
Fix lifespan syntax 2019-06-22 12:05:22 -07:00
23 changed files with 833 additions and 801 deletions

13
.editorconfig Normal file
View File

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

7
.gitignore vendored
View File

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

View File

@ -3,51 +3,21 @@
> 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!)
- Thread-pool size is now automatically increasing on demand with use of CachedThreadPool, instead of a fixed hard-coded number
- 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
- SQL Driver is configurable
- 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.
#### Syntax
```
[the] data(base|[ ]source) [(of|at)] %string%
```
#### Examples
```
set {sql} to the database "mysql://localhost:3306/mydatabase?user=admin&password=12345&useSSL=false"
```
---
### Effect `Execute Statement`
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.
<p>
If a single variable, such as `{test}`, is passed, the variable will be set to the number of
affected rows.
<p>
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`
Specifying `synchronously` will make skript-db execute the query on the event thread, which is useful for async
events. Note that skript-db will ignore this flag if you attempt to run this on the main thread.
#### Syntax
```
[quickly] 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%]
```
#### Examples
@ -55,7 +25,7 @@ Executes a statement on a database and optionally stores the result in a variabl
execute "select * from table" in {sql} and store the result in {output::*}
```
```
execute "select * from %{table variable}%" in {sql} and store the result in {output::*}
execute "select * where player=%{player}%" in {sql} and store the result in {output::*}
```
---
@ -85,10 +55,21 @@ execute unsafe {fully dynamic query} in {sql}
```
---
### Configuration
plugins/skript-db/config.yml
### 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.
#### Syntax
```
# 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"
```
[the] data(base|[ ]source) [(of|at)] %string% [with [a] [max[imum]] [connection] life[ ]time of %timespan%]"
```
#### Examples
```
set {sql} to the database "mysql://localhost:3306/mydatabase?user=admin&password=12345&useSSL=false"
```
---

55
build.gradle Normal file
View File

@ -0,0 +1,55 @@
group 'com.btk5h.skript-db'
version '0.2.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:3.4.3'
}
task buildReadme(type: Javadoc) {
source = sourceSets.main.allJava
classpath = sourceSets.main.compileClasspath
destinationDir = projectDir
options.docletpath = [file('tools/skriptdoclet.jar')]
options.doclet = 'com.btk5h.skriptdoclet.SkriptDoclet'
options.addStringOption('file', 'README.md')
options.addStringOption('markdown', '-quiet')
}
task fatJar(type: Jar) {
manifest {
attributes 'Implementation-Title': 'Gradle Jar File Example',
'Implementation-Version': version,
'Main-Class': 'com.mkyong.DateUtils'
}
baseName = project.name + '-all'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}

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

Binary file not shown.

View File

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

172
gradlew vendored Executable file
View File

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

84
gradlew.bat vendored Normal file
View File

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

109
pom.xml
View File

@ -1,109 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.btk5h</groupId>
<artifactId>skript-db</artifactId>
<version>1.3.5</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>commons-pool2</id>
<url>https://mvnrepository.com/artifact/org.apache.commons/commons-pool2</url>
</repository>
<repository>
<id>PaperMC</id>
<url>https://repo.destroystokyo.com/repository/maven-public/</url>
</repository>
<repository>
<id>sk89q</id>
<url>http://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>
<minimizeJar>true</minimizeJar>
</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-beta3</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.0.3</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

2
settings.gradle Normal file
View File

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

View File

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

View File

@ -25,11 +25,9 @@
package com.btk5h.skriptdb;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.io.IOException;
import java.sql.SQLException;
import javax.sql.rowset.RowSetFactory;
@ -51,7 +49,6 @@ public final class SkriptDB extends JavaPlugin {
private static SkriptAddon addonInstance;
private static RowSetFactory rowSetFactory;
protected FileConfiguration config;
public SkriptDB() {
if (instance == null) {
@ -61,49 +58,16 @@ 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("# Only change this if you wish to use a different driver than Java's default, like MariaDB driver.\n");
out.write("# If you use MariaDB, its driver is shaded together with skript-db, so you can just specify:" + "\"org.mariadb.jdbc.Driver\"" + ".\n");
out.write("sql-driver-class-name: " + "\"default\"" + "\n");
} catch (IOException e) {
e.printStackTrace();
}
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void onEnable() {
try {
setupConfig();
rowSetFactory = RowSetProvider.newFactory();
getAddonInstance().loadClasses("com.btk5h.skriptdb.skript");
} catch (SQLException e) {
throw new RuntimeException(e);
} 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,17 +1,25 @@
package com.btk5h.skriptdb;
import ch.njol.skript.Skript;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.VariableString;
import org.bukkit.event.Event;
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;
@ -24,6 +32,25 @@ 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"))
@ -40,6 +67,33 @@ 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) {
@ -58,4 +112,12 @@ 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

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

View File

@ -1,31 +1,36 @@
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.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
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.
@ -36,320 +41,240 @@ import java.util.concurrent.Executors;
* 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>}`
*
* Specifying `synchronously` will make skript-db execute the query on the event thread, which is useful for async
* events. Note that skript-db will ignore this flag if you attempt to run this on the main thread.
*
* @name Execute Statement
* @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::*}
* @example execute "select * where player=%{player}%" in {sql} and store the result in {output::*}
* @since 0.1.0
*/
public class EffExecuteStatement extends Effect {
private static final ExecutorService threadPool =
Executors.newCachedThreadPool();
static String lastError;
public class EffExecuteStatement extends Delay {
static {
Skript.registerEffect(EffExecuteStatement.class, "[(1¦synchronously)] execute %string% (in|on) %datasource% [and store [[the] (output|result)[s]] (to|in) [the] [var[iable]] %-objects%]");
}
static {
Skript.registerEffect(EffExecuteStatement.class,
"execute %string% (in|on) %datasource% " +
"[and store [[the] (output|result)[s]] (to|in) [the] [var[iable]] %-objects%]", "quickly execute %string% (in|on) %datasource% " +
"[and store [[the] (output|result)[s]] (to|in) [the] [var[iable]] %-objects%]");
static String lastError;
private static final ExecutorService threadPool =
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
private Expression<String> query;
private Expression<HikariDataSource> dataSource;
private VariableString var;
private boolean isLocal;
private boolean isList;
private boolean isSync;
private void continueScriptExecution(Event e, String res) {
lastError = res;
if (getNext() != null) {
TriggerItem.walk(getNext(), e);
}
}
@Override
protected void execute(Event e) {
boolean isMainThread = Bukkit.isPrimaryThread();
if (isSync) {
String result = executeStatement(e);
continueScriptExecution(e, result);
} else {
CompletableFuture<String> sql =
CompletableFuture.supplyAsync(() -> executeStatement(e), threadPool);
sql.whenComplete((res, err) -> {
if (err != null) {
err.printStackTrace();
}
Bukkit.getScheduler().runTask(SkriptDB.getInstance(), () -> continueScriptExecution(e, res));
});
}
}
@Override
protected TriggerItem walk(Event e) {
debug(e, true);
if (!isSync) {
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";
}
private Expression<String> query;
private Expression<HikariDataSource> dataSource;
private VariableString var;
private boolean isLocal;
private boolean isList;
private boolean isSync;
try (Connection conn = ds.getConnection();
PreparedStatement stmt = createStatement(e, conn)) {
private void continueScriptExecution(Event e, Object populatedVariables) {
lastError = null;
if (populatedVariables instanceof String) {
lastError = (String) populatedVariables;
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));
}
} else {
parameters.add(expressionValue);
sb.append('?');
if (getNext() != null) {
((Map<String, Object>) populatedVariables).forEach((name, value) -> setVariable(e, name, value));
}
if (standaloneString) {
Skript.warning("Do not surround expressions with quotes!");
}
}
TriggerItem.walk(getNext(), e);
}
}
@Override
protected void execute(Event e) {
DataSource ds = dataSource.getSingle(e);
Pair<String, List<Object>> query = parseQuery(e);
String baseVariable = var != null ? var.toString(e).toLowerCase(Locale.ENGLISH) : null;
//if data source isn't set
if (ds == null) return;
PreparedStatement stmt = conn.prepareStatement(sb.toString());
boolean sync = !Bukkit.isPrimaryThread();
//if current thread is not main thread, then make this query to not have delays
Object locals = Variables.removeLocals(e);
//execute SQL statement
CompletableFuture<Object> sql =
CompletableFuture.supplyAsync(() -> executeStatement(ds, baseVariable, query), threadPool);
//when SQL statement is completed
boolean finalSync = sync;
sql.whenComplete((res, err) -> {
if (err != null) {
err.printStackTrace();
}
//handle last error syntax data
lastError = null;
if (res instanceof String) {
lastError = (String) res;
}
if (getNext() != null) {
//if local variables are present
//bring back local variables
//populate SQL data into variables
if (!(res instanceof String)) {
//also set variables in the sql query complete event
//TEMPORARILY DISABLED, AS THIS WOULD WORSEN PERFORMANCE OF THE QUERIES AND NOT BE USED BY MOST PEOPLE.
//I may add config option to enable this later?
//SQLQueryCompleteEvent event = new SQLQueryCompleteEvent(this.query.getSingle(e));
//((Map<String, Object>) res).forEach((name, value) -> setVariable(event, name, value));
//SkriptDB.getPlugin(SkriptDB.class).getServer().getPluginManager().callEvent(event);
}
if (isSync || finalSync) {
if (locals != null) {
Variables.setLocalVariables(e, locals);
}
if (!(res instanceof String)) {
((Map<String, Object>) res).forEach((name, value) -> setVariable(e, name, value));
}
TriggerItem.walk(getNext(), e);
Variables.removeLocals(e);
} else {
Bukkit.getScheduler().runTask(SkriptDB.getInstance(), () -> {
if (locals != 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
//no functionality difference notice with it being removed from my test, but the memory gets filled with leaks
//so it must be kept
Variables.removeLocals(e);
});
}
}
});
for (int i = 0; i < parameters.size(); i++) {
stmt.setObject(i + 1, parameters.get(i));
}
@Override
protected TriggerItem walk(Event e) {
debug(e, true);
//I think no longer needed as of 1.3.0, uncomment if something breaks
if (!isSync) {
Delay.addDelayedEvent(e);
}
execute(e);
return null;
return stmt;
}
private String getString(Object[] objects, int index) {
if (index < 0 || index >= objects.length) {
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);
}
Object object = objects[index];
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);
if (object instanceof String) {
return (String) object;
}
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)) {
return null;
}
boolean hasResultSet = stmt.execute();
private void setVariable(Event e, String name, Object obj) {
Variables.setVariable(name.toLowerCase(Locale.ENGLISH), obj, e, isLocal);
}
if (baseVariable != null) {
if (isList) {
baseVariable = baseVariable.substring(0, baseVariable.length() - 1);
}
private void populateVariable(Event e, CachedRowSet crs, String baseVariable)
throws SQLException {
ResultSetMetaData meta = crs.getMetaData();
int columnCount = meta.getColumnCount();
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;
for (int i = 1; i <= columnCount; i++) {
String label = meta.getColumnLabel(i);
setVariable(e, baseVariable + label, label);
}
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;
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 String getString(Object[] objects, int index) {
if (index < 0 || index >= objects.length) {
return null;
}
@Override
public String toString(@Nullable Event e, boolean debug) {
return "execute " + query.toString(e, debug) + " in " + dataSource.toString(e, debug);
}
Object object = objects[index];
if (object instanceof String) {
return (String) object;
}
return null;
@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;
}
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);
dataSource = (Expression<HikariDataSource>) exprs[1];
Expression<?> expr = exprs[2];
isSync = parseResult.mark == 1;
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;
}
@Override
public String toString(Event e, boolean debug) {
return "execute " + query.toString(e, debug) + " in " + dataSource.toString(e, debug);
}
@SuppressWarnings("unchecked")
@Override
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed,
SkriptParser.ParseResult parseResult) {
Expression<String> statementExpr = (Expression<String>) exprs[0];
if (statementExpr instanceof VariableString || statementExpr instanceof ExprUnsafe) {
query = statementExpr;
} else {
Skript.error("Database statements must be string literals. If you must use an expression, " +
"you may use \"%unsafe (your expression)%\", but keep in mind, you may be vulnerable " +
"to SQL injection attacks!");
return false;
}
dataSource = (Expression<HikariDataSource>) exprs[1];
Expression<?> expr = exprs[2];
isSync = matchedPattern == 1;
if (expr instanceof Variable) {
Variable<?> varExpr = (Variable<?>) expr;
var = varExpr.getName();
isLocal = varExpr.isLocal();
isList = varExpr.isList();
} else if (expr != null) {
Skript.error(expr + " is not a variable");
return false;
}
return true;
}
}
return true;
}
}

View File

@ -1,29 +0,0 @@
package com.btk5h.skriptdb.skript;
import ch.njol.skript.Skript;
import ch.njol.skript.lang.Literal;
import ch.njol.skript.lang.SkriptEvent;
import ch.njol.skript.lang.SkriptParser;
import com.btk5h.skriptdb.events.SQLQueryCompleteEvent;
import org.bukkit.event.Event;
public class EvtSQLQueryComplete extends SkriptEvent {
static {
Skript.registerEvent("complete of sql query", EvtSQLQueryComplete.class, SQLQueryCompleteEvent.class, "complete of [(sql|database)] query");
}
@Override
public boolean init(final Literal<?>[] literals, final int i, final SkriptParser.ParseResult parseResult) {
return true;
}
@Override
public boolean check(Event event) {
return (event instanceof SQLQueryCompleteEvent);
}
@Override
public String toString(Event event, boolean debug) {
return "complete of sql query";
}
}

View File

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

View File

@ -1,5 +1,12 @@
package com.btk5h.skriptdb.skript;
import com.zaxxer.hikari.HikariDataSource;
import org.bukkit.event.Event;
import java.util.HashMap;
import java.util.Map;
import ch.njol.skript.Skript;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.ExpressionType;
@ -7,17 +14,11 @@ 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
@ -28,74 +29,69 @@ import java.util.Map;
* @since 0.1.0
*/
public class ExprDataSource extends SimpleExpression<HikariDataSource> {
private static final Map<String, HikariDataSource> connectionCache = new HashMap<>();
static {
Skript.registerExpression(ExprDataSource.class, HikariDataSource.class,
ExpressionType.COMBINED, "[the] data(base|[ ]source) [(of|at)] %string% " +
"[with [a] [max[imum]] [connection] life[ ]time of %-timespan%]");
}
static {
Skript.registerExpression(ExprDataSource.class, HikariDataSource.class,
ExpressionType.COMBINED, "[the] data(base|[ ]source) [(of|at)] %string% " +
"[with [a] [max[imum]] [connection] life[ ]time of %-timespan%]");
private static Map<String, HikariDataSource> connectionCache = new HashMap<>();
private Expression<String> url;
private Expression<Timespan> maxLifetime;
@Override
protected HikariDataSource[] get(Event e) {
String jdbcUrl = url.getSingle(e);
if (jdbcUrl == null) {
return null;
}
private Expression<String> url;
private Expression<Timespan> maxLifetime;
@Override
protected HikariDataSource[] get(Event e) {
String jdbcUrl = url.getSingle(e);
if (jdbcUrl == null) {
return null;
}
if (!jdbcUrl.startsWith("jdbc:")) {
jdbcUrl = "jdbc:" + jdbcUrl;
}
if (connectionCache.containsKey(jdbcUrl)) {
return new HikariDataSource[]{connectionCache.get(jdbcUrl)};
}
HikariDataSource ds = new HikariDataSource();
//allow specifying of own sql driver class name
if (!SkriptDB.getInstance().getConfig().getString("sql-driver-class-name", "default").equals("default")) {
ds.setDriverClassName(SkriptDB.getInstance().getConfig().getString("sql-driver-class-name"));
}
ds.setJdbcUrl(jdbcUrl);
if (maxLifetime != null) {
Timespan l = maxLifetime.getSingle(e);
if (l != null) {
ds.setMaxLifetime(l.getMilliSeconds());
}
}
connectionCache.put(jdbcUrl, ds);
return new HikariDataSource[]{ds};
if (!jdbcUrl.startsWith("jdbc:")) {
jdbcUrl = "jdbc:" + jdbcUrl;
}
@Override
public boolean isSingle() {
return true;
if (connectionCache.containsKey(jdbcUrl)) {
return new HikariDataSource[]{connectionCache.get(jdbcUrl)};
}
@Override
public Class<? extends HikariDataSource> getReturnType() {
return HikariDataSource.class;
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl(jdbcUrl);
if (maxLifetime != null) {
Timespan l = maxLifetime.getSingle(e);
if (l != null) {
ds.setMaxLifetime(l.getMilliSeconds());
}
}
@Override
public String toString(Event e, boolean debug) {
return "datasource " + url.toString(e, debug);
}
connectionCache.put(jdbcUrl, ds);
@SuppressWarnings("unchecked")
@Override
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed,
SkriptParser.ParseResult parseResult) {
url = (Expression<String>) exprs[0];
maxLifetime = (Expression<Timespan>) exprs[1];
return true;
}
return new HikariDataSource[]{ds};
}
@Override
public boolean isSingle() {
return true;
}
@Override
public Class<? extends HikariDataSource> getReturnType() {
return HikariDataSource.class;
}
@Override
public String toString(Event e, boolean debug) {
return "datasource " + url.toString(e, debug);
}
@SuppressWarnings("unchecked")
@Override
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed,
SkriptParser.ParseResult parseResult) {
url = (Expression<String>) exprs[0];
maxLifetime = (Expression<Timespan>) exprs[1];
return true;
}
}

View File

@ -1,60 +0,0 @@
package com.btk5h.skriptdb.skript;
import ch.njol.skript.ScriptLoader;
import ch.njol.skript.Skript;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.ExpressionType;
import ch.njol.skript.lang.SkriptParser;
import ch.njol.skript.lang.util.SimpleExpression;
import ch.njol.skript.log.ErrorQuality;
import ch.njol.util.Kleenean;
import com.btk5h.skriptdb.events.SQLQueryCompleteEvent;
import org.bukkit.event.Event;
/**
* Stores the error from the last executed statement, if there was one.
*
* @name Last Data Source Error
* @pattern [the] [last] (sql|db|data(base|[ ]source)) error
* @return text
* @since 0.1.0
*/
public class ExprSQLQuery extends SimpleExpression<String> {
static {
Skript.registerExpression(ExprSQLQuery.class, String.class,
ExpressionType.SIMPLE, "sql query");
}
@Override
protected String[] get(Event e) {
if (e instanceof SQLQueryCompleteEvent) {
return new String[]{((SQLQueryCompleteEvent) e).getQuery()};
}
return null;
}
@Override
public boolean isSingle() {
return true;
}
@Override
public Class<? extends String> getReturnType() {
return String.class;
}
@Override
public String toString(Event e, boolean debug) {
return "sql query";
}
@Override
public boolean init(final Expression<?>[] expressions, final int matchedPattern, final Kleenean isDelayed, final SkriptParser.ParseResult parseResult) {
if (!ScriptLoader.isCurrentEvent(SQLQueryCompleteEvent.class)) {
Skript.error("Cannot use 'sql query' outside of a complete of sql query event", ErrorQuality.SEMANTIC_ERROR);
return false;
}
return true;
}
}

View File

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

View File

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

View File

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

BIN
tools/skriptdoclet.jar Normal file

Binary file not shown.