diff --git a/build.gradle b/build.gradle index 91cada3..e079fe1 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java' } -version '1.3.2' +version '1.3.3' sourceCompatibility = 1.8 diff --git a/driver/src/main/java/com/dbschema/CassandraBaseStatement.java b/driver/src/main/java/com/dbschema/CassandraBaseStatement.java index f55642f..658c875 100644 --- a/driver/src/main/java/com/dbschema/CassandraBaseStatement.java +++ b/driver/src/main/java/com/dbschema/CassandraBaseStatement.java @@ -35,9 +35,9 @@ public boolean isClosed() { return isClosed; } - boolean executeInner(com.datastax.driver.core.ResultSet resultSet) throws SQLException { + boolean executeInner(com.datastax.driver.core.ResultSet resultSet, boolean returnNullStrings) throws SQLException { try { - result = new CassandraResultSet(this, resultSet); + result = new CassandraResultSet(this, resultSet, returnNullStrings); if (!result.isQuery()) { result = null; return false; diff --git a/driver/src/main/java/com/dbschema/CassandraConnection.java b/driver/src/main/java/com/dbschema/CassandraConnection.java index a1e84b2..9fe09c6 100644 --- a/driver/src/main/java/com/dbschema/CassandraConnection.java +++ b/driver/src/main/java/com/dbschema/CassandraConnection.java @@ -9,14 +9,29 @@ import java.util.concurrent.Executor; public class CassandraConnection implements Connection { + /** + * This query retrieves Cassandra 2.x columns in DataGrip. + *

+ * DataGrip before 2019.3 version assumes that index_name column does not contain null values. + * Driver since v1.3.2 version returns null value if a string is null (used to return "null") + * It means that DataGrip <2019.3 and driver v1.3.2 are incompatible. + *

+ * To make driver and DG compatible driver will return "null" strings instead of null values + * for this particular query in PreparedStatement. + * See also https://youtrack.jetbrains.com/issue/DBE-9091 + */ + private static final String SELECT_COLUMNS_INTRO_QUERY = "SELECT column_name as name,\n validator,\n columnfamily_name as table_name,\n type,\n index_name,\n index_options,\n index_type,\n component_index as position\nFROM system.schema_columns\nWHERE keyspace_name = ?"; + private final Session session; private CassandraJdbcDriver driver; + private boolean returnNullStringsFromIntroQuery; private boolean isClosed = false; private boolean isReadOnly = false; - CassandraConnection(Session session, CassandraJdbcDriver cassandraJdbcDriver) { + CassandraConnection(Session session, CassandraJdbcDriver cassandraJdbcDriver, boolean returnNullStringsFromIntroQuery) { this.session = session; driver = cassandraJdbcDriver; + this.returnNullStringsFromIntroQuery = returnNullStringsFromIntroQuery; } public String getCatalog() throws SQLException { @@ -69,7 +84,7 @@ public Statement createStatement(int resultSetType, int resultSetConcurrency, in public PreparedStatement prepareStatement(String sql) throws SQLException { checkClosed(); try { - return new CassandraPreparedStatement(session, session.prepare(sql)); + return new CassandraPreparedStatement(session, session.prepare(sql), returnNullStringsFromIntroQuery || !SELECT_COLUMNS_INTRO_QUERY.equals(sql)); } catch (Throwable t) { throw new SQLException(t.getMessage(), t); } diff --git a/driver/src/main/java/com/dbschema/CassandraJdbcDriver.java b/driver/src/main/java/com/dbschema/CassandraJdbcDriver.java index d785b14..c320257 100644 --- a/driver/src/main/java/com/dbschema/CassandraJdbcDriver.java +++ b/driver/src/main/java/com/dbschema/CassandraJdbcDriver.java @@ -35,6 +35,8 @@ */ public class CassandraJdbcDriver implements Driver { + private static final String RETURN_NULL_STRINGS_FROM_INTRO_QUERY_KEY = "cassandra.jdbc.return.null.strings.from.intro.query"; + static { try { DriverManager.registerDriver(new CassandraJdbcDriver()); @@ -63,7 +65,8 @@ public Connection connect(String url, Properties info) throws SQLException { } catch (NoHostAvailableException | AuthenticationException | IllegalStateException e) { throw new SQLException(e.getMessage(), e); } - return new CassandraConnection(session, this); + boolean returnNullStringsFromIntroQuery = Boolean.parseBoolean(info.getProperty(RETURN_NULL_STRINGS_FROM_INTRO_QUERY_KEY)); + return new CassandraConnection(session, this, returnNullStringsFromIntroQuery); } catch (UnknownHostException e) { throw new SQLException(e.getMessage(), e); } @@ -116,7 +119,7 @@ public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) { } String getVersion() { - return "1.3.2"; + return "1.3.3"; } @Override diff --git a/driver/src/main/java/com/dbschema/CassandraPreparedStatement.java b/driver/src/main/java/com/dbschema/CassandraPreparedStatement.java index 34107a8..366ee1c 100644 --- a/driver/src/main/java/com/dbschema/CassandraPreparedStatement.java +++ b/driver/src/main/java/com/dbschema/CassandraPreparedStatement.java @@ -10,6 +10,7 @@ import java.math.BigDecimal; import java.net.URL; import java.sql.*; +import java.util.Arrays; import java.util.Calendar; import static com.dbschema.DateUtil.Direction; @@ -18,11 +19,13 @@ public class CassandraPreparedStatement extends CassandraBaseStatement implements PreparedStatement { private final com.datastax.driver.core.PreparedStatement preparedStatement; + private final boolean returnNullStrings; private Object[] params; - CassandraPreparedStatement(Session session, final com.datastax.driver.core.PreparedStatement preparedStatement) { + CassandraPreparedStatement(Session session, final com.datastax.driver.core.PreparedStatement preparedStatement, boolean returnNullStrings) { super(session); this.preparedStatement = preparedStatement; + this.returnNullStrings = returnNullStrings; } @Override @@ -64,7 +67,7 @@ public void clearBatch() throws SQLException { public int executeUpdate() throws SQLException { checkClosed(); try { - result = new CassandraResultSet(this, session.execute(bindParameters())); + result = new CassandraResultSet(this, session.execute(bindParameters()), returnNullStrings); if (result.isQuery()) { throw new SQLException("Not an update statement"); } @@ -241,7 +244,7 @@ public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQ public boolean execute() throws SQLException { checkClosed(); try { - return executeInner(session.execute(bindParameters())); + return executeInner(session.execute(bindParameters()), returnNullStrings); } catch (Throwable t) { throw new SQLException(t.getMessage(), t); } @@ -257,9 +260,7 @@ private BoundStatement bindParameters() { private void clearParams() { if (params == null) return; - for (int i = 0; i < params.length; i++) { - params[i] = null; - } + Arrays.fill(params, null); } @Override diff --git a/driver/src/main/java/com/dbschema/CassandraResultSet.java b/driver/src/main/java/com/dbschema/CassandraResultSet.java index 7344887..b5f3964 100644 --- a/driver/src/main/java/com/dbschema/CassandraResultSet.java +++ b/driver/src/main/java/com/dbschema/CassandraResultSet.java @@ -14,10 +14,10 @@ import java.nio.ByteBuffer; import java.sql.Date; import java.sql.*; -import java.text.ParseException; import java.util.*; -import static com.dbschema.DateUtil.*; +import static com.dbschema.DateUtil.Direction; +import static com.dbschema.DateUtil.considerTimeZone; public class CassandraResultSet implements ResultSet { @@ -26,12 +26,18 @@ public class CassandraResultSet implements ResultSet { private final Statement statement; private final com.datastax.driver.core.ResultSet dsResultSet; private final Iterator iterator; + private final boolean returnNullStrings; private Row currentRow; - public CassandraResultSet(Statement statement, com.datastax.driver.core.ResultSet dsResultSet) { + CassandraResultSet(Statement statement, com.datastax.driver.core.ResultSet dsResultSet, boolean returnNullStrings) { this.statement = statement; this.dsResultSet = dsResultSet; this.iterator = dsResultSet.iterator(); + this.returnNullStrings = returnNullStrings; + } + + CassandraResultSet(Statement statement, com.datastax.driver.core.ResultSet dsResultSet) { + this(statement, dsResultSet, true); } @Override @@ -70,11 +76,11 @@ public boolean wasNull() { @Override public String getString(int columnIndex) throws SQLException { checkClosed(); - if (currentRow != null) { - Object object = currentRow.getObject(columnIndex - 1); - return object == null ? null : String.valueOf(object); - } - throw new SQLException("Exhausted ResultSet."); + if (currentRow == null) throw new SQLException("Exhausted ResultSet."); + Object o = currentRow.getObject(columnIndex - 1); + return o == null ? + returnNullStrings ? null : "" : + String.valueOf(o); } @Override diff --git a/driver/src/main/java/com/dbschema/CassandraStatement.java b/driver/src/main/java/com/dbschema/CassandraStatement.java index 6579752..68b965c 100644 --- a/driver/src/main/java/com/dbschema/CassandraStatement.java +++ b/driver/src/main/java/com/dbschema/CassandraStatement.java @@ -50,7 +50,7 @@ public int executeUpdate(String sql) throws SQLException { public boolean execute(String sql) throws SQLException { checkClosed(); try { - return executeInner(session.execute(sql)); + return executeInner(session.execute(sql), true); } catch (Throwable t) { throw new SQLException(t.getMessage(), t); }