1 /*-------------------------------------------------------------------------
4 This class is a port of the class Fastpath.java implemented by
5 PostgreSQL Global Development Group
7 Copyright (c) 2004, Emiliano Necciari
8 Original Code: Copyright (c) 2003, PostgreSQL Global Development Group
10 Note: (Francisco Figueiredo Jr.)
11 Changed case of method names to conform to .Net names standard.
12 Also changed type names to their true names. i.e. int -> Int32
14 This library is free software; you can redistribute it and/or
15 modify it under the terms of the GNU Lesser General Public
16 License as published by the Free Software Foundation; either
17 version 2.1 of the License, or (at your option) any later version.
19 This library is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 Lesser General Public License for more details.
24 You should have received a copy of the GNU Lesser General Public
25 License along with this library; if not, write to the Free Software
26 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 -------------------------------------------------------------------------
31 using System
.Collections
;
41 * This class implements the Fastpath api.
48 // This maps the functions names to their id's (possible unique just
50 protected Hashtable func
= new Hashtable();
52 protected NpgsqlConnection conn
; // our connection
53 protected Stream stream
; // the network stream
56 * Initialises the fastpath system
58 * @param conn BaseConnection to attach to
59 * @param stream The network stream to the backend
61 public Fastpath(NpgsqlConnection conn
, Stream stream
)
69 * Initialises the fastpath system
71 * @param conn BaseConnection to attach to
72 * @param stream The network stream to the backend
74 public Fastpath(NpgsqlConnection conn
)
77 // check if the connection is closed ?
78 this.stream
= conn
.Connector
.Stream
;
82 * Send a function call to the PostgreSQL backend
84 * @param fnid Function id
85 * @param resulttype True if the result is an integer, false for other results
86 * @param args FastpathArguments to pass to fastpath
87 * @return null if no data, Integer if an integer result, or byte[] otherwise
88 * @exception SQLException if a database-access error occurs.
90 public Object
FastpathCall(Int32 fnid
, Boolean resulttype
, FastpathArg
[] args
)
92 if (conn
.BackendProtocolVersion
== ProtocolVersion
.Version3
)
94 return FastpathV3(fnid
, resulttype
, args
);
98 return FastpathV2(fnid
, resulttype
, args
);
102 private Object
FastpathV3(Int32 fnid
, Boolean resulttype
, FastpathArg
[] args
)
104 // give thread safety
107 // send the function call
112 for (Int32 i
=0;i
< args
.Length
;i
++)
113 l_msgLen
+= args
[i
].SendSize();
115 stream
.WriteByte((Byte
)'F');
116 PGUtil
.WriteInt32(stream
,l_msgLen
);
117 PGUtil
.WriteInt32(stream
,fnid
);
118 PGUtil
.WriteInt16(stream
,1);
119 PGUtil
.WriteInt16(stream
,1);
120 PGUtil
.WriteInt16(stream
,(short)args
.Length
);
122 for (Int32 i
= 0;i
< args
.Length
;i
++)
123 args
[i
].Send(stream
);
125 PGUtil
.WriteInt16(stream
,1);
127 // This is needed, otherwise data can be lost
133 throw new Exception(ex
.ToString());
136 // Now handle the result
138 // Now loop, reading the results
139 Object result
= null; // our result
140 Exception error
= null;
142 Boolean l_endQuery
= false;
143 Byte
[] input_buffer
= new Byte
[512];
147 c
= (Char
)stream
.ReadByte();
151 case 'A': // Asynchronous Notify
152 Int32 msglen
= PGUtil
.ReadInt32(stream
,input_buffer
);
153 Int32 pid
= PGUtil
.ReadInt32(stream
,input_buffer
);
154 String msg
= PGUtil
.ReadString(stream
,conn
.Connector
.Encoding
);
155 PGUtil
.ReadString(stream
,conn
.Connector
.Encoding
);
156 String param
= PGUtil
.ReadString(stream
,conn
.Connector
.Encoding
);
158 conn
.Connector
.CheckErrorsAndNotifications();
160 //------------------------------
161 // Error message returned
163 NpgsqlError e
= new NpgsqlError(conn
.BackendProtocolVersion
);
164 e
.ReadFromStream(stream
,conn
.Connector
.Encoding
);
165 throw new Exception(e
.ToString());
167 //------------------------------
168 // Notice from backend
170 Int32 l_nlen
= PGUtil
.ReadInt32(stream
,input_buffer
);
172 NpgsqlError e1
= new NpgsqlError(conn
.BackendProtocolVersion
);
173 e1
.ReadFromStream(stream
,conn
.Connector
.Encoding
);
174 conn
.Connector
.Mediator
.Errors
.Add(e1
);
179 Int32 l_msgLen
= PGUtil
.ReadInt32(stream
,input_buffer
);
180 Int32 l_valueLen
= PGUtil
.ReadInt32(stream
,input_buffer
);
182 if (l_valueLen
== -1)
186 else if (l_valueLen
== 0)
188 result
= new Byte
[0];
192 // Return an Integer if
195 result
= PGUtil
.ReadInt32(stream
,input_buffer
);
198 Byte
[] buf
= new Byte
[l_valueLen
];
200 Int32 bytes_from_stream
= 0;
201 Int32 total_bytes_read
= 0;
202 Int32 size
= l_valueLen
;
205 bytes_from_stream
= stream
.Read(buf
, total_bytes_read
, size
);
206 total_bytes_read
+= bytes_from_stream
;
207 size
-= bytes_from_stream
;
217 //TODO: use size better
218 if (PGUtil
.ReadInt32(stream
,input_buffer
) != 5)
219 throw new Exception("Received Z" );
220 //TODO: handle transaction status
221 Char l_tStatus
= (Char
)stream
.ReadByte();
226 throw new Exception("postgresql.fp.protocol received " + c
.ToString());
237 private Object
FastpathV2(Int32 fnid
, Boolean resulttype
, FastpathArg
[] args
)
239 // added Oct 7 1998 to give us thread safety
242 // send the function call
245 // 70 is 'F' in ASCII. Note: don't use SendChar() here as it adds padding
246 // that confuses the backend. The 0 terminates the command line.
247 stream
.WriteByte((Byte
)70);
248 stream
.WriteByte((Byte
)0);
250 PGUtil
.WriteInt32(stream
,fnid
);
251 PGUtil
.WriteInt32(stream
,args
.Length
);
254 for (Int32 i
= 0;i
< args
.Length
;i
++)
255 args
[i
].Send(stream
);
257 // This is needed, otherwise data can be lost
261 catch (IOException ioe
)
263 //Should be sending exception as second arg.
264 throw new Exception("postgresql.fp.send: " + ioe
.ToString());
267 // Now handle the result
269 // Now loop, reading the results
270 Object result
= null; // our result
271 String errorMessage
= "";
272 Byte
[] input_buffer
= new Byte
[512];
274 Boolean l_endQuery
= false;
277 c
= (Char
)stream
.ReadByte();
281 case 'A': // Asynchronous Notify
282 //TODO: do something with this
283 Int32 pid
= PGUtil
.ReadInt32(stream
,input_buffer
);
284 String msg
= PGUtil
.ReadString(stream
,conn
.Connector
.Encoding
);
287 conn
.Connector
.CheckErrorsAndNotifications();
291 //------------------------------
292 // Error message returned
294 NpgsqlError e
= new NpgsqlError(conn
.BackendProtocolVersion
);
295 e
.ReadFromStream(stream
,conn
.Connector
.Encoding
);
296 errorMessage
+= e
.Message
;
299 //------------------------------
300 // Notice from backend
302 NpgsqlError notice
= new NpgsqlError(conn
.BackendProtocolVersion
);
303 notice
.ReadFromStream(stream
,conn
.Connector
.Encoding
);
304 errorMessage
+= notice
.Message
;
308 Char l_nextChar
= (Char
)stream
.ReadByte();
309 if (l_nextChar
== 'G')
311 Int32 sz
= PGUtil
.ReadInt32(stream
,input_buffer
);
312 // Return an Integer if
314 result
= PGUtil
.ReadInt32(stream
,input_buffer
);
317 Byte
[] buf
= new Byte
[sz
];
319 Int32 bytes_from_stream
= 0;
320 Int32 total_bytes_read
= 0;
324 bytes_from_stream
= stream
.Read(buf
, total_bytes_read
, size
);
325 total_bytes_read
+= bytes_from_stream
;
326 size
-= bytes_from_stream
;
332 //There should be a trailing '0'
333 Int32 l_endChar
= (Char
)stream
.ReadByte();
337 //it must have been a '0', thus no results
346 throw new Exception("postgresql.fp.protocol " + c
.ToString());
350 if ( errorMessage
!= null )
351 throw new Exception("postgresql.fp.error" + errorMessage
);
358 * Send a function call to the PostgreSQL backend by name.
360 * Note: the mapping for the procedure name to function id needs to exist,
361 * usually to an earlier call to addfunction().
363 * This is the prefered method to call, as function id's can/may change
364 * between versions of the backend.
366 * For an example of how this works, refer to org.postgresql.largeobject.LargeObject
368 * @param name Function name
369 * @param resulttype True if the result is an integer, false for other
371 * @param args FastpathArguments to pass to fastpath
372 * @return null if no data, Integer if an integer result, or byte[] otherwise
373 * @exception SQLException if name is unknown or if a database-access error
375 * @see org.postgresql.largeobject.LargeObject
377 public Object
FastpathCall(String name
, Boolean resulttype
, FastpathArg
[] args
)
379 return FastpathCall(GetID(name
), resulttype
, args
);
383 * This convenience method assumes that the return value is an Integer
384 * @param name Function name
385 * @param args Function arguments
386 * @return integer result
387 * @exception SQLException if a database-access error occurs or no result
389 public Int32
GetInteger(String name
, FastpathArg
[] args
)
391 Int32 i
= (Int32
)FastpathCall(name
, true, args
);
397 * This convenience method assumes that the return value is an Integer
398 * @param name Function name
399 * @param args Function arguments
400 * @return byte[] array containing result
401 * @exception SQLException if a database-access error occurs or no result
403 public Byte
[] GetData(String name
, FastpathArg
[] args
)
405 return (Byte
[])FastpathCall(name
, false, args
);
409 * This adds a function to our lookup table.
411 * <p>User code should use the addFunctions method, which is based upon a
412 * query, rather than hard coding the oid. The oid for a function is not
413 * guaranteed to remain static, even on different servers of the same
416 * @param name Function name
417 * @param fnid Function id
419 public void AddFunction(String name
, Int32 fnid
)
421 func
.Add(name
, fnid
);
425 * This takes a ResultSet containing two columns. Column 1 contains the
426 * function name, Column 2 the oid.
428 * <p>It reads the entire ResultSet, loading the values into the function
431 * <p><b>REMEMBER</b> to close() the resultset after calling this!!
433 * <p><b><em>Implementation note about function name lookups:</em></b>
435 * <p>PostgreSQL stores the function id's and their corresponding names in
436 * the pg_proc table. To speed things up locally, instead of querying each
437 * function from that table when required, a Hashtable is used. Also, only
438 * the function's required are entered into this table, keeping connection
439 * times as fast as possible.
441 * <p>The org.postgresql.largeobject.LargeObject class performs a query upon it's startup,
442 * and passes the returned ResultSet to the addFunctions() method here.
444 * <p>Once this has been done, the LargeObject api refers to the functions by
447 * <p>Dont think that manually converting them to the oid's will work. Ok,
448 * they will for now, but they can change during development (there was some
449 * discussion about this for V7.0), so this is implemented to prevent any
450 * unwarranted headaches in the future.
452 * @param rs ResultSet
453 * @exception SQLException if a database-access error occurs.
454 * @see org.postgresql.largeobject.LargeObjectManager
456 public void AddFunctions(IDataReader rs
)
460 String key
= (String
)rs
[0];
461 if( !func
.ContainsKey(key
) )
462 func
.Add(key
, Int32
.Parse(rs
[1].ToString()));
467 * This returns the function id associated by its name
469 * <p>If addFunction() or addFunctions() have not been called for this name,
470 * then an SQLException is thrown.
472 * @param name Function name to lookup
473 * @return Function ID for fastpath call
474 * @exception SQLException is function is unknown.
476 public Int32
GetID(String name
)
478 Int32 id
= (Int32
)func
[name
];