Skip to content

Commit

Permalink
FIR-33118: implemented blob and clob (#414)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexradzin authored May 28, 2024
1 parent 1cd9cf4 commit cc48926
Show file tree
Hide file tree
Showing 11 changed files with 873 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import javax.sql.rowset.serial.SerialBlob;
import javax.sql.rowset.serial.SerialClob;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
Expand Down Expand Up @@ -279,12 +279,12 @@ void shouldInsertAndSelectBlobClob() throws SQLException, IOException {
try (PreparedStatement statement = connection
.prepareStatement("INSERT INTO prepared_statement_test (sales, make, signature) VALUES (?,?,?)")) {
statement.setLong(1, car1.getSales());
statement.setClob(2, new SerialClob(car1.getMake().toCharArray()));
statement.setBlob(3, new SerialBlob(car1.getSignature()));
statement.setClob(2, clob(connection, car1.getMake()));
statement.setBlob(3, blob(connection, car1.getSignature()));
statement.addBatch();
statement.setLong(1, car2.getSales());
statement.setClob(2, new SerialClob(car2.getMake().toCharArray()));
statement.setBlob(3, new SerialBlob(car2.getSignature()));
statement.setClob(2, clob(connection, car2.getMake()));
statement.setBlob(3, blob(connection, car2.getSignature()));
statement.addBatch();
int[] result = statement.executeBatch();
assertArrayEquals(new int[] { SUCCESS_NO_INFO, SUCCESS_NO_INFO }, result);
Expand All @@ -305,6 +305,18 @@ void shouldInsertAndSelectBlobClob() throws SQLException, IOException {
}
}

private Blob blob(Connection connection, byte[] bytes) throws SQLException {
Blob blob = connection.createBlob();
blob.setBytes(1, bytes);
return blob;
}

private Clob clob(Connection connection, String text) throws SQLException {
Clob clob = connection.createClob();
clob.setString(1, text);
return clob;
}

@Test
void shouldInsertAndSelectStreams() throws SQLException, IOException {
Car car1 = Car.builder().make("Ford").sales(12345).signature("Henry Ford".getBytes()).build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import com.firebolt.jdbc.statement.preparedstatement.FireboltPreparedStatement;
import com.firebolt.jdbc.type.FireboltDataType;
import com.firebolt.jdbc.type.array.FireboltArray;
import com.firebolt.jdbc.type.lob.FireboltBlob;
import com.firebolt.jdbc.type.lob.FireboltClob;
import com.firebolt.jdbc.util.PropertyUtil;
import lombok.NonNull;
import okhttp3.OkHttpClient;
Expand Down Expand Up @@ -603,21 +605,18 @@ public CallableStatement prepareCall(String sql, int resultSetType, int resultSe
}

@Override
@NotImplemented
public Clob createClob() throws SQLException {
throw new SQLFeatureNotSupportedException();
public Clob createClob() {
return new FireboltClob();
}

@Override
@NotImplemented
public Blob createBlob() throws SQLException {
throw new SQLFeatureNotSupportedException();
public Blob createBlob() {
return new FireboltBlob();
}

@Override
@NotImplemented
public NClob createNClob() throws SQLException {
throw new SQLFeatureNotSupportedException();
public NClob createNClob() {
return new FireboltClob();
}

@Override
Expand Down
17 changes: 5 additions & 12 deletions src/main/java/com/firebolt/jdbc/resultset/FireboltResultSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
import com.firebolt.jdbc.type.FireboltDataType;
import com.firebolt.jdbc.type.array.FireboltArray;
import com.firebolt.jdbc.type.array.SqlArrayUtil;
import com.firebolt.jdbc.type.lob.FireboltBlob;
import com.firebolt.jdbc.type.lob.FireboltClob;
import com.firebolt.jdbc.util.LoggerUtil;
import org.apache.commons.text.StringEscapeUtils;

import javax.sql.rowset.serial.SerialBlob;
import javax.sql.rowset.serial.SerialClob;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
Expand Down Expand Up @@ -966,14 +966,12 @@ public Ref getRef(int columnIndex) throws SQLException {

@Override
public Blob getBlob(int columnIndex) throws SQLException {
byte[] bytes = getBytes(columnIndex);
return bytes == null ? null : new SerialBlob(bytes);
return Optional.ofNullable(getBytes(columnIndex)).map(FireboltBlob::new).orElse(null);
}

@Override
public Clob getClob(int columnIndex) throws SQLException {
String str = getString(columnIndex);
return str == null ? null : new SerialClob(str.toCharArray());
return Optional.ofNullable(getString(columnIndex)).map(String::toCharArray).map(FireboltClob::new).orElse(null);
}

@Override
Expand Down Expand Up @@ -1124,12 +1122,7 @@ public void updateNClob(String columnLabel, NClob nClob) throws SQLException {
@Override
public NClob getNClob(int columnIndex) throws SQLException {
String str = getString(columnIndex);
class FireboltNClob extends SerialClob implements NClob {
public FireboltNClob(char[] ch) throws SQLException {
super(ch);
}
}
return str == null ? null : new FireboltNClob(str.toCharArray());
return str == null ? null : new FireboltClob(str.toCharArray());
}

@Override
Expand Down
83 changes: 83 additions & 0 deletions src/main/java/com/firebolt/jdbc/type/lob/FireboltBlob.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.firebolt.jdbc.type.lob;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Array;
import java.sql.Blob;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.LinkedList;

public class FireboltBlob extends FireboltLob<byte[], Byte> implements Blob {
public FireboltBlob() {
this(new byte[0]);
}

public FireboltBlob(byte[] buf) {
super(buf, (a, i) -> a[i], Integer::byteValue, n -> (byte[])Array.newInstance(byte.class, n));
}

@Override
public byte[] getBytes(long pos, int length) throws SQLException {
isValid(buf);
validateGetRange(pos, length, buf.length);
int from = (int)pos - 1;
byte[] bytes = new byte[length];
System.arraycopy(buf, from, bytes, 0, bytes.length);
return bytes;
}

@Override
public InputStream getBinaryStream() throws SQLException {
isValid(buf);
return new ByteArrayInputStream(buf);
}

@Override
public long position(byte[] pattern, long start) throws SQLException {
return super.position(pattern, start);
}

@Override
public long position(Blob pattern, long start) throws SQLException {
return position(pattern.getBytes(1, (int)(pattern.length())), start);
}

@Override
public int setBytes(long pos, byte[] bytes) throws SQLException {
return setBytes(pos, bytes, 0, bytes.length);
}

@Override
public int setBytes(long pos, byte[] bytes, int offset, int len) throws SQLException {
return setData(pos, bytes, offset, len);
}

@Override
public OutputStream setBinaryStream(long pos) throws SQLException {
return setStream(pos, new LinkedList<>());
}

@Override
public void truncate(long length) throws SQLException {
isValid(buf);
buf = length == 0 ? new byte[0] : getBytes(1, (int)length);
}

@Override
public InputStream getBinaryStream(long pos, long length) throws SQLException {
return new ByteArrayInputStream(getBytes(pos, (int)length));
}

@Override
@SuppressWarnings("java:S6201") // Pattern Matching for "instanceof" was introduced in java 16 while we still try to be compliant with java 11
public boolean equals(Object obj) {
return this == obj || (obj instanceof FireboltBlob && Arrays.equals(buf, ((FireboltBlob)obj).buf));
}

@Override
public int hashCode() {
return 31 * FireboltBlob.class.hashCode() + Arrays.hashCode(buf);
}
}
101 changes: 101 additions & 0 deletions src/main/java/com/firebolt/jdbc/type/lob/FireboltClob.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.firebolt.jdbc.type.lob;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.lang.reflect.Array;
import java.sql.Clob;
import java.sql.NClob;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.LinkedList;

public class FireboltClob extends FireboltLob<char[], Character> implements NClob {
public FireboltClob() {
this(new char[0]);
}

public FireboltClob(char[] buf) {
super(buf, (a, i) -> a[i], i -> (char)i.intValue(), n -> (char[])Array.newInstance(char.class, n));
}

@Override
public String getSubString(long pos, int length) throws SQLException {
isValid(buf);
validateGetRange(pos, length, buf.length);
int from = (int)(pos - 1);
return new String(buf, from, Math.min(buf.length - from, length));
}

@Override
public Reader getCharacterStream() throws SQLException {
isValid(buf);
return new StringReader(new String(buf));
}

@Override
public InputStream getAsciiStream() throws SQLException {
isValid(buf);
return new ByteArrayInputStream(new String(buf).getBytes());
}

@Override
public long position(String searchStr, long start) throws SQLException {
return position(searchStr.toCharArray(), start);
}

@Override
public long position(Clob searchStr, long start) throws SQLException {
return position(searchStr.getSubString(1, (int)searchStr.length()), start);
}

@Override
public int setString(long pos, String str) throws SQLException {
return setString(pos, str, 0, str.length());
}

@Override
public int setString(long pos, String str, int offset, int len) throws SQLException {
return setChars(pos, str.toCharArray(), offset, len);
}

private int setChars(long pos, char[] chars, int offset, int len) throws SQLException {
return setData(pos, chars, offset, len);
}

@Override
public OutputStream setAsciiStream(long pos) throws SQLException {
return setStream(pos, new LinkedList<>());
}

@Override
public Writer setCharacterStream(long pos) throws SQLException {
return new OutputStreamWriter(setAsciiStream(pos));
}

@Override
public void truncate(long length) throws SQLException {
isValid(buf);
buf = length == 0 ? new char[0] : getSubString(1, (int)length).toCharArray();
}

@Override
public Reader getCharacterStream(long pos, long length) throws SQLException {
return new StringReader(getSubString(pos, (int)length));
}

@Override
@SuppressWarnings("java:S6201") // Pattern Matching for "instanceof" was introduced in java 16 while we still try to be compliant with java 11
public boolean equals(Object obj) {
return this == obj || (obj instanceof FireboltClob && Arrays.equals(buf, ((FireboltClob)obj).buf));
}

@Override
public int hashCode() {
return 31 * FireboltClob.class.hashCode() + Arrays.hashCode(buf);
}
}
Loading

0 comments on commit cc48926

Please sign in to comment.