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]