Skip to content

Commit 5a7e158

Browse files
committed
Support of Statement.setMaxFieldSize
Add a capability to limit resultSet values of character or binary types. Closes: #189
1 parent 05ce89d commit 5a7e158

File tree

7 files changed

+173
-32
lines changed

7 files changed

+173
-32
lines changed

src/main/java/org/tarantool/jdbc/SQLResultSet.java

+22-4
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,14 @@
3939
public class SQLResultSet implements ResultSet {
4040

4141
private CursorIterator<List<Object>> iterator;
42-
private SQLResultSetMetaData metaData;
42+
private TarantoolResultSetMetaData metaData;
4343

4444
private Map<String, Integer> columnByNameLookups;
4545
private boolean lastColumnWasNull;
4646

4747
private final TarantoolStatement statement;
4848
private final int maxRows;
49+
private final int maxFieldSize;
4950

5051
private AtomicBoolean isClosed = new AtomicBoolean(false);
5152

@@ -59,7 +60,8 @@ public SQLResultSet(SQLResultHolder holder, TarantoolStatement ownerStatement) t
5960
scrollType = statement.getResultSetType();
6061
concurrencyLevel = statement.getResultSetConcurrency();
6162
holdability = statement.getResultSetHoldability();
62-
this.maxRows = statement.getMaxRows();
63+
maxRows = statement.getMaxRows();
64+
maxFieldSize = statement.getMaxFieldSize();
6365

6466
List<List<Object>> fetchedRows = holder.getRows();
6567
List<List<Object>> rows = maxRows == 0 || maxRows >= fetchedRows.size()
@@ -127,7 +129,13 @@ public boolean wasNull() throws SQLException {
127129
@Override
128130
public String getString(int columnIndex) throws SQLException {
129131
Object raw = getRaw(columnIndex);
130-
return raw == null ? null : String.valueOf(raw);
132+
if (raw == null) {
133+
return null;
134+
}
135+
String value = String.valueOf(raw);
136+
return (maxFieldSize > 0 && value.length() > maxFieldSize && metaData.isTrimmable(columnIndex))
137+
? value.substring(0, maxFieldSize)
138+
: value;
131139
}
132140

133141
@Override
@@ -232,7 +240,17 @@ public BigDecimal getBigDecimal(String columnLabel) throws SQLException {
232240

233241
@Override
234242
public byte[] getBytes(int columnIndex) throws SQLException {
235-
return (byte[]) getRaw(columnIndex);
243+
Object raw = getRaw(columnIndex);
244+
if (raw == null) {
245+
return null;
246+
}
247+
byte[] bytes = (byte[]) raw;
248+
if (maxFieldSize > 0 && bytes.length > maxFieldSize && metaData.isTrimmable(columnIndex)) {
249+
byte[] trimmedBytes = new byte[maxFieldSize];
250+
System.arraycopy(bytes, 0, trimmedBytes, 0, maxFieldSize);
251+
return trimmedBytes;
252+
}
253+
return bytes;
236254
}
237255

238256
@Override

src/main/java/org/tarantool/jdbc/SQLResultSetMetaData.java

+9-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import java.sql.SQLNonTransientException;
99
import java.util.List;
1010

11-
public class SQLResultSetMetaData implements ResultSetMetaData {
11+
public class SQLResultSetMetaData implements TarantoolResultSetMetaData {
1212

1313
private final List<SqlProtoUtils.SQLMetaData> sqlMetadata;
1414
private final boolean readOnly;
@@ -181,7 +181,8 @@ public boolean isWrapperFor(Class<?> type) throws SQLException {
181181
return type.isAssignableFrom(this.getClass());
182182
}
183183

184-
void checkColumnIndex(int columnIndex) throws SQLException {
184+
@Override
185+
public void checkColumnIndex(int columnIndex) throws SQLException {
185186
if (columnIndex < 1 || columnIndex > getColumnCount()) {
186187
throw new SQLNonTransientException(
187188
String.format("Column index %d is out of range. Max index is %d", columnIndex, getColumnCount()),
@@ -190,6 +191,12 @@ void checkColumnIndex(int columnIndex) throws SQLException {
190191
}
191192
}
192193

194+
@Override
195+
public boolean isTrimmable(int columnIndex) throws SQLException {
196+
checkColumnIndex(columnIndex);
197+
return sqlMetadata.get(columnIndex - 1).getType().isTrimmable();
198+
}
199+
193200
@Override
194201
public String toString() {
195202
return "SQLResultSetMetaData{" +

src/main/java/org/tarantool/jdbc/SQLStatement.java

+10-3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public class SQLStatement implements TarantoolStatement {
3838
private final int resultSetHoldability;
3939

4040
private int maxRows;
41+
private int maxFieldSize;
4142

4243
/**
4344
* Query timeout in millis.
@@ -114,12 +115,18 @@ public void close() throws SQLException {
114115

115116
@Override
116117
public int getMaxFieldSize() throws SQLException {
117-
throw new SQLFeatureNotSupportedException();
118+
return maxFieldSize;
118119
}
119120

120121
@Override
121-
public void setMaxFieldSize(int max) throws SQLException {
122-
throw new SQLFeatureNotSupportedException();
122+
public void setMaxFieldSize(int size) throws SQLException {
123+
if (size < 0) {
124+
throw new SQLException(
125+
"The max field size must be positive or zero",
126+
SQLStates.INVALID_PARAMETER_VALUE.getSqlState()
127+
);
128+
}
129+
maxFieldSize = size;
123130
}
124131

125132
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package org.tarantool.jdbc;
2+
3+
import java.sql.ResultSetMetaData;
4+
import java.sql.SQLException;
5+
6+
/**
7+
* Tarantool specific result set metadata extensions.
8+
*/
9+
public interface TarantoolResultSetMetaData extends ResultSetMetaData {
10+
11+
/**
12+
* Checks a column accessibility.
13+
*
14+
* @param columnIndex column number
15+
* @throws SQLException if column is not accessible by the index
16+
*/
17+
void checkColumnIndex(int columnIndex) throws SQLException;
18+
19+
/**
20+
* Determines whether a column type can be trimmed or not.
21+
* This status depends on JDBC types that are defined as
22+
* trimmable such as {@literal VARCHAR} or {@literal BINARY}
23+
*
24+
* @param columnIndex column number
25+
* @return {@literal true} if the column is trimmable
26+
* @throws SQLException if column is not accessible by the index
27+
*
28+
* @see java.sql.Statement#setMaxFieldSize(int)
29+
*/
30+
boolean isTrimmable(int columnIndex) throws SQLException;
31+
32+
}

src/main/java/org/tarantool/jdbc/type/JdbcType.java

+29-23
Original file line numberDiff line numberDiff line change
@@ -16,42 +16,44 @@
1616
*/
1717
public enum JdbcType {
1818

19-
UNKNOWN(Object.class, JDBCType.OTHER),
19+
UNKNOWN(Object.class, JDBCType.OTHER, false),
2020

21-
CHAR(String.class, JDBCType.CHAR),
22-
VARCHAR(String.class, JDBCType.VARCHAR),
23-
LONGVARCHAR(String.class, JDBCType.LONGNVARCHAR),
21+
CHAR(String.class, JDBCType.CHAR, true),
22+
VARCHAR(String.class, JDBCType.VARCHAR, true),
23+
LONGVARCHAR(String.class, JDBCType.LONGNVARCHAR, true),
2424

25-
NCHAR(String.class, JDBCType.NCHAR),
26-
NVARCHAR(String.class, JDBCType.NVARCHAR),
27-
LONGNVARCHAR(String.class, JDBCType.LONGNVARCHAR),
25+
NCHAR(String.class, JDBCType.NCHAR, true),
26+
NVARCHAR(String.class, JDBCType.NVARCHAR, true),
27+
LONGNVARCHAR(String.class, JDBCType.LONGNVARCHAR, true),
2828

29-
BINARY(byte[].class, JDBCType.BINARY),
30-
VARBINARY(byte[].class, JDBCType.VARBINARY),
31-
LONGVARBINARY(byte[].class, JDBCType.LONGVARBINARY),
29+
BINARY(byte[].class, JDBCType.BINARY, true),
30+
VARBINARY(byte[].class, JDBCType.VARBINARY, true),
31+
LONGVARBINARY(byte[].class, JDBCType.LONGVARBINARY, true),
3232

33-
BIT(Boolean.class, JDBCType.BIT),
34-
BOOLEAN(Boolean.class, JDBCType.BOOLEAN),
33+
BIT(Boolean.class, JDBCType.BIT, false),
34+
BOOLEAN(Boolean.class, JDBCType.BOOLEAN, false),
3535

36-
REAL(Float.class, JDBCType.REAL),
37-
FLOAT(Double.class, JDBCType.FLOAT),
38-
DOUBLE(Double.class, JDBCType.DOUBLE),
36+
REAL(Float.class, JDBCType.REAL, false),
37+
FLOAT(Double.class, JDBCType.FLOAT, false),
38+
DOUBLE(Double.class, JDBCType.DOUBLE, false),
3939

40-
TINYINT(Byte.class, JDBCType.TINYINT),
41-
SMALLINT(Short.class, JDBCType.SMALLINT),
42-
INTEGER(Integer.class, JDBCType.INTEGER),
43-
BIGINT(Long.class, JDBCType.BIGINT),
40+
TINYINT(Byte.class, JDBCType.TINYINT, false),
41+
SMALLINT(Short.class, JDBCType.SMALLINT, false),
42+
INTEGER(Integer.class, JDBCType.INTEGER, false),
43+
BIGINT(Long.class, JDBCType.BIGINT, false),
4444

45-
CLOB(Clob.class, JDBCType.CLOB),
46-
NCLOB(NClob.class, JDBCType.NCLOB),
47-
BLOB(Blob.class, JDBCType.BLOB);
45+
CLOB(Clob.class, JDBCType.CLOB, false),
46+
NCLOB(NClob.class, JDBCType.NCLOB, false),
47+
BLOB(Blob.class, JDBCType.BLOB, false);
4848

4949
private final Class<?> javaType;
5050
private final JDBCType targetJdbcType;
51+
private final boolean trimmable;
5152

52-
JdbcType(Class<?> javaType, JDBCType targetJdbcType) {
53+
JdbcType(Class<?> javaType, JDBCType targetJdbcType, boolean trimmable) {
5354
this.javaType = javaType;
5455
this.targetJdbcType = targetJdbcType;
56+
this.trimmable = trimmable;
5557
}
5658

5759
public Class<?> getJavaType() {
@@ -62,6 +64,10 @@ public JDBCType getTargetJdbcType() {
6264
return targetJdbcType;
6365
}
6466

67+
public boolean isTrimmable() {
68+
return trimmable;
69+
}
70+
6571
public int getTypeNumber() {
6672
return targetJdbcType.getVendorTypeNumber();
6773
}

src/main/java/org/tarantool/jdbc/type/TarantoolSqlType.java

+4
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ public boolean isCaseSensitive() {
9898
return tarantoolType.isCaseSensitive();
9999
}
100100

101+
public boolean isTrimmable() {
102+
return jdbcType.isTrimmable();
103+
}
104+
101105
public int getPrecision() {
102106
return tarantoolType.getPrecision();
103107
}

src/test/java/org/tarantool/jdbc/JdbcResultSetIT.java

+67
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import org.tarantool.ServerVersion;
1212
import org.tarantool.TarantoolTestHelper;
13+
import org.tarantool.util.SQLStates;
1314

1415
import org.junit.jupiter.api.AfterAll;
1516
import org.junit.jupiter.api.AfterEach;
@@ -370,4 +371,70 @@ public void testTraversal() throws SQLException {
370371
assertEquals(0, resultSet.getRow());
371372
}
372373

374+
@Test
375+
public void testMaxFieldSize() throws SQLException {
376+
assertEquals(0, stmt.getMaxFieldSize());
377+
378+
int expectedMaxSize = 256;
379+
stmt.setMaxFieldSize(expectedMaxSize);
380+
assertEquals(expectedMaxSize, stmt.getMaxFieldSize());
381+
}
382+
383+
@Test
384+
public void testNegativeMaxFieldSize() throws SQLException {
385+
SQLException error = assertThrows(SQLException.class, () -> stmt.setMaxFieldSize(-12));
386+
assertEquals(SQLStates.INVALID_PARAMETER_VALUE.getSqlState(), error.getSQLState());
387+
}
388+
389+
@Test
390+
public void testPositiveMaxFieldSize() throws SQLException {
391+
testHelper.executeSql("INSERT INTO test VALUES (1, 'greater-than-ten-characters-value')");
392+
393+
stmt.setMaxFieldSize(10);
394+
try (ResultSet resultSet = stmt.executeQuery("SELECT * FROM test WHERE id = 1")) {
395+
resultSet.next();
396+
assertEquals("greater-th", resultSet.getString(2));
397+
}
398+
}
399+
400+
@Test
401+
public void testMaxFieldSizeBiggerThanValue() throws SQLException {
402+
testHelper.executeSql("INSERT INTO test VALUES (1, 'less-than-one-hundred-characters-value')");
403+
404+
stmt.setMaxFieldSize(100);
405+
try (ResultSet resultSet = stmt.executeQuery("SELECT * FROM test WHERE id = 1")) {
406+
resultSet.next();
407+
assertEquals("less-than-one-hundred-characters-value", resultSet.getString(2));
408+
}
409+
}
410+
411+
@Test
412+
public void testMaxFieldSizeBinaryType() throws SQLException {
413+
testHelper.executeSql("CREATE TABLE test_bin(id INT PRIMARY KEY, val SCALAR)");
414+
testHelper.executeSql("INSERT INTO test_bin VALUES (1, X'6c6f6e672d62696e6172792d737472696e67')");
415+
416+
stmt.setMaxFieldSize(12);
417+
try (ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_bin WHERE id = 1")) {
418+
resultSet.next();
419+
assertEquals(12, resultSet.getBytes(2).length);
420+
}
421+
422+
testHelper.executeSql("DROP TABLE test_bin");
423+
}
424+
425+
@Test
426+
public void testMaxFieldSizeNotTrimmableType() throws SQLException {
427+
testHelper.executeSql("CREATE TABLE test_num(id INT PRIMARY KEY, val INT)");
428+
testHelper.executeSql("INSERT INTO test_num VALUES (1, 1234567890)");
429+
430+
String expectedUntrimmedValue = "1234567890";
431+
stmt.setMaxFieldSize(5);
432+
try (ResultSet resultSet = stmt.executeQuery("SELECT * FROM test_num WHERE id = 1")) {
433+
resultSet.next();
434+
assertEquals(expectedUntrimmedValue, resultSet.getString(2));
435+
}
436+
437+
testHelper.executeSql("DROP TABLE test_num");
438+
}
439+
373440
}

0 commit comments

Comments
 (0)