diff --git a/pljava-api/src/main/java/org/postgresql/pljava/annotation/processing/DDRProcessor.java b/pljava-api/src/main/java/org/postgresql/pljava/annotation/processing/DDRProcessor.java index e59c63c1a..92a5d68d7 100644 --- a/pljava-api/src/main/java/org/postgresql/pljava/annotation/processing/DDRProcessor.java +++ b/pljava-api/src/main/java/org/postgresql/pljava/annotation/processing/DDRProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2025 Tada AB and other contributors, as listed below. + * Copyright (c) 2004-2026 Tada AB and other contributors, as listed below. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the The BSD 3-Clause License @@ -62,7 +62,7 @@ public SourceVersion getSupportedSourceVersion() * Update latest_tested to be the latest Java release on which this * annotation processor has been tested without problems. */ - int latest_tested = 25; + int latest_tested = 26; int ordinal_9 = SourceVersion.RELEASE_9.ordinal(); int ordinal_latest = latest_tested - 9 + ordinal_9; diff --git a/pljava-examples/src/main/java/org/postgresql/pljava/example/annotation/LegacyBlob.java b/pljava-examples/src/main/java/org/postgresql/pljava/example/annotation/LegacyBlob.java new file mode 100644 index 000000000..5ecc33d02 --- /dev/null +++ b/pljava-examples/src/main/java/org/postgresql/pljava/example/annotation/LegacyBlob.java @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2026 Tada AB and other contributors, as listed below. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the The BSD 3-Clause License + * which accompanies this distribution, and is available at + * http://opensource.org/licenses/BSD-3-Clause + * + * Contributors: + * Chapman Flack + */ +package org.postgresql.pljava.example.annotation; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.IOException; + +import java.sql.Blob; +import java.sql.Connection; +import static java.sql.DriverManager.getConnection; +import java.sql.SQLData; +import java.sql.SQLException; +import java.sql.SQLInput; +import java.sql.SQLOutput; +import java.sql.PreparedStatement; +import java.sql.ResultSet; + +import java.util.Arrays; + +import org.postgresql.pljava.annotation.Function; +import org.postgresql.pljava.annotation.MappedUDT; +import org.postgresql.pljava.annotation.SQLAction; +import org.postgresql.pljava.annotation.SQLType; + +/** + * Captures how PL/Java's {@code Blob} implementation has and hasn't worked. + *
+ * The {@link Blob} implementation in PL/Java, from inception and as currently + * found in the 1.6 series releases, has never been especially useful. It has + * used {@code Blob} objects as an alternative interface to binary byte + * sequences stored inline in a tuple, such as could also be accessed + * using, for example, {@link ResultSet#getBytes getBytes}. In this legacy + * design, you would apply {@code getBlob} to a column containing a large byte + * string, and be able to manipulate that content using the methods of {@code + * Blob} instead of as a byte array. + *
+ * That contrasts with the function of {@code Blob} in the PGJDBC client-side + * driver: with that driver, you would apply {@code getBlob} to a column + * containing the oid of a PostgreSQL + * large + * object, and the {@code Blob} object returned would allow you + * to manipulate the content of that out-of-tuple large object. That is almost + * certainly the way the JDBC {@code Blob} API was intended to be used, and + * the legacy PL/Java approach is not. On top of that, even the rather less + * useful PL/Java realization has never been quite fully implemented. It has, + * therefore, probably never been widely used, if at all. + *
+ * These are not shortcomings to be corrected in the middle of a release series; + * some future PL/Java major release will need to include all-new {@code Blob} + * support in a thoroughly-revamped JDBC layer. The purpose of this example code + * is simply to document the current working (and non-working) of the current + * {@code Blob} support, as a guard against bit-rot making it even worse, just + * in case anyone anywhere has used it for something. + *
+ * The {@link Clob} implementation in PL/Java, from inception and as currently + * found in the 1.6 series releases, has never been especially useful. It has + * used {@code Clob} objects as an alternative interface to character strings + * stored inline in a tuple, such as could also be accessed + * using, for example, {@link ResultSet#getString getString}. In this legacy + * design, you would apply {@code getClob} to a column containing a large text + * string, and be able to manipulate that content using the methods of {@code + * Clob} instead of as a character string. + *
+ * That contrasts with the function of {@code Clob} in the PGJDBC client-side + * driver: with that driver, you would apply {@code getClob} to a column + * containing the oid of a PostgreSQL + * large + * object, and the {@code Clob} object returned would allow you + * to manipulate the content of that out-of-tuple large object. That is almost + * certainly the way the JDBC {@code Clob} API was intended to be used, and + * the legacy PL/Java approach is not. On top of that, even the rather less + * useful PL/Java realization has never been close to fully implemented. It has, + * therefore, probably never been widely used, if at all. + *
+ * These are not shortcomings to be corrected in the middle of a release series; + * some future PL/Java major release will need to include all-new {@code Clob} + * support in a thoroughly-revamped JDBC layer. The purpose of this example code + * is simply to document the current working (and non-working) of the current + * {@code Clob} support, as a guard against bit-rot making it even worse, just + * in case anyone anywhere has used it for something. + *
BlobValue
- */
- public OutputStream setBinaryStream(long pos)
- {
- throw new UnsupportedOperationException();
- }
-
- /**
- * In this method is not supported by BlobValue
- */
- public int setBytes(long pos, byte[] bytes)
- {
- throw new UnsupportedOperationException();
- }
-
- /**
- * In this method is not supported by BlobValue
- */
- public int setBytes(long pos, byte[] bytes, int offset, int len)
- {
- throw new UnsupportedOperationException();
- }
-
- public synchronized long skip(long nBytes)
- throws IOException
- {
- long skipped = m_stream.skip(nBytes);
- m_streamPos += skipped;
- return skipped;
- }
-
- /**
- * In this method is not supported by BlobValue
- */
- public void truncate(long len)
- {
- throw new UnsupportedOperationException();
- }
-
- // ************************************************************
- // Non-implementation of JDBC 4 methods.
- // ************************************************************
-
- public InputStream getBinaryStream(long pos,
- long length)
- throws SQLException
- {
- throw new SQLFeatureNotSupportedException
- ( this.getClass()
- + ".getBinaryStream( long,long ) not "
- + "implemented yet.",
- "0A000" );
- }
-
- public void free()
- throws SQLException
- {
- throw new SQLFeatureNotSupportedException
- ( this.getClass()
- + ".free() not implemented yet.",
- "0A000" );
- }
-}
diff --git a/pljava/src/main/java/org/postgresql/pljava/jdbc/ClobValue.java b/pljava/src/main/java/org/postgresql/pljava/jdbc/ClobValue.java
deleted file mode 100644
index 9e594967d..000000000
--- a/pljava/src/main/java/org/postgresql/pljava/jdbc/ClobValue.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright (c) 2004-2020 Tada AB and other contributors, as listed below.
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the The BSD 3-Clause License
- * which accompanies this distribution, and is available at
- * http://opensource.org/licenses/BSD-3-Clause
- *
- * Contributors:
- * Thomas Hallgren
- * PostgreSQL Global Development Group
- * Chapman Flack
- */
-package org.postgresql.pljava.jdbc;
-
-import java.io.BufferedInputStream;
-import java.io.CharConversionException;
-import java.io.InputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.Reader;
-import java.io.StringReader;
-import java.io.Writer;
-import java.sql.Clob;
-import java.sql.SQLException;
-import java.sql.SQLFeatureNotSupportedException;
-
-/**
- * Implementation of {@link Clob} for the SPI connection.
- * @author Thomas Hallgren
- */
-public class ClobValue extends Reader implements Clob
-{
- public static int getReaderLength(Reader value) throws SQLException
- {
- try
- {
- value.mark(Integer.MAX_VALUE);
- long length = value.skip(Long.MAX_VALUE);
- if(length > Integer.MAX_VALUE)
- throw new SQLException("stream content too large");
- value.reset();
- return (int)length;
- }
- catch(IOException e)
- {
- throw new SQLException(e.getMessage());
- }
- }
-
- private long m_markPos;
-
- private final long m_nChars;
-
- private final Reader m_reader;
-
- private long m_readerPos;
-
- public ClobValue(Reader reader, long nChars)
- {
- m_reader = reader;
- m_nChars = nChars;
- m_readerPos = 0L;
- m_markPos = 0L;
- }
-
- public ClobValue(String value)
- {
- this(new StringReader(value), value.length());
- }
-
- public void close() throws IOException
- {
- m_reader.close();
- m_readerPos = 0;
- m_markPos = 0;
- }
-
- public InputStream getAsciiStream()
- {
- return new BufferedInputStream(new InputStream()
- {
- public int read() throws IOException
- {
- int nextChar = ClobValue.this.read();
- if(nextChar > 127)
- throw new CharConversionException(
- "Non ascii character in Clob data");
- return nextChar;
- }
- });
- }
-
- public Reader getCharacterStream()
- {
- return this;
- }
-
- public String getSubString(long pos, int length) throws SQLException
- {
- if(pos < 0L || length < 0)
- throw new IllegalArgumentException();
- if(length == 0)
- return "";
-
- if(pos + length > m_nChars)
- throw new SQLException("Attempt to read beyond end of Clob data");
-
- long skip = pos - m_readerPos;
- if(skip < 0)
- throw new SQLException("Cannot position Clob stream backwards");
-
- try
- {
- if(skip > 0)
- this.skip(skip);
-
- char[] buf = new char[length];
- int nr = this.read(buf);
- if(nr < length)
- throw new SQLException("Clob data read not fulfilled");
- return new String(buf);
- }
- catch(IOException e)
- {
- throw new SQLException("Error reading Blob data: " + e.getMessage());
- }
- }
-
- public long length()
- {
- return m_nChars;
- }
-
- public synchronized void mark(int readLimit) throws IOException
- {
- m_reader.mark(readLimit);
- m_markPos = m_readerPos;
- }
-
- public boolean markSupported()
- {
- return m_reader.markSupported();
- }
-
- /**
- * Not supported.
- */
- public long position(Clob pattern, long start)
- {
- throw new UnsupportedOperationException();
- }
-
- /**
- * In this method is not supported by ClobValue
- */
- public long position(String pattern, long start)
- {
- throw new UnsupportedOperationException();
- }
-
- public synchronized int read() throws IOException
- {
- int rs = m_reader.read();
- m_readerPos++;
- return rs;
- }
-
- public synchronized int read(char[] b) throws IOException
- {
- int rs = m_reader.read(b);
- m_readerPos += rs;
- return rs;
- }
-
- public synchronized int read(char[] b, int off, int len) throws IOException
- {
- int rs = m_reader.read(b, off, len);
- m_readerPos += rs;
- return rs;
- }
-
- public synchronized boolean ready() throws IOException
- {
- return m_reader.ready();
- }
-
- public synchronized void reset() throws IOException
- {
- m_reader.reset();
- m_readerPos = m_markPos;
- }
-
- /**
- * In this method is not supported by ClobValue
- */
- public OutputStream setAsciiStream(long pos)
- {
- throw new UnsupportedOperationException();
- }
-
- /**
- * In this method is not supported by ClobValue
- */
- public Writer setCharacterStream(long pos)
- {
- throw new UnsupportedOperationException();
- }
-
- /**
- * In this method is not supported by ClobValue
- */
- public int setString(long pos, String str)
- {
- throw new UnsupportedOperationException();
- }
-
- /**
- * In this method is not supported by ClobValue
- */
- public int setString(long pos, String str, int offset, int len)
- {
- throw new UnsupportedOperationException();
- }
-
- public synchronized long skip(long nBytes) throws IOException
- {
- long skipped = m_reader.skip(nBytes);
- m_readerPos += skipped;
- return skipped;
- }
-
- /**
- * In this method is not supported by ClobValue
- */
- public void truncate(long len)
- {
- throw new UnsupportedOperationException();
- }
-
- //************************************************************
- // Non-implementation of JDBC 4 methods.
- //************************************************************
-
- public Reader getCharacterStream(long pos,
- long length)
- throws SQLException
- {
- throw new SQLFeatureNotSupportedException
- ( this.getClass()
- + ".getCharacterStream( long,long ) not implemented yet.",
- "0A000" );
- }
-
- public void free()
- throws SQLException
- {
- throw new SQLFeatureNotSupportedException
- ( this.getClass()
- + ".free() not implemented yet.",
- "0A000" );
- }
-}
diff --git a/pljava/src/main/java/org/postgresql/pljava/jdbc/ObjectResultSet.java b/pljava/src/main/java/org/postgresql/pljava/jdbc/ObjectResultSet.java
index 0393c5d13..dfae4794a 100644
--- a/pljava/src/main/java/org/postgresql/pljava/jdbc/ObjectResultSet.java
+++ b/pljava/src/main/java/org/postgresql/pljava/jdbc/ObjectResultSet.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2020 Tada AB and other contributors, as listed below.
+ * Copyright (c) 2004-2026 Tada AB and other contributors, as listed below.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the The BSD 3-Clause License
@@ -20,6 +20,7 @@
import java.sql.Ref;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
+import java.sql.SQLNonTransientException;
import java.sql.SQLWarning;
import java.sql.Time;
import java.sql.Timestamp;
@@ -28,11 +29,18 @@
import java.util.Calendar;
import java.util.Map;
+import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.IOException;
import java.io.Reader;
+import java.io.StringReader;
import static java.nio.charset.StandardCharsets.US_ASCII;
+import javax.sql.rowset.serial.SerialBlob;
+import javax.sql.rowset.serial.SerialClob;
+
+import static org.postgresql.pljava.jdbc.SPIDatabaseMetaData.readNCharsAsString;
/**
* Implements most getters in terms of {@link #getValue}, {@link #getNumber},
@@ -102,14 +110,16 @@ public Array getArray(int columnIndex)
}
/**
- * Implemented over {@link #getClob(int) getClob}.
+ * Implemented over {@link #getString(int) getString}.
*/
@Override
public InputStream getAsciiStream(int columnIndex)
throws SQLException
{
- Clob c = getClob(columnIndex);
- return (c == null) ? null : c.getAsciiStream();
+ String s = getString(columnIndex);
+ if ( null == s )
+ return null;
+ return new ByteArrayInputStream(s.getBytes(US_ASCII));
}
/**
@@ -133,14 +143,14 @@ public BigDecimal getBigDecimal(int columnIndex, int scale)
}
/**
- * Implemented over {@link #getBlob(int) getBlob}.
+ * Implemented over {@link #getBytes(int) getBytes}.
*/
@Override
public InputStream getBinaryStream(int columnIndex)
throws SQLException
{
- Blob b = getBlob(columnIndex);
- return (b == null) ? null : b.getBinaryStream();
+ byte[] bytes = getBytes(columnIndex);
+ return (bytes == null) ? null : new ByteArrayInputStream(bytes);
}
/**
@@ -151,7 +161,7 @@ public Blob getBlob(int columnIndex)
throws SQLException
{
byte[] bytes = getBytes(columnIndex);
- return (bytes == null) ? null : new BlobValue(bytes);
+ return (bytes == null) ? null : new SerialBlob(bytes);
}
/**
@@ -187,14 +197,14 @@ public byte[] getBytes(int columnIndex)
}
/**
- * Implemented over {@link #getClob(int) getClob}.
+ * Implemented over {@link #getString(int) getString}.
*/
@Override
public Reader getCharacterStream(int columnIndex)
throws SQLException
{
- Clob c = getClob(columnIndex);
- return (c == null) ? null : c.getCharacterStream();
+ String s = getString(columnIndex);
+ return (s == null) ? null : new StringReader(s);
}
/**
@@ -205,7 +215,7 @@ public Clob getClob(int columnIndex)
throws SQLException
{
String str = getString(columnIndex);
- return (str == null) ? null : new ClobValue(str);
+ return (str == null) ? null : new SerialClob(str.toCharArray());
}
/**
@@ -413,15 +423,14 @@ public void updateArray(int columnIndex, Array x) throws SQLException
}
/**
- * Implemented over {@link ClobValue} and
- * {@link #updateObject updateObject}.
+ * Implemented over {@link #updateObject updateObject}.
*/
@Override
public void updateAsciiStream(int columnIndex, InputStream x, int length)
throws SQLException
{
- updateObject(columnIndex,
- new ClobValue(new InputStreamReader(x, US_ASCII), length));
+ updateObject(columnIndex, null == x ? null :
+ readNCharsAsString(new InputStreamReader(x, US_ASCII), length));
}
/**
@@ -435,14 +444,28 @@ public void updateBigDecimal(int columnIndex, BigDecimal x)
}
/**
- * Implemented over {@link BlobValue} and
- * {@link #updateBlob updateBlob}.
+ * Implemented over {@link #updateBytes updateBytes}.
*/
@Override
public void updateBinaryStream(int columnIndex, InputStream x, int length)
throws SQLException
{
- updateBlob(columnIndex, (Blob) new BlobValue(x, length));
+ // Java >= 11: bytes = x.readNBytes(length)
+ byte[] bytes = new byte[length];
+ try
+ {
+ int got = x.readNBytes(bytes, 0, length);
+ if ( got != length || -1 != x.read() )
+ {
+ throw new SQLNonTransientException(
+ "updateBinaryStream explicit length incorrect", "38000");
+ }
+ }
+ catch ( IOException e )
+ {
+ throw new SQLException(e.getMessage(), e);
+ }
+ updateBytes(columnIndex, bytes);
}
/**
@@ -486,14 +509,13 @@ public void updateBytes(int columnIndex, byte[] x)
}
/**
- * Implemented over {@link ClobValue} and
- * {@link #updateClob updateClob}.
+ * Implemented over {@link #updateString updateString}.
*/
@Override
public void updateCharacterStream(int columnIndex, Reader x, int length)
throws SQLException
{
- updateClob(columnIndex, (Clob) new ClobValue(x, length));
+ updateString(columnIndex, readNCharsAsString(x, length));
}
/**
diff --git a/pljava/src/main/java/org/postgresql/pljava/jdbc/SPIDatabaseMetaData.java b/pljava/src/main/java/org/postgresql/pljava/jdbc/SPIDatabaseMetaData.java
index 067bfa0fa..c579f36b0 100644
--- a/pljava/src/main/java/org/postgresql/pljava/jdbc/SPIDatabaseMetaData.java
+++ b/pljava/src/main/java/org/postgresql/pljava/jdbc/SPIDatabaseMetaData.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005-2020 Tada AB and other contributors, as listed below.
+ * Copyright (c) 2005-2026 Tada AB and other contributors, as listed below.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the The BSD 3-Clause License
@@ -14,6 +14,12 @@
package org.postgresql.pljava.jdbc;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.Reader;
+
+import java.nio.CharBuffer;
+
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
@@ -21,6 +27,7 @@
import java.sql.RowIdLifetime;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
+import java.sql.SQLNonTransientException;
import java.sql.Statement;
import java.util.ArrayList;
import static java.util.Arrays.sort;
@@ -58,6 +65,109 @@ public SPIDatabaseMetaData(SPIConnection conn)
private int INDEX_MAX_KEYS = 0; // maximum number of keys in an index.
+ /*
+ * Common methods used in the (mostly legacy, largely broken) Blob/Clob
+ * implementation. Located here for their dependence, logically, on some
+ * "database metadata" like VARHDRSZ.
+ */
+
+ static byte[] readNBytes(InputStream is, long length)
+ throws SQLException
+ {
+ if ( null == is )
+ return null;
+
+ if ( 0 > length )
+ {
+ throw new SQLNonTransientException(
+ "explicit length passed with an InputStream is negative",
+ "22000");
+ }
+ else if ( length > Integer.MAX_VALUE - VARHDRSZ )
+ {
+ throw new SQLNonTransientException(
+ "explicit length passed with an InputStream is too large",
+ "54000");
+ }
+
+ // Java >= 11: bytes = x.readNBytes(length)
+ byte[] bytes = new byte[(int)length];
+ try
+ {
+ int got = is.readNBytes(bytes, 0, bytes.length);
+ if ( bytes.length == got && -1 == is.read() )
+ return bytes;
+ throw new SQLNonTransientException(
+ "explicit length passed with an InputStream is incorrect",
+ "22000");
+ }
+ catch ( IOException e )
+ {
+ throw new SQLException(e.getMessage(), e);
+ }
+ }
+
+ public static String readAllAsString(Reader r) throws SQLException
+ {
+ if ( null == r )
+ return null;
+
+ // Java >= 10: can use r.transferTo(...a StringWriter...)
+ // Java >= 25: can use r.readAllAsString()
+ try
+ {
+ CharBuffer cb = CharBuffer.allocate(2048);
+ StringBuilder sb = new StringBuilder();
+ while ( -1 != r.read(cb) )
+ {
+ sb.append(cb.flip());
+ cb.clear();
+ }
+ return sb.toString();
+ }
+ catch ( IOException e )
+ {
+ throw new SQLException(e.getMessage(), e);
+ }
+ }
+
+ public static String readNCharsAsString(Reader r, long length)
+ throws SQLException
+ {
+ if ( null == r )
+ return null;
+
+ if ( 0 > length )
+ {
+ throw new SQLNonTransientException(
+ "explicit length passed with a Reader is negative",
+ "22000");
+ }
+ else if ( length > Integer.MAX_VALUE )
+ {
+ throw new SQLNonTransientException(
+ "explicit length passed with a Reader is too large",
+ "54000");
+ }
+
+ try
+ {
+ CharBuffer cb = CharBuffer.allocate((int)length);
+ int got;
+ while ( 0 < (got = r.read(cb)) )
+ ;
+ if ( 0 == cb.remaining() && -1 == r.read() )
+ return cb.flip().toString();
+ throw new SQLNonTransientException(
+ "explicit length passed with a Reader is incorrect",
+ "22000");
+ }
+ catch ( IOException e )
+ {
+ throw new SQLException(e.getMessage(), e);
+ }
+ }
+
protected int getMaxIndexKeys() throws SQLException
{
if(INDEX_MAX_KEYS == 0)
diff --git a/pljava/src/main/java/org/postgresql/pljava/jdbc/SPIPreparedStatement.java b/pljava/src/main/java/org/postgresql/pljava/jdbc/SPIPreparedStatement.java
index 02ccd4070..0edf141ad 100644
--- a/pljava/src/main/java/org/postgresql/pljava/jdbc/SPIPreparedStatement.java
+++ b/pljava/src/main/java/org/postgresql/pljava/jdbc/SPIPreparedStatement.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2020 Tada AB and other contributors, as listed below.
+ * Copyright (c) 2004-2026 Tada AB and other contributors, as listed below.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the The BSD 3-Clause License
@@ -15,6 +15,7 @@
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.IOException;
import java.io.Reader;
import java.math.BigDecimal;
import java.net.URL;
@@ -42,6 +43,9 @@
import org.postgresql.pljava.internal.ExecutionPlan;
import org.postgresql.pljava.internal.Oid;
+import static org.postgresql.pljava.jdbc.SPIDatabaseMetaData.readNBytes;
+import static org.postgresql.pljava.jdbc.SPIDatabaseMetaData.readNCharsAsString;
+
/**
* Implementation of {@link PreparedStatement} for the SPI connection.
* @author Thomas Hallgren
@@ -179,11 +183,11 @@ public void setTimestamp(int columnIndex, Timestamp value) throws SQLException
}
@Override
- public void setAsciiStream(int columnIndex, InputStream value, int length) throws SQLException
+ public void setAsciiStream(int columnIndex, InputStream value, int length)
+ throws SQLException
{
- setObject(columnIndex,
- new ClobValue(new InputStreamReader(value, US_ASCII), length),
- Types.CLOB);
+ setObject(columnIndex, null == value ? null :
+ new String(readNBytes(value, length), US_ASCII), Types.CLOB);
}
@SuppressWarnings("deprecation") @Override
@@ -193,9 +197,17 @@ public void setUnicodeStream(int columnIndex, InputStream value, int arg2) throw
}
@Override
- public void setBinaryStream(int columnIndex, InputStream value, int length) throws SQLException
+ public void setBinaryStream(int columnIndex, InputStream value, int length)
+ throws SQLException
{
- setObject(columnIndex, new BlobValue(value, length), Types.BLOB);
+ try
+ {
+ setObject(columnIndex, value.readAllBytes(), Types.VARBINARY);
+ }
+ catch ( IOException e )
+ {
+ throw new SQLException(e.getMessage(), e);
+ }
}
@Override
@@ -378,7 +390,7 @@ public void addBatch(String statement)
public void setCharacterStream(int columnIndex, Reader value, int length)
throws SQLException
{
- setObject(columnIndex, new ClobValue(value, length), Types.CLOB);
+ setObject(columnIndex, readNCharsAsString(value, length), Types.CLOB);
}
@Override
diff --git a/pljava/src/main/java/org/postgresql/pljava/jdbc/SQLInputFromTuple.java b/pljava/src/main/java/org/postgresql/pljava/jdbc/SQLInputFromTuple.java
index 2b99e01ba..a29c0f761 100644
--- a/pljava/src/main/java/org/postgresql/pljava/jdbc/SQLInputFromTuple.java
+++ b/pljava/src/main/java/org/postgresql/pljava/jdbc/SQLInputFromTuple.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2019 Tada AB and other contributors, as listed below.
+ * Copyright (c) 2004-2026 Tada AB and other contributors, as listed below.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the The BSD 3-Clause License
@@ -12,10 +12,17 @@
*/
package org.postgresql.pljava.jdbc;
+import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.Reader;
+import java.io.StringReader;
+
import java.math.BigDecimal;
+
import java.net.URL;
+
+import static java.nio.charset.StandardCharsets.US_ASCII;
+
import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
@@ -31,6 +38,9 @@
import java.sql.Time;
import java.sql.Timestamp;
+import javax.sql.rowset.serial.SerialBlob;
+import javax.sql.rowset.serial.SerialClob;
+
import org.postgresql.pljava.internal.Backend;
import org.postgresql.pljava.internal.DualState;
import org.postgresql.pljava.internal.TupleDesc;
@@ -78,13 +88,15 @@ public Array readArray() throws SQLException
}
/**
- * Implemented over {@link #readClob}.
+ * Implemented over {@link #readString}.
*/
@Override
public InputStream readAsciiStream() throws SQLException
{
- Clob c = readClob();
- return (c == null) ? null : c.getAsciiStream();
+ String s = readString();
+ if ( null == s )
+ return null;
+ return new ByteArrayInputStream(s.getBytes(US_ASCII));
}
/**
@@ -97,13 +109,13 @@ public BigDecimal readBigDecimal() throws SQLException
}
/**
- * Implemented over {@link #readBlob}.
+ * Implemented over {@link #readBytes}.
*/
@Override
public InputStream readBinaryStream() throws SQLException
{
- Blob b = readBlob();
- return (b == null) ? null : b.getBinaryStream();
+ byte[] bytes = readBytes();
+ return (bytes == null) ? null : new ByteArrayInputStream(bytes);
}
/**
@@ -113,7 +125,7 @@ public InputStream readBinaryStream() throws SQLException
public Blob readBlob() throws SQLException
{
byte[] bytes = readBytes();
- return (bytes == null) ? null : new BlobValue(bytes);
+ return (bytes == null) ? null : new SerialBlob(bytes);
}
/**
@@ -150,8 +162,8 @@ public byte[] readBytes() throws SQLException
*/
public Reader readCharacterStream() throws SQLException
{
- Clob c = readClob();
- return (c == null) ? null : c.getCharacterStream();
+ String s = readString();
+ return (s == null) ? null : new StringReader(s);
}
/**
@@ -160,7 +172,7 @@ public Reader readCharacterStream() throws SQLException
public Clob readClob() throws SQLException
{
String str = readString();
- return (str == null) ? null : new ClobValue(str);
+ return (str == null) ? null : new SerialClob(str.toCharArray());
}
/**
diff --git a/pljava/src/main/java/org/postgresql/pljava/jdbc/SQLOutputToTuple.java b/pljava/src/main/java/org/postgresql/pljava/jdbc/SQLOutputToTuple.java
index ec2365bb1..7ab09f2c1 100644
--- a/pljava/src/main/java/org/postgresql/pljava/jdbc/SQLOutputToTuple.java
+++ b/pljava/src/main/java/org/postgresql/pljava/jdbc/SQLOutputToTuple.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2020 Tada AB and other contributors, as listed below.
+ * Copyright (c) 2004-2026 Tada AB and other contributors, as listed below.
* Copyright (c) 2010, 2011 PostgreSQL Global Development Group
*
* All rights reserved. This program and the accompanying materials
@@ -17,6 +17,7 @@
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.IOException;
import java.io.Reader;
import java.math.BigDecimal;
import java.net.URL;
@@ -40,6 +41,8 @@
import org.postgresql.pljava.internal.Tuple;
import org.postgresql.pljava.internal.TupleDesc;
+import static org.postgresql.pljava.jdbc.SPIDatabaseMetaData.readAllAsString;
+
/**
* Implementation of {@link SQLOutput} for the case of a composite data type.
* @author Thomas Hallgren
@@ -86,8 +89,7 @@ public void writeArray(Array value) throws SQLException
public void writeAsciiStream(InputStream value) throws SQLException
{
- Reader rdr = new BufferedReader(new InputStreamReader(value, US_ASCII));
- writeClob(new ClobValue(rdr, ClobValue.getReaderLength(rdr)));
+ writeCharacterStream(new InputStreamReader(value, US_ASCII));
}
public void writeBigDecimal(BigDecimal value) throws SQLException
@@ -97,9 +99,14 @@ public void writeBigDecimal(BigDecimal value) throws SQLException
public void writeBinaryStream(InputStream value) throws SQLException
{
- if(!value.markSupported())
- value = new BufferedInputStream(value);
- writeBlob(new BlobValue(value, BlobValue.getStreamLength(value)));
+ try
+ {
+ writeBytes(value.readAllBytes());
+ }
+ catch ( IOException e )
+ {
+ throw new SQLException(e.getMessage(), e);
+ }
}
public void writeBlob(Blob value) throws SQLException
@@ -124,9 +131,7 @@ public void writeBytes(byte[] value) throws SQLException
public void writeCharacterStream(Reader value) throws SQLException
{
- if(!value.markSupported())
- value = new BufferedReader(value);
- writeClob(new ClobValue(value, ClobValue.getReaderLength(value)));
+ writeString(readAllAsString(value));
}
public void writeClob(Clob value) throws SQLException
diff --git a/pljava/src/main/java/org/postgresql/pljava/jdbc/SingleRowWriter.java b/pljava/src/main/java/org/postgresql/pljava/jdbc/SingleRowWriter.java
index 99f21341c..ac54fe9d9 100644
--- a/pljava/src/main/java/org/postgresql/pljava/jdbc/SingleRowWriter.java
+++ b/pljava/src/main/java/org/postgresql/pljava/jdbc/SingleRowWriter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2020 Tada AB and other contributors, as listed below.
+ * Copyright (c) 2004-2026 Tada AB and other contributors, as listed below.
* Copyright (c) 2010, 2011 PostgreSQL Global Development Group
*
* All rights reserved. This program and the accompanying materials
@@ -13,6 +13,7 @@
*/
package org.postgresql.pljava.jdbc;
+import java.sql.Blob;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -91,8 +92,8 @@ public void updateObject(int columnIndex, Object x)
Class> c = m_tupleDesc.getColumnClass(columnIndex);
TypeBridge>.Holder xAlt = TypeBridge.wrap(x);
- if(null == xAlt && !c.isInstance(x)
- && !(c == byte[].class && (x instanceof BlobValue)))
+ if ( null == xAlt && !c.isInstance(x)
+ && !( c == byte[].class && (x instanceof Blob) ) )
{
if(Number.class.isAssignableFrom(c))
x = SPIConnection.basicNumericCoercion(c, x);