2004-09-30 Francisco Figueiredo Jr. <fxjrlists@yahoo.com.br>
authorFrancisco Figueiredo Jr. <fxjr@mono-cvs.ximian.com>
Thu, 30 Sep 2004 21:36:09 +0000 (21:36 -0000)
committerFrancisco Figueiredo Jr. <fxjr@mono-cvs.ximian.com>
Thu, 30 Sep 2004 21:36:09 +0000 (21:36 -0000)
    * NpgsqlTypes/FastPath.cs,
    NpgsqlTypes/FastPathArg.cs,
    NpgsqlTypes/LargeObject.cs,
    NpgsqlTypes/LargeObjectManager.cs,
    Npgsql.dll.sources: Added large object support. Thanks Emiliano Necciari (e dot necciari at blogic dot it) for the patch.

svn path=/trunk/mcs/; revision=34575

mcs/class/Npgsql/ChangeLog
mcs/class/Npgsql/Npgsql.dll.sources
mcs/class/Npgsql/NpgsqlTypes/FastPath.cs [new file with mode: 0644]
mcs/class/Npgsql/NpgsqlTypes/FastPathArg.cs [new file with mode: 0644]
mcs/class/Npgsql/NpgsqlTypes/LargeObject.cs [new file with mode: 0644]
mcs/class/Npgsql/NpgsqlTypes/LargeObjectManager.cs [new file with mode: 0644]

index 30061d43a72f04e728bec829dc9ec50aee74ce8a..9fe48f650a637d67610b87b0d7dcecd0f1224675 100644 (file)
@@ -1,3 +1,11 @@
+2004-09-30  Francisco Figueiredo Jr.  <fxjrlists@yahoo.com.br>
+    * NpgsqlTypes/FastPath.cs,
+    NpgsqlTypes/FastPathArg.cs,
+    NpgsqlTypes/LargeObject.cs,
+    NpgsqlTypes/LargeObjectManager.cs,
+    Npgsql.dll.sources: Added large object support. Thanks Emiliano Necciari (e dot necciari at blogic dot it) for the patch.
+    
+
 2004-09-26  Francisco Figueiredo Jr.  <fxjrlists@yahoo.com.br>
 
     * Npgsql/NpgsqlDataReader.cs:
index f0de5823ebee1049bb9b74c8a041dd9288ff56e2..f4521e7c870ca61d6637b1871636c799bd63bb38 100755 (executable)
@@ -46,8 +46,16 @@ Npgsql/Design/ConnectionStringEditorForm.cs
 Npgsql/Design/NpgsqlParameterConverter.cs
 Npgsql/Design/NpgsqlParametersEditor.cs
 Npgsql/Design/NpgsqlSysDescriptionAttribute.cs
-NpgsqlTypes/NpgsqlTypesHelper.cs
+NpgsqlTypes/FastPath.cs
+NpgsqlTypes/FastPathArg.cs
+NpgsqlTypes/LargeObject.cs
+NpgsqlTypes/LargeObjectManager.cs
 NpgsqlTypes/NpgsqlDbType.cs
 NpgsqlTypes/NpgsqlTypeConverters.cs
 NpgsqlTypes/NpgsqlTypes.cs
+NpgsqlTypes/NpgsqlTypesHelper.cs
+
+
+
+
 
diff --git a/mcs/class/Npgsql/NpgsqlTypes/FastPath.cs b/mcs/class/Npgsql/NpgsqlTypes/FastPath.cs
new file mode 100644 (file)
index 0000000..e6aa2d6
--- /dev/null
@@ -0,0 +1,486 @@
+/*-------------------------------------------------------------------------
+  Fastpath.cs
+      This class is a port of the class Fastpath.java implemented by 
+      PostgreSQL Global Development Group
+ Copyright (c) 2004, Emiliano Necciari
+ Original Code: Copyright (c) 2003, PostgreSQL Global Development Group
+ Note: (Francisco Figueiredo Jr.)
+       Changed case of method names to conform to .Net names standard.
+       Also changed type names to their true names. i.e. int -> Int32
+    
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Lesser General Public License for more details.
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+-------------------------------------------------------------------------
+*/
+
+using System;
+using System.Collections;
+using System.IO;
+using System.Data;
+using Npgsql;
+
+namespace NpgsqlTypes
+{
+
+
+    /*
+     * This class implements the Fastpath api.
+     *
+     *
+     *
+     */
+    public class Fastpath
+    {
+        // This maps the functions names to their id's (possible unique just
+        // to a connection).
+        protected Hashtable func = new Hashtable();
+
+        protected NpgsqlConnection conn;               // our connection
+        protected Stream  stream;      // the network stream
+
+        /*
+         * Initialises the fastpath system
+         *
+         * @param conn BaseConnection to attach to
+         * @param stream The network stream to the backend
+         */
+        public Fastpath(NpgsqlConnection conn, Stream stream)
+        {
+            this.conn = conn;
+            this.stream = stream;
+        }
+
+
+        /*
+         * Initialises the fastpath system
+         *
+         * @param conn BaseConnection to attach to
+         * @param stream The network stream to the backend
+         */
+        public Fastpath(NpgsqlConnection conn)
+        {
+            this.conn = conn;
+            // check if the connection is closed ?
+            this.stream = conn.Connector.Stream;
+        }
+
+        /*
+         * 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 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 FastpathCall(Int32 fnid, Boolean resulttype, FastpathArg[] args)
+        {
+            if (conn.BackendProtocolVersion == ProtocolVersion.Version3)
+            {
+                return FastpathV3(fnid, resulttype, args);
+            }
+            else
+            {
+                return FastpathV2(fnid, resulttype, args);
+            }
+        }
+
+        private Object FastpathV3(Int32 fnid, Boolean resulttype, FastpathArg[] args)
+        {
+            // give  thread safety
+            lock (stream)
+            {
+                // send the function call
+                try
+                {
+                    Int32 l_msgLen = 0;
+                    l_msgLen += 16;
+                    for (Int32 i=0;i < args.Length;i++)
+                        l_msgLen += args[i].SendSize();
+
+                    stream.WriteByte((Byte)'F');
+                    PGUtil.WriteInt32(stream,l_msgLen);
+                    PGUtil.WriteInt32(stream,fnid);
+                    PGUtil.WriteInt16(stream,1);
+                    PGUtil.WriteInt16(stream,1);
+                    PGUtil.WriteInt16(stream,(short)args.Length);
+
+                    for (Int32 i = 0;i < args.Length;i++)
+                        args[i].Send(stream);
+
+                    PGUtil.WriteInt16(stream,1);
+
+                    // This is needed, otherwise data can be lost
+                    stream.Flush();
+
+                }
+                catch (Exception ex)
+                {
+                    throw new Exception(ex.ToString());
+                }
+
+                // Now handle the result
+
+                // Now loop, reading the results
+                Object result = null; // our result
+                Exception error = null;
+                Int32 c;
+                Boolean l_endQuery = false;
+                Byte[] input_buffer = new Byte[512];
+
+                while (!l_endQuery)
+                {
+                    c = (Char)stream.ReadByte();
+
+                    switch (c)
+                    {
+                    case 'A':  // Asynchronous Notify
+                        Int32 msglen = PGUtil.ReadInt32(stream,input_buffer);
+                        Int32 pid = PGUtil.ReadInt32(stream,input_buffer);
+                        String msg = PGUtil.ReadString(stream,conn.Connector.Encoding);
+                        PGUtil.ReadString(stream,conn.Connector.Encoding);
+                        String param = PGUtil.ReadString(stream,conn.Connector.Encoding);
+
+                        conn.Connector.CheckErrorsAndNotifications();
+                        break;
+                        //------------------------------
+                        // Error message returned
+                    case 'E':
+                        NpgsqlError e = new NpgsqlError(conn.BackendProtocolVersion);
+                        e.ReadFromStream(stream,conn.Connector.Encoding);
+                        throw new Exception(e.ToString());
+
+                        //------------------------------
+                        // Notice from backend
+                    case 'N':
+                        Int32 l_nlen = PGUtil.ReadInt32(stream,input_buffer);
+
+                        NpgsqlError e1 = new NpgsqlError(conn.BackendProtocolVersion);
+                        e1.ReadFromStream(stream,conn.Connector.Encoding);
+                        conn.Connector.Mediator.Errors.Add(e1);
+
+                        break;
+
+                    case 'V':
+                        Int32 l_msgLen = PGUtil.ReadInt32(stream,input_buffer);
+                        Int32 l_valueLen = PGUtil.ReadInt32(stream,input_buffer);
+
+                        if (l_valueLen == -1)
+                        {
+                            //null value
+                        }
+                        else if (l_valueLen == 0)
+                        {
+                            result = new Byte[0];
+                        }
+                        else
+                        {
+                            // Return an Integer if
+                            if (resulttype)
+
+                                result = PGUtil.ReadInt32(stream,input_buffer);
+                            else
+                            {
+                                Byte[] buf = new Byte[l_valueLen];
+
+                                Int32 bytes_from_stream = 0;
+                                Int32 total_bytes_read = 0;
+                                Int32 size = l_valueLen;
+                                do
+                                {
+                                    bytes_from_stream = stream.Read(buf, total_bytes_read, size);
+                                    total_bytes_read += bytes_from_stream;
+                                    size -= bytes_from_stream;
+                                }
+                                while(size > 0);
+
+                                result = buf;
+                            }
+                        }
+                        break;
+
+                    case 'Z':
+                        //TODO: use size better
+                        if (PGUtil.ReadInt32(stream,input_buffer) != 5)
+                            throw new Exception("Received Z" );
+                        //TODO: handle transaction status
+                        Char l_tStatus = (Char)stream.ReadByte();
+                        l_endQuery = true;
+                        break;
+
+                    default:
+                        throw new Exception("postgresql.fp.protocol received " + c.ToString());
+                    }
+                }
+
+                if ( error != null )
+                    throw error;
+
+                return result;
+            }
+        }
+
+        private Object FastpathV2(Int32 fnid, Boolean resulttype, FastpathArg[] args)
+        {
+            // added Oct 7 1998 to give us thread safety
+            lock (stream)
+            {
+                // send the function call
+                try
+                {
+                    // 70 is 'F' in ASCII. Note: don't use SendChar() here as it adds padding
+                    // that confuses the backend. The 0 terminates the command line.
+                    stream.WriteByte((Byte)70);
+                    stream.WriteByte((Byte)0);
+
+                    PGUtil.WriteInt32(stream,fnid);
+                    PGUtil.WriteInt32(stream,args.Length);
+
+
+                    for (Int32 i = 0;i < args.Length;i++)
+                        args[i].Send(stream);
+
+                    // This is needed, otherwise data can be lost
+                    stream.Flush();
+
+                }
+                catch (IOException ioe)
+                {
+                    //Should be sending exception as second arg.
+                    throw new Exception("postgresql.fp.send: "  + ioe.ToString());
+                }
+
+                // Now handle the result
+
+                // Now loop, reading the results
+                Object result = null; // our result
+                String errorMessage = "";
+                Byte[] input_buffer = new Byte[512];
+                Int32 c;
+                Boolean l_endQuery = false;
+                while (!l_endQuery)
+                {
+                    c = (Char)stream.ReadByte();
+
+                    switch (c)
+                    {
+                    case 'A':  // Asynchronous Notify
+                        //TODO: do something with this
+                        Int32 pid = PGUtil.ReadInt32(stream,input_buffer);
+                        String msg = PGUtil.ReadString(stream,conn.Connector.Encoding);
+
+
+                        conn.Connector.CheckErrorsAndNotifications();
+
+                        break;
+
+                        //------------------------------
+                        // Error message returned
+                    case 'E':
+                        NpgsqlError e = new NpgsqlError(conn.BackendProtocolVersion);
+                        e.ReadFromStream(stream,conn.Connector.Encoding);
+                        errorMessage +=  e.Message;
+                        break;
+
+                        //------------------------------
+                        // Notice from backend
+                    case 'N':
+                        NpgsqlError notice = new NpgsqlError(conn.BackendProtocolVersion);
+                        notice.ReadFromStream(stream,conn.Connector.Encoding);
+                        errorMessage +=  notice.Message;
+                        break;
+
+                    case 'V':
+                        Char l_nextChar = (Char)stream.ReadByte();
+                        if (l_nextChar == 'G')
+                        {
+                            Int32 sz = PGUtil.ReadInt32(stream,input_buffer);
+                            // Return an Integer if
+                            if (resulttype)
+                                result = PGUtil.ReadInt32(stream,input_buffer);
+                            else
+                            {
+                                Byte[] buf = new Byte[sz];
+
+                                Int32 bytes_from_stream = 0;
+                                Int32 total_bytes_read = 0;
+                                Int32 size = sz;
+                                do
+                                {
+                                    bytes_from_stream = stream.Read(buf, total_bytes_read, size);
+                                    total_bytes_read += bytes_from_stream;
+                                    size -= bytes_from_stream;
+                                }
+                                while(size > 0);
+
+                                result = buf;
+                            }
+                            //There should be a trailing '0'
+                            Int32 l_endChar = (Char)stream.ReadByte();
+                        }
+                        else
+                        {
+                            //it must have been a '0', thus no results
+                        }
+                        break;
+
+                    case 'Z':
+                        l_endQuery = true;
+                        break;
+
+                    default:
+                        throw new Exception("postgresql.fp.protocol " + c.ToString());
+                    }
+                }
+
+                if ( errorMessage != null )
+                    throw new Exception("postgresql.fp.error" + errorMessage);
+
+                return result;
+            }
+        }
+
+        /*
+         * Send a function call to the PostgreSQL backend by name.
+         *
+         * Note: the mapping for the procedure name to function id needs to exist,
+         * usually to an earlier call to addfunction().
+         *
+         * This is the prefered method to call, as function id's can/may change
+         * between versions of the backend.
+         *
+         * For an example of how this works, refer to org.postgresql.largeobject.LargeObject
+         *
+         * @param name Function name
+         * @param resulttype True if the result is an integer, false for other
+         * results
+         * @param args FastpathArguments to pass to fastpath
+         * @return null if no data, Integer if an integer result, or byte[] otherwise
+         * @exception SQLException if name is unknown or if a database-access error
+         * occurs.
+         * @see org.postgresql.largeobject.LargeObject
+         */
+        public Object FastpathCall(String name, Boolean resulttype, FastpathArg[] args)
+        {
+            return FastpathCall(GetID(name), resulttype, args);
+        }
+
+        /*
+         * This convenience method assumes that the return value is an Integer
+         * @param name Function name
+         * @param args Function arguments
+         * @return integer result
+         * @exception SQLException if a database-access error occurs or no result
+         */
+        public Int32 GetInteger(String name, FastpathArg[] args)
+        {
+            Int32 i = (Int32)FastpathCall(name, true, args);
+
+            return i;
+        }
+
+        /*
+         * This convenience method assumes that the return value is an Integer
+         * @param name Function name
+         * @param args Function arguments
+         * @return byte[] array containing result
+         * @exception SQLException if a database-access error occurs or no result
+         */
+        public Byte[] GetData(String name, FastpathArg[] args)
+        {
+            return (Byte[])FastpathCall(name, false, args);
+        }
+
+        /*
+         * This adds a function to our lookup table.
+         *
+         * <p>User code should use the addFunctions method, which is based upon a
+         * query, rather than hard coding the oid. The oid for a function is not
+         * guaranteed to remain static, even on different servers of the same
+         * version.
+         *
+         * @param name Function name
+         * @param fnid Function id
+         */
+        public void AddFunction(String name, Int32 fnid)
+        {
+            func.Add(name, fnid);
+        }
+
+        /*
+         * This takes a ResultSet containing two columns. Column 1 contains the
+         * function name, Column 2 the oid.
+         *
+         * <p>It reads the entire ResultSet, loading the values into the function
+         * table.
+         *
+         * <p><b>REMEMBER</b> to close() the resultset after calling this!!
+         *
+         * <p><b><em>Implementation note about function name lookups:</em></b>
+         *
+         * <p>PostgreSQL stores the function id's and their corresponding names in
+         * the pg_proc table. To speed things up locally, instead of querying each
+         * function from that table when required, a Hashtable is used. Also, only
+         * the function's required are entered into this table, keeping connection
+         * times as fast as possible.
+         *
+         * <p>The org.postgresql.largeobject.LargeObject class performs a query upon it's startup,
+         * and passes the returned ResultSet to the addFunctions() method here.
+         *
+         * <p>Once this has been done, the LargeObject api refers to the functions by
+         * name.
+         *
+         * <p>Dont think that manually converting them to the oid's will work. Ok,
+         * they will for now, but they can change during development (there was some
+         * discussion about this for V7.0), so this is implemented to prevent any
+         * unwarranted headaches in the future.
+         *
+         * @param rs ResultSet
+         * @exception SQLException if a database-access error occurs.
+         * @see org.postgresql.largeobject.LargeObjectManager
+         */
+        public void AddFunctions(IDataReader rs)
+        {
+            while (rs.Read())
+            {
+                String key = (String)rs[0];
+                if( !func.ContainsKey(key ) )
+                    func.Add(key, Int32.Parse(rs[1].ToString()));
+            }
+        }
+
+        /*
+         * This returns the function id associated by its name
+         *
+         * <p>If addFunction() or addFunctions() have not been called for this name,
+         * then an SQLException is thrown.
+         *
+         * @param name Function name to lookup
+         * @return Function ID for fastpath call
+         * @exception SQLException is function is unknown.
+         */
+        public Int32 GetID(String name)
+        {
+            Int32 id = (Int32)func[name];
+
+
+
+            return id;
+        }
+    }
+
+}
diff --git a/mcs/class/Npgsql/NpgsqlTypes/FastPathArg.cs b/mcs/class/Npgsql/NpgsqlTypes/FastPathArg.cs
new file mode 100644 (file)
index 0000000..5127726
--- /dev/null
@@ -0,0 +1,135 @@
+/*-------------------------------------------------------------------------
+  FastpathArg.cs
+      This class is a port of the class FastpathArg.java implemented by 
+      PostgreSQL Global Development Group
+  
+ Copyright (c) 2004, Emiliano Necciari
+ Original Code: Copyright (c) 2003, PostgreSQL Global Development Group
+ Note: (Francisco Figueiredo Jr.)
+       Changed case of method names to conform to .Net names standard.
+       Also changed type names to their true names. i.e. int -> Int32
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Lesser General Public License for more details.
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+-------------------------------------------------------------------------
+*/
+
+using System;
+using System.IO;
+using Npgsql;
+
+namespace NpgsqlTypes
+{
+    public class FastpathArg
+    {
+        /*
+         * Type of argument, true=integer, false=byte[]
+         */
+        public Boolean type;
+
+        /*
+         * Integer value if type=true
+         */
+        public Int32 value;
+
+        /*
+         * Byte value if type=false;
+         */
+        public Byte[] bytes;
+
+        /*
+         * Constructs an argument that consists of an integer value
+         * @param value int value to set
+         */
+        public FastpathArg(Int32 value)
+        {
+            type = true;
+            this.value = value;
+        }
+
+        /*
+         * Constructs an argument that consists of an array of bytes
+         * @param bytes array to store
+         */
+        public FastpathArg(Byte[] bytes)
+        {
+            type = false;
+            this.bytes = bytes;
+        }
+
+        /*
+         * Constructs an argument that consists of part of a byte array
+         * @param buf source array
+         * @param off offset within array
+         * @param len length of data to include
+         */
+        public FastpathArg(Byte[] buf, Int32 off, Int32 len)
+        {
+            type = false;
+            bytes = new Byte[len];
+            //TODO:
+            bytes = buf;
+        }
+
+        /*
+         * Constructs an argument that consists of a String.
+         * @param s String to store
+         */
+        public FastpathArg(String s)
+        {
+            //this(s.ToCharArray());
+        }
+
+        /*
+         * This sends this argument down the network stream.
+         *
+         * <p>The stream sent consists of the length.int4 then the contents.
+         *
+         * <p><b>Note:</b> This is called from Fastpath, and cannot be called from
+         * client code.
+         *
+         * @param s output stream
+         * @exception IOException if something failed on the network stream
+         */
+        public void Send(Stream s)
+        {
+            if (type)
+            {
+                // argument is an integer
+                PGUtil.WriteInt32(s, 4);
+                PGUtil.WriteInt32(s, value);   // integer value of argument
+            }
+            else
+            {
+                // argument is a byte array
+                PGUtil.WriteInt32(s, bytes.Length);
+                s.Write(bytes,0,bytes.Length);
+            }
+        }
+
+        public Int32 SendSize()
+        {
+            if (type)
+            {
+                return 8;
+            }
+            else
+            {
+                return 4+bytes.Length;
+            }
+        }
+    }
+}
diff --git a/mcs/class/Npgsql/NpgsqlTypes/LargeObject.cs b/mcs/class/Npgsql/NpgsqlTypes/LargeObject.cs
new file mode 100644 (file)
index 0000000..b109071
--- /dev/null
@@ -0,0 +1,263 @@
+/*-------------------------------------------------------------------------
+  LargeObject.cs
+      This class is a port of the class LargeObject.java implemented by 
+      PostgreSQL Global Development Group
+  Copyright (c) 2004, Emiliano Necciari
+  Original Code: Copyright (c) 2003, PostgreSQL Global Development Group
+  
+  Note: (Francisco Figueiredo Jr.)
+       Changed case of method names to conform to .Net names standard.
+       Also changed type names to their true names. i.e. int -> Int32
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Lesser General Public License for more details.
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+-------------------------------------------------------------------------
+*/
+
+using System;
+using System.IO;
+using Npgsql;
+
+namespace NpgsqlTypes
+{
+
+    public class LargeObject
+    {
+        /*
+         * Indicates a seek from the begining of a file
+         */
+        public  const Int32 SEEK_SET = 0;
+
+        /*
+         * Indicates a seek from the current position
+         */
+        public  const Int32 SEEK_CUR = 1;
+
+        /*
+         * Indicates a seek from the end of a file
+         */
+        public  const Int32 SEEK_END = 2;
+
+        private Fastpath       fp; // Fastpath API to use
+        private Int32 oid;     // OID of this object
+        private Int32 fd; // the descriptor of the open large object
+
+        private Boolean closed = false; // true when we are closed
+
+        /*
+         * This opens a large object.
+         *
+         * <p>If the object does not exist, then an SQLException is thrown.
+         *
+         * @param fp FastPath API for the connection to use
+         * @param oid of the Large Object to open
+         * @param mode Mode of opening the large object
+         * (defined in LargeObjectManager)
+         * @exception SQLException if a database-access error occurs.
+         * @see org.postgresql.largeobject.LargeObjectManager
+         */
+        public LargeObject(Fastpath fp, Int32 oid, Int32 mode)
+        {
+            this.fp = fp;
+            this.oid = oid;
+
+            FastpathArg[] args = new FastpathArg[2];
+            args[0] = new FastpathArg(oid);
+            args[1] = new FastpathArg(mode);
+            this.fd = fp.GetInteger("lo_open", args);
+        }
+
+
+        /*
+         * @return the OID of this LargeObject
+         */
+        public Int32 GetOID()
+        {
+            return oid;
+        }
+
+        /*
+         * This method closes the object. You must not call methods in this
+         * object after this is called.
+         * @exception SQLException if a database-access error occurs.
+         */
+        public void Close()
+        {
+            if (!closed)
+            {
+
+                // finally close
+                FastpathArg[] args = new FastpathArg[1];
+                args[0] = new FastpathArg(fd);
+                fp.FastpathCall("lo_close", false, args); // true here as we dont care!!
+                closed = true;
+            }
+        }
+
+        /*
+         * Reads some data from the object, and return as a byte[] array
+         *
+         * @param len number of bytes to read
+         * @return byte[] array containing data read
+         * @exception SQLException if a database-access error occurs.
+         */
+        public Byte[] Read(Int32 len)
+        {
+            // This is the original method, where the entire block (len bytes)
+            // is retrieved in one go.
+            FastpathArg[] args = new FastpathArg[2];
+            args[0] = new FastpathArg(fd);
+            args[1] = new FastpathArg(len);
+            return fp.GetData("loread", args);
+
+            // This version allows us to break this down Int32o 4k blocks
+            //if (len<=4048) {
+            //// handle as before, return the whole block in one go
+            //FastpathArg args[] = new FastpathArg[2];
+            //args[0] = new FastpathArg(fd);
+            //args[1] = new FastpathArg(len);
+            //return fp.getData("loread",args);
+            //} else {
+            //// return in 4k blocks
+            //byte[] buf=new byte[len];
+            //int off=0;
+            //while (len>0) {
+            //int bs=4048;
+            //len-=bs;
+            //if (len<0) {
+            //bs+=len;
+            //len=0;
+            //}
+            //read(buf,off,bs);
+            //off+=bs;
+            //}
+            //return buf;
+            //}
+        }
+
+        /*
+         * Reads some data from the object into an existing array
+         *
+         * @param buf destination array
+         * @param off offset within array
+         * @param len number of bytes to read
+         * @return the number of bytes actually read
+         * @exception SQLException if a database-access error occurs.
+         */
+        public Int32 Read(Byte[] buf, Int32 off, Int32 len)
+        {
+            Byte[] b = Read(len);
+            if (b.Length < len)
+                len = b.Length;
+            Array.Copy(b,0,buf,off,len);
+            return len;
+        }
+
+        /*
+         * Writes an array to the object
+         *
+         * @param buf array to write
+         * @exception SQLException if a database-access error occurs.
+         */
+        public void Write(Byte[] buf)
+        {
+            FastpathArg[] args = new FastpathArg[2];
+            args[0] = new FastpathArg(fd);
+            args[1] = new FastpathArg(buf);
+            fp.FastpathCall("lowrite", false, args);
+        }
+
+        /*
+         * Writes some data from an array to the object
+         *
+         * @param buf destination array
+         * @param off offset within array
+         * @param len number of bytes to write
+         * @exception SQLException if a database-access error occurs.
+         */
+        public void Write(Byte[] buf, Int32 off, Int32 len)
+        {
+            Byte[] data = new Byte[len];
+
+            System.Array.Copy(buf, off, data, 0, len);
+            Write(data);
+        }
+
+        /*
+         * Sets the current position within the object.
+         *
+         * <p>This is similar to the fseek() call in the standard C library. It
+         * allows you to have random access to the large object.
+         *
+         * @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 Seek(Int32 pos, Int32 refi)
+        {
+            FastpathArg[] args = new FastpathArg[3];
+            args[0] = new FastpathArg(fd);
+            args[1] = new FastpathArg(pos);
+            args[2] = new FastpathArg(refi);
+            fp.FastpathCall("lo_lseek", false, args);
+        }
+
+        /*
+         * Sets the current position within the object.
+         *
+         * <p>This is similar to the fseek() call in the standard C library. It
+         * allows you to have random access to the large object.
+         *
+         * @param pos position within object from begining
+         * @exception SQLException if a database-access error occurs.
+         */
+        public void Seek(Int32 pos)
+        {
+            Seek(pos, SEEK_SET);
+        }
+
+        /*
+         * @return the current position within the object
+         * @exception SQLException if a database-access error occurs.
+         */
+        public Int32 Tell()
+        {
+            FastpathArg[] args = new FastpathArg[1];
+            args[0] = new FastpathArg(fd);
+            return fp.GetInteger("lo_tell", 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
+         * return to the original position.
+         *
+         * <p>A better method will be found in the future.
+         *
+         * @return the size of the large object
+         * @exception SQLException if a database-access error occurs.
+         */
+        public Int32 Size()
+        {
+            Int32 cp = Tell();
+            Seek(0, SEEK_END);
+            Int32 sz = Tell();
+            Seek(cp, SEEK_SET);
+            return sz;
+        }
+
+    }
+}
diff --git a/mcs/class/Npgsql/NpgsqlTypes/LargeObjectManager.cs b/mcs/class/Npgsql/NpgsqlTypes/LargeObjectManager.cs
new file mode 100644 (file)
index 0000000..10e2532
--- /dev/null
@@ -0,0 +1,209 @@
+/*-------------------------------------------------------------------------
+  LargeObjectManager.cs
+      This class is a port of the class LargeObjectManager.java implemented by 
+      PostgreSQL Global Development Group
+ Copyright (c) 2004, Emiliano Necciari
+ Original Code: Copyright (c) 2003, PostgreSQL Global Development Group
+ Note: (Francisco Figueiredo Jr.)
+       Changed case of method names to conform to .Net names standard.
+       Also changed type names to their true names. i.e. int -> Int32
+    
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Lesser General Public License for more details.
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+-------------------------------------------------------------------------
+*/
+
+using System;
+using System.Data;
+using Npgsql;
+
+namespace NpgsqlTypes
+{
+    /// <summary>
+    /// Summary description for LargeObjectManager.
+    /// </summary>
+    public class LargeObjectManager
+    {
+        // the fastpath api for this connection
+        private Fastpath fp;
+
+        /*
+         * This mode indicates we want to write to an object
+         */
+        public const Int32 WRITE = 0x00020000;
+
+        /*
+         * This mode indicates we want to read an object
+         */
+        public static  Int32 READ = 0x00040000;
+
+        /*
+         * This mode is the default. It indicates we want read and write access to
+         * a large object
+         */
+        public static Int32 READWRITE = READ | WRITE;
+
+        /*
+         * This prevents us being created by mere mortals
+         */
+        private LargeObjectManager()
+        {}
+
+        /*
+         * Constructs the LargeObject API.
+         *
+         * <p><b>Important Notice</b>
+         * <br>This method should only be called by org.postgresql.Connection
+         *
+         * <p>There should only be one LargeObjectManager per Connection. The
+         * org.postgresql.Connection class keeps track of the various extension API's
+         * and it's advised you use those to gain access, and not going direct.
+         */
+        public LargeObjectManager(NpgsqlConnection conn)
+        {
+            // We need Fastpath to do anything
+            // Now get the function oid's for the api
+            //
+            // This is an example of Fastpath.addFunctions();
+            //
+            String sql;
+            if (conn.ServerVersion > new ServerVersion(7,3,0) )
+            {
+
+                sql = "SELECT p.proname,p.oid "+
+                      " FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n "+
+                      " WHERE p.pronamespace=n.oid AND n.nspname='pg_catalog' AND (";
+            }
+            else
+            {
+                sql = "SELECT proname,oid FROM pg_proc WHERE ";
+            }
+            sql += " proname = 'lo_open'" +
+                   " or proname = 'lo_close'" +
+                   " or proname = 'lo_creat'" +
+                   " or proname = 'lo_unlink'" +
+                   " or proname = 'lo_lseek'" +
+                   " or proname = 'lo_tell'" +
+                   " or proname = 'loread'" +
+                   " or proname = 'lowrite'";
+
+            if (conn.ServerVersion > new ServerVersion(7,3,0) )
+            {
+                sql += ")";
+            }
+
+            IDbCommand cmd = new NpgsqlCommand(sql);
+            cmd.Connection = conn;
+
+            this.fp = new Fastpath(conn,conn.Connector.Stream);
+
+            IDataReader res = cmd.ExecuteReader(CommandBehavior.CloseConnection);
+
+
+            if (res == null)
+                throw new Exception("postgresql.lo.init");
+
+
+            fp.AddFunctions(res);
+        }
+
+        /*
+         * This opens an existing large object, based on its OID. This method
+         * assumes that READ and WRITE access is required (the default).
+         *
+         * @param oid of large object
+         * @return LargeObject instance providing access to the object
+         * @exception SQLException on error
+         */
+        public LargeObject Open(Int32 oid)
+        {
+            return new LargeObject(fp, oid, READWRITE);
+        }
+
+        /*
+         * This opens an existing large object, based on its OID
+         *
+         * @param oid of large object
+         * @param mode mode of open
+         * @return LargeObject instance providing access to the object
+         * @exception SQLException on error
+         */
+        public LargeObject Open(Int32 oid, Int32 mode)
+        {
+            return new LargeObject(fp, oid, mode);
+        }
+
+        /*
+         * This creates a large object, returning its OID.
+         *
+         * <p>It defaults to READWRITE for the new object's attributes.
+         *
+         * @return oid of new object
+         * @exception SQLException on error
+         */
+        public Int32 Create()
+        {
+            FastpathArg[] args = new FastpathArg[1];
+            args[0] = new FastpathArg(READWRITE);
+            return fp.GetInteger("lo_creat", args);
+        }
+
+        /*
+         * This creates a large object, returning its OID
+         *
+         * @param mode a bitmask describing different attributes of the new object
+         * @return oid of new object
+         * @exception SQLException on error
+         */
+        public Int32 Create(Int32 mode)
+        {
+            FastpathArg[] args = new FastpathArg[1];
+            args[0] = new FastpathArg(mode);
+            return fp.GetInteger("lo_creat", args);
+        }
+
+        /*
+         * This deletes a large object.
+         *
+         * @param oid describing object to delete
+         * @exception SQLException on error
+         */
+        public void Delete(Int32 oid)
+        {
+            FastpathArg[] args = new FastpathArg[1];
+            args[0] = new FastpathArg(oid);
+            fp.FastpathCall("lo_unlink", false, args);
+        }
+
+        /*
+         * This deletes a large object.
+         *
+         * <p>It is identical to the delete method, and is supplied as the C API uses
+         * unlink.
+         *
+         * @param oid describing object to delete
+         * @exception SQLException on error
+         */
+        public void Unlink(Int32 oid)
+        {
+            Delete(oid);
+        }
+
+    }
+
+}