(DISTFILES): Comment out a few missing files.
[mono-project.git] / mcs / class / Npgsql / NpgsqlTypes / FastPath.cs
blobe6aa2d62b2dc79b757868a7e602171a71c819919
1 /*-------------------------------------------------------------------------
3 Fastpath.cs
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 -------------------------------------------------------------------------
30 using System;
31 using System.Collections;
32 using System.IO;
33 using System.Data;
34 using Npgsql;
36 namespace NpgsqlTypes
41 * This class implements the Fastpath api.
46 public class Fastpath
48 // This maps the functions names to their id's (possible unique just
49 // to a connection).
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)
63 this.conn = conn;
64 this.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)
76 this.conn = 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);
96 else
98 return FastpathV2(fnid, resulttype, args);
102 private Object FastpathV3(Int32 fnid, Boolean resulttype, FastpathArg[] args)
104 // give thread safety
105 lock (stream)
107 // send the function call
110 Int32 l_msgLen = 0;
111 l_msgLen += 16;
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
128 stream.Flush();
131 catch (Exception ex)
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;
141 Int32 c;
142 Boolean l_endQuery = false;
143 Byte[] input_buffer = new Byte[512];
145 while (!l_endQuery)
147 c = (Char)stream.ReadByte();
149 switch (c)
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();
159 break;
160 //------------------------------
161 // Error message returned
162 case 'E':
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
169 case 'N':
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);
176 break;
178 case 'V':
179 Int32 l_msgLen = PGUtil.ReadInt32(stream,input_buffer);
180 Int32 l_valueLen = PGUtil.ReadInt32(stream,input_buffer);
182 if (l_valueLen == -1)
184 //null value
186 else if (l_valueLen == 0)
188 result = new Byte[0];
190 else
192 // Return an Integer if
193 if (resulttype)
195 result = PGUtil.ReadInt32(stream,input_buffer);
196 else
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;
209 while(size > 0);
211 result = buf;
214 break;
216 case 'Z':
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();
222 l_endQuery = true;
223 break;
225 default:
226 throw new Exception("postgresql.fp.protocol received " + c.ToString());
230 if ( error != null )
231 throw error;
233 return result;
237 private Object FastpathV2(Int32 fnid, Boolean resulttype, FastpathArg[] args)
239 // added Oct 7 1998 to give us thread safety
240 lock (stream)
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
258 stream.Flush();
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];
273 Int32 c;
274 Boolean l_endQuery = false;
275 while (!l_endQuery)
277 c = (Char)stream.ReadByte();
279 switch (c)
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();
289 break;
291 //------------------------------
292 // Error message returned
293 case 'E':
294 NpgsqlError e = new NpgsqlError(conn.BackendProtocolVersion);
295 e.ReadFromStream(stream,conn.Connector.Encoding);
296 errorMessage += e.Message;
297 break;
299 //------------------------------
300 // Notice from backend
301 case 'N':
302 NpgsqlError notice = new NpgsqlError(conn.BackendProtocolVersion);
303 notice.ReadFromStream(stream,conn.Connector.Encoding);
304 errorMessage += notice.Message;
305 break;
307 case 'V':
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
313 if (resulttype)
314 result = PGUtil.ReadInt32(stream,input_buffer);
315 else
317 Byte[] buf = new Byte[sz];
319 Int32 bytes_from_stream = 0;
320 Int32 total_bytes_read = 0;
321 Int32 size = sz;
324 bytes_from_stream = stream.Read(buf, total_bytes_read, size);
325 total_bytes_read += bytes_from_stream;
326 size -= bytes_from_stream;
328 while(size > 0);
330 result = buf;
332 //There should be a trailing '0'
333 Int32 l_endChar = (Char)stream.ReadByte();
335 else
337 //it must have been a '0', thus no results
339 break;
341 case 'Z':
342 l_endQuery = true;
343 break;
345 default:
346 throw new Exception("postgresql.fp.protocol " + c.ToString());
350 if ( errorMessage != null )
351 throw new Exception("postgresql.fp.error" + errorMessage);
353 return result;
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
370 * results
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
374 * occurs.
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);
393 return i;
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
414 * version.
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
429 * table.
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
445 * name.
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)
458 while (rs.Read())
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];
482 return id;