diff --git a/driver/datacap-driver-redis/pom.xml b/driver/datacap-driver-redis/pom.xml
new file mode 100644
index 00000000..d35cae51
--- /dev/null
+++ b/driver/datacap-driver-redis/pom.xml
@@ -0,0 +1,45 @@
+
+
+
+ 4.0.0
+
+ datacap
+ io.edurt.datacap
+ 1.6.0
+ ../../pom.xml
+
+
+ io.edurt.datacap
+ datacap-driver-redis
+ DataCap - Driver
+
+
+ UTF-8
+ 1.8
+ 1.8
+
+
+
+
+
+ redis.clients
+ jedis
+ ${redis.version}
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+ ${plugin.maven.checkstyle.version}
+
+ true
+
+
+
+
+
diff --git a/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/EmptyResultSet.java b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/EmptyResultSet.java
new file mode 100644
index 00000000..e4285777
--- /dev/null
+++ b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/EmptyResultSet.java
@@ -0,0 +1,974 @@
+package io.edurt.datacap.core;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.sql.*;
+import java.util.Calendar;
+import java.util.Map;
+
+public class EmptyResultSet implements ResultSet {
+ private final static Logger LOGGER = new Logger(EmptyResultSet.class);
+
+ @Override
+ public boolean next() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public void close() throws SQLException {
+ LOGGER.log("close()");
+ }
+
+ @Override
+ public boolean wasNull() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public String getString(int columnIndex) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public boolean getBoolean(int columnIndex) throws SQLException {
+ return false;
+ }
+
+ @Override
+ public byte getByte(int columnIndex) throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public short getShort(int columnIndex) throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public int getInt(int columnIndex) throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public long getLong(int columnIndex) throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public float getFloat(int columnIndex) throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public double getDouble(int columnIndex) throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public byte[] getBytes(int columnIndex) throws SQLException {
+ return new byte[0];
+ }
+
+ @Override
+ public Date getDate(int columnIndex) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Time getTime(int columnIndex) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Timestamp getTimestamp(int columnIndex) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public InputStream getAsciiStream(int columnIndex) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public InputStream getUnicodeStream(int columnIndex) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public InputStream getBinaryStream(int columnIndex) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public String getString(String columnLabel) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public boolean getBoolean(String columnLabel) throws SQLException {
+ return false;
+ }
+
+ @Override
+ public byte getByte(String columnLabel) throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public short getShort(String columnLabel) throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public int getInt(String columnLabel) throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public long getLong(String columnLabel) throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public float getFloat(String columnLabel) throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public double getDouble(String columnLabel) throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public byte[] getBytes(String columnLabel) throws SQLException {
+ return new byte[0];
+ }
+
+ @Override
+ public Date getDate(String columnLabel) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Time getTime(String columnLabel) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Timestamp getTimestamp(String columnLabel) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public InputStream getAsciiStream(String columnLabel) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public InputStream getUnicodeStream(String columnLabel) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public InputStream getBinaryStream(String columnLabel) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public SQLWarning getWarnings() throws SQLException {
+ return null;
+ }
+
+ @Override
+ public void clearWarnings() throws SQLException {
+
+ }
+
+ @Override
+ public String getCursorName() throws SQLException {
+ return null;
+ }
+
+ @Override
+ public ResultSetMetaData getMetaData() throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Object getObject(int columnIndex) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Object getObject(String columnLabel) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public int findColumn(String columnLabel) throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public Reader getCharacterStream(int columnIndex) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Reader getCharacterStream(String columnLabel) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public BigDecimal getBigDecimal(int columnIndex) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public BigDecimal getBigDecimal(String columnLabel) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public boolean isBeforeFirst() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean isAfterLast() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean isFirst() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean isLast() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public void beforeFirst() throws SQLException {
+
+ }
+
+ @Override
+ public void afterLast() throws SQLException {
+
+ }
+
+ @Override
+ public boolean first() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean last() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public int getRow() throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public boolean absolute(int row) throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean relative(int rows) throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean previous() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public void setFetchDirection(int direction) throws SQLException {
+
+ }
+
+ @Override
+ public int getFetchDirection() throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public void setFetchSize(int rows) throws SQLException {
+
+ }
+
+ @Override
+ public int getFetchSize() throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public int getType() throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public int getConcurrency() throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public boolean rowUpdated() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean rowInserted() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean rowDeleted() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public void updateNull(int columnIndex) throws SQLException {
+
+ }
+
+ @Override
+ public void updateBoolean(int columnIndex, boolean x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateByte(int columnIndex, byte x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateShort(int columnIndex, short x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateInt(int columnIndex, int x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateLong(int columnIndex, long x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateFloat(int columnIndex, float x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateDouble(int columnIndex, double x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateString(int columnIndex, String x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateBytes(int columnIndex, byte[] x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateDate(int columnIndex, Date x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateTime(int columnIndex, Time x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateAsciiStream(int columnIndex, InputStream x, int length) throws SQLException {
+
+ }
+
+ @Override
+ public void updateBinaryStream(int columnIndex, InputStream x, int length) throws SQLException {
+
+ }
+
+ @Override
+ public void updateCharacterStream(int columnIndex, Reader x, int length) throws SQLException {
+
+ }
+
+ @Override
+ public void updateObject(int columnIndex, Object x, int scaleOrLength) throws SQLException {
+
+ }
+
+ @Override
+ public void updateObject(int columnIndex, Object x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateNull(String columnLabel) throws SQLException {
+
+ }
+
+ @Override
+ public void updateBoolean(String columnLabel, boolean x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateByte(String columnLabel, byte x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateShort(String columnLabel, short x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateInt(String columnLabel, int x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateLong(String columnLabel, long x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateFloat(String columnLabel, float x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateDouble(String columnLabel, double x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateBigDecimal(String columnLabel, BigDecimal x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateString(String columnLabel, String x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateBytes(String columnLabel, byte[] x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateDate(String columnLabel, Date x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateTime(String columnLabel, Time x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateTimestamp(String columnLabel, Timestamp x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateAsciiStream(String columnLabel, InputStream x, int length) throws SQLException {
+
+ }
+
+ @Override
+ public void updateBinaryStream(String columnLabel, InputStream x, int length) throws SQLException {
+
+ }
+
+ @Override
+ public void updateCharacterStream(String columnLabel, Reader reader, int length) throws SQLException {
+
+ }
+
+ @Override
+ public void updateObject(String columnLabel, Object x, int scaleOrLength) throws SQLException {
+
+ }
+
+ @Override
+ public void updateObject(String columnLabel, Object x) throws SQLException {
+
+ }
+
+ @Override
+ public void insertRow() throws SQLException {
+
+ }
+
+ @Override
+ public void updateRow() throws SQLException {
+
+ }
+
+ @Override
+ public void deleteRow() throws SQLException {
+
+ }
+
+ @Override
+ public void refreshRow() throws SQLException {
+
+ }
+
+ @Override
+ public void cancelRowUpdates() throws SQLException {
+
+ }
+
+ @Override
+ public void moveToInsertRow() throws SQLException {
+
+ }
+
+ @Override
+ public void moveToCurrentRow() throws SQLException {
+
+ }
+
+ @Override
+ public Statement getStatement() throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Object getObject(int columnIndex, Map> map) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Ref getRef(int columnIndex) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Blob getBlob(int columnIndex) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Clob getClob(int columnIndex) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Array getArray(int columnIndex) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Object getObject(String columnLabel, Map> map) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Ref getRef(String columnLabel) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Blob getBlob(String columnLabel) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Clob getClob(String columnLabel) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Array getArray(String columnLabel) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Date getDate(int columnIndex, Calendar cal) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Date getDate(String columnLabel, Calendar cal) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Time getTime(int columnIndex, Calendar cal) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Time getTime(String columnLabel, Calendar cal) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public URL getURL(int columnIndex) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public URL getURL(String columnLabel) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public void updateRef(int columnIndex, Ref x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateRef(String columnLabel, Ref x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateBlob(int columnIndex, Blob x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateBlob(String columnLabel, Blob x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateClob(int columnIndex, Clob x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateClob(String columnLabel, Clob x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateArray(int columnIndex, Array x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateArray(String columnLabel, Array x) throws SQLException {
+
+ }
+
+ @Override
+ public RowId getRowId(int columnIndex) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public RowId getRowId(String columnLabel) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public void updateRowId(int columnIndex, RowId x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateRowId(String columnLabel, RowId x) throws SQLException {
+
+ }
+
+ @Override
+ public int getHoldability() throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public boolean isClosed() throws SQLException {
+ LOGGER.log("isClosed = %s", false);
+ return false;
+ }
+
+ @Override
+ public void updateNString(int columnIndex, String nString) throws SQLException {
+
+ }
+
+ @Override
+ public void updateNString(String columnLabel, String nString) throws SQLException {
+
+ }
+
+ @Override
+ public void updateNClob(int columnIndex, NClob nClob) throws SQLException {
+
+ }
+
+ @Override
+ public void updateNClob(String columnLabel, NClob nClob) throws SQLException {
+
+ }
+
+ @Override
+ public NClob getNClob(int columnIndex) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public NClob getNClob(String columnLabel) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public SQLXML getSQLXML(int columnIndex) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public SQLXML getSQLXML(String columnLabel) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException {
+
+ }
+
+ @Override
+ public void updateSQLXML(String columnLabel, SQLXML xmlObject) throws SQLException {
+
+ }
+
+ @Override
+ public String getNString(int columnIndex) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public String getNString(String columnLabel) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Reader getNCharacterStream(int columnIndex) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public Reader getNCharacterStream(String columnLabel) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException {
+
+ }
+
+ @Override
+ public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException {
+
+ }
+
+ @Override
+ public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException {
+
+ }
+
+ @Override
+ public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException {
+
+ }
+
+ @Override
+ public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException {
+
+ }
+
+ @Override
+ public void updateAsciiStream(String columnLabel, InputStream x, long length) throws SQLException {
+
+ }
+
+ @Override
+ public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException {
+
+ }
+
+ @Override
+ public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException {
+
+ }
+
+ @Override
+ public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException {
+
+ }
+
+ @Override
+ public void updateBlob(String columnLabel, InputStream inputStream, long length) throws SQLException {
+
+ }
+
+ @Override
+ public void updateClob(int columnIndex, Reader reader, long length) throws SQLException {
+
+ }
+
+ @Override
+ public void updateClob(String columnLabel, Reader reader, long length) throws SQLException {
+
+ }
+
+ @Override
+ public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException {
+
+ }
+
+ @Override
+ public void updateNClob(String columnLabel, Reader reader, long length) throws SQLException {
+
+ }
+
+ @Override
+ public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException {
+
+ }
+
+ @Override
+ public void updateAsciiStream(int columnIndex, InputStream x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateCharacterStream(int columnIndex, Reader x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException {
+
+ }
+
+ @Override
+ public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException {
+
+ }
+
+ @Override
+ public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException {
+
+ }
+
+ @Override
+ public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException {
+
+ }
+
+ @Override
+ public void updateClob(int columnIndex, Reader reader) throws SQLException {
+
+ }
+
+ @Override
+ public void updateClob(String columnLabel, Reader reader) throws SQLException {
+
+ }
+
+ @Override
+ public void updateNClob(int columnIndex, Reader reader) throws SQLException {
+
+ }
+
+ @Override
+ public void updateNClob(String columnLabel, Reader reader) throws SQLException {
+
+ }
+
+ @Override
+ public T getObject(int columnIndex, Class type) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public T getObject(String columnLabel, Class type) throws SQLException {
+ return null;
+ }
+
+ @Override
+ public T unwrap(Class iface) throws SQLException {
+ try {
+ return iface.cast(this);
+ } catch (ClassCastException cce) {
+ LOGGER.log("Unable to unwrap to %s", iface);
+ throw new SQLException("Unable to unwrap to " + iface);
+ }
+ }
+
+ @Override
+ public boolean isWrapperFor(Class> iface) throws SQLException {
+ return iface.isInstance(this);
+ }
+}
diff --git a/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/Hint.java b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/Hint.java
new file mode 100644
index 00000000..5eef80e9
--- /dev/null
+++ b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/Hint.java
@@ -0,0 +1,21 @@
+package io.edurt.datacap.core;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Hint {
+ /**
+ * hint key
+ */
+ private HintKey key;
+ /**
+ * hint value
+ */
+ private String value;
+}
diff --git a/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/HintKey.java b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/HintKey.java
new file mode 100644
index 00000000..4cac6a45
--- /dev/null
+++ b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/HintKey.java
@@ -0,0 +1,17 @@
+package io.edurt.datacap.core;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+public enum HintKey {
+ decoder,
+ sample_key,
+ noop;
+
+ public static HintKey fromString(String string) {
+ return Arrays.stream(values())
+ .filter(t -> Objects.equals(t.toString(), string))
+ .findFirst()
+ .orElse(noop);
+ }
+}
diff --git a/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/Logger.java b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/Logger.java
new file mode 100644
index 00000000..902e554e
--- /dev/null
+++ b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/Logger.java
@@ -0,0 +1,23 @@
+package io.edurt.datacap.core;
+
+import java.util.Arrays;
+
+public class Logger {
+ private final Class> mark;
+
+ public Logger(Class> mark) {
+ this.mark = mark;
+ }
+
+ public synchronized void log(String format, Object... arguments) {
+ Object[] objs = Arrays.stream(arguments)
+ .map(t -> {
+ if (t instanceof Throwable) {
+ Throwable th = (Throwable) t;
+ return th.getMessage();
+ }
+ return t;
+ }).toArray(Object[]::new);
+ System.out.printf(mark + ":::" + format + "\n", objs);
+ }
+}
diff --git a/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/Op.java b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/Op.java
new file mode 100644
index 00000000..c038a2f0
--- /dev/null
+++ b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/Op.java
@@ -0,0 +1,32 @@
+package io.edurt.datacap.core;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class Op {
+ /**
+ * 执行的原始SQL
+ */
+ private String originSql;
+
+ /**
+ * SQL中解析出来的hint
+ */
+ private List hints;
+
+ /**
+ * SQL中解析出来的command
+ */
+ private String command;
+
+ /**
+ * SQL解析出来的参数
+ */
+ private String[] params;
+}
diff --git a/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/RedisClient.java b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/RedisClient.java
new file mode 100644
index 00000000..7398ca0b
--- /dev/null
+++ b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/RedisClient.java
@@ -0,0 +1,11 @@
+package io.edurt.datacap.core;
+
+import java.sql.SQLException;
+
+public interface RedisClient {
+ String[] sendCommand(String sql) throws SQLException;
+
+ void select(int dbIndex) throws SQLException;
+
+ void close();
+}
diff --git a/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/RedisConnection.java b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/RedisConnection.java
new file mode 100644
index 00000000..6192f676
--- /dev/null
+++ b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/RedisConnection.java
@@ -0,0 +1,357 @@
+package io.edurt.datacap.core;
+
+import java.sql.*;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.Executor;
+
+public class RedisConnection implements Connection {
+ private final static Logger LOGGER = new Logger(RedisConnection.class);
+
+ private final RedisClient redisClient;
+ private final Properties properties;
+
+ private String dbIndex;
+
+ public RedisConnection(RedisClient redisClient, String dbIndex, Properties properties) {
+ this.redisClient = redisClient;
+ this.dbIndex = dbIndex;
+ this.properties = properties;
+ }
+
+ private boolean isClosed = false;
+
+
+ @Override
+ public Statement createStatement() throws SQLException {
+ this.checkClosed();
+ return new RedisStatement(this, this.redisClient);
+ }
+
+ @Override
+ public PreparedStatement prepareStatement(String sql) throws SQLException {
+ // TODO 暂不实现,感觉意义不大,未来看是否需要实现
+ LOGGER.log("prepareStatement not implemented");
+ throw new SQLFeatureNotSupportedException("prepareStatement not implemented");
+ }
+
+ @Override
+ public CallableStatement prepareCall(String sql) throws SQLException {
+ // TODO 暂不实现,感觉无意义,因为主要是执行存储过程的,redis没这玩意
+ LOGGER.log("prepareCall not implemented");
+ throw new SQLFeatureNotSupportedException("prepareCall not implemented");
+ }
+
+ @Override
+ public String nativeSQL(String sql) throws SQLException {
+ LOGGER.log("nativeSQL not implemented");
+ throw new SQLFeatureNotSupportedException("nativeSQL not implemented");
+ }
+
+ @Override
+ public void setAutoCommit(boolean autoCommit) throws SQLException {
+ }
+
+ @Override
+ public boolean getAutoCommit() throws SQLException {
+ return true;
+ }
+
+ @Override
+ public void commit() throws SQLException {
+ // TODO 待支持事务
+ }
+
+ @Override
+ public void rollback() throws SQLException {
+ // TODO
+ }
+
+ @Override
+ public void close() throws SQLException {
+ this.redisClient.close();
+ LOGGER.log("Connection close");
+ this.isClosed = true;
+ }
+
+ @Override
+ public boolean isClosed() throws SQLException {
+ LOGGER.log("Connection isClosed = %s", isClosed);
+ return this.isClosed;
+ }
+
+ @Override
+ public DatabaseMetaData getMetaData() throws SQLException {
+ return new RedisDatabaseMetadata(this, this.dbIndex);
+ }
+
+ @Override
+ public void setReadOnly(boolean readOnly) throws SQLException {
+ // do nothing
+ }
+
+ @Override
+ public boolean isReadOnly() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public void setCatalog(String catalog) throws SQLException {
+ LOGGER.log("setCatalog(%s)", catalog);
+ // do nothing
+ }
+
+ @Override
+ public String getCatalog() throws SQLException {
+ return null;
+ }
+
+ @Override
+ public void setTransactionIsolation(int level) throws SQLException {
+ }
+
+ @Override
+ public int getTransactionIsolation() throws SQLException {
+ return Connection.TRANSACTION_NONE;
+ }
+
+ @Override
+ public SQLWarning getWarnings() throws SQLException {
+ LOGGER.log("getWarnings returns null");
+ return null;
+ }
+
+ @Override
+ public void clearWarnings() throws SQLException {
+ }
+
+ @Override
+ public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
+ return this.createStatement();
+ }
+
+ @Override
+ public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
+ return this.prepareStatement(sql);
+ }
+
+ @Override
+ public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
+ return this.prepareCall(sql);
+ }
+
+ @Override
+ public Map> getTypeMap() throws SQLException {
+ LOGGER.log("getTypeMap not implemented");
+ throw new SQLFeatureNotSupportedException("getTypeMap not implemented");
+ }
+
+ @Override
+ public void setTypeMap(Map> map) throws SQLException {
+ LOGGER.log("setTypeMap not implemented");
+ throw new SQLFeatureNotSupportedException("setTypeMap not implemented");
+ }
+
+ @Override
+ public void setHoldability(int holdability) throws SQLException {
+ // do nothing
+ }
+
+ @Override
+ public int getHoldability() throws SQLException {
+ return ResultSet.HOLD_CURSORS_OVER_COMMIT;
+ }
+
+ @Override
+ public Savepoint setSavepoint() throws SQLException {
+ LOGGER.log("setSavepoint not implemented");
+ throw new SQLFeatureNotSupportedException("setSavepoint not implemented");
+ }
+
+ @Override
+ public Savepoint setSavepoint(String name) throws SQLException {
+ LOGGER.log("setSavepoint not implemented");
+ throw new SQLFeatureNotSupportedException("setSavepoint not implemented");
+ }
+
+ @Override
+ public void rollback(Savepoint savepoint) throws SQLException {
+ LOGGER.log("rollback not implemented");
+ throw new SQLFeatureNotSupportedException("rollback not implemented");
+ }
+
+ @Override
+ public void releaseSavepoint(Savepoint savepoint) throws SQLException {
+ LOGGER.log("releaseSavepoint not implemented");
+ throw new SQLFeatureNotSupportedException("releaseSavepoint not implemented");
+ }
+
+ @Override
+ public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
+ return this.createStatement();
+ }
+
+ @Override
+ public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
+ return this.prepareStatement(sql);
+ }
+
+ @Override
+ public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
+ return this.prepareCall(sql);
+ }
+
+ @Override
+ public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
+ return this.prepareStatement(sql);
+ }
+
+ @Override
+ public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
+ return this.prepareStatement(sql);
+ }
+
+ @Override
+ public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
+ return this.prepareStatement(sql);
+ }
+
+ @Override
+ public Clob createClob() throws SQLException {
+ LOGGER.log("createClob not implemented");
+ throw new SQLFeatureNotSupportedException("createClob not implemented");
+ }
+
+ @Override
+ public Blob createBlob() throws SQLException {
+ LOGGER.log("createBlob not implemented");
+ throw new SQLFeatureNotSupportedException("createBlob not implemented");
+ }
+
+ @Override
+ public NClob createNClob() throws SQLException {
+ LOGGER.log("createNClob not implemented");
+ throw new SQLFeatureNotSupportedException("createNClob not implemented");
+ }
+
+ @Override
+ public SQLXML createSQLXML() throws SQLException {
+ LOGGER.log("createSQLXML not implemented");
+ throw new SQLFeatureNotSupportedException("createSQLXML not implemented");
+ }
+
+ @Override
+ public boolean isValid(int timeout) throws SQLException {
+ LOGGER.log("isValid = %s", isClosed);
+ if (this.isClosed) {
+ return false;
+ }
+ // TODO 超时时间
+ try {
+ this.redisClient.sendCommand("PING");
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ @Override
+ public void setClientInfo(String name, String value) throws SQLClientInfoException {
+ this.properties.put(name, value);
+ }
+
+ @Override
+ public void setClientInfo(Properties properties) throws SQLClientInfoException {
+ this.properties.putAll(properties);
+ }
+
+ @Override
+ public String getClientInfo(String name) throws SQLException {
+ this.checkClosed();
+
+ String property = this.properties.getProperty(name);
+ property = property == null ? "" : property;
+ LOGGER.log("getClientInfo(%s) = %s, properties = %s", name, property, properties);
+ return property;
+ }
+
+ @Override
+ public Properties getClientInfo() throws SQLException {
+ LOGGER.log("getClientInfo() = %s", properties);
+ return this.properties;
+ }
+
+ @Override
+ public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
+ LOGGER.log("createArrayOf not implemented");
+ throw new SQLFeatureNotSupportedException("createArrayOf not implemented");
+ }
+
+ @Override
+ public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
+ LOGGER.log("createStruct not implemented");
+ throw new SQLFeatureNotSupportedException("createStruct not implemented");
+ }
+
+ @Override
+ public void setSchema(String schema) throws SQLException {
+ synchronized (RedisConnection.class) {
+ LOGGER.log("setSchema(%s)", schema);
+ this.checkClosed();
+
+ this.redisClient.select(Integer.parseInt(schema));
+
+ this.dbIndex = schema;
+ }
+ }
+
+ @Override
+ public String getSchema() throws SQLException {
+ synchronized (RedisConnection.class) {
+ this.checkClosed();
+ LOGGER.log("getSchema() = %s", this.dbIndex);
+ return this.dbIndex;
+ }
+ }
+
+ @Override
+ public void abort(Executor executor) throws SQLException {
+ LOGGER.log("abort not implemented");
+ throw new SQLFeatureNotSupportedException("abort not implemented");
+ }
+
+ @Override
+ public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
+ LOGGER.log("setNetworkTimeout not implemented");
+ throw new SQLFeatureNotSupportedException("setNetworkTimeout not implemented");
+ }
+
+ @Override
+ public int getNetworkTimeout() throws SQLException {
+ LOGGER.log("getNetworkTimeout not implemented");
+ throw new SQLFeatureNotSupportedException("getNetworkTimeout not implemented");
+ }
+
+ @Override
+ public T unwrap(Class iface) throws SQLException {
+ try {
+ return iface.cast(this);
+ } catch (ClassCastException cce) {
+ LOGGER.log("Unable to unwrap to %s", iface);
+ throw new SQLException("Unable to unwrap to " + iface);
+ }
+ }
+
+ @Override
+ public boolean isWrapperFor(Class> iface) throws SQLException {
+ this.checkClosed();
+ return iface.isInstance(this);
+ }
+
+ private void checkClosed() throws SQLException {
+ if (isClosed()) {
+ LOGGER.log("Connection is closed.");
+ throw new SQLException("Connection is closed.");
+ }
+ }
+}
diff --git a/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/RedisDatabaseMetadata.java b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/RedisDatabaseMetadata.java
new file mode 100644
index 00000000..7a400161
--- /dev/null
+++ b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/RedisDatabaseMetadata.java
@@ -0,0 +1,1017 @@
+package io.edurt.datacap.core;
+
+import java.io.BufferedReader;
+import java.io.StringReader;
+import java.sql.*;
+import java.util.stream.IntStream;
+
+public class RedisDatabaseMetadata implements DatabaseMetaData {
+ private final static Logger LOGGER = new Logger(RedisDatabaseMetadata.class);
+
+ private final RedisConnection connection;
+ private final String dbIndex;
+
+ public RedisDatabaseMetadata(RedisConnection connection, String dbIndex) {
+ this.connection = connection;
+ this.dbIndex = dbIndex;
+ }
+
+ @Override
+ public boolean allProceduresAreCallable() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean allTablesAreSelectable() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public String getURL() throws SQLException {
+ return null;
+ }
+
+ @Override
+ public String getUserName() throws SQLException {
+ return null;
+ }
+
+ @Override
+ public boolean isReadOnly() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean nullsAreSortedHigh() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean nullsAreSortedLow() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean nullsAreSortedAtStart() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean nullsAreSortedAtEnd() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public String getDatabaseProductName() throws SQLException {
+ return "Redis";
+ }
+
+ @Override
+ public String getDatabaseProductVersion() throws SQLException {
+ return this.getVersion().getOrigin();
+ }
+
+ private ServerVersion getVersion() throws SQLException {
+ Statement statement = connection.createStatement();
+ String version = "0.0.1";
+ try {
+ ResultSet rs = statement.executeQuery("INFO server");
+ while (rs.next()) {
+ String result = rs.getString(0);
+ String versionX = new BufferedReader(new StringReader(result))
+ .lines()
+ .filter(line -> line.startsWith("redis_version"))
+ .findFirst()
+ .map(line -> line.substring(line.indexOf(":") + 1))
+ .orElse(null);
+ if (versionX != null) {
+ version = versionX;
+ break;
+ }
+ }
+ } catch (Exception e) {
+ LOGGER.log("getDatabaseProductVersion exception occurs, " + e.getMessage());
+ }
+ return new ServerVersion(version);
+ }
+
+ @Override
+ public String getDriverName() throws SQLException {
+ return "Redis JDBC Connector/J";
+ }
+
+ @Override
+ public String getDriverVersion() throws SQLException {
+ return "redis-jdbc-driver 1.0.0";
+ }
+
+ @Override
+ public int getDriverMajorVersion() {
+ return 1;
+ }
+
+ @Override
+ public int getDriverMinorVersion() {
+ return 0;
+ }
+
+ @Override
+ public boolean usesLocalFiles() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean usesLocalFilePerTable() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsMixedCaseIdentifiers() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean storesUpperCaseIdentifiers() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean storesLowerCaseIdentifiers() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean storesMixedCaseIdentifiers() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean storesUpperCaseQuotedIdentifiers() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean storesLowerCaseQuotedIdentifiers() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean storesMixedCaseQuotedIdentifiers() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public String getIdentifierQuoteString() throws SQLException {
+ return null;
+ }
+
+ @Override
+ public String getSQLKeywords() throws SQLException {
+ return null;
+ }
+
+ @Override
+ public String getNumericFunctions() throws SQLException {
+ return null;
+ }
+
+ @Override
+ public String getStringFunctions() throws SQLException {
+ return null;
+ }
+
+ @Override
+ public String getSystemFunctions() throws SQLException {
+ return null;
+ }
+
+ @Override
+ public String getTimeDateFunctions() throws SQLException {
+ return null;
+ }
+
+ @Override
+ public String getSearchStringEscape() throws SQLException {
+ return null;
+ }
+
+ @Override
+ public String getExtraNameCharacters() throws SQLException {
+ return null;
+ }
+
+ @Override
+ public boolean supportsAlterTableWithAddColumn() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsAlterTableWithDropColumn() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsColumnAliasing() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean nullPlusNonNullIsNull() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsConvert() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsConvert(int fromType, int toType) throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsTableCorrelationNames() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsDifferentTableCorrelationNames() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsExpressionsInOrderBy() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsOrderByUnrelated() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsGroupBy() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsGroupByUnrelated() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsGroupByBeyondSelect() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsLikeEscapeClause() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsMultipleResultSets() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsMultipleTransactions() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsNonNullableColumns() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsMinimumSQLGrammar() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsCoreSQLGrammar() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsExtendedSQLGrammar() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsANSI92EntryLevelSQL() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsANSI92IntermediateSQL() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsANSI92FullSQL() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsIntegrityEnhancementFacility() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsOuterJoins() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsFullOuterJoins() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsLimitedOuterJoins() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public String getSchemaTerm() throws SQLException {
+ return "SCHEMA";
+ }
+
+ @Override
+ public String getProcedureTerm() throws SQLException {
+ return "PROCEDURE";
+ }
+
+ @Override
+ public String getCatalogTerm() throws SQLException {
+ return "CATALOG";
+ }
+
+ @Override
+ public boolean isCatalogAtStart() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public String getCatalogSeparator() throws SQLException {
+ return ".";
+ }
+
+ @Override
+ public boolean supportsSchemasInDataManipulation() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsSchemasInProcedureCalls() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsSchemasInTableDefinitions() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsSchemasInIndexDefinitions() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsCatalogsInDataManipulation() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsCatalogsInProcedureCalls() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsCatalogsInTableDefinitions() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsCatalogsInIndexDefinitions() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsPositionedDelete() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsPositionedUpdate() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsSelectForUpdate() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsStoredProcedures() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsSubqueriesInComparisons() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsSubqueriesInExists() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsSubqueriesInIns() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsSubqueriesInQuantifieds() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsCorrelatedSubqueries() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsUnion() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsUnionAll() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsOpenCursorsAcrossCommit() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsOpenCursorsAcrossRollback() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsOpenStatementsAcrossCommit() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean supportsOpenStatementsAcrossRollback() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public int getMaxBinaryLiteralLength() throws SQLException {
+ return 100;
+ }
+
+ @Override
+ public int getMaxCharLiteralLength() throws SQLException {
+ return 100;
+ }
+
+ @Override
+ public int getMaxColumnNameLength() throws SQLException {
+ return 100;
+ }
+
+ @Override
+ public int getMaxColumnsInGroupBy() throws SQLException {
+ return 100;
+ }
+
+ @Override
+ public int getMaxColumnsInIndex() throws SQLException {
+ return 100;
+ }
+
+ @Override
+ public int getMaxColumnsInOrderBy() throws SQLException {
+ return 100;
+ }
+
+ @Override
+ public int getMaxColumnsInSelect() throws SQLException {
+ return 100;
+ }
+
+ @Override
+ public int getMaxColumnsInTable() throws SQLException {
+ return 100;
+ }
+
+ @Override
+ public int getMaxConnections() throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public int getMaxCursorNameLength() throws SQLException {
+ return 64;
+ }
+
+ @Override
+ public int getMaxIndexLength() throws SQLException {
+ return 100;
+ }
+
+ @Override
+ public int getMaxSchemaNameLength() throws SQLException {
+ return 100;
+ }
+
+ @Override
+ public int getMaxProcedureNameLength() throws SQLException {
+ return 100;
+ }
+
+ @Override
+ public int getMaxCatalogNameLength() throws SQLException {
+ return 16;
+ }
+
+ @Override
+ public int getMaxRowSize() throws SQLException {
+ return Integer.MAX_VALUE;
+ }
+
+ @Override
+ public boolean doesMaxRowSizeIncludeBlobs() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public int getMaxStatementLength() throws SQLException {
+ return 65535;
+ }
+
+ @Override
+ public int getMaxStatements() throws SQLException {
+ return 64;
+ }
+
+ @Override
+ public int getMaxTableNameLength() throws SQLException {
+ return 64;
+ }
+
+ @Override
+ public int getMaxTablesInSelect() throws SQLException {
+ return 64;
+ }
+
+ @Override
+ public int getMaxUserNameLength() throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public int getDefaultTransactionIsolation() throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public boolean supportsTransactions() throws SQLException {
+ LOGGER.log("supportsTransactions() = false");
+ return false;
+ }
+
+ @Override
+ public boolean supportsTransactionIsolationLevel(int level) throws SQLException {
+ LOGGER.log("supportsTransactionIsolationLevel() = false");
+ return false;
+ }
+
+ @Override
+ public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException {
+ LOGGER.log("supportsDataDefinitionAndDataManipulationTransactions() = false");
+ return false;
+ }
+
+ @Override
+ public boolean supportsDataManipulationTransactionsOnly() throws SQLException {
+ LOGGER.log("supportsDataManipulationTransactionsOnly() = false");
+ return false;
+ }
+
+ @Override
+ public boolean dataDefinitionCausesTransactionCommit() throws SQLException {
+ LOGGER.log("dataDefinitionCausesTransactionCommit() = false");
+ return false;
+ }
+
+ @Override
+ public boolean dataDefinitionIgnoredInTransactions() throws SQLException {
+ LOGGER.log("dataDefinitionIgnoredInTransactions() = false");
+ return false;
+ }
+
+ @Override
+ public ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException {
+ LOGGER.log("getProcedures()");
+ return new EmptyResultSet();
+ }
+
+ @Override
+ public ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) throws SQLException {
+ LOGGER.log("getProcedureColumns()");
+ return new EmptyResultSet();
+ }
+
+ @Override
+ public ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String[] types) throws SQLException {
+ LOGGER.log("getTables(%s, %s, %s, %s)", catalog, schemaPattern, tableNamePattern, types);
+ return new RedisResultSet(new String[]{"haha"}, connection.createStatement());
+ }
+
+ @Override
+ public ResultSet getSchemas() throws SQLException {
+ LOGGER.log("getSchemas()");
+ ResultSet rs;
+ Statement statement = connection.createStatement();
+
+ String[] databases = IntStream.range(0, 16)
+ .mapToObj(i -> i + "")
+ .toArray(String[]::new);
+ rs = new RedisResultSet(databases, statement);
+ return rs;
+ }
+
+ @Override
+ public ResultSet getCatalogs() throws SQLException {
+ LOGGER.log("DatabaseMetadata getCatalogs()");
+ return new RedisResultSet(new String[0], connection.createStatement());
+ }
+
+ @Override
+ public ResultSet getTableTypes() throws SQLException {
+ LOGGER.log("getTableTypes()");
+ return new EmptyResultSet();
+ }
+
+ @Override
+ public ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException {
+ LOGGER.log("getColumns(%s, %s, %s, %s)", catalog, schemaPattern, tableNamePattern, columnNamePattern);
+ return new EmptyResultSet();
+ }
+
+ @Override
+ public ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException {
+ LOGGER.log("getColumnPrivileges()");
+ return new EmptyResultSet();
+ }
+
+ @Override
+ public ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) throws SQLException {
+ LOGGER.log("getTablePrivileges()");
+ return new EmptyResultSet();
+ }
+
+ @Override
+ public ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) throws SQLException {
+ LOGGER.log("getBestRowIdentifier()");
+ return new EmptyResultSet();
+ }
+
+ @Override
+ public ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException {
+ LOGGER.log("getVersionColumns()");
+ return new EmptyResultSet();
+ }
+
+ @Override
+ public ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException {
+ LOGGER.log("getPrimaryKeys()");
+ return new EmptyResultSet();
+ }
+
+ @Override
+ public ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException {
+ LOGGER.log("getImportedKeys()");
+ return new EmptyResultSet();
+ }
+
+ @Override
+ public ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException {
+ LOGGER.log("getExportedKeys()");
+ return new EmptyResultSet();
+ }
+
+ @Override
+ public ResultSet getCrossReference(String parentCatalog, String parentSchema, String parentTable, String foreignCatalog, String foreignSchema, String foreignTable) throws SQLException {
+ LOGGER.log("getCrossReference()");
+ return new EmptyResultSet();
+ }
+
+ @Override
+ public ResultSet getTypeInfo() throws SQLException {
+ LOGGER.log("getTypeInfo()");
+ return new EmptyResultSet();
+ }
+
+ @Override
+ public ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) throws SQLException {
+ LOGGER.log("getIndexInfo()");
+ return new EmptyResultSet();
+ }
+
+ @Override
+ public boolean supportsResultSetType(int type) throws SQLException {
+ LOGGER.log("supportsResultSetType(%s)", type);
+ return type == ResultSet.TYPE_FORWARD_ONLY;
+ }
+
+ @Override
+ public boolean supportsResultSetConcurrency(int type, int concurrency) throws SQLException {
+ LOGGER.log("supportsResultSetConcurrency(%s, concurrency)", type, concurrency);
+ return false;
+ }
+
+ @Override
+ public boolean ownUpdatesAreVisible(int type) throws SQLException {
+ LOGGER.log("ownUpdatesAreVisible(%s)", type);
+ return false;
+ }
+
+ @Override
+ public boolean ownDeletesAreVisible(int type) throws SQLException {
+ LOGGER.log("ownDeletesAreVisible(%s)", type);
+ return false;
+ }
+
+ @Override
+ public boolean ownInsertsAreVisible(int type) throws SQLException {
+ LOGGER.log("ownInsertsAreVisible(%s)", type);
+ return false;
+ }
+
+ @Override
+ public boolean othersUpdatesAreVisible(int type) throws SQLException {
+ LOGGER.log("othersUpdatesAreVisible(%s)", type);
+ return false;
+ }
+
+ @Override
+ public boolean othersDeletesAreVisible(int type) throws SQLException {
+ LOGGER.log("othersDeletesAreVisible(%s)", type);
+ return false;
+ }
+
+ @Override
+ public boolean othersInsertsAreVisible(int type) throws SQLException {
+ LOGGER.log("othersInsertsAreVisible(%s)", type);
+ return false;
+ }
+
+ @Override
+ public boolean updatesAreDetected(int type) throws SQLException {
+ LOGGER.log("updatesAreDetected(%s)", type);
+ return false;
+ }
+
+ @Override
+ public boolean deletesAreDetected(int type) throws SQLException {
+ LOGGER.log("deletesAreDetected(%s)", type);
+ return false;
+ }
+
+ @Override
+ public boolean insertsAreDetected(int type) throws SQLException {
+ LOGGER.log("insertsAreDetected(%s)", type);
+ return false;
+ }
+
+ @Override
+ public boolean supportsBatchUpdates() throws SQLException {
+ LOGGER.log("supportsBatchUpdates()");
+ return false;
+ }
+
+ @Override
+ public ResultSet getUDTs(String catalog, String schemaPattern, String typeNamePattern, int[] types) throws SQLException {
+ LOGGER.log("getUDTs()");
+ return new EmptyResultSet();
+ }
+
+ @Override
+ public Connection getConnection() throws SQLException {
+ LOGGER.log("getConnection()");
+ return this.connection;
+ }
+
+ @Override
+ public boolean supportsSavepoints() throws SQLException {
+ LOGGER.log("supportsSavepoints()");
+ return false;
+ }
+
+ @Override
+ public boolean supportsNamedParameters() throws SQLException {
+ LOGGER.log("supportsNamedParameters()");
+ return false;
+ }
+
+ @Override
+ public boolean supportsMultipleOpenResults() throws SQLException {
+ LOGGER.log("supportsMultipleOpenResults()");
+ return false;
+ }
+
+ @Override
+ public boolean supportsGetGeneratedKeys() throws SQLException {
+ LOGGER.log("supportsGetGeneratedKeys()");
+ return false;
+ }
+
+ @Override
+ public ResultSet getSuperTypes(String catalog, String schemaPattern, String typeNamePattern) throws SQLException {
+ LOGGER.log("getSuperTypes()");
+ return new EmptyResultSet();
+ }
+
+ @Override
+ public ResultSet getSuperTables(String catalog, String schemaPattern, String tableNamePattern) throws SQLException {
+ LOGGER.log("getSuperTables()");
+ return new EmptyResultSet();
+ }
+
+ @Override
+ public ResultSet getAttributes(String catalog, String schemaPattern, String typeNamePattern, String attributeNamePattern) throws SQLException {
+ LOGGER.log("getAttributes()");
+ return new EmptyResultSet();
+ }
+
+ @Override
+ public boolean supportsResultSetHoldability(int holdability) throws SQLException {
+ LOGGER.log("supportsResultSetHoldability()");
+ return holdability == ResultSet.HOLD_CURSORS_OVER_COMMIT;
+ }
+
+ @Override
+ public int getResultSetHoldability() throws SQLException {
+ LOGGER.log("getResultSetHoldability()");
+ return ResultSet.HOLD_CURSORS_OVER_COMMIT;
+ }
+
+ @Override
+ public int getDatabaseMajorVersion() throws SQLException {
+ LOGGER.log("getDatabaseMajorVersion()");
+ return this.getVersion().getMajor();
+ }
+
+ @Override
+ public int getDatabaseMinorVersion() throws SQLException {
+ LOGGER.log("getDatabaseMinorVersion()");
+ return this.getVersion().getMinor();
+ }
+
+ @Override
+ public int getJDBCMajorVersion() throws SQLException {
+ LOGGER.log("getJDBCMajorVersion()");
+ return 4;
+ }
+
+ @Override
+ public int getJDBCMinorVersion() throws SQLException {
+ LOGGER.log("getJDBCMinorVersion()");
+ return 0;
+ }
+
+ @Override
+ public int getSQLStateType() throws SQLException {
+ LOGGER.log("getSQLStateType()");
+ return DatabaseMetaData.sqlStateSQL;
+ }
+
+ @Override
+ public boolean locatorsUpdateCopy() throws SQLException {
+ LOGGER.log("locatorsUpdateCopy()");
+ return false;
+ }
+
+ @Override
+ public boolean supportsStatementPooling() throws SQLException {
+ LOGGER.log("supportsStatementPooling()");
+ return false;
+ }
+
+ @Override
+ public RowIdLifetime getRowIdLifetime() throws SQLException {
+ LOGGER.log("getRowIdLifetime()");
+ return null;
+ }
+
+ @Override
+ public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException {
+ LOGGER.log("getSchemas(%s, %s)", catalog, schemaPattern);
+ if (schemaPattern.equals("%")) {
+ return this.getSchemas();
+ }
+
+ schemaPattern = Utils.isNumber(schemaPattern) ? schemaPattern : dbIndex;
+
+ return new RedisResultSet(new String[]{schemaPattern}, this.connection.createStatement());
+ }
+
+ @Override
+ public boolean supportsStoredFunctionsUsingCallSyntax() throws SQLException {
+ LOGGER.log("supportsStoredFunctionsUsingCallSyntax()");
+ return false;
+ }
+
+ @Override
+ public boolean autoCommitFailureClosesAllResultSets() throws SQLException {
+ LOGGER.log("autoCommitFailureClosesAllResultSets()");
+ return false;
+ }
+
+ @Override
+ public ResultSet getClientInfoProperties() throws SQLException {
+ LOGGER.log("autoCommitFailureClosesAllResultSets()");
+ //TODO https://docs.oracle.com/javase/8/docs/api/java/sql/Connection.html#setClientInfo-java.lang.String-java.lang.String-
+ return new EmptyResultSet();
+ }
+
+ @Override
+ public ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern) throws SQLException {
+ LOGGER.log("getFunctions()");
+ return new EmptyResultSet();
+ }
+
+ @Override
+ public ResultSet getFunctionColumns(String catalog, String schemaPattern, String functionNamePattern, String columnNamePattern) throws SQLException {
+ LOGGER.log("getFunctionColumns()");
+ return new EmptyResultSet();
+ }
+
+ @Override
+ public ResultSet getPseudoColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException {
+ LOGGER.log("getPseudoColumns()");
+ return new EmptyResultSet();
+ }
+
+ @Override
+ public boolean generatedKeyAlwaysReturned() throws SQLException {
+ LOGGER.log("generatedKeyAlwaysReturned()");
+ return false;
+ }
+
+ @Override
+ public long getMaxLogicalLobSize() throws SQLException {
+ LOGGER.log("getMaxLogicalLobSize()");
+ return DatabaseMetaData.super.getMaxLogicalLobSize();
+ }
+
+ @Override
+ public boolean supportsRefCursors() throws SQLException {
+ LOGGER.log("supportsRefCursors()");
+ return DatabaseMetaData.super.supportsRefCursors();
+ }
+
+ @Override
+ public T unwrap(Class iface) throws SQLException {
+ try {
+ LOGGER.log("unwrap()");
+ return iface.cast(this);
+ } catch (ClassCastException cce) {
+ LOGGER.log("Unable to unwrap to %s", iface);
+ throw new SQLException("Unable to unwrap to " + iface);
+ }
+ }
+
+ @Override
+ public boolean isWrapperFor(Class> iface) throws SQLException {
+ LOGGER.log("isWrapperFor()");
+ return iface.isInstance(this);
+ }
+}
diff --git a/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/RedisResultSet.java b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/RedisResultSet.java
new file mode 100644
index 00000000..a6b83b12
--- /dev/null
+++ b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/RedisResultSet.java
@@ -0,0 +1,1231 @@
+package io.edurt.datacap.core;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.sql.*;
+import java.util.Calendar;
+import java.util.Map;
+
+public class RedisResultSet implements ResultSet {
+ private final static Logger LOGGER = new Logger(RedisResultSet.class);
+
+ private final String[] result;
+ private final Statement owningStatement;
+
+ private int position = -1;
+ private boolean isClosed = false;
+
+ public RedisResultSet(final String[] result, final Statement owningStatement) {
+ this.result = result;
+ this.owningStatement = owningStatement;
+ }
+
+ private void checkClosed() throws SQLException {
+ if (isClosed()) {
+ LOGGER.log("ResultSet is closed.");
+ throw new SQLException("RedisSet is closed.");
+ }
+ }
+
+ @Override
+ public boolean next() throws SQLException {
+ this.checkClosed();
+
+ if (position < result.length - 1) {
+ position++;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void close() throws SQLException {
+ LOGGER.log("ResultSet close");
+ this.isClosed = true;
+ }
+
+ @Override
+ public boolean wasNull() throws SQLException {
+ this.checkClosed();
+ return result[position] == null;
+ }
+
+ @Override
+ public String getString(int columnIndex) throws SQLException {
+ this.checkClosed();
+ LOGGER.log("getString(%s)", columnIndex);
+ return result[position];
+ }
+
+ @Override
+ public boolean getBoolean(int columnIndex) throws SQLException {
+ this.checkClosed();
+
+ String r = this.getString(0);
+
+ if ("0".equals(r) || "false".equals(r)) {
+ return false;
+ } else if ("1".equals(r) || "true".equals(r)) {
+ return true;
+ } else {
+ LOGGER.log("Cannot convert " + r + " into a boolean.");
+ throw new SQLException("Cannot convert " + r + " into a boolean.");
+ }
+ }
+
+ @Override
+ public byte getByte(int columnIndex) throws SQLException {
+ this.checkClosed();
+
+ String string = this.getString(0);
+
+ if (string == null) {
+ return 0;
+ }
+ return string.getBytes()[0];
+ }
+
+ @Override
+ public short getShort(int columnIndex) throws SQLException {
+ this.checkClosed();
+
+ String string = this.getString(0);
+ if (string == null) {
+ return 0;
+ }
+
+ return Short.parseShort(string);
+ }
+
+ @Override
+ public int getInt(int columnIndex) throws SQLException {
+ this.checkClosed();
+
+ String string = this.getString(0);
+ if (string == null) {
+ return 0;
+ }
+
+ return Integer.parseInt(string);
+ }
+
+ @Override
+ public long getLong(int columnIndex) throws SQLException {
+ this.checkClosed();
+
+ String string = this.getString(0);
+ if (string == null) {
+ return 0;
+ }
+
+ return Long.parseLong(string);
+ }
+
+ @Override
+ public float getFloat(int columnIndex) throws SQLException {
+ this.checkClosed();
+
+ String string = this.getString(0);
+ if (string == null) {
+ return 0;
+ }
+
+ return Float.parseFloat(string);
+ }
+
+ @Override
+ public double getDouble(int columnIndex) throws SQLException {
+ this.checkClosed();
+
+ String string = this.getString(0);
+ if (string == null) {
+ return 0;
+ }
+
+ return Double.parseDouble(string);
+ }
+
+ @Override
+ public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException {
+ return this.getBigDecimal(0);
+ }
+
+ @Override
+ public byte[] getBytes(int columnIndex) throws SQLException {
+ this.checkClosed();
+
+ String string = this.getString(0);
+ if (string == null) {
+ return null;
+ }
+
+ return string.getBytes();
+ }
+
+ @Override
+ public Date getDate(int columnIndex) throws SQLException {
+ LOGGER.log("getDate not implemented");
+ throw new SQLFeatureNotSupportedException("getDate not implemented");
+ }
+
+ @Override
+ public Time getTime(int columnIndex) throws SQLException {
+ LOGGER.log("getTime not implemented");
+ throw new SQLFeatureNotSupportedException("getTime not implemented");
+ }
+
+ @Override
+ public Timestamp getTimestamp(int columnIndex) throws SQLException {
+ LOGGER.log("getTimestamp not implemented");
+ throw new SQLFeatureNotSupportedException("getTimestamp not implemented");
+ }
+
+ @Override
+ public InputStream getAsciiStream(int columnIndex) throws SQLException {
+ LOGGER.log("getAsciiStream not implemented");
+ throw new SQLFeatureNotSupportedException("getAsciiStream not implemented");
+ }
+
+ @Override
+ public InputStream getUnicodeStream(int columnIndex) throws SQLException {
+ LOGGER.log("getAsciiStream not implemented");
+ throw new SQLFeatureNotSupportedException("getAsciiStream not implemented");
+ }
+
+ @Override
+ public InputStream getBinaryStream(int columnIndex) throws SQLException {
+ LOGGER.log("getAsciiStream not implemented");
+ throw new SQLFeatureNotSupportedException("getAsciiStream not implemented");
+ }
+
+ @Override
+ public String getString(String columnLabel) throws SQLException {
+ return this.getString(0);
+ }
+
+ @Override
+ public boolean getBoolean(String columnLabel) throws SQLException {
+ return this.getBoolean(0);
+ }
+
+ @Override
+ public byte getByte(String columnLabel) throws SQLException {
+ return this.getByte(0);
+ }
+
+ @Override
+ public short getShort(String columnLabel) throws SQLException {
+ return this.getShort(0);
+ }
+
+ @Override
+ public int getInt(String columnLabel) throws SQLException {
+ return this.getInt(0);
+ }
+
+ @Override
+ public long getLong(String columnLabel) throws SQLException {
+ return this.getLong(0);
+ }
+
+ @Override
+ public float getFloat(String columnLabel) throws SQLException {
+ return this.getFloat(0);
+ }
+
+ @Override
+ public double getDouble(String columnLabel) throws SQLException {
+ return this.getDouble(0);
+ }
+
+ @Override
+ public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLException {
+ return this.getBigDecimal(0);
+ }
+
+ @Override
+ public byte[] getBytes(String columnLabel) throws SQLException {
+ return this.getBytes(0);
+ }
+
+ @Override
+ public Date getDate(String columnLabel) throws SQLException {
+ return this.getDate(0);
+ }
+
+ @Override
+ public Time getTime(String columnLabel) throws SQLException {
+ return this.getTime(0);
+ }
+
+ @Override
+ public Timestamp getTimestamp(String columnLabel) throws SQLException {
+ return this.getTimestamp(0);
+ }
+
+ @Override
+ public InputStream getAsciiStream(String columnLabel) throws SQLException {
+ return this.getAsciiStream(0);
+ }
+
+ @Override
+ public InputStream getUnicodeStream(String columnLabel) throws SQLException {
+ return this.getUnicodeStream(0);
+ }
+
+ @Override
+ public InputStream getBinaryStream(String columnLabel) throws SQLException {
+ return this.getBinaryStream(0);
+ }
+
+ @Override
+ public SQLWarning getWarnings() throws SQLException {
+ return null;
+ }
+
+ @Override
+ public void clearWarnings() throws SQLException {
+ }
+
+ @Override
+ public String getCursorName() throws SQLException {
+ LOGGER.log("getCursorName not implemented");
+ throw new SQLFeatureNotSupportedException("getCursorName not implemented");
+ }
+
+ @Override
+ public ResultSetMetaData getMetaData() throws SQLException {
+ return new RedisResultSetMetaData();
+ }
+
+ @Override
+ public Object getObject(int columnIndex) throws SQLException {
+ LOGGER.log("getObject not implemented");
+ throw new SQLFeatureNotSupportedException("getObject not implemented");
+ }
+
+ @Override
+ public Object getObject(String columnLabel) throws SQLException {
+ return this.getObject(0);
+ }
+
+ @Override
+ public int findColumn(String columnLabel) throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public Reader getCharacterStream(int columnIndex) throws SQLException {
+ LOGGER.log("getCharacterStream not implemented");
+ throw new SQLFeatureNotSupportedException("getCharacterStream not implemented");
+ }
+
+ @Override
+ public Reader getCharacterStream(String columnLabel) throws SQLException {
+ LOGGER.log("getCharacterStream not implemented");
+ throw new SQLFeatureNotSupportedException("getCharacterStream not implemented");
+ }
+
+ @Override
+ public BigDecimal getBigDecimal(int columnIndex) throws SQLException {
+ this.checkClosed();
+
+ String string = this.getString(0);
+ if (string == null) {
+ return null;
+ }
+
+ return new BigDecimal(string);
+ }
+
+ @Override
+ public BigDecimal getBigDecimal(String columnLabel) throws SQLException {
+ return this.getBigDecimal(0);
+ }
+
+ @Override
+ public boolean isBeforeFirst() throws SQLException {
+ this.checkClosed();
+ return position < 0;
+ }
+
+ @Override
+ public boolean isAfterLast() throws SQLException {
+ this.checkClosed();
+ return position >= result.length;
+ }
+
+ @Override
+ public boolean isFirst() throws SQLException {
+ this.checkClosed();
+ return position == 0;
+ }
+
+ @Override
+ public boolean isLast() throws SQLException {
+ this.checkClosed();
+ return position == result.length - 1;
+ }
+
+ @Override
+ public void beforeFirst() throws SQLException {
+ this.checkClosed();
+ position = -1;
+ }
+
+ @Override
+ public void afterLast() throws SQLException {
+ this.checkClosed();
+ position = result.length;
+ }
+
+ @Override
+ public boolean first() throws SQLException {
+ this.checkClosed();
+ position = 0;
+ return result.length > 0;
+ }
+
+ @Override
+ public boolean last() throws SQLException {
+ this.checkClosed();
+ position = result.length - 1;
+ return result.length > 0;
+ }
+
+ @Override
+ public int getRow() throws SQLException {
+ this.checkClosed();
+ return this.position + 1;
+ }
+
+ @Override
+ public boolean absolute(int row) throws SQLException {
+ // TODO
+ return false;
+ }
+
+ @Override
+ public boolean relative(int rows) throws SQLException {
+ LOGGER.log("relative not implemented");
+ throw new SQLFeatureNotSupportedException("relative not implemented");
+ }
+
+ @Override
+ public boolean previous() throws SQLException {
+ LOGGER.log("previous not implemented");
+ throw new SQLFeatureNotSupportedException("previous not implemented");
+ }
+
+ @Override
+ public void setFetchDirection(int direction) throws SQLException {
+ LOGGER.log("setFetchDirection not implemented");
+ throw new SQLFeatureNotSupportedException("setFetchDirection not implemented");
+ }
+
+ @Override
+ public int getFetchDirection() throws SQLException {
+ this.checkClosed();
+ return FETCH_FORWARD;
+ }
+
+ @Override
+ public void setFetchSize(int rows) throws SQLException {
+ LOGGER.log("setFetchSize not implemented");
+ throw new SQLFeatureNotSupportedException("setFetchSize not implemented");
+ }
+
+ @Override
+ public int getFetchSize() throws SQLException {
+ this.checkClosed();
+ return result.length;
+ }
+
+ @Override
+ public int getType() throws SQLException {
+ this.checkClosed();
+ return TYPE_FORWARD_ONLY;
+ }
+
+ @Override
+ public int getConcurrency() throws SQLException {
+ this.checkClosed();
+ return ResultSet.CONCUR_READ_ONLY;
+ }
+
+ @Override
+ public boolean rowUpdated() throws SQLException {
+ LOGGER.log("rowUpdated not implemented");
+ throw new SQLFeatureNotSupportedException("rowUpdated not implemented");
+ }
+
+ @Override
+ public boolean rowInserted() throws SQLException {
+ LOGGER.log("rowInserted not implemented");
+ throw new SQLFeatureNotSupportedException("rowInserted not implemented");
+ }
+
+ @Override
+ public boolean rowDeleted() throws SQLException {
+ LOGGER.log("rowDeleted not implemented");
+ throw new SQLFeatureNotSupportedException("rowDeleted not implemented");
+ }
+
+ @Override
+ public void updateNull(int columnIndex) throws SQLException {
+ LOGGER.log("updateNull not implemented");
+ throw new SQLFeatureNotSupportedException("updateNull not implemented");
+ }
+
+ @Override
+ public void updateBoolean(int columnIndex, boolean x) throws SQLException {
+ LOGGER.log("updateBoolean not implemented");
+ throw new SQLFeatureNotSupportedException("updateBoolean not implemented");
+ }
+
+ @Override
+ public void updateByte(int columnIndex, byte x) throws SQLException {
+ LOGGER.log("updateByte not implemented");
+ throw new SQLFeatureNotSupportedException("updateByte not implemented");
+ }
+
+ @Override
+ public void updateShort(int columnIndex, short x) throws SQLException {
+ LOGGER.log("updateShort not implemented");
+ throw new SQLFeatureNotSupportedException("updateShort not implemented");
+ }
+
+ @Override
+ public void updateInt(int columnIndex, int x) throws SQLException {
+ LOGGER.log("updateInt not implemented");
+ throw new SQLFeatureNotSupportedException("updateInt not implemented");
+ }
+
+ @Override
+ public void updateLong(int columnIndex, long x) throws SQLException {
+ LOGGER.log("updateLong not implemented");
+ throw new SQLFeatureNotSupportedException("updateLong not implemented");
+ }
+
+ @Override
+ public void updateFloat(int columnIndex, float x) throws SQLException {
+ LOGGER.log("updateFloat not implemented");
+ throw new SQLFeatureNotSupportedException("updateFloat not implemented");
+ }
+
+ @Override
+ public void updateDouble(int columnIndex, double x) throws SQLException {
+ LOGGER.log("updateDouble not implemented");
+ throw new SQLFeatureNotSupportedException("updateDouble not implemented");
+ }
+
+ @Override
+ public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException {
+ LOGGER.log("updateBigDecimal not implemented");
+ throw new SQLFeatureNotSupportedException("updateBigDecimal not implemented");
+ }
+
+ @Override
+ public void updateString(int columnIndex, String x) throws SQLException {
+ LOGGER.log("updateString not implemented");
+ throw new SQLFeatureNotSupportedException("updateString not implemented");
+ }
+
+ @Override
+ public void updateBytes(int columnIndex, byte[] x) throws SQLException {
+ LOGGER.log("updateBytes not implemented");
+ throw new SQLFeatureNotSupportedException("updateBytes not implemented");
+ }
+
+ @Override
+ public void updateDate(int columnIndex, Date x) throws SQLException {
+ LOGGER.log("updateDate not implemented");
+ throw new SQLFeatureNotSupportedException("updateDate not implemented");
+ }
+
+ @Override
+ public void updateTime(int columnIndex, Time x) throws SQLException {
+ LOGGER.log("updateTime not implemented");
+ throw new SQLFeatureNotSupportedException("updateTime not implemented");
+ }
+
+ @Override
+ public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException {
+ LOGGER.log("updateTimestamp not implemented");
+ throw new SQLFeatureNotSupportedException("updateTimestamp not implemented");
+ }
+
+ @Override
+ public void updateAsciiStream(int columnIndex, InputStream x, int length) throws SQLException {
+ LOGGER.log("updateAsciiStream not implemented");
+ throw new SQLFeatureNotSupportedException("updateAsciiStream not implemented");
+ }
+
+ @Override
+ public void updateBinaryStream(int columnIndex, InputStream x, int length) throws SQLException {
+ LOGGER.log("updateBinaryStream not implemented");
+ throw new SQLFeatureNotSupportedException("updateBinaryStream not implemented");
+ }
+
+ @Override
+ public void updateCharacterStream(int columnIndex, Reader x, int length) throws SQLException {
+ LOGGER.log("updateCharacterStream not implemented");
+ throw new SQLFeatureNotSupportedException("updateCharacterStream not implemented");
+ }
+
+ @Override
+ public void updateObject(int columnIndex, Object x, int scaleOrLength) throws SQLException {
+ LOGGER.log("updateObject not implemented");
+ throw new SQLFeatureNotSupportedException("updateObject not implemented");
+ }
+
+ @Override
+ public void updateObject(int columnIndex, Object x) throws SQLException {
+ LOGGER.log("updateObject not implemented");
+ throw new SQLFeatureNotSupportedException("updateObject not implemented");
+ }
+
+ @Override
+ public void updateNull(String columnLabel) throws SQLException {
+ LOGGER.log("updateNull not implemented");
+ throw new SQLFeatureNotSupportedException("updateNull not implemented");
+ }
+
+ @Override
+ public void updateBoolean(String columnLabel, boolean x) throws SQLException {
+ LOGGER.log("updateBoolean not implemented");
+ throw new SQLFeatureNotSupportedException("updateBoolean not implemented");
+ }
+
+ @Override
+ public void updateByte(String columnLabel, byte x) throws SQLException {
+ LOGGER.log("updateByte not implemented");
+ throw new SQLFeatureNotSupportedException("updateByte not implemented");
+ }
+
+ @Override
+ public void updateShort(String columnLabel, short x) throws SQLException {
+ LOGGER.log("updateShort not implemented");
+ throw new SQLFeatureNotSupportedException("updateShort not implemented");
+ }
+
+ @Override
+ public void updateInt(String columnLabel, int x) throws SQLException {
+ LOGGER.log("updateInt not implemented");
+ throw new SQLFeatureNotSupportedException("updateInt not implemented");
+ }
+
+ @Override
+ public void updateLong(String columnLabel, long x) throws SQLException {
+ LOGGER.log("updateLong not implemented");
+ throw new SQLFeatureNotSupportedException("updateLong not implemented");
+ }
+
+ @Override
+ public void updateFloat(String columnLabel, float x) throws SQLException {
+ LOGGER.log("updateFloat not implemented");
+ throw new SQLFeatureNotSupportedException("updateFloat not implemented");
+ }
+
+ @Override
+ public void updateDouble(String columnLabel, double x) throws SQLException {
+ LOGGER.log("updateDouble not implemented");
+ throw new SQLFeatureNotSupportedException("updateDouble not implemented");
+ }
+
+ @Override
+ public void updateBigDecimal(String columnLabel, BigDecimal x) throws SQLException {
+ LOGGER.log("updateBigDecimal not implemented");
+ throw new SQLFeatureNotSupportedException("updateBigDecimal not implemented");
+ }
+
+ @Override
+ public void updateString(String columnLabel, String x) throws SQLException {
+ LOGGER.log("updateString not implemented");
+ throw new SQLFeatureNotSupportedException("updateString not implemented");
+ }
+
+ @Override
+ public void updateBytes(String columnLabel, byte[] x) throws SQLException {
+ LOGGER.log("updateBytes not implemented");
+ throw new SQLFeatureNotSupportedException("updateBytes not implemented");
+ }
+
+ @Override
+ public void updateDate(String columnLabel, Date x) throws SQLException {
+ LOGGER.log("updateDate not implemented");
+ throw new SQLFeatureNotSupportedException("updateDate not implemented");
+ }
+
+ @Override
+ public void updateTime(String columnLabel, Time x) throws SQLException {
+ LOGGER.log("updateTime not implemented");
+ throw new SQLFeatureNotSupportedException("updateTime not implemented");
+ }
+
+ @Override
+ public void updateTimestamp(String columnLabel, Timestamp x) throws SQLException {
+ LOGGER.log("updateTimestamp not implemented");
+ throw new SQLFeatureNotSupportedException("updateTimestamp not implemented");
+ }
+
+ @Override
+ public void updateAsciiStream(String columnLabel, InputStream x, int length) throws SQLException {
+ LOGGER.log("updateAsciiStream not implemented");
+ throw new SQLFeatureNotSupportedException("updateAsciiStream not implemented");
+ }
+
+ @Override
+ public void updateBinaryStream(String columnLabel, InputStream x, int length) throws SQLException {
+ LOGGER.log("updateBinaryStream not implemented");
+ throw new SQLFeatureNotSupportedException("updateBinaryStream not implemented");
+ }
+
+ @Override
+ public void updateCharacterStream(String columnLabel, Reader reader, int length) throws SQLException {
+ LOGGER.log("updateCharacterStream not implemented");
+ throw new SQLFeatureNotSupportedException("updateCharacterStream not implemented");
+ }
+
+ @Override
+ public void updateObject(String columnLabel, Object x, int scaleOrLength) throws SQLException {
+ LOGGER.log("updateObject not implemented");
+ throw new SQLFeatureNotSupportedException("updateObject not implemented");
+ }
+
+ @Override
+ public void updateObject(String columnLabel, Object x) throws SQLException {
+ LOGGER.log("updateObject not implemented");
+ throw new SQLFeatureNotSupportedException("updateObject not implemented");
+ }
+
+ @Override
+ public void insertRow() throws SQLException {
+ LOGGER.log("insertRow not implemented");
+ throw new SQLFeatureNotSupportedException("insertRow not implemented");
+ }
+
+ @Override
+ public void updateRow() throws SQLException {
+ LOGGER.log("updateRow not implemented");
+ throw new SQLFeatureNotSupportedException("updateRow not implemented");
+ }
+
+ @Override
+ public void deleteRow() throws SQLException {
+ LOGGER.log("deleteRow not implemented");
+ throw new SQLFeatureNotSupportedException("deleteRow not implemented");
+ }
+
+ @Override
+ public void refreshRow() throws SQLException {
+ LOGGER.log("refreshRow not implemented");
+ throw new SQLFeatureNotSupportedException("refreshRow not implemented");
+ }
+
+ @Override
+ public void cancelRowUpdates() throws SQLException {
+ LOGGER.log("cancelRowUpdates not implemented");
+ throw new SQLFeatureNotSupportedException("cancelRowUpdates not implemented");
+ }
+
+ @Override
+ public void moveToInsertRow() throws SQLException {
+ LOGGER.log("moveToInsertRow not implemented");
+ throw new SQLFeatureNotSupportedException("moveToInsertRow not implemented");
+ }
+
+ @Override
+ public void moveToCurrentRow() throws SQLException {
+ LOGGER.log("moveToCurrentRow not implemented");
+ throw new SQLFeatureNotSupportedException("moveToCurrentRow not implemented");
+ }
+
+ @Override
+ public Statement getStatement() throws SQLException {
+ this.checkClosed();
+ return this.owningStatement;
+ }
+
+ @Override
+ public Object getObject(int columnIndex, Map> map) throws SQLException {
+ LOGGER.log("getObject not implemented");
+ throw new SQLFeatureNotSupportedException("getObject not implemented");
+ }
+
+ @Override
+ public Ref getRef(int columnIndex) throws SQLException {
+ LOGGER.log("getRef not implemented");
+ throw new SQLFeatureNotSupportedException("getRef not implemented");
+ }
+
+ @Override
+ public Blob getBlob(int columnIndex) throws SQLException {
+ LOGGER.log("getBlob not implemented");
+ throw new SQLFeatureNotSupportedException("getBlob not implemented");
+ }
+
+ @Override
+ public Clob getClob(int columnIndex) throws SQLException {
+ LOGGER.log("getClob not implemented");
+ throw new SQLFeatureNotSupportedException("getClob not implemented");
+ }
+
+ @Override
+ public Array getArray(int columnIndex) throws SQLException {
+ LOGGER.log("getArray not implemented");
+ throw new SQLFeatureNotSupportedException("getArray not implemented");
+ }
+
+ @Override
+ public Object getObject(String columnLabel, Map> map) throws SQLException {
+ LOGGER.log("getObject not implemented");
+ throw new SQLFeatureNotSupportedException("getObject not implemented");
+ }
+
+ @Override
+ public Ref getRef(String columnLabel) throws SQLException {
+ LOGGER.log("getRef not implemented");
+ throw new SQLFeatureNotSupportedException("getRef not implemented");
+ }
+
+ @Override
+ public Blob getBlob(String columnLabel) throws SQLException {
+ LOGGER.log("getBlob not implemented");
+ throw new SQLFeatureNotSupportedException("getBlob not implemented");
+ }
+
+ @Override
+ public Clob getClob(String columnLabel) throws SQLException {
+ LOGGER.log("getClob not implemented");
+ throw new SQLFeatureNotSupportedException("getClob not implemented");
+ }
+
+ @Override
+ public Array getArray(String columnLabel) throws SQLException {
+ LOGGER.log("getArray not implemented");
+ throw new SQLFeatureNotSupportedException("getArray not implemented");
+ }
+
+ @Override
+ public Date getDate(int columnIndex, Calendar cal) throws SQLException {
+ LOGGER.log("getDate not implemented");
+ throw new SQLFeatureNotSupportedException("getDate not implemented");
+ }
+
+ @Override
+ public Date getDate(String columnLabel, Calendar cal) throws SQLException {
+ LOGGER.log("getDate not implemented");
+ throw new SQLFeatureNotSupportedException("getDate not implemented");
+ }
+
+ @Override
+ public Time getTime(int columnIndex, Calendar cal) throws SQLException {
+ LOGGER.log("getTime not implemented");
+ throw new SQLFeatureNotSupportedException("getTime not implemented");
+ }
+
+ @Override
+ public Time getTime(String columnLabel, Calendar cal) throws SQLException {
+ LOGGER.log("getTime not implemented");
+ throw new SQLFeatureNotSupportedException("getTime not implemented");
+ }
+
+ @Override
+ public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException {
+ LOGGER.log("getTimestamp not implemented");
+ throw new SQLFeatureNotSupportedException("getTimestamp not implemented");
+ }
+
+ @Override
+ public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLException {
+ LOGGER.log("getTimestamp not implemented");
+ throw new SQLFeatureNotSupportedException("getTimestamp not implemented");
+ }
+
+ @Override
+ public URL getURL(int columnIndex) throws SQLException {
+ this.checkClosed();
+ String string = this.getString(columnIndex);
+ if (string == null) {
+ return null;
+ }
+
+ try {
+ return new URL(string);
+ } catch (MalformedURLException e) {
+ throw new SQLException(e);
+ }
+ }
+
+ @Override
+ public URL getURL(String columnLabel) throws SQLException {
+ return this.getURL(0);
+ }
+
+ @Override
+ public void updateRef(int columnIndex, Ref x) throws SQLException {
+ LOGGER.log("updateRef not implemented");
+ throw new SQLFeatureNotSupportedException("updateRef not implemented");
+ }
+
+ @Override
+ public void updateRef(String columnLabel, Ref x) throws SQLException {
+ LOGGER.log("updateRef not implemented");
+ throw new SQLFeatureNotSupportedException("updateRef not implemented");
+ }
+
+ @Override
+ public void updateBlob(int columnIndex, Blob x) throws SQLException {
+ LOGGER.log("updateBlob not implemented");
+ throw new SQLFeatureNotSupportedException("updateBlob not implemented");
+ }
+
+ @Override
+ public void updateBlob(String columnLabel, Blob x) throws SQLException {
+ LOGGER.log("updateBlob not implemented");
+ throw new SQLFeatureNotSupportedException("updateBlob not implemented");
+ }
+
+ @Override
+ public void updateClob(int columnIndex, Clob x) throws SQLException {
+ LOGGER.log("updateClob not implemented");
+ throw new SQLFeatureNotSupportedException("updateClob not implemented");
+ }
+
+ @Override
+ public void updateClob(String columnLabel, Clob x) throws SQLException {
+ LOGGER.log("updateClob not implemented");
+ throw new SQLFeatureNotSupportedException("updateClob not implemented");
+ }
+
+ @Override
+ public void updateArray(int columnIndex, Array x) throws SQLException {
+ LOGGER.log("updateArray not implemented");
+ throw new SQLFeatureNotSupportedException("updateArray not implemented");
+ }
+
+ @Override
+ public void updateArray(String columnLabel, Array x) throws SQLException {
+ LOGGER.log("updateArray not implemented");
+ throw new SQLFeatureNotSupportedException("updateArray not implemented");
+ }
+
+ @Override
+ public RowId getRowId(int columnIndex) throws SQLException {
+ LOGGER.log("getRowId not implemented");
+ throw new SQLFeatureNotSupportedException("getRowId not implemented");
+ }
+
+ @Override
+ public RowId getRowId(String columnLabel) throws SQLException {
+ LOGGER.log("getRowId not implemented");
+ throw new SQLFeatureNotSupportedException("getRowId not implemented");
+ }
+
+ @Override
+ public void updateRowId(int columnIndex, RowId x) throws SQLException {
+ LOGGER.log("updateRowId not implemented");
+ throw new SQLFeatureNotSupportedException("updateRowId not implemented");
+ }
+
+ @Override
+ public void updateRowId(String columnLabel, RowId x) throws SQLException {
+ LOGGER.log("updateRowId not implemented");
+ throw new SQLFeatureNotSupportedException("updateRowId not implemented");
+ }
+
+ @Override
+ public int getHoldability() throws SQLException {
+ LOGGER.log("getHoldability not implemented");
+ throw new SQLFeatureNotSupportedException("getHoldability not implemented");
+ }
+
+ @Override
+ public boolean isClosed() throws SQLException {
+ LOGGER.log("isClosed = %s", isClosed);
+ return this.isClosed;
+ }
+
+ @Override
+ public void updateNString(int columnIndex, String nString) throws SQLException {
+ LOGGER.log("updateNString not implemented");
+ throw new SQLFeatureNotSupportedException("updateNString not implemented");
+ }
+
+ @Override
+ public void updateNString(String columnLabel, String nString) throws SQLException {
+ LOGGER.log("updateNString not implemented");
+ throw new SQLFeatureNotSupportedException("updateNString not implemented");
+ }
+
+ @Override
+ public void updateNClob(int columnIndex, NClob nClob) throws SQLException {
+ LOGGER.log("updateNClob not implemented");
+ throw new SQLFeatureNotSupportedException("updateNClob not implemented");
+ }
+
+ @Override
+ public void updateNClob(String columnLabel, NClob nClob) throws SQLException {
+ LOGGER.log("updateNClob not implemented");
+ throw new SQLFeatureNotSupportedException("updateNClob not implemented");
+ }
+
+ @Override
+ public NClob getNClob(int columnIndex) throws SQLException {
+ LOGGER.log("getNClob not implemented");
+ throw new SQLFeatureNotSupportedException("getNClob not implemented");
+ }
+
+ @Override
+ public NClob getNClob(String columnLabel) throws SQLException {
+ LOGGER.log("getNClob not implemented");
+ throw new SQLFeatureNotSupportedException("getNClob not implemented");
+ }
+
+ @Override
+ public SQLXML getSQLXML(int columnIndex) throws SQLException {
+ LOGGER.log("getSQLXML not implemented");
+ throw new SQLFeatureNotSupportedException("getSQLXML not implemented");
+ }
+
+ @Override
+ public SQLXML getSQLXML(String columnLabel) throws SQLException {
+ LOGGER.log("getSQLXML not implemented");
+ throw new SQLFeatureNotSupportedException("getSQLXML not implemented");
+ }
+
+ @Override
+ public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException {
+ LOGGER.log("updateSQLXML not implemented");
+ throw new SQLFeatureNotSupportedException("updateSQLXML not implemented");
+ }
+
+ @Override
+ public void updateSQLXML(String columnLabel, SQLXML xmlObject) throws SQLException {
+ LOGGER.log("updateSQLXML not implemented");
+ throw new SQLFeatureNotSupportedException("updateSQLXML not implemented");
+ }
+
+ @Override
+ public String getNString(int columnIndex) throws SQLException {
+ this.checkClosed();
+ return result[position];
+ }
+
+ @Override
+ public String getNString(String columnLabel) throws SQLException {
+ return this.getNString(0);
+ }
+
+ @Override
+ public Reader getNCharacterStream(int columnIndex) throws SQLException {
+ LOGGER.log("getNCharacterStream not implemented");
+ throw new SQLFeatureNotSupportedException("getNCharacterStream not implemented");
+ }
+
+ @Override
+ public Reader getNCharacterStream(String columnLabel) throws SQLException {
+ LOGGER.log("getNCharacterStream not implemented");
+ throw new SQLFeatureNotSupportedException("getNCharacterStream not implemented");
+ }
+
+ @Override
+ public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException {
+ LOGGER.log("updateNCharacterStream not implemented");
+ throw new SQLFeatureNotSupportedException("updateNCharacterStream not implemented");
+ }
+
+ @Override
+ public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException {
+ LOGGER.log("updateNCharacterStream not implemented");
+ throw new SQLFeatureNotSupportedException("updateNCharacterStream not implemented");
+ }
+
+ @Override
+ public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException {
+ LOGGER.log("updateAsciiStream not implemented");
+ throw new SQLFeatureNotSupportedException("updateAsciiStream not implemented");
+ }
+
+ @Override
+ public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException {
+ LOGGER.log("updateBinaryStream not implemented");
+ throw new SQLFeatureNotSupportedException("updateBinaryStream not implemented");
+ }
+
+ @Override
+ public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException {
+ LOGGER.log("updateCharacterStream not implemented");
+ throw new SQLFeatureNotSupportedException("updateCharacterStream not implemented");
+ }
+
+ @Override
+ public void updateAsciiStream(String columnLabel, InputStream x, long length) throws SQLException {
+ LOGGER.log("updateAsciiStream not implemented");
+ throw new SQLFeatureNotSupportedException("updateAsciiStream not implemented");
+ }
+
+ @Override
+ public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException {
+ LOGGER.log("updateBinaryStream not implemented");
+ throw new SQLFeatureNotSupportedException("updateBinaryStream not implemented");
+ }
+
+ @Override
+ public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException {
+ LOGGER.log("updateCharacterStream not implemented");
+ throw new SQLFeatureNotSupportedException("updateCharacterStream not implemented");
+ }
+
+ @Override
+ public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException {
+ LOGGER.log("updateBlob not implemented");
+ throw new SQLFeatureNotSupportedException("updateBlob not implemented");
+ }
+
+ @Override
+ public void updateBlob(String columnLabel, InputStream inputStream, long length) throws SQLException {
+ LOGGER.log("updateBlob not implemented");
+ throw new SQLFeatureNotSupportedException("updateBlob not implemented");
+ }
+
+ @Override
+ public void updateClob(int columnIndex, Reader reader, long length) throws SQLException {
+ LOGGER.log("updateClob not implemented");
+ throw new SQLFeatureNotSupportedException("updateClob not implemented");
+ }
+
+ @Override
+ public void updateClob(String columnLabel, Reader reader, long length) throws SQLException {
+ LOGGER.log("updateClob not implemented");
+ throw new SQLFeatureNotSupportedException("updateClob not implemented");
+ }
+
+ @Override
+ public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException {
+ LOGGER.log("updateNClob not implemented");
+ throw new SQLFeatureNotSupportedException("updateNClob not implemented");
+ }
+
+ @Override
+ public void updateNClob(String columnLabel, Reader reader, long length) throws SQLException {
+ LOGGER.log("updateNClob not implemented");
+ throw new SQLFeatureNotSupportedException("updateNClob not implemented");
+ }
+
+ @Override
+ public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException {
+ LOGGER.log("updateNCharacterStream not implemented");
+ throw new SQLFeatureNotSupportedException("updateNCharacterStream not implemented");
+ }
+
+ @Override
+ public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException {
+ LOGGER.log("updateNCharacterStream not implemented");
+ throw new SQLFeatureNotSupportedException("updateNCharacterStream not implemented");
+ }
+
+ @Override
+ public void updateAsciiStream(int columnIndex, InputStream x) throws SQLException {
+ LOGGER.log("updateAsciiStream not implemented");
+ throw new SQLFeatureNotSupportedException("updateAsciiStream not implemented");
+ }
+
+ @Override
+ public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException {
+ LOGGER.log("updateBinaryStream not implemented");
+ throw new SQLFeatureNotSupportedException("updateBinaryStream not implemented");
+ }
+
+ @Override
+ public void updateCharacterStream(int columnIndex, Reader x) throws SQLException {
+ LOGGER.log("updateCharacterStream not implemented");
+ throw new SQLFeatureNotSupportedException("updateCharacterStream not implemented");
+ }
+
+ @Override
+ public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException {
+ LOGGER.log("updateAsciiStream not implemented");
+ throw new SQLFeatureNotSupportedException("updateAsciiStream not implemented");
+ }
+
+ @Override
+ public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException {
+ LOGGER.log("updateBinaryStream not implemented");
+ throw new SQLFeatureNotSupportedException("updateBinaryStream not implemented");
+ }
+
+ @Override
+ public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException {
+ LOGGER.log("updateCharacterStream not implemented");
+ throw new SQLFeatureNotSupportedException("updateCharacterStream not implemented");
+ }
+
+ @Override
+ public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException {
+ LOGGER.log("updateBlob not implemented");
+ throw new SQLFeatureNotSupportedException("updateBlob not implemented");
+ }
+
+ @Override
+ public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException {
+ LOGGER.log("updateBlob not implemented");
+ throw new SQLFeatureNotSupportedException("updateBlob not implemented");
+ }
+
+ @Override
+ public void updateClob(int columnIndex, Reader reader) throws SQLException {
+ LOGGER.log("updateClob not implemented");
+ throw new SQLFeatureNotSupportedException("updateClob not implemented");
+ }
+
+ @Override
+ public void updateClob(String columnLabel, Reader reader) throws SQLException {
+ LOGGER.log("updateClob not implemented");
+ throw new SQLFeatureNotSupportedException("updateClob not implemented");
+ }
+
+ @Override
+ public void updateNClob(int columnIndex, Reader reader) throws SQLException {
+ LOGGER.log("updateNClob not implemented");
+ throw new SQLFeatureNotSupportedException("updateNClob not implemented");
+ }
+
+ @Override
+ public void updateNClob(String columnLabel, Reader reader) throws SQLException {
+ LOGGER.log("updateNClob not implemented");
+ throw new SQLFeatureNotSupportedException("updateNClob not implemented");
+ }
+
+ @Override
+ public T getObject(int columnIndex, Class type) throws SQLException {
+ LOGGER.log("getObject not implemented");
+ throw new SQLFeatureNotSupportedException("getObject not implemented");
+ }
+
+ @Override
+ public T getObject(String columnLabel, Class type) throws SQLException {
+ LOGGER.log("getObject not implemented");
+ throw new SQLFeatureNotSupportedException("getObject not implemented");
+ }
+
+ @Override
+ public T unwrap(Class iface) throws SQLException {
+ try {
+ return iface.cast(this);
+ } catch (ClassCastException cce) {
+ LOGGER.log("Unable to unwrap to %s" + iface);
+ throw new SQLException("Unable to unwrap to " + iface);
+ }
+ }
+
+ @Override
+ public boolean isWrapperFor(Class> iface) throws SQLException {
+ return iface.isInstance(this);
+ }
+}
diff --git a/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/RedisResultSetMetaData.java b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/RedisResultSetMetaData.java
new file mode 100644
index 00000000..7bc268bc
--- /dev/null
+++ b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/RedisResultSetMetaData.java
@@ -0,0 +1,133 @@
+package io.edurt.datacap.core;
+
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Types;
+
+public class RedisResultSetMetaData implements ResultSetMetaData {
+ private final static Logger LOGGER = new Logger(RedisResultSetMetaData.class);
+
+ public static final int MAX_SIZE = 1024;
+
+ @Override
+ public T unwrap(Class iface) throws SQLException {
+ try {
+ return iface.cast(this);
+ } catch (ClassCastException cce) {
+ LOGGER.log("Unable to unwrap to %s", iface);
+ throw new SQLException("Unable to unwrap to " + iface);
+ }
+ }
+
+ @Override
+ public boolean isWrapperFor(Class> iface) throws SQLException {
+ return iface.isInstance(this);
+ }
+
+ @Override
+ public int getColumnCount() throws SQLException {
+ return 1;
+ }
+
+ @Override
+ public boolean isAutoIncrement(int column) throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean isCaseSensitive(int column) throws SQLException {
+ return true;
+ }
+
+ @Override
+ public boolean isSearchable(int column) throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean isCurrency(int column) throws SQLException {
+ return false;
+ }
+
+ @Override
+ public int isNullable(int column) throws SQLException {
+ return ResultSetMetaData.columnNoNulls;
+ }
+
+ @Override
+ public boolean isSigned(int column) throws SQLException {
+ return false;
+ }
+
+ @Override
+ public int getColumnDisplaySize(int column) throws SQLException {
+ return MAX_SIZE;
+ }
+
+ @Override
+ public String getColumnLabel(int column) throws SQLException {
+ return "RESULTS";
+ }
+
+ @Override
+ public String getColumnName(int column) throws SQLException {
+ return "RESULTS";
+ }
+
+ @Override
+ public String getSchemaName(int column) throws SQLException {
+ LOGGER.log("getSchemaName(%s)", column);
+ return "9";
+ }
+
+ @Override
+ public int getPrecision(int column) throws SQLException {
+ return MAX_SIZE;
+ }
+
+ @Override
+ public int getScale(int column) throws SQLException {
+ return 0;
+ }
+
+ @Override
+ public String getTableName(int column) throws SQLException {
+ return "";
+ }
+
+ @Override
+ public String getCatalogName(int column) throws SQLException {
+ return "";
+ }
+
+ @Override
+ public int getColumnType(int column) throws SQLException {
+ return Types.NVARCHAR;
+ }
+
+ @Override
+ public String getColumnTypeName(int column) throws SQLException {
+ return "String";
+ }
+
+ @Override
+ public boolean isReadOnly(int column) throws SQLException {
+ return true;
+ }
+
+ @Override
+ public boolean isWritable(int column) throws SQLException {
+ return false;
+ }
+
+ @Override
+ public boolean isDefinitelyWritable(int column) throws SQLException {
+ return false;
+ }
+
+ @Override
+ public String getColumnClassName(int column) throws SQLException {
+ return "java.lang.String";
+ }
+
+}
diff --git a/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/RedisStatement.java b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/RedisStatement.java
new file mode 100644
index 00000000..65944423
--- /dev/null
+++ b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/RedisStatement.java
@@ -0,0 +1,314 @@
+package io.edurt.datacap.core;
+
+import java.sql.*;
+
+public class RedisStatement implements Statement {
+ private final static Logger LOGGER = new Logger(RedisStatement.class);
+
+ private final RedisConnection connection;
+ private final RedisClient redisClient;
+
+ private ResultSet resultSet;
+ private boolean isClosed = false;
+ private int fetchSize = 1;
+
+ public RedisStatement(RedisConnection connection, RedisClient redisClient) {
+ this.connection = connection;
+ this.redisClient = redisClient;
+ }
+
+ @Override
+ public ResultSet executeQuery(String sql) throws SQLException {
+ LOGGER.log("executeQuery(%s)", sql);
+
+ this.checkClosed();
+
+ String[] result = this.redisClient.sendCommand(sql);
+ return new RedisResultSet(result, this);
+ }
+
+
+ @Override
+ public int executeUpdate(String sql) throws SQLException {
+ this.checkClosed();
+
+ String[] result = this.redisClient.sendCommand(sql);
+ return result.length;
+ }
+
+ @Override
+ public void close() throws SQLException {
+ LOGGER.log("close()");
+ if (isClosed) {
+ LOGGER.log("Statement has been closed.");
+ return;
+ }
+ if (this.resultSet != null && !this.resultSet.isClosed()) {
+ this.resultSet.close();
+ }
+ isClosed = true;
+ }
+
+ @Override
+ public int getMaxFieldSize() throws SQLException {
+ this.checkClosed();
+ return Integer.MAX_VALUE;
+ }
+
+ @Override
+ public void setMaxFieldSize(int max) throws SQLException {
+ this.checkClosed();
+ // do nothing
+ }
+
+ @Override
+ public int getMaxRows() throws SQLException {
+ this.checkClosed();
+ return 0;
+ }
+
+ @Override
+ public void setMaxRows(int max) throws SQLException {
+ this.checkClosed();
+ // do nothing
+ }
+
+ @Override
+ public void setEscapeProcessing(boolean enable) throws SQLException {
+ this.checkClosed();
+ // do nothing
+ }
+
+ @Override
+ public int getQueryTimeout() throws SQLException {
+ this.checkClosed();
+ return 0;
+ }
+
+ @Override
+ public void setQueryTimeout(int seconds) throws SQLException {
+ LOGGER.log("setQueryTimeout..");
+ }
+
+ @Override
+ public void cancel() throws SQLException {
+ throw new SQLFeatureNotSupportedException("cancel not implemented");
+ }
+
+ @Override
+ public SQLWarning getWarnings() throws SQLException {
+ LOGGER.log("getWarnings returns null");
+ return null;
+ }
+
+ @Override
+ public void clearWarnings() throws SQLException {
+ this.checkClosed();
+ }
+
+ @Override
+ public void setCursorName(String name) throws SQLException {
+ LOGGER.log("setCursorName not implemented");
+ throw new SQLFeatureNotSupportedException("setCursorName not implemented");
+ }
+
+ @Override
+ public boolean execute(String sql) throws SQLException {
+ this.checkClosed();
+
+ String[] result = this.redisClient.sendCommand(sql);
+ this.resultSet = new RedisResultSet(result, this);
+
+ return true;
+ }
+
+ @Override
+ public ResultSet getResultSet() throws SQLException {
+ this.checkClosed();
+ return this.resultSet;
+ }
+
+ @Override
+ public int getUpdateCount() throws SQLException {
+ this.checkClosed();
+ LOGGER.log("getUpdateCount()");
+ // 原因在父类
+ return -1;
+ }
+
+ @Override
+ public boolean getMoreResults() throws SQLException {
+ this.checkClosed();
+ LOGGER.log("getMoreResults()");
+ return this.getMoreResults(CLOSE_CURRENT_RESULT);
+ }
+
+ @Override
+ public void setFetchDirection(int direction) throws SQLException {
+ this.checkClosed();
+ LOGGER.log("setFetchDirection not implemented");
+ throw new SQLFeatureNotSupportedException("setFetchDirection not implemented");
+ }
+
+ @Override
+ public int getFetchDirection() throws SQLException {
+ this.checkClosed();
+ return java.sql.ResultSet.FETCH_FORWARD;
+ }
+
+ @Override
+ public void setFetchSize(int rows) throws SQLException {
+ this.checkClosed();
+ this.fetchSize = rows;
+ }
+
+ @Override
+ public int getFetchSize() throws SQLException {
+ this.checkClosed();
+ return this.fetchSize;
+ }
+
+ @Override
+ public int getResultSetConcurrency() throws SQLException {
+ this.checkClosed();
+ return ResultSet.CONCUR_READ_ONLY;
+ }
+
+ @Override
+ public int getResultSetType() throws SQLException {
+ this.checkClosed();
+ return ResultSet.TYPE_FORWARD_ONLY;
+ }
+
+ @Override
+ public void addBatch(String sql) throws SQLException {
+ this.checkClosed();
+ LOGGER.log("addBatch not implemented");
+ throw new SQLFeatureNotSupportedException("addBatch not implemented");
+ }
+
+ @Override
+ public void clearBatch() throws SQLException {
+ this.checkClosed();
+ LOGGER.log("clearBatch not implemented");
+ throw new SQLFeatureNotSupportedException("addBatch not implemented");
+ }
+
+ @Override
+ public int[] executeBatch() throws SQLException {
+ this.checkClosed();
+ LOGGER.log("executeBatch not implemented");
+ throw new SQLFeatureNotSupportedException("executeBatch not implemented");
+ }
+
+ @Override
+ public Connection getConnection() throws SQLException {
+ this.checkClosed();
+ return this.connection;
+ }
+
+ @Override
+ public boolean getMoreResults(int current) throws SQLException {
+ this.checkClosed();
+ LOGGER.log("getMoreResults(%s)", current);
+ return false;
+ }
+
+ @Override
+ public ResultSet getGeneratedKeys() throws SQLException {
+ LOGGER.log("getGeneratedKeys not implemented");
+ throw new SQLFeatureNotSupportedException("getGeneratedKeys not implemented");
+ }
+
+ @Override
+ public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
+ this.executeUpdate(sql);
+ return 0;
+ }
+
+ @Override
+ public int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
+ return this.executeUpdate(sql, 0);
+ }
+
+ @Override
+ public int executeUpdate(String sql, String[] columnNames) throws SQLException {
+ return this.executeUpdate(sql, 0);
+ }
+
+ @Override
+ public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
+ this.executeUpdate(sql);
+ return true;
+ }
+
+ @Override
+ public boolean execute(String sql, int[] columnIndexes) throws SQLException {
+ return this.execute(sql, 0);
+ }
+
+ @Override
+ public boolean execute(String sql, String[] columnNames) throws SQLException {
+ return this.execute(sql, 0);
+ }
+
+ @Override
+ public int getResultSetHoldability() throws SQLException {
+ this.checkClosed();
+ return ResultSet.HOLD_CURSORS_OVER_COMMIT;
+ }
+
+ @Override
+ public boolean isClosed() throws SQLException {
+ LOGGER.log("Statement isClosed = %s", isClosed);
+ return this.isClosed;
+ }
+
+ @Override
+ public void setPoolable(boolean poolable) throws SQLException {
+ LOGGER.log("setPoolable not implemented");
+ throw new SQLFeatureNotSupportedException("setPoolable not implemented");
+ }
+
+ @Override
+ public boolean isPoolable() throws SQLException {
+ this.checkClosed();
+ return false;
+ }
+
+ @Override
+ public void closeOnCompletion() throws SQLException {
+ this.checkClosed();
+ LOGGER.log("closeOnCompletion not implemented");
+ throw new SQLFeatureNotSupportedException("closeOnCompletion not implemented");
+ }
+
+ @Override
+ public boolean isCloseOnCompletion() throws SQLException {
+ this.checkClosed();
+ LOGGER.log("isCloseOnCompletion not implemented");
+ return false;
+ }
+
+ private void checkClosed() throws SQLException {
+ if (isClosed()) {
+ LOGGER.log("Statement is closed.");
+ throw new SQLException("Statement is closed.");
+ }
+ }
+
+ @Override
+ public T unwrap(Class iface) throws SQLException {
+ try {
+ return iface.cast(this);
+ } catch (ClassCastException cce) {
+ LOGGER.log("Unable to unwrap to %s", iface);
+ throw new SQLException("Unable to unwrap to " + iface);
+ }
+ }
+
+ @Override
+ public boolean isWrapperFor(Class> iface) throws SQLException {
+ return iface.isInstance(this);
+ }
+}
diff --git a/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/ServerVersion.java b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/ServerVersion.java
new file mode 100644
index 00000000..0c02fae3
--- /dev/null
+++ b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/ServerVersion.java
@@ -0,0 +1,29 @@
+package io.edurt.datacap.core;
+
+import lombok.Data;
+
+@Data
+public class ServerVersion {
+ private String origin;
+
+ private Integer major;
+ private Integer minor;
+ private Integer patch;
+
+ public ServerVersion(String origin) {
+ this.origin = origin;
+
+ String[] arr = origin.split("\\.");
+ for (int i = 0; i < arr.length; i++) {
+ String str = arr[i];
+ int v = Utils.isNumber(str) ? Integer.parseInt(str) : 0;
+ if (i == 0) {
+ this.major = v;
+ } else if (i == 1) {
+ minor = v;
+ } else if (i == 2) {
+ patch = v;
+ }
+ }
+ }
+}
diff --git a/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/Utils.java b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/Utils.java
new file mode 100644
index 00000000..ffb9475f
--- /dev/null
+++ b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/core/Utils.java
@@ -0,0 +1,90 @@
+package io.edurt.datacap.core;
+
+import java.io.BufferedReader;
+import java.io.StringReader;
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class Utils {
+ public static boolean isNumber(String str) {
+ if (str == null || str.length() == 0) {
+ return false;
+ }
+ for (int i = 0; i < str.length(); i++) {
+ boolean digit = Character.isDigit(str.charAt(i));
+ if (!digit) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static List toList(T[] arr) {
+ if (arr == null) {
+ return null;
+ }
+ return Arrays.stream(arr)
+ .collect(Collectors.toList());
+ }
+
+ public static Op parseSql(String rawSql) {
+ // for IDEA database tool only
+ if (rawSql.contains("SELECT 'keep alive'")) {
+ return new Op(rawSql, null, "PING", new String[0]);
+ }
+
+ // hints
+ List lines = new BufferedReader(new StringReader(rawSql))
+ .lines()
+ .collect(Collectors.toList());
+
+ List hintLines = new ArrayList<>();
+ List sqlLines = new ArrayList<>();
+ lines.forEach(line -> {
+ if (line.startsWith("--")) {
+ hintLines.add(line);
+ } else {
+ sqlLines.add(line);
+ }
+ });
+
+ List hints = hintLines
+ .stream()
+ .map(line -> {
+ String hintStr = line.replace("--", "")
+ .replaceAll(" ", "");
+ String[] arr = hintStr.split(":");
+ return new Hint(HintKey.fromString(arr[0]), arr[1]);
+ }).collect(Collectors.toList());
+
+
+ // sql to execute
+ StringBuilder sb = new StringBuilder();
+ sqlLines.forEach(sb::append);
+
+ String sql = sb.toString();
+
+ String[] arr = sql.split(" ");
+
+ String commandString = arr[0];
+
+ if (arr.length == 1) {
+ return new Op(rawSql, hints, commandString, new String[0]);
+ } else {
+ String[] commandParams = Arrays.copyOfRange(arr, 1, arr.length);
+ return new Op(rawSql, hints, commandString, commandParams);
+ }
+ }
+
+ public static Map parseQueryStringToMap(String queryString) {
+ String[] params = queryString.split("&");
+ Map map = new HashMap<>();
+ for (String param : params) {
+ String[] p = param.split("=");
+ if (p.length == 2) {
+ map.put(p[0], p[1]);
+ }
+ }
+ return map;
+ }
+}
diff --git a/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/AbstractRedisClient.java b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/AbstractRedisClient.java
new file mode 100644
index 00000000..61e7d3c4
--- /dev/null
+++ b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/AbstractRedisClient.java
@@ -0,0 +1,83 @@
+package io.edurt.datacap.driver;
+
+import io.edurt.datacap.core.Hint;
+import io.edurt.datacap.core.Logger;
+import io.edurt.datacap.core.Op;
+import io.edurt.datacap.core.RedisClient;
+import io.edurt.datacap.core.Utils;
+import redis.clients.jedis.Protocol;
+import redis.clients.jedis.util.SafeEncoder;
+
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Stream;
+
+public abstract class AbstractRedisClient implements RedisClient
+{
+ public static final Logger LOGGER = new Logger(AbstractRedisClient.class);
+
+ @Override
+ public String[] sendCommand(String sql) throws SQLException {
+ try {
+ Op op = Utils.parseSql(sql);
+
+ Object result = this.sendCommand(op);
+
+ return this.decodeResult(sql, result, op.getHints());
+ } catch (Throwable e) {
+ throw new SQLException(e);
+ }
+ }
+
+ protected abstract Object sendCommand(Op op);
+
+ protected Protocol.Command convertCommand(String commandString) {
+ return Arrays.stream(Protocol.Command.values())
+ .filter(t -> {
+ String string = t.toString();
+ return string.equalsIgnoreCase(commandString);
+ })
+ .findFirst()
+ .orElseThrow(() -> new RuntimeException(
+ String.format("command invalided. commandString = %s", commandString)
+ ));
+ }
+
+
+ /**
+ * hint:
+ * -- decoder:jdk
+ * TODO
+ *
+ * @param sql
+ * @param originResult
+ * @param hints
+ * @return
+ */
+ protected String[] decodeResult(String sql, Object originResult, List hints) {
+ String[] decodedResult;
+ if (originResult == null) {
+ decodedResult = new String[]{null};
+ } else if (originResult.getClass().isArray()) {
+ String decoded = SafeEncoder.encode((byte[]) originResult);
+ decodedResult = Stream.of(decoded)
+ .toArray(String[]::new);
+
+ } else if (originResult instanceof Collection) {
+ List> list = (List>) originResult;
+ decodedResult = list.stream()
+ .map(t -> SafeEncoder.encode((byte[]) t))
+ .toArray(String[]::new);
+
+ } else {
+ LOGGER.log("cannot decode result. originResult = %s", originResult);
+ decodedResult = Stream.of(originResult.toString())
+ .toArray(String[]::new);
+ }
+ LOGGER.log("decode success. sql = %s, originResult = %s, decodedResult = %s",
+ sql, originResult, Utils.toList(decodedResult));
+ return decodedResult;
+ }
+}
diff --git a/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/cluster/JedisRedisClusterClient.java b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/cluster/JedisRedisClusterClient.java
new file mode 100644
index 00000000..9a8bf877
--- /dev/null
+++ b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/cluster/JedisRedisClusterClient.java
@@ -0,0 +1,56 @@
+package io.edurt.datacap.driver.cluster;
+
+import io.edurt.datacap.driver.AbstractRedisClient;
+import io.edurt.datacap.core.Hint;
+import io.edurt.datacap.core.HintKey;
+import io.edurt.datacap.core.Op;
+import lombok.RequiredArgsConstructor;
+import redis.clients.jedis.JedisCluster;
+import redis.clients.jedis.Protocol;
+
+import java.sql.SQLException;
+import java.util.List;
+
+@RequiredArgsConstructor
+public class JedisRedisClusterClient extends AbstractRedisClient {
+ private final JedisCluster jedisCluster;
+
+ @Override
+ protected Object sendCommand(Op op) {
+ String rawSql = op.getOriginSql();
+ String commandString = op.getCommand();
+ String[] params = op.getParams();
+ List hints = op.getHints();
+
+ try {
+ Protocol.Command command = this.convertCommand(commandString);
+
+ String sampleKey = hints.stream()
+ .filter(hint -> hint.getKey().equals(HintKey.sample_key))
+ .findFirst()
+ .map(Hint::getValue)
+ .orElse(null);
+
+ Object result;
+ if (params == null || params.length == 0) {
+ result = this.jedisCluster.sendCommand(sampleKey, command);
+ } else {
+ result = this.jedisCluster.sendCommand(sampleKey, command, params);
+ }
+ return result;
+ } catch (Throwable e) {
+ LOGGER.log("command `%s` cannot execute.", rawSql);
+ throw new RuntimeException(String.format("command `%s` cannot execute.", rawSql));
+ }
+ }
+
+ @Override
+ public void select(int dbIndex) throws SQLException {
+ throw new SQLException("Redis Cluster does not support this operation");
+ }
+
+ @Override
+ public void close() {
+ this.jedisCluster.close();
+ }
+}
diff --git a/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/cluster/RedisClusterDriver.java b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/cluster/RedisClusterDriver.java
new file mode 100644
index 00000000..688ac0be
--- /dev/null
+++ b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/cluster/RedisClusterDriver.java
@@ -0,0 +1,91 @@
+package io.edurt.datacap.driver.cluster;
+
+import io.edurt.datacap.core.Logger;
+import io.edurt.datacap.core.RedisConnection;
+import io.edurt.datacap.driver.conf.RedisClusterConnectionInfo;
+import io.edurt.datacap.driver.redis.RedisDriver;
+import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+import redis.clients.jedis.JedisCluster;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.sql.*;
+import java.util.Properties;
+
+public class RedisClusterDriver implements Driver {
+ private final static Logger LOGGER = new Logger(RedisDriver.class);
+
+ private static final String REDIS_CLUSTER_JDBC_PREFIX = "jdbc:redis-cluster:";
+
+ static {
+ try {
+ DriverManager.registerDriver(new RedisClusterDriver());
+ } catch (Exception e) {
+ LOGGER.log("Can't register driver!");
+ throw new RuntimeException("Can't register driver!", e);
+ }
+ }
+
+ @Override
+ public Connection connect(String url, Properties info) throws SQLException {
+ if (!this.acceptsURL(url)) {
+ LOGGER.log("wrong url. url is %s", url);
+ return null;
+ }
+ if (info == null) {
+ info = new Properties();
+ }
+
+ String rawUrl = url.replaceFirst("jdbc:", "");
+ RedisClusterConnectionInfo connectionInfo = new RedisClusterConnectionInfo(rawUrl, info);
+
+ JedisCluster jedisCluster = new JedisCluster(
+ connectionInfo.getNodes(),
+ connectionInfo.getTimeout(),
+ connectionInfo.getTimeout(),
+ connectionInfo.getMaxAttempts(),
+ connectionInfo.getUsername(),
+ connectionInfo.getPassword(),
+ null,
+ new GenericObjectPoolConfig<>()
+ );
+ JedisRedisClusterClient jedisRedisClusterClient = new JedisRedisClusterClient(jedisCluster);
+
+ return new RedisConnection(jedisRedisClusterClient, "0", info);
+ }
+
+ @Override
+ public boolean acceptsURL(String url) throws SQLException {
+ return url.toLowerCase().startsWith(REDIS_CLUSTER_JDBC_PREFIX);
+ }
+
+ @Override
+ public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {
+ return new DriverPropertyInfo[0];
+ }
+
+ @Override
+ public int getMajorVersion() {
+ return 0;
+ }
+
+ @Override
+ public int getMinorVersion() {
+ return 0;
+ }
+
+ @Override
+ public boolean jdbcCompliant() {
+ return false;
+ }
+
+ @Override
+ public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException {
+ return null;
+ }
+
+ public static void main(String[] args) throws URISyntaxException {
+ URI uri = new URI("cluster:///?hosts=localhost:3306");
+ System.out.println(uri.getQuery());
+ }
+}
diff --git a/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/conf/BaseConnectionInfo.java b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/conf/BaseConnectionInfo.java
new file mode 100644
index 00000000..a10df81a
--- /dev/null
+++ b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/conf/BaseConnectionInfo.java
@@ -0,0 +1,29 @@
+package io.edurt.datacap.driver.conf;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Properties;
+
+@Data
+@NoArgsConstructor
+public class BaseConnectionInfo {
+ protected String username;
+ protected String password;
+ protected boolean ssl;
+ protected int timeout;
+
+ public BaseConnectionInfo(Properties info) {
+ String username = info.getProperty("user");
+ String password = info.getProperty("password");
+ String sslString = info.getProperty("ssl");
+ Object timeoutString = info.getOrDefault("timeout", "1000");
+
+ boolean ssl = "on".equalsIgnoreCase(sslString) || "true".equalsIgnoreCase(sslString);
+
+ this.username = username;
+ this.password = password;
+ this.timeout = Integer.parseInt(timeoutString.toString());
+ this.ssl = ssl;
+ }
+}
diff --git a/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/conf/RedisClusterConnectionInfo.java b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/conf/RedisClusterConnectionInfo.java
new file mode 100644
index 00000000..71c1420d
--- /dev/null
+++ b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/conf/RedisClusterConnectionInfo.java
@@ -0,0 +1,51 @@
+package io.edurt.datacap.driver.conf;
+
+import io.edurt.datacap.core.Logger;
+import io.edurt.datacap.core.Utils;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import redis.clients.jedis.HostAndPort;
+import redis.clients.jedis.JedisCluster;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+public class RedisClusterConnectionInfo extends BaseConnectionInfo {
+ public static final Logger LOGGER = new Logger(RedisClusterConnectionInfo.class);
+
+ private Set nodes;
+ private int maxAttempts;
+
+ public RedisClusterConnectionInfo(String rawUrl, Properties info) {
+ super((info));
+ try {
+ URI uri = new URI(rawUrl);
+ Object maxAttemptsString = info.getOrDefault("maxAttempts", JedisCluster.DEFAULT_MAX_ATTEMPTS);
+ int maxAttempts = Integer.parseInt(maxAttemptsString.toString());
+
+ String query = uri.getQuery();
+ Map paramMap = Utils.parseQueryStringToMap(query);
+ String host = paramMap.get("host");
+ String[] hosts = host.split(";");
+
+ this.nodes = Arrays.stream(hosts)
+ .map(h -> {
+ String[] split = h.split(":");
+ return new HostAndPort(split[0], Integer.parseInt(split[1]));
+ }).collect(Collectors.toSet());
+
+ this.maxAttempts = maxAttempts;
+ } catch (Exception e) {
+ LOGGER.log("Cannot parse JDBC URL %s", rawUrl);
+ throw new RuntimeException("Cannot parse JDBC URL: " + rawUrl, e);
+ }
+ }
+}
diff --git a/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/conf/RedisConnectionInfo.java b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/conf/RedisConnectionInfo.java
new file mode 100644
index 00000000..c70f6e46
--- /dev/null
+++ b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/conf/RedisConnectionInfo.java
@@ -0,0 +1,41 @@
+package io.edurt.datacap.driver.conf;
+
+import io.edurt.datacap.core.Logger;
+import lombok.*;
+
+import java.net.URI;
+import java.util.Properties;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class RedisConnectionInfo extends BaseConnectionInfo {
+ private final static Logger LOGGER = new Logger(RedisConnectionInfo.class);
+
+ private String host;
+ private int port;
+ private int dbIndex;
+
+ public RedisConnectionInfo(String rawUrl, Properties info) {
+ super(info);
+ try {
+ URI uri = new URI(rawUrl);
+
+ String host = uri.getHost() != null ? uri.getHost() : "localhost";
+ int port = uri.getPort() > 0 ? uri.getPort() : 6379;
+
+ String path = uri.getPath();
+ int dbIndex = 0;
+ if (path != null && path.length() > 1) {
+ dbIndex = Integer.parseInt(path.replaceAll("/", ""));
+ }
+
+ this.host = host;
+ this.port = port;
+ this.dbIndex = dbIndex;
+
+ } catch (Exception e) {
+ LOGGER.log("Cannot parse JDBC URL %s", rawUrl);
+ throw new RuntimeException("Cannot parse JDBC URL: " + rawUrl, e);
+ }
+ }
+}
diff --git a/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/redis/JedisRedisClient.java b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/redis/JedisRedisClient.java
new file mode 100644
index 00000000..92fb66dc
--- /dev/null
+++ b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/redis/JedisRedisClient.java
@@ -0,0 +1,52 @@
+package io.edurt.datacap.driver.redis;
+
+import io.edurt.datacap.driver.AbstractRedisClient;
+import io.edurt.datacap.core.Logger;
+import io.edurt.datacap.core.Op;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.Protocol;
+
+public class JedisRedisClient extends AbstractRedisClient {
+ public static final Logger LOGGER = new Logger(JedisRedisClient.class);
+
+ private final Jedis jedis;
+
+ public JedisRedisClient(Jedis jedis) {
+ this.jedis = jedis;
+ }
+
+ @Override
+ protected synchronized Object sendCommand(Op op) {
+ String rawSql = op.getOriginSql();
+ String commandString = op.getCommand();
+ String[] params = op.getParams();
+
+ int db = -1;
+ try {
+ db = jedis.getDB();
+ Protocol.Command command = this.convertCommand(commandString);
+
+ Object result;
+ if (params == null || params.length == 0) {
+ result = this.jedis.sendCommand(command);
+ } else {
+ result = this.jedis.sendCommand(command, params);
+ }
+ return result;
+ } catch (Throwable e) {
+ LOGGER.log("command on db %s `%s` cannot execute.", db, rawSql);
+ throw new RuntimeException(String.format("command on db %s `%s` cannot execute.", db, rawSql));
+ }
+ }
+
+ @Override
+ public synchronized void select(int dbIndex) {
+ this.jedis.select(dbIndex);
+ }
+
+ @Override
+ public synchronized void close() {
+ LOGGER.log("close()");
+ this.jedis.close();
+ }
+}
diff --git a/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/redis/RedisDriver.java b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/redis/RedisDriver.java
new file mode 100644
index 00000000..bab01191
--- /dev/null
+++ b/driver/datacap-driver-redis/src/main/java/io/edurt/datacap/driver/redis/RedisDriver.java
@@ -0,0 +1,100 @@
+package io.edurt.datacap.driver.redis;
+
+import io.edurt.datacap.core.Logger;
+import io.edurt.datacap.core.RedisConnection;
+import io.edurt.datacap.driver.conf.RedisConnectionInfo;
+import redis.clients.jedis.Jedis;
+
+import java.sql.*;
+import java.util.Properties;
+
+public class RedisDriver implements Driver {
+ private final static Logger LOGGER = new Logger(RedisDriver.class);
+
+ private static final String REDIS_JDBC_PREFIX = "jdbc:redis:";
+
+ static {
+ try {
+ DriverManager.registerDriver(new RedisDriver());
+ } catch (Exception e) {
+ LOGGER.log("Can't register driver!");
+ throw new RuntimeException("Can't register driver!", e);
+ }
+ }
+
+ @Override
+ public Connection connect(String url, Properties info) throws SQLException {
+ if (!this.acceptsURL(url)) {
+ LOGGER.log("wrong url. url is %s", url);
+ return null;
+ }
+ if (info == null) {
+ info = new Properties();
+ }
+
+ String rawUrl = url.replaceFirst("jdbc:", "");
+ RedisConnectionInfo redisConnectionInfo = new RedisConnectionInfo(rawUrl, info);
+
+ String host = redisConnectionInfo.getHost();
+ int port = redisConnectionInfo.getPort();
+ int dbIndex = redisConnectionInfo.getDbIndex();
+ int timeout = redisConnectionInfo.getTimeout();
+ boolean ssl = redisConnectionInfo.isSsl();
+ String username = redisConnectionInfo.getUsername();
+ String password = redisConnectionInfo.getPassword();
+
+ try {
+ final Jedis jedis = new Jedis(host, port, timeout, timeout, ssl);
+ jedis.connect();
+
+ if (username != null) {
+ jedis.auth(username, password);
+ } else if (password != null) {
+ jedis.auth(password);
+ }
+ if (dbIndex != 0) {
+ jedis.select(dbIndex);
+ }
+// if (clientName != null) {
+// jedis.clientSetname(clientName);
+// }
+
+ return new RedisConnection(new JedisRedisClient(jedis), dbIndex + "", info);
+ } catch (Exception e) {
+ LOGGER.log("Cannot init RedisConnection %s", e);
+ throw new SQLException("Cannot init RedisConnection", e);
+ }
+ }
+
+ @Override
+ public boolean acceptsURL(String url) throws SQLException {
+ return url.toLowerCase().startsWith(REDIS_JDBC_PREFIX);
+ }
+
+ @Override
+ public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {
+ return new DriverPropertyInfo[0];
+ }
+
+ @Override
+ public int getMajorVersion() {
+ return 1;
+ }
+
+ @Override
+ public int getMinorVersion() {
+ return 0;
+ }
+
+ @Override
+ public boolean jdbcCompliant() {
+ return false;
+ }
+
+ @Override
+ public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException {
+ // ref: com.mysql.cj.jdbc.NonRegisteringDriver.getParentLogger
+ LOGGER.log("getParentLogger not implemented");
+ throw new SQLFeatureNotSupportedException("getParentLogger not implemented");
+ }
+}
diff --git a/driver/datacap-driver-redis/src/main/resources/META-INF/services/java.sql.Driver b/driver/datacap-driver-redis/src/main/resources/META-INF/services/java.sql.Driver
new file mode 100644
index 00000000..c1aa6383
--- /dev/null
+++ b/driver/datacap-driver-redis/src/main/resources/META-INF/services/java.sql.Driver
@@ -0,0 +1,2 @@
+io.edurt.datacap.driver.redis.RedisDriver
+io.edurt.datacap.driver.cluster.RedisClusterDriver
\ No newline at end of file
diff --git a/driver/datacap-driver-redis/src/test/java/io/edurt/datacap/RedisTest.java b/driver/datacap-driver-redis/src/test/java/io/edurt/datacap/RedisTest.java
new file mode 100644
index 00000000..55893560
--- /dev/null
+++ b/driver/datacap-driver-redis/src/test/java/io/edurt/datacap/RedisTest.java
@@ -0,0 +1,82 @@
+package io.edurt.datacap;
+
+import io.edurt.datacap.core.Logger;
+import io.edurt.datacap.core.RedisStatement;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+import java.sql.*;
+
+public class RedisTest {
+ private final static Logger LOGGER = new Logger(RedisStatement.class);
+
+ public static void main(String[] args) throws SQLException, ClassNotFoundException {
+ Class.forName("com.itmuch.redis.jdbc.redis.RedisDriver");
+
+ Connection connection = DriverManager.getConnection("jdbc:redis://localhost:6379/0");
+ Statement statement = connection.createStatement();
+
+ connection.setSchema("11");
+ ResultSet rs = statement.executeQuery("get a");
+ while (rs.next()) {
+ String string = rs.getString(0);
+ System.out.println(string);
+ }
+
+// statement.execute("set a b");
+// ResultSet rs = statement.executeQuery("get a");
+// while (rs.next()) {
+// LOGGER.log("rs1:" + rs.getString(0));
+// }
+//
+ ResultSet resultSet = statement.executeQuery("keys *");
+ while (resultSet.next()) {
+ LOGGER.log(resultSet.getString(0));
+ }
+
+ connection.setSchema("11");
+ ResultSet resultSet2 = statement.executeQuery("set ab99 ab88");
+ while (resultSet2.next()) {
+ LOGGER.log(resultSet.getString(0));
+ }
+
+ resultSet.close();
+ statement.close();
+ connection.close();
+
+// statement.execute("ZADD runoobkey 2 mongodb");
+// statement.execute("ZADD runoobkey 3 elasticsearch");
+// statement.execute("ZADD runoobkey 4 mysql");
+//
+// ResultSet rs2 = statement.executeQuery("ZRANGE runoobkey 0 10 WITHSCORES");
+// while (rs2.next()) {
+// LOGGER.log("rs2:" + rs2.getString(0));
+// }
+//
+// statement.execute("HMSET myhash field1 field2");
+// ResultSet rs3 = statement.executeQuery("HGETALL myhash");
+// while (rs3.next()) {
+// LOGGER.log("rs3:" + rs3.getString(0));
+// }
+
+// ResultSet rs4 = statement.executeQuery("get user");
+// while (rs4.next()) {
+// LOGGER.log("rs4:" + rs4.getString(0));
+// }
+ }
+}
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+class User {
+ private String name;
+ private Short age;
+ private String email;
+ private BigDecimal money;
+
+}
diff --git a/plugin/datacap-jdbc-redis/pom.xml b/plugin/datacap-jdbc-redis/pom.xml
index 7ad736dc..f25b58ad 100644
--- a/plugin/datacap-jdbc-redis/pom.xml
+++ b/plugin/datacap-jdbc-redis/pom.xml
@@ -14,7 +14,6 @@
DataCap - Redis
- 0.0.1
jdbc-redis
@@ -25,13 +24,13 @@
provided
- commons-beanutils
- commons-beanutils
+ io.edurt.datacap
+ datacap-driver-redis
+ provided
- com.itmuch.redis
- redis-jdbc-driver
- ${redis-jdbc.version}
+ commons-beanutils
+ commons-beanutils
diff --git a/plugin/datacap-jdbc-redis/src/main/java/io/edurt/datacap/plugin/jdbc/redis/RedisPlugin.java b/plugin/datacap-jdbc-redis/src/main/java/io/edurt/datacap/plugin/jdbc/redis/RedisPlugin.java
index a773c3f4..f5d53a73 100644
--- a/plugin/datacap-jdbc-redis/src/main/java/io/edurt/datacap/plugin/jdbc/redis/RedisPlugin.java
+++ b/plugin/datacap-jdbc-redis/src/main/java/io/edurt/datacap/plugin/jdbc/redis/RedisPlugin.java
@@ -50,7 +50,7 @@ public class RedisPlugin
this.response = new Response();
this.jdbcConfigure = new JdbcConfigure();
BeanUtils.copyProperties(this.jdbcConfigure, configure);
- this.jdbcConfigure.setJdbcDriver("com.itmuch.redis.jdbc.redis.RedisDriver");
+ this.jdbcConfigure.setJdbcDriver("io.edurt.datacap.driver.redis.RedisDriver");
this.jdbcConfigure.setJdbcType("redis");
this.jdbcConfigure.setUsername(configure.getUsername().map(u -> "".equals(u) ? null : u));
this.jdbcConfigure.setPassword(configure.getPassword().map(pwd -> "".equals(pwd) ? null : pwd));
diff --git a/pom.xml b/pom.xml
index e5fe8cb1..e8e20796 100644
--- a/pom.xml
+++ b/pom.xml
@@ -49,6 +49,7 @@
plugin/datacap-jdbc-sqlserver
plugin/datacap-jdbc-tdengine
plugin/datacap-jdbc-trino
+ driver/datacap-driver-redis
DataCap
@@ -107,6 +108,7 @@
2.0.3
31.1-jre
1.9.4
+ 4.3.1
3.1.1
3.0.0
3.0.5
@@ -211,6 +213,11 @@
datacap-parser
${project.version}
+
+ io.edurt.datacap
+ datacap-driver-redis
+ ${project.version}
+