postgresql-jdbc.git / summary / log / commit / refs

commit    8077deb52b8aaf77cd7bb93d3f65d287d5efbd85
Author:   Alexis Meneses <alexismeneses@users.noreply.github.com>
Date:     Sun Nov 23 16:32:48 2014 +0000

    Support for 64-bit (bigint) LOB functions


org/postgresql/fastpath/Fastpath.java | 82 ++++++++++++++++------ org/postgresql/fastpath/FastpathArg.java | 19 +++++ org/postgresql/jdbc2/AbstractJdbc2BlobClob.java | 44 ++++++++++-- org/postgresql/largeobject/LargeObject.java | 64 +++++++++++++++-- org/postgresql/largeobject/LargeObjectManager.java | 7 +- org/postgresql/test/jdbc2/BlobTest.java | 18 +++++ 6 files changed, 200 insertions(+), 34 deletions(-) diff --git a/org/postgresql/fastpath/Fastpath.java b/org/postgresql/fastpath/Fastpath.java index 1724addc..d7cd2c9b 100644 --- a/org/postgresql/fastpath/Fastpath.java +++ b/org/postgresql/fastpath/Fastpath.java @@ -56,12 +56,12 @@ public class Fastpath * Send a function call to the PostgreSQL backend * * @param fnId Function id - * @param resultType True if the result is an integer, false for other results + * @param resultType True if the result is a numeric (integer or long) * @param args FastpathArguments to pass to fastpath * @return null if no data, Integer if an integer result, or byte[] otherwise * @exception SQLException if a database-access error occurs. */ - public Object fastpath(int fnId, boolean resultType, FastpathArg[] args) throws SQLException + public byte[] fastpath(int fnId, FastpathArg[] args) throws SQLException { // Turn fastpath array into a parameter list. ParameterList params = executor.createFastpathParameters(args.length); @@ -74,17 +74,7 @@ public class Fastpath byte[] returnValue = executor.fastpathCall(fnId, params, connection.getAutoCommit()); // Interpret results. - if (!resultType || returnValue == null) - return returnValue; - - if (returnValue.length != 4) - throw new PSQLException(GT.tr("Fastpath call {0} - No result was returned and we expected an integer.", new Integer(fnId)), - PSQLState.NO_DATA); - - return new Integer((returnValue[3] & 255) | - ((returnValue[2] & 255) << 8) | - ((returnValue[1] & 255) << 16) | - ((returnValue[0] & 255) << 24)); + return returnValue; } /** @@ -107,15 +97,15 @@ public class Fastpath * occurs. * @see org.postgresql.largeobject.LargeObject */ - public Object fastpath(String name, boolean resulttype, FastpathArg[] args) throws SQLException + public byte[] fastpath(String name, FastpathArg[] args) throws SQLException { if (connection.getLogger().logDebug()) connection.getLogger().debug("Fastpath: calling " + name); - return fastpath(getID(name), resulttype, args); + return fastpath(getID(name), args); } /** - * This convenience method assumes that the return value is an Integer + * This convenience method assumes that the return value is an integer * @param name Function name * @param args Function arguments * @return integer result @@ -123,11 +113,61 @@ public class Fastpath */ public int getInteger(String name, FastpathArg[] args) throws SQLException { - Integer i = (Integer)fastpath(name, true, args); - if (i == null) + byte[] returnValue = fastpath(name, args); + if (returnValue == null) + { throw new PSQLException(GT.tr("Fastpath call {0} - No result was returned and we expected an integer.", name), - PSQLState.NO_DATA); - return i.intValue(); + PSQLState.NO_DATA); + } + + if (returnValue.length == 4) + { + int i = ((returnValue[3] & 255) | + ((returnValue[2] & 255) << 8) | + ((returnValue[1] & 255) << 16) | + ((returnValue[0] & 255) << 24)); + return i; + } + else + { + throw new PSQLException(GT.tr("Fastpath call {0} - No result was returned or wrong size while expecting an integer.", name), + PSQLState.NO_DATA); + } + } + + /** + * This convenience method assumes that the return value is a long (bigint) + * @param name Function name + * @param args Function arguments + * @return long result + * @exception SQLException if a database-access error occurs or no result + */ + public long getLong(String name, FastpathArg[] args) throws SQLException + { + byte[] returnValue = fastpath(name, args); + if (returnValue == null) + { + throw new PSQLException(GT.tr("Fastpath call {0} - No result was returned and we expected a long.", name), + PSQLState.NO_DATA); + } + if (returnValue.length == 8) + { + long l = ((long)(returnValue[7] & 255) | + ((long)(returnValue[6] & 255) << 8) | + ((long)(returnValue[5] & 255) << 16) | + ((long)(returnValue[4] & 255) << 24) | + ((long)(returnValue[3] & 255) << 32) | + ((long)(returnValue[2] & 255) << 40) | + ((long)(returnValue[1] & 255) << 48) | + ((long)(returnValue[0] & 255) << 56)); + return l; + + } + else + { + throw new PSQLException(GT.tr("Fastpath call {0} - No result was returned or wrong size while expecting a long.", name), + PSQLState.NO_DATA); + } } /** @@ -153,7 +193,7 @@ public class Fastpath */ public byte[] getData(String name, FastpathArg[] args) throws SQLException { - return (byte[])fastpath(name, false, args); + return (byte[])fastpath(name, args); } /** diff --git a/org/postgresql/fastpath/FastpathArg.java b/org/postgresql/fastpath/FastpathArg.java index 64dfb5ae..0eecf407 100644 --- a/org/postgresql/fastpath/FastpathArg.java +++ b/org/postgresql/fastpath/FastpathArg.java @@ -43,6 +43,25 @@ public class FastpathArg bytesLength = 4; } + /** + * Constructs an argument that consists of an integer value + * @param value int value to set + */ + public FastpathArg(long value) + { + bytes = new byte[8]; + bytes[7] = (byte) (value); + bytes[6] = (byte) (value >> 8); + bytes[5] = (byte) (value >> 16); + bytes[4] = (byte) (value >> 24); + bytes[3] = (byte) (value >> 32); + bytes[2] = (byte) (value >> 40); + bytes[1] = (byte) (value >> 48); + bytes[0] = (byte) (value >> 56); + bytesStart = 0; + bytesLength = 8; + } + /** * Constructs an argument that consists of an array of bytes * @param bytes array to store diff --git a/org/postgresql/jdbc2/AbstractJdbc2BlobClob.java b/org/postgresql/jdbc2/AbstractJdbc2BlobClob.java index 17f91c87..1cdd74fe 100644 --- a/org/postgresql/jdbc2/AbstractJdbc2BlobClob.java +++ b/org/postgresql/jdbc2/AbstractJdbc2BlobClob.java @@ -34,6 +34,7 @@ public abstract class AbstractJdbc2BlobClob private LargeObject currentLo; private long loPos; private boolean currentLoIsWriteable; + private boolean support64bit; /** @@ -51,7 +52,15 @@ public abstract class AbstractJdbc2BlobClob this.currentLo = null; this.currentLoIsWriteable = false; - + if (conn.haveMinimumServerVersion(90300)) + { + support64bit = true; + } + else + { + support64bit = false; + } + subLOs = new ArrayList(); } @@ -88,16 +97,32 @@ public abstract class AbstractJdbc2BlobClob } if (len > Integer.MAX_VALUE) { - throw new PSQLException(GT.tr("PostgreSQL LOBs can only index to: {0}", new Integer(Integer.MAX_VALUE)), PSQLState.INVALID_PARAMETER_VALUE); + if (support64bit) + { + getLo(true).truncate64(len); + } + else + { + throw new PSQLException(GT.tr("PostgreSQL LOBs can only index to: {0}", new Integer(Integer.MAX_VALUE)), PSQLState.INVALID_PARAMETER_VALUE); + } + } + else + { + getLo(true).truncate((int)len); } - - getLo(true).truncate((int)len); } public synchronized long length() throws SQLException { checkFreed(); - return getLo(false).size(); + if (support64bit) + { + return getLo(false).size64(); + } + else + { + return getLo(false).size(); + } } public synchronized byte[] getBytes(long pos, int length) throws SQLException @@ -112,7 +137,7 @@ public abstract class AbstractJdbc2BlobClob { checkFreed(); LargeObject subLO = getLo(false).copy(); - subLOs.add(subLO); + addSubLO(subLO); subLO.seek(0, LargeObject.SEEK_SET); return subLO.getInputStream(); } @@ -121,7 +146,7 @@ public abstract class AbstractJdbc2BlobClob { assertPosition(pos); LargeObject subLO = getLo(true).copy(); - subLOs.add(subLO); + addSubLO(subLO); subLO.seek((int)(pos-1)); return subLO.getOutputStream(); } @@ -281,4 +306,9 @@ public abstract class AbstractJdbc2BlobClob currentLoIsWriteable = forWrite; return currentLo; } + + protected void addSubLO(LargeObject subLO) + { + subLOs.add(subLO); + } } diff --git a/org/postgresql/largeobject/LargeObject.java b/org/postgresql/largeobject/LargeObject.java index 77072b8b..8a2928a4 100644 --- a/org/postgresql/largeobject/LargeObject.java +++ b/org/postgresql/largeobject/LargeObject.java @@ -190,7 +190,7 @@ public class LargeObject // finally close FastpathArg args[] = new FastpathArg[1]; args[0] = new FastpathArg(fd); - fp.fastpath("lo_close", false, args); // true here as we dont care!! + fp.fastpath("lo_close", args); // true here as we dont care!! closed = true; if (this.commitOnClose == true) { @@ -245,7 +245,7 @@ public class LargeObject FastpathArg args[] = new FastpathArg[2]; args[0] = new FastpathArg(fd); args[1] = new FastpathArg(buf); - fp.fastpath("lowrite", false, args); + fp.fastpath("lowrite", args); } /** @@ -261,7 +261,7 @@ public class LargeObject FastpathArg args[] = new FastpathArg[2]; args[0] = new FastpathArg(fd); args[1] = new FastpathArg(buf, off, len); - fp.fastpath("lowrite", false, args); + fp.fastpath("lowrite", args); } /** @@ -280,7 +280,23 @@ public class LargeObject args[0] = new FastpathArg(fd); args[1] = new FastpathArg(pos); args[2] = new FastpathArg(ref); - fp.fastpath("lo_lseek", false, args); + fp.fastpath("lo_lseek", args); + } + + /** + * Sets the current position within the object using 64-bit value (9.3+) + * + * @param pos position within object + * @param ref Either SEEK_SET, SEEK_CUR or SEEK_END + * @exception SQLException if a database-access error occurs. + */ + public void seek64(long pos, int ref) throws SQLException + { + FastpathArg args[] = new FastpathArg[3]; + args[0] = new FastpathArg(fd); + args[1] = new FastpathArg(pos); + args[2] = new FastpathArg(ref); + fp.fastpath("lo_lseek64", args); } /** @@ -308,6 +324,17 @@ public class LargeObject return fp.getInteger("lo_tell", args); } + /** + * @return the current position within the object + * @exception SQLException if a database-access error occurs. + */ + public long tell64() throws SQLException + { + FastpathArg args[] = new FastpathArg[1]; + args[0] = new FastpathArg(fd); + return fp.getLong("lo_tell64", args); + } + /** * This method is inefficient, as the only way to find out the size of * the object is to seek to the end, record the current position, then @@ -327,6 +354,21 @@ public class LargeObject return sz; } + /** + * See #size() for information about efficiency. + * + * @return the size of the large object + * @exception SQLException if a database-access error occurs. + */ + public long size64() throws SQLException + { + long cp = tell64(); + seek64(0, SEEK_END); + long sz = tell64(); + seek64(cp, SEEK_SET); + return sz; + } + /** * Truncates the large object to the given length in bytes. * If the number of bytes is larger than the current large @@ -341,6 +383,20 @@ public class LargeObject fp.getInteger("lo_truncate", args); } + /** + * Truncates the large object to the given length in bytes. + * If the number of bytes is larger than the current large + * object length, the large object will be filled with zero + * bytes. This method does not modify the current file offset. + */ + public void truncate64(long len) throws SQLException + { + FastpathArg args[] = new FastpathArg[2]; + args[0] = new FastpathArg(fd); + args[1] = new FastpathArg(len); + fp.getInteger("lo_truncate64", args); + } + /** * Returns an InputStream from this object. * diff --git a/org/postgresql/largeobject/LargeObjectManager.java b/org/postgresql/largeobject/LargeObjectManager.java index 233cac77..431696bc 100644 --- a/org/postgresql/largeobject/LargeObjectManager.java +++ b/org/postgresql/largeobject/LargeObjectManager.java @@ -123,10 +123,13 @@ public class LargeObjectManager " or proname = 'lo_creat'" + " or proname = 'lo_unlink'" + " or proname = 'lo_lseek'" + + " or proname = 'lo_lseek64'" + " or proname = 'lo_tell'" + + " or proname = 'lo_tell64'" + " or proname = 'loread'" + " or proname = 'lowrite'" + - " or proname = 'lo_truncate'"; + " or proname = 'lo_truncate'" + + " or proname = 'lo_truncate64'"; if (conn.getMetaData().supportsSchemasInTableDefinitions()) { @@ -329,7 +332,7 @@ public class LargeObjectManager { FastpathArg args[] = new FastpathArg[1]; args[0] = Fastpath.createOIDArg(oid); - fp.fastpath("lo_unlink", false, args); + fp.fastpath("lo_unlink", args); } /** diff --git a/org/postgresql/test/jdbc2/BlobTest.java b/org/postgresql/test/jdbc2/BlobTest.java index cd877dff..9b885f34 100644 --- a/org/postgresql/test/jdbc2/BlobTest.java +++ b/org/postgresql/test/jdbc2/BlobTest.java @@ -199,6 +199,24 @@ public class BlobTest extends TestCase is2.close(); } + public void testLargeLargeObject() throws Exception + { + if (!TestUtil.haveMinimumServerVersion(con, "9.3")) + { + return; + } + + Statement stmt = con.createStatement(); + stmt.execute("INSERT INTO testblob(id,lo) VALUES ('1', lo_creat(-1))"); + ResultSet rs = stmt.executeQuery("SELECT lo FROM testblob"); + assertTrue(rs.next()); + + Blob lob = rs.getBlob(1); + long length = ((long)Integer.MAX_VALUE) + 1024; + lob.truncate(length); + assertEquals(length, lob.length()); + } + /* * Helper - uploads a file into a blob using old style methods. We use this * because it always works, and we can use it as a base to test the new [parent: 8d11432ed522]