2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / Mono.Data.DB2Client / Mono.Data.Db2Client / DB2ClientDataReader.cs
1
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining
4 // a copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to
8 // permit persons to whom the Software is furnished to do so, subject to
9 // the following conditions:
10 // 
11 // The above copyright notice and this permission notice shall be
12 // included in all copies or substantial portions of the Software.
13 // 
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 //
22 #region Licence\r
23         /// DB2DriverCS - A DB2 driver for .Net\r
24         /// Copyright 2003 By Christopher Bockner\r
25         /// Released under the terms of the MIT/X11 Licence\r
26         /// Please refer to the Licence.txt file that should be distributed with this package\r
27         /// This software requires that DB2 client software be installed correctly on the machine\r
28         /// (or instance) on which the driver is running.  \r
29 #endregion\r
30 using System;\r
31 using System.Data;\r
32 using System.Runtime.InteropServices;\r
33 using System.Globalization;\r
34 using System.Text;\r
35 \r
36 namespace DB2ClientCS\r
37 {\r
38         /// <summary>\r
39         /// Summary description for DB2ClientDataReader.\r
40         /// DB2ClientDataReader. \r
41         /// </summary>\r
42         public class DB2ClientDataReader : IDataReader\r
43         {\r
44                 internal DataTable rs;                  //Our result set is a datatable\r
45                 internal DB2ClientConnection db2Conn; //The connection we're working with\r
46                 internal IntPtr hwndStmt;               //The statement handle returning the results\r
47                 private int row=-1;                             //Row pointer\r
48                 private int numCols=0;\r
49                 \r
50                 #region Constructors and destructors\r
51                 /// <summary>\r
52                 /// \r
53                 /// </summary>\r
54                 /// <param name="con"></Connection object to DB2>\r
55                 /// <param name="com"></Command object>\r
56                 internal DB2ClientDataReader(DB2ClientConnection con, DB2ClientCommand com)\r
57                 {\r
58                         db2Conn = con;\r
59                         hwndStmt = com.statementHandle;    //We have access to the results through the statement handle\r
60 \r
61                         short sqlRet;\r
62 \r
63                         DB2ClientUtils util = new DB2ClientUtils();\r
64                         rs = new DataTable();\r
65                         \r
66                         sqlRet = DB2ClientPrototypes.SQLNumResultCols(hwndStmt, ref numCols);\r
67                         util.DB2CheckReturn(sqlRet, DB2ClientConstants.SQL_HANDLE_STMT, hwndStmt, "DB2ClientDataReader - SQLNumResultCols");\r
68 \r
69                         byte[][] dbVals = new byte[(int)numCols][];\r
70                         IntPtr[] sqlLen_or_IndPtr = new IntPtr[numCols];\r
71 \r
72                         PrepareResults(dbVals, sqlLen_or_IndPtr);\r
73                         FetchResults(dbVals, sqlLen_or_IndPtr, rs);\r
74                         isClosed = false;\r
75                 }\r
76                 /// <summary>\r
77                 /// Constructor for use with prepared statements\r
78                 /// </summary>\r
79                 /// \r
80                 internal DB2ClientDataReader(DB2ClientConnection con, DB2ClientCommand com, bool prepared)\r
81                 {\r
82                         db2Conn = con;\r
83                         hwndStmt = com.statementHandle;    //We have access to the results through the statement handle\r
84 \r
85                         short sqlRet;\r
86 \r
87                         DB2ClientUtils util = new DB2ClientUtils();\r
88                         rs = new DataTable();\r
89                         \r
90                         sqlRet = DB2ClientPrototypes.SQLNumResultCols(hwndStmt, ref numCols);\r
91                         util.DB2CheckReturn(sqlRet, DB2ClientConstants.SQL_HANDLE_STMT, hwndStmt, "DB2ClientDataReader - SQLNumResultCols");\r
92 \r
93                         byte[][] dbVals = new byte[(int)numCols][];\r
94                         IntPtr[] sqlLen_or_IndPtr = new IntPtr[numCols];\r
95 \r
96 \r
97                         PrepareResults(dbVals, sqlLen_or_IndPtr);\r
98                         FetchResults(dbVals, sqlLen_or_IndPtr, rs);\r
99                         isClosed = false;\r
100                 }\r
101 \r
102                 public void Dispose()\r
103                 {\r
104                         Close();\r
105                 }\r
106                 #endregion\r
107                 #region Properties\r
108                 #region Depth property \r
109                 ///\r
110                 ///Depth of nesting for the current row, need to figure out what this translates into \r
111                 ///with DB2.\r
112                 ///\r
113                 private int depth = 0;\r
114                 public int Depth\r
115                 {\r
116                         get\r
117                         {\r
118                                 return depth;\r
119                         }\r
120                 }\r
121                 #endregion\r
122                 #region IsClosed property\r
123                 /// <summary>\r
124                 /// True if the reader is closed.\r
125                 /// </summary>\r
126                 private bool isClosed = true;\r
127                 public bool IsClosed\r
128                 {\r
129                         get\r
130                         {\r
131                                 return isClosed;\r
132                         }\r
133                 }\r
134                 #endregion\r
135                 #region RecordsAffected property\r
136                 ///\r
137                 /// Number of records affected by this operation.  Will be zero until we close the \r
138                 /// reader\r
139                 /// \r
140                 private int recordsAffected = 0;\r
141                 public int RecordsAffected\r
142                 {\r
143                         get\r
144                         {\r
145                                 return recordsAffected;\r
146                         }\r
147                 }\r
148                 #endregion\r
149                 #endregion\r
150                 #region Methods\r
151                 #region Close method\r
152                 ///\r
153                 ///\r
154                 public void Close()\r
155                 {\r
156                         if (rs != null) \r
157                         {\r
158                                 recordsAffected = rs.Rows.Count;\r
159                                 rs.Dispose();\r
160                                 rs = null;\r
161                                 isClosed=true;\r
162                         }\r
163                 }\r
164                 #endregion\r
165                 #region GetSchemaTable \r
166                 ///\r
167                 /// We'll return an empty table for now...ughh this one will be tedious to write\r
168                 /// \r
169                 public DataTable GetSchemaTable()\r
170                 {\r
171                         throw new DB2ClientException ("TBD");\r
172                 }\r
173                 #endregion\r
174                 #region NextResult \r
175                 ///\r
176                 /// Ummm is this related to SQLBulkOperations stuff..?\r
177                 /// \r
178                 public bool NextResult()\r
179                 {\r
180                         throw new DB2ClientException("To be done");\r
181 \r
182                 //Deferring the meat of this until the batch stuff is implemented\r
183                 }\r
184                 #endregion\r
185                 #region Read\r
186                 ///\r
187                 /// Apparently, this function does nothing other than tell you if you can move to the \r
188                 /// next row in the resultset.  I have to move the fetching stuff elswhere...\r
189                 /// \r
190                 public bool Read()\r
191                 {\r
192                         if (isClosed) return false;\r
193                         row++;\r
194                         //do something with the fetched data now...\r
195                         if(row < rs.Rows.Count)\r
196                                 return true;\r
197                         else\r
198                                 return false;\r
199                         \r
200                 }\r
201                 #endregion\r
202 \r
203                 #region Describe/Bind/Fetch functions\r
204                 ///\r
205                 ///Broke these out so that we can use different paths for Immediate executions and Prepared executions\r
206                 /// <summary>\r
207                 /// Does the describe and bind steps for the query result set.  Called for both immediate and prepared queries. \r
208                 /// </summary>\r
209                 private void PrepareResults(byte[][] dbVals, IntPtr[] sqlLen_or_IndPtr)\r
210                 {\r
211                         short sqlRet;\r
212                         StringBuilder colName = new StringBuilder(18);\r
213                         short colNameMaxLength=18;\r
214                         IntPtr colNameLength=IntPtr.Zero;\r
215                         IntPtr sqlDataType=IntPtr.Zero;\r
216                         IntPtr colSize=IntPtr.Zero;\r
217                         IntPtr scale=IntPtr.Zero;\r
218                         IntPtr nullable=IntPtr.Zero;\r
219                         DB2ClientUtils util = new DB2ClientUtils();\r
220                         for (ushort i=1; i<=numCols; i++) \r
221                         {\r
222                                 sqlRet = DB2ClientPrototypes.SQLDescribeCol(hwndStmt, i, colName, colNameMaxLength, colNameLength, ref sqlDataType, ref colSize, ref scale, ref nullable);\r
223                                 util.DB2CheckReturn(sqlRet, DB2ClientConstants.SQL_HANDLE_STMT, hwndStmt, "DB2ClientDataReader - SQLDescribeCol");\r
224                                 ///At this point I have the data type information as well, but for now I will insert the data as\r
225                                 ///Ansi strings and see how it goes.  Maybe we can speed things up later...\r
226                                 ///\r
227                                 rs.Columns.Add(colName.ToString());\r
228 \r
229                                 sqlLen_or_IndPtr[i-1] = new IntPtr();\r
230                                 dbVals[i-1] = new byte[(int)colSize];\r
231 \r
232 \r
233                                 try \r
234                                 {\r
235                                         switch ((int)sqlDataType) \r
236                                         {\r
237                                                 case DB2ClientConstants.SQL_DECIMAL:    //These types are treated as SQL_C_CHAR for binding purposes\r
238                                                 case DB2ClientConstants.SQL_TYPE_DATE:\r
239                                                 case DB2ClientConstants.SQL_TYPE_TIME:\r
240                                                 case DB2ClientConstants.SQL_TYPE_TIMESTAMP:\r
241                                                 case DB2ClientConstants.SQL_VARCHAR:\r
242                                                         sqlRet = DB2ClientPrototypes.SQLBindCol(hwndStmt, i, DB2ClientConstants.SQL_C_CHAR, dbVals[i-1],(short)colSize+1, (int)sqlLen_or_IndPtr[i-1]);\r
243                                                         break;\r
244                                                 default:\r
245                                                         sqlRet = DB2ClientPrototypes.SQLBindCol(hwndStmt, i, (short)sqlDataType, dbVals[i-1],(short)colSize+1, (int)sqlLen_or_IndPtr[i-1]);\r
246                                                         break;\r
247                                         }\r
248                                         util.DB2CheckReturn(sqlRet, DB2ClientConstants.SQL_HANDLE_STMT, hwndStmt, "DB2ClientDataReader - SQLBindCol");\r
249                                 }\r
250                                 catch(DB2ClientException e) \r
251                                 {\r
252                                         System.Console.Write(e.Message);\r
253                                 }\r
254                         }\r
255                         isClosed = false;\r
256                 }\r
257 /// <summary>\r
258 /// FetchResults does  what it says.\r
259 /// </summary>\r
260 /// <param name="dbVals"></param>\r
261 /// <param name="sqlLen_or_IndPtr"></param>\r
262 /// <param name="rs"></param>\r
263                 private void FetchResults(byte[][] dbVals, IntPtr[] sqlLen_or_IndPtr, DataTable rs) \r
264                 {\r
265                         short sqlRet = 0;\r
266                         DB2ClientUtils util = new DB2ClientUtils();\r
267 \r
268                         sqlRet = DB2ClientPrototypes.SQLFetch(hwndStmt);\r
269                         util.DB2CheckReturn(sqlRet, DB2ClientConstants.SQL_HANDLE_STMT, hwndStmt, "DB2ClientDataReader - SQLFetch 1");\r
270 \r
271                         while(sqlRet != DB2ClientConstants.SQL_NO_DATA_FOUND)\r
272                         {\r
273                                 DataRow newRow = rs.NewRow();\r
274                                 for (short y=1;y<=numCols;y++) \r
275                                         newRow[y-1] = System.Text.Encoding.Default.GetString(dbVals[y-1]);\r
276 \r
277                                 rs.Rows.Add(newRow);\r
278                                 sqlRet = DB2ClientPrototypes.SQLFetch(hwndStmt);\r
279                                 util.DB2CheckReturn(sqlRet, DB2ClientConstants.SQL_HANDLE_STMT, hwndStmt, "DB2ClientDataReader - SQLFetch 2");\r
280                         }               \r
281                 }\r
282                 #endregion\r
283 \r
284                 #region IDataRecord Interface\r
285                 ///Code for the IDataRecord interface\r
286                 ///\r
287                 #region FieldCount\r
288                 ///\r
289                 ///\r
290                 private int fieldCount = -1;\r
291                 public int FieldCount\r
292                 {\r
293                         get\r
294                         {\r
295                                 if (IsClosed)\r
296                                         fieldCount = 0;\r
297                                 else\r
298                                         fieldCount = rs.Columns.Count;\r
299                                 return fieldCount;\r
300                         }\r
301                 }\r
302                 #endregion\r
303                 #region Item accessors\r
304                 public object this[string name]\r
305                 {\r
306                         get\r
307                         {\r
308                                 return rs.Rows[row][name];\r
309                         }\r
310                 }\r
311                 public object this[int col]\r
312                 {\r
313                         get\r
314                         {\r
315                                 return rs.Rows[row][col];\r
316                         }\r
317                 }\r
318                 #endregion\r
319                 #region GetBoolean method\r
320                 ///\r
321                 ///Use the Convert class for all of these returns\r
322                 ///\r
323                 public bool GetBoolean(int col)\r
324                 {\r
325                         return Convert.ToBoolean(this[col]);\r
326                 }\r
327                 #endregion\r
328                 #region GetByte\r
329                 ///\r
330                 ///GetByte\r
331                 ///\r
332                 public byte GetByte(int col)\r
333                 {\r
334                         return Convert.ToByte(this[col]);\r
335                 }\r
336                 #endregion\r
337                 #region GetBytes\r
338                 ///\r
339                 ///  GetBytes, return a stream of bytes\r
340                 ///\r
341                 public long GetBytes(int col, long fieldOffset, byte[] buffer, int bufferOffset, int length)\r
342                 {\r
343                         //Hmm... How shall we deal with this one?  \r
344                         return 0;\r
345                 }\r
346                 #endregion\r
347                 #region GetChar\r
348                 ///\r
349                 ///GetChar, return column as a char\r
350                 ///\r
351                 public char GetChar(int col)\r
352                 {\r
353                         return Convert.ToChar(this[col]);\r
354                 }\r
355                 #endregion\r
356                 #region GetChars\r
357                 ///\r
358                 ///GetChars, returns char array\r
359                 ///\r
360                 public long GetChars(int col, long fieldOffset, char[] buffer, int bufferOffset, int length)\r
361                 {\r
362                         //Again, not sure how I'll deal with this just yet\r
363                         return 0;\r
364                 }\r
365                 #endregion\r
366                 #region GetData\r
367                 ///\r
368                 /// GetData method\r
369                 /// \r
370                 public IDataReader GetData(int col)\r
371                 {\r
372                         //Have to research this one, not quite sure what the docs mean\r
373                         //DB2 does have some structured data types, is that what this is for?\r
374                         throw new DB2ClientException("Not yet supported.");\r
375                 }\r
376                 #endregion\r
377                 #region GetDataTypeName\r
378                 ///\r
379                 ///GetDataTypeName return the type of data\r
380                 ///\r
381                 public string GetDataTypeName(int col)\r
382                 {\r
383                         //I could check the meta data as a starting point for this one, but until I implement \r
384                         //returning the result sets, I'm not exactly sure what info I'll have, so this function\r
385                         //waits until then...\r
386                         throw new DB2ClientException("Not yet implemented");\r
387                 }\r
388                 #endregion\r
389                 #region GetDateTime\r
390                 ///\r
391                 /// GetDateTime method\r
392                 /// \r
393                 public string NewGetDateTime(int col)\r
394                 {\r
395                         return Convert.ToString(this[col]);\r
396                 }\r
397                 public DateTime GetDateTime(int col)\r
398                 {\r
399                         return Convert.ToDateTime(this[col]);\r
400                 }\r
401                 #endregion\r
402                 #region GetDecimal\r
403                 ///\r
404                 ///GetDecimal method\r
405                 ///\r
406                 public decimal GetDecimal(int col)\r
407                 {\r
408                         return Convert.ToDecimal(this[col]);\r
409                 }\r
410                 #endregion\r
411                 #region GetDouble \r
412                 ///\r
413                 /// GetDouble \r
414                 /// \r
415                 public double GetDouble(int col)\r
416                 {\r
417                         return Convert.ToDouble(this[col]);\r
418                 }\r
419                 #endregion\r
420                 #region GetFieldType\r
421                 ///\r
422                 /// Type GetFieldType\r
423                 ///\r
424                 public Type GetFieldType(int col)\r
425                 {\r
426                         //Again need more research here\r
427                         return typeof(int);\r
428                 }\r
429                 #endregion\r
430                 #region GetFloat\r
431                 ///\r
432                 /// GetFloat\r
433                 /// \r
434                 public float GetFloat(int col)\r
435                 {\r
436                         return (float) Convert.ToDouble(this[col].ToString(),new CultureInfo("en-US").NumberFormat);\r
437                 }\r
438                 #endregion\r
439                 #region GetGuid\r
440                 ///\r
441                 /// GetGuid\r
442                 /// \r
443                 public Guid GetGuid(int col)\r
444                 {\r
445                         // a Guid is a 128 bit unique value.  Could be like a GENERATE UNIQUE in DB2\r
446                         // as usual, need more research\r
447                         throw new DB2ClientException("TBD");\r
448                 }\r
449                 #endregion\r
450                 #region The GetInt?? series\r
451                 ///\r
452                 ///GetInt16\r
453                 ///\r
454                 public short GetInt16(int col)\r
455                 {\r
456                         return Convert.ToInt16(this[col]);\r
457                 }\r
458                 ///\r
459                 ///GetInt32\r
460                 ///\r
461                 public int GetInt32(int col)\r
462                 {\r
463                         return Convert.ToInt32(this[col]);\r
464                 }\r
465                 ///\r
466                 ///GetInt64\r
467                 ///\r
468                 public long GetInt64(int col)\r
469                 {\r
470                         return Convert.ToInt64(this[col]);\r
471                 }\r
472                 #endregion\r
473                 #region GetName\r
474                 ///\r
475                 ///GetName, returns the name of the field\r
476                 ///\r
477                 public string GetName(int col)\r
478                 {\r
479                         return (rs.Columns[col].ColumnName);\r
480                 }\r
481                 #endregion\r
482                 #region GetOrdinal\r
483                 ///\r
484                 /// GetOrdinal, return the index of the named column\r
485                 /// \r
486                 public int GetOrdinal(string name)\r
487                 {\r
488                         return rs.Columns[name].Ordinal;\r
489                 }\r
490                 #endregion\r
491                 #region GetString\r
492                 ///\r
493                 /// GetString returns a string\r
494                 /// \r
495                 public string GetString(int col)\r
496                 {\r
497                         return Convert.ToString(this[col]);\r
498                 }\r
499                 #endregion\r
500 \r
501                 #region GetLobLocator\r
502                 ///\r
503                 ///Returns a LOB Locator class\r
504                 ///\r
505                 //DB2ClientLOBLocator GetLobLocator(int col)\r
506                 //{\r
507 \r
508                 #endregion\r
509 \r
510         \r
511                 #region GetValue\r
512                 ///\r
513                 /// GetVCalue, returns an object\r
514                 /// \r
515                 public object GetValue(int col)\r
516                 {\r
517                         return this[col];\r
518                 }\r
519                 #endregion\r
520                 #region GetValues\r
521                 ///\r
522                 /// GetValues returns all columns in the row through the argument, and the number of columns in the return value\r
523                 /// \r
524                 public int GetValues(object[] values)\r
525                 {\r
526                         int numCols = FieldCount;\r
527                         if (values.Length<numCols)\r
528                                 throw new DB2ClientException("GetValues argument too small for number of columns in row.");\r
529                         for (int i = 0; i<=numCols; i++)\r
530                           values[i] = this[i];\r
531                         return numCols;\r
532                 }\r
533                 #endregion\r
534                 #region IsDBNull\r
535                 ///\r
536                 /// IsDBNull Is the column null\r
537                 /// \r
538                 public bool IsDBNull(int col)\r
539                 {\r
540                         //Proper implementation once I get the SQLDescribe/SQLBind/SQLFetch stuff in place\r
541                         return false;\r
542                 }\r
543                 #endregion\r
544 \r
545                 #endregion  ///For IDataRecord\r
546         }\r
547 \r
548 }\r
549 #endregion