GetBytes should return the data length if target buffer is null
[mono.git] / mcs / class / System.Data / System.Data.ProviderBase.jvm / AbstractDataReader.cs
1 //\r
2 // System.Data.Common.AbstractDataReader\r
3 //
4 // Authors:
5 //      Konstantin Triger <kostat@mainsoft.com>
6 //      Boris Kirzner <borisk@mainsoft.com>
7 //      
8 // (C) 2005 Mainsoft Corporation (http://www.mainsoft.com)
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //\r
31 \r
32 \r
33 using System;\r
34 using System.Data;\r
35 using System.Collections;\r
36 using System.Data.ProviderBase;\r
37 \r
38 using java.io;\r
39 using java.sql;\r
40 \r
41 namespace System.Data.Common\r
42 {\r
43         public abstract class AbstractDataReader : DbDataReaderBase, ISafeDataRecord {\r
44 \r
45                 #region Fields\r
46 \r
47                 private ResultSetMetaData _resultsMetaData;\r
48                 protected AbstractDbCommand _command;\r
49                 private DataTable _schemaTable;\r
50                 private ReaderState _readerState = ReaderState.Uninitialized;\r
51 \r
52                 private IReaderCacheContainer[] _readerCache;\r
53                 private int _currentCacheFilledPosition; \r
54                 private Stack _resultSetStack = new Stack();\r
55                 private bool _isClosed = false;\r
56 \r
57                 [Flags]\r
58                 private enum ReaderState { Uninitialized = 0, Empty = 1, HasRows = 2, FirstRed = 4, Eof = 8, Fetching = 16 };\r
59 \r
60                 internal enum SCHEMA_TABLE { ColumnName,\r
61                         ColumnOrdinal,\r
62                         ColumnSize,\r
63                         NumericPrecision,\r
64                         NumericScale,\r
65                         IsUnique,\r
66                         IsKey,\r
67                         BaseServerName,\r
68                         BaseCatalogName,\r
69                         BaseColumnName,\r
70                         BaseSchemaName,\r
71                         BaseTableName,\r
72                         DataType,\r
73                         AllowDBNull,\r
74                         ProviderType,\r
75                         IsAliased,\r
76                         IsExpression,\r
77                         IsIdentity,\r
78                         IsAutoIncrement,\r
79                         IsRowVersion,\r
80                         IsHidden,\r
81                         IsLong,\r
82                         IsReadOnly};\r
83 \r
84                 #endregion // Fields\r
85 \r
86                 #region Constructors\r
87 \r
88                 protected AbstractDataReader() : base (CommandBehavior.Default) {\r
89                 }\r
90                 \r
91                 public AbstractDataReader(AbstractDbCommand command): base(command.Behavior) {\r
92                         _command = command;\r
93                         if (_command.Connection != null) {\r
94                                 ((AbstractDBConnection)_command.Connection).AddReference(this);\r
95                         }\r
96                 }\r
97 \r
98                 #endregion // Constructors\r
99 \r
100                 #region Properties\r
101 \r
102                 public override bool HasRows {\r
103                         get {\r
104                                 if (IsClosed) {\r
105                                         throw new InvalidOperationException("Invalid attempt to HasRows when reader is closed.");\r
106                                 }\r
107 \r
108                                 try {\r
109                                         if(null == Results)\r
110                                                 return false;\r
111                                 }\r
112                                 catch(SystemException) {\r
113                                         //suppress\r
114                                         return false;\r
115                                 }\r
116 \r
117                                 return (_readerState & ReaderState.HasRows) != 0;\r
118                         }\r
119                 }\r
120 \r
121                 public override int RecordsAffected\r
122                 {\r
123                         // MSDN : The RecordsAffected property is not set \r
124                         // until all rows are read and you close the reader.\r
125                         get { \r
126                                 return _command.RecordsAffected; \r
127                         }\r
128                 }\r
129 \r
130                 public override int FieldCount\r
131                 {\r
132                         get {\r
133                                 if (ResultsMetaData == null)\r
134                                         return 0;\r
135 \r
136                                 try {\r
137                                         return ResultsMetaData.getColumnCount();\r
138                                 }\r
139                                 catch (SQLException exp) {\r
140                                         throw CreateException(exp);\r
141                                 }\r
142 \r
143                         }\r
144                 }\r
145 \r
146                 protected internal CommandBehavior Behavior\r
147                 {\r
148                         get {\r
149                                 return _command.Behavior;\r
150                         }\r
151                 }\r
152 \r
153                 public override Object this[String columnName]\r
154                 {\r
155                         get {\r
156                                 try {\r
157                                         int columnIndex = Results.findColumn(columnName) - 1;\r
158                                         return this[columnIndex];\r
159                                 }\r
160                                 catch (SQLException exp) {\r
161                                         throw new IndexOutOfRangeException(exp.Message, exp);\r
162                                 }                               \r
163                         }\r
164                 }\r
165 \r
166                 public override Object this[int columnIndex]\r
167                 {\r
168                         get { return GetValue(columnIndex); }\r
169                 }\r
170 \r
171                 protected ResultSet Results\r
172                 {\r
173                         get {\r
174                                 if (_readerState == ReaderState.Uninitialized) {\r
175 \r
176                                         if (_resultSetStack.Count == 0) {\r
177                                                 ResultSet resultSet =  _command.CurrentResultSet;\r
178                                                 if (resultSet == null)\r
179                                                         return null;\r
180 \r
181                                                 _resultSetStack.Push(resultSet);\r
182                                         }\r
183 \r
184                                         _readerState = ReaderState.Fetching;\r
185                                         for (;;) {\r
186                                                 try {\r
187                                                         Configuration.BooleanSetting prefetchSchema = Configuration.Switches.PrefetchSchema;\r
188 \r
189                                                         if (prefetchSchema == Configuration.BooleanSetting.NotSet) {\r
190                                                                 AbstractDBConnection conn = (AbstractDBConnection)((ICloneable)_command.Connection);\r
191                                                                 string driverName = conn.JdbcConnection.getMetaData().getDriverName();\r
192                                                                 if (driverName.IndexOf("DB2") >= 0)\r
193                                                                         prefetchSchema = Configuration.BooleanSetting.True;\r
194                                                         }\r
195 \r
196                                                         if (prefetchSchema == Configuration.BooleanSetting.True)\r
197                                                                 GetSchemaTable();\r
198 \r
199                                                         ResultSet resultSet = (ResultSet)_resultSetStack.Peek();\r
200                                                         if (resultSet.next()) {\r
201                                                                 _readerState = (ReaderState.HasRows | ReaderState.FirstRed);\r
202                                                                 ResultSetMetaData rsMetaData = ResultsMetaData;\r
203                                                                 DbTypes.JavaSqlTypes javaSqlType = (DbTypes.JavaSqlTypes)rsMetaData.getColumnType(1);\r
204                                                                 if (javaSqlType == DbTypes.JavaSqlTypes.OTHER) {\r
205                                                                         object value = GetValue(0);\r
206                                                                         if (value != null && value is ResultSet) {\r
207                                                                                 _resultsMetaData = null;\r
208                                                                                 _readerCache = null;\r
209                                                                                 SchemaTable = null;\r
210                                                                                 _readerState = ReaderState.Fetching;\r
211                                                                                 _resultSetStack.Push(value);\r
212                                                                                 continue;\r
213                                                                         }\r
214                                                                 }\r
215                                                         }\r
216                                                         else\r
217                                                                 _readerState = ReaderState.Empty;\r
218 \r
219                                                         break;\r
220                                                 }\r
221                                                 catch(SQLException e) {\r
222                                                         throw CreateException(e);\r
223                                                 }\r
224                                         }\r
225                                 }\r
226 \r
227                                 return (_resultSetStack.Count > 0) ? (ResultSet)_resultSetStack.Peek() : null;\r
228                         }\r
229                 }\r
230 \r
231                 protected ResultSetMetaData ResultsMetaData\r
232                 {\r
233                         get {\r
234                                 ResultSet results = Results;\r
235                                 if (results == null) {\r
236                                         return null;\r
237                                 }\r
238                                 if(_resultsMetaData == null) {\r
239                                         _resultsMetaData = results.getMetaData();\r
240                                 }\r
241                                 return _resultsMetaData;\r
242                         }                       \r
243                 }\r
244 \r
245                 protected DataTable SchemaTable\r
246                 {\r
247                         get {\r
248                                 if (_schemaTable == null) {\r
249                                         _schemaTable = ConstructSchemaTable();\r
250                                 }\r
251                                 return _schemaTable;\r
252                         }\r
253 \r
254                         set {_schemaTable = value; }\r
255                 }\r
256 \r
257                 internal protected IReaderCacheContainer[] ReaderCache\r
258                 {\r
259                         get {\r
260                                 if (_readerCache == null) {\r
261                                         _readerCache = CreateReaderCache();\r
262                                         _currentCacheFilledPosition = -1;\r
263                                 }\r
264                                 return _readerCache;\r
265                         }\r
266                 }\r
267
268                 public override bool IsClosed {\r
269                         get { return _isClosed; }\r
270                 }\r
271 \r
272                 #endregion // Properties\r
273 \r
274                 #region Methods\r
275 \r
276                 protected abstract int GetProviderType(int jdbcType);\r
277 \r
278                 protected abstract SystemException CreateException(string message, SQLException e);\r
279 \r
280                 protected abstract SystemException CreateException(IOException e);\r
281 \r
282                 protected SystemException CreateException(SQLException e)\r
283                 {\r
284                         return CreateException(e.Message,e);    \r
285                 }\r
286 \r
287                 private bool CloseCurrentResultSet() {\r
288                         if (_resultSetStack.Count > 0) {\r
289                                 try{\r
290                                         _resultsMetaData = null;\r
291                                         _readerCache = null;\r
292                                         _readerState = ReaderState.Uninitialized;\r
293                                         ResultSet rs = (ResultSet)_resultSetStack.Pop();\r
294                                         rs.close();\r
295                                         return true;\r
296                                 }\r
297                                 catch (SQLException exp) {\r
298                                         throw CreateException(exp);\r
299                                 }\r
300                         }\r
301 \r
302                         return false;\r
303                 }\r
304 \r
305                 // FIXME : add Close(bool readAllRecords) and pass this bool to skip looping over NextResult(), override AbstractDbCommand.ExecuteScalar\r
306                 public override void Close()\r
307                 {\r
308                         if (IsClosed)\r
309                                 return;\r
310 \r
311                         try {\r
312                                 CloseCurrentResultSet();\r
313                                 _command.OnReaderClosed(this);\r
314                         }\r
315                         finally {\r
316                                 CloseInternal();\r
317                         }\r
318                 }\r
319 \r
320                 internal void CloseInternal()\r
321                 {\r
322                         _resultsMetaData = null;\r
323                         _readerCache = null;\r
324                         _isClosed = true;\r
325                 }\r
326 \r
327                 public override bool NextResult()\r
328                 {\r
329                         CloseCurrentResultSet();\r
330 \r
331                         if ((_command.Behavior & CommandBehavior.SingleResult) != 0) {\r
332                                 while (CloseCurrentResultSet());\r
333                                 while (_command.NextResultSet());\r
334                                 return false;\r
335                         }\r
336 \r
337                         try {\r
338                                 while (_resultSetStack.Count > 0) {\r
339                                         ResultSet rs = (ResultSet)_resultSetStack.Peek();\r
340 \r
341                                         if(!rs.next()) {\r
342                                                 CloseCurrentResultSet();\r
343                                                 continue;\r
344                                         }\r
345 \r
346                                         // must be a ResultSet\r
347                                         object childRs = rs.getObject(1);\r
348                                         if (childRs != null) {\r
349                                                 SchemaTable = null;\r
350                                                 _resultSetStack.Push(childRs);\r
351                                                 return true;\r
352                                         }\r
353                                 }\r
354                         }\r
355                         catch (SQLException exp) {\r
356                                 throw CreateException(exp);\r
357                         }\r
358                                 \r
359                         if (_command.NextResultSet()) {\r
360                                 SchemaTable = null;     \r
361                                 return true;\r
362                         }\r
363                         return false;\r
364                 }\r
365 \r
366                 public override bool Read()\r
367                 {\r
368                         if(null == Results ||\r
369                                 (_readerState & (ReaderState.HasRows | ReaderState.Eof)) != ReaderState.HasRows)\r
370                                 return false;\r
371 \r
372                         bool firstRead = false;\r
373 \r
374                         try {\r
375                                 if ((_readerState & ReaderState.FirstRed) != 0) {\r
376                                         firstRead = true;\r
377                                         _readerState &= ~ReaderState.FirstRed;\r
378                                         return true;\r
379                                 }\r
380                                 else {\r
381                                         bool next = Results.next();\r
382 \r
383                                         if (!next)\r
384                                                 _readerState |= ReaderState.Eof;\r
385 \r
386                                         return next;\r
387                                 }\r
388                         }                       \r
389                         catch (SQLException exp) {\r
390                                 // suppress exception as .Net does\r
391                                 return false;\r
392                         }\r
393                         finally {\r
394                                 // in case of first read we could sampled the first value\r
395                                 // to see whether there is a resultset, so _currentCacheFilledPosition\r
396                                 // might be already inited\r
397                                 if (!firstRead)\r
398                                         _currentCacheFilledPosition = -1;\r
399                         }\r
400                 }\r
401 \r
402                 public override bool GetBoolean(int columnIndex)\r
403                 {\r
404                         FillReaderCache(columnIndex);\r
405                         return ((BooleanReaderCacheContainer)ReaderCache[columnIndex]).GetBoolean();\r
406                 }\r
407 \r
408                 public bool GetBooleanSafe(int columnIndex)\r
409                 {\r
410                         if (ReaderCache[columnIndex] is BooleanReaderCacheContainer) {\r
411                                 return GetBoolean(columnIndex);\r
412                         }\r
413                         else {\r
414                                 return Convert.ToBoolean(GetValue(columnIndex));\r
415                         }\r
416                 }\r
417 \r
418                 public override byte GetByte(int columnIndex)\r
419                 {\r
420                         FillReaderCache(columnIndex);\r
421                         return ((ByteReaderCacheContainer)ReaderCache[columnIndex]).GetByte();\r
422                 }\r
423 \r
424                 public byte GetByteSafe(int columnIndex)\r
425                 {\r
426                         if (ReaderCache[columnIndex] is ByteReaderCacheContainer) {\r
427                                 return GetByte(columnIndex);\r
428                         }\r
429                         else {\r
430                                 return Convert.ToByte(GetValue(columnIndex));\r
431                         }\r
432                 }\r
433 \r
434                 public override long GetBytes(\r
435                         int columnIndex,\r
436                         long dataIndex,\r
437                         byte[] buffer,\r
438                         int bufferIndex,\r
439                         int length)\r
440                 {\r
441                         FillReaderCache(columnIndex);\r
442                         byte[] byteArr = ((BytesReaderCacheContainer)ReaderCache[columnIndex]).GetBytes();\r
443                         if (byteArr == null)\r
444                                 return 0;\r
445                         if (buffer == null)\r
446                                 return byteArr.LongLength;\r
447                         long actualLength = ((dataIndex + length) >= byteArr.Length) ? (byteArr.Length - dataIndex) : length;\r
448                         Array.Copy(byteArr,dataIndex,buffer,bufferIndex,actualLength);\r
449                         return actualLength;\r
450                 }\r
451 \r
452                 public virtual byte[] GetBytes(int columnIndex)\r
453                 {\r
454                         FillReaderCache(columnIndex);\r
455                         return ((BytesReaderCacheContainer)ReaderCache[columnIndex]).GetBytes();\r
456                 }\r
457 \r
458                 public override char GetChar(int columnIndex)\r
459                 {\r
460                         FillReaderCache(columnIndex);\r
461                         string s = ((StringReaderCacheContainer)ReaderCache[columnIndex]).GetString();\r
462                         if(s == null) {\r
463                                 return '\0';\r
464                         }\r
465                         return s[0];\r
466                 }\r
467 \r
468                 public char GetCharSafe(int columnIndex)\r
469                 {\r
470                         if (ReaderCache[columnIndex] is StringReaderCacheContainer) {\r
471                                 return GetChar(columnIndex);\r
472                         }\r
473                         else {\r
474                                 return Convert.ToChar(GetValue(columnIndex));\r
475                         }\r
476                 }\r
477 \r
478                 public override long GetChars(\r
479                         int columnIndex,\r
480                         long dataIndex,\r
481                         char[] buffer,\r
482                         int bufferIndex,\r
483                         int length)\r
484                 {\r
485                         FillReaderCache(columnIndex);\r
486                         char[] charArr = ((CharsReaderCacheContainer)ReaderCache[columnIndex]).GetChars();\r
487                         long actualLength = ((dataIndex + length) >= charArr.Length) ? (charArr.Length - dataIndex) : length;\r
488                         Array.Copy(charArr,dataIndex,buffer,bufferIndex,actualLength);\r
489                         return actualLength;\r
490                 }\r
491 \r
492                 public override string GetDataTypeName(int columnIndex)\r
493                 {\r
494                         try {\r
495                                 if (ResultsMetaData == null) {\r
496                                         return String.Empty;\r
497                                 }\r
498                                 return ResultsMetaData.getColumnTypeName(columnIndex + 1);\r
499                         }\r
500                         catch (SQLException exp) {\r
501                                 throw CreateException(exp);\r
502                         }\r
503                 }\r
504 \r
505                 public override DateTime GetDateTime(int columnIndex)\r
506                 {\r
507                         FillReaderCache(columnIndex);\r
508                         return ((DateTimeReaderCacheContainer)ReaderCache[columnIndex]).GetDateTime();\r
509                 }\r
510 \r
511                 public DateTime GetDateTimeSafe(int columnIndex)\r
512                 {\r
513                         if (ReaderCache[columnIndex] is DateTimeReaderCacheContainer) {\r
514                                 return GetDateTime(columnIndex);\r
515                         }\r
516                         else {\r
517                                 return Convert.ToDateTime(GetValue(columnIndex));\r
518                         }\r
519                 }\r
520 \r
521                 public virtual TimeSpan GetTimeSpan(int columnIndex)\r
522                 {\r
523                         FillReaderCache(columnIndex);\r
524                         return ((TimeSpanReaderCacheContainer)ReaderCache[columnIndex]).GetTimeSpan();\r
525                 }\r
526 \r
527                 public override Guid GetGuid(int columnIndex)\r
528                 {\r
529                         FillReaderCache(columnIndex);\r
530                         return ((GuidReaderCacheContainer)ReaderCache[columnIndex]).GetGuid();\r
531                 }\r
532 \r
533                 public override decimal GetDecimal(int columnIndex)\r
534                 {\r
535                         FillReaderCache(columnIndex);\r
536                         return ((DecimalReaderCacheContainer)ReaderCache[columnIndex]).GetDecimal();\r
537                 }\r
538 \r
539                 public decimal GetDecimalSafe(int columnIndex)\r
540                 {\r
541                         if (ReaderCache[columnIndex] is DecimalReaderCacheContainer) {\r
542                                 return GetDecimal(columnIndex);\r
543                         }\r
544                         else {\r
545                                 return Convert.ToDecimal(GetValue(columnIndex));\r
546                         }\r
547                 }\r
548 \r
549                 public override double GetDouble(int columnIndex)\r
550                 {\r
551                         FillReaderCache(columnIndex);\r
552                         return ((DoubleReaderCacheContainer)ReaderCache[columnIndex]).GetDouble();\r
553                 }\r
554 \r
555                 public double GetDoubleSafe(int columnIndex)\r
556                 {\r
557                         if (ReaderCache[columnIndex] is DoubleReaderCacheContainer) {\r
558                                 return GetDouble(columnIndex);\r
559                         }\r
560                         else {\r
561                                 return Convert.ToDouble(GetValue(columnIndex));\r
562                         }\r
563                 }\r
564 \r
565                 public override float GetFloat(int columnIndex)\r
566                 {\r
567                         FillReaderCache(columnIndex);\r
568                         return ((FloatReaderCacheContainer)ReaderCache[columnIndex]).GetFloat();\r
569                 }\r
570 \r
571                 public float GetFloatSafe(int columnIndex)\r
572                 {\r
573                         if (ReaderCache[columnIndex] is FloatReaderCacheContainer) {\r
574                                 return GetFloat(columnIndex);\r
575                         }\r
576                         else {\r
577                                 return Convert.ToSingle(GetValue(columnIndex));\r
578                         }\r
579                 }\r
580 \r
581                 public override short GetInt16(int columnIndex)\r
582                 {\r
583                         FillReaderCache(columnIndex);\r
584                         return ((Int16ReaderCacheContainer)ReaderCache[columnIndex]).GetInt16();\r
585                 }\r
586 \r
587                 public short GetInt16Safe(int columnIndex)\r
588                 {\r
589                         if (ReaderCache[columnIndex] is Int16ReaderCacheContainer) {\r
590                                 return GetInt16(columnIndex);\r
591                         }\r
592                         else {\r
593                                 return Convert.ToInt16(GetValue(columnIndex));\r
594                         }\r
595                 }\r
596 \r
597                 public override int GetInt32(int columnIndex)\r
598                 {\r
599                         FillReaderCache(columnIndex);\r
600                         return ((Int32ReaderCacheContainer)ReaderCache[columnIndex]).GetInt32();\r
601                 }\r
602 \r
603                 public int GetInt32Safe(int columnIndex)\r
604                 {\r
605                         if (ReaderCache[columnIndex] is Int32ReaderCacheContainer) {\r
606                                 return GetInt32(columnIndex);\r
607                         }\r
608                         else {\r
609                                 return Convert.ToInt32(GetValue(columnIndex));\r
610                         }\r
611                 }\r
612 \r
613                 public override long GetInt64(int columnIndex)\r
614                 {\r
615                         FillReaderCache(columnIndex);\r
616                         return ((Int64ReaderCacheContainer)ReaderCache[columnIndex]).GetInt64();\r
617                 }\r
618 \r
619                 public long GetInt64Safe(int columnIndex)\r
620                 {\r
621                         if (ReaderCache[columnIndex] is Int64ReaderCacheContainer) {\r
622                                 return GetInt64(columnIndex);\r
623                         }\r
624                         else {\r
625                                 return Convert.ToInt64(GetValue(columnIndex));\r
626                         }\r
627                 }\r
628 \r
629                 public override string GetName(int columnIndex)\r
630                 {\r
631                         try {\r
632                                 if (ResultsMetaData == null) {\r
633                                         return String.Empty;\r
634                                 }\r
635                                 return ResultsMetaData.getColumnName(columnIndex + 1);\r
636                         }\r
637                         catch (SQLException exp) {\r
638                                 throw new IndexOutOfRangeException(exp.Message, exp);\r
639                         }\r
640                 }\r
641 \r
642                 public override int GetOrdinal(String columnName)\r
643                 {\r
644                         try {\r
645                                 int retVal = Results.findColumn(columnName);\r
646                                 if(retVal != -1) {\r
647                                         retVal -= 1;\r
648                                 }\r
649                                 return  retVal;\r
650                         }\r
651                         catch (SQLException exp) {\r
652                                 throw new IndexOutOfRangeException(exp.Message, exp);\r
653                         }\r
654                 }\r
655 \r
656                 public override string GetString(int columnIndex)\r
657                 {\r
658                         FillReaderCache(columnIndex);\r
659                         return ((StringReaderCacheContainer)ReaderCache[columnIndex]).GetString();\r
660                 }\r
661 \r
662                 public string GetStringSafe(int columnIndex) {\r
663                         if (ReaderCache[columnIndex] is StringReaderCacheContainer) {\r
664                                 return GetString(columnIndex);\r
665                         }\r
666                         else {\r
667                                 return Convert.ToString(GetValue(columnIndex));\r
668                         }\r
669                 }\r
670 \r
671                 public override object GetValue(int columnIndex)\r
672                 {\r
673                         FillReaderCache(columnIndex);\r
674                         if (ReaderCache[columnIndex].IsNull()) {\r
675                                 return DBNull.Value;\r
676                         }\r
677                         return ReaderCache[columnIndex].GetValue();\r
678                 }\r
679 \r
680                 public override int GetValues(Object[] values)\r
681                 {       \r
682                         int columnCount = FieldCount;\r
683                         int i = 0;\r
684                         for (; i < values.Length && i < columnCount; i++) {\r
685                                 values[i] = GetValue(i);\r
686                         }\r
687                         return i;\r
688                 }\r
689 \r
690                 private void FillReaderCache(int columnIndex)\r
691                 {\r
692                         try {\r
693                                 IReaderCacheContainer[] readerCache = ReaderCache;\r
694                                 if ((Behavior & CommandBehavior.SequentialAccess) == 0) {                                       \r
695                                         while (_currentCacheFilledPosition < columnIndex) {\r
696                                                 _currentCacheFilledPosition++;\r
697                                                 readerCache[_currentCacheFilledPosition].Fetch(Results,_currentCacheFilledPosition);\r
698                                         }                                       \r
699                                 }\r
700                                 else {\r
701                                         readerCache[columnIndex].Fetch(Results,columnIndex);\r
702                                 }\r
703                         }\r
704                         catch(SQLException e) {\r
705                                 _currentCacheFilledPosition = -1;\r
706                                 throw CreateException(e);\r
707                         }\r
708                         catch (IOException e) {\r
709                                 _currentCacheFilledPosition = -1;\r
710                                 throw CreateException(e);\r
711                         }\r
712                 }\r
713 \r
714                 private IReaderCacheContainer[] CreateReaderCache()\r
715                 {\r
716                         try {\r
717                                 IReaderCacheContainer[] readerCache = new IReaderCacheContainer[FieldCount];\r
718                                 for(int i=0; i < readerCache.Length; i++) {\r
719                                         DbTypes.JavaSqlTypes javaSqlType = (DbTypes.JavaSqlTypes) ResultsMetaData.getColumnType(i + 1);\r
720                                         switch (javaSqlType) {\r
721                                                 case DbTypes.JavaSqlTypes.ARRAY :\r
722                                                         readerCache[i] = new ArrayReaderCacheContainer();\r
723                                                         break;\r
724                                                 case DbTypes.JavaSqlTypes.BIGINT :\r
725                                                         readerCache[i] = new Int64ReaderCacheContainer();\r
726                                                         break;\r
727                                                 case DbTypes.JavaSqlTypes.BINARY :\r
728                                                 case DbTypes.JavaSqlTypes.VARBINARY :\r
729                                                 case DbTypes.JavaSqlTypes.LONGVARBINARY :\r
730                                                         readerCache[i] = new BytesReaderCacheContainer();\r
731                                                         break;\r
732                                                 case DbTypes.JavaSqlTypes.BIT :\r
733                                                         readerCache[i] = new BooleanReaderCacheContainer();\r
734                                                         break;\r
735                                                 case DbTypes.JavaSqlTypes.BLOB :\r
736                                                         readerCache[i] = new BlobReaderCacheContainer();\r
737                                                         break;  \r
738                                                 case DbTypes.JavaSqlTypes.CHAR :                                                \r
739                                                         if ("uniqueidentifier".Equals(ResultsMetaData.getColumnTypeName(i + 1))) {\r
740                                                                 readerCache[i] = new GuidReaderCacheContainer();\r
741                                                         }\r
742                                                         else {\r
743                                                                 readerCache[i] = new StringReaderCacheContainer();\r
744                                                         }\r
745                                                         break;\r
746                                                 case DbTypes.JavaSqlTypes.CLOB :\r
747                                                         readerCache[i] = new ClobReaderCacheContainer();\r
748                                                         break;          \r
749                                                 case DbTypes.JavaSqlTypes.TIME :\r
750                                                         readerCache[i] = new TimeSpanReaderCacheContainer();\r
751                                                         break;  \r
752                                                 case DbTypes.JavaSqlTypes.DATE :\r
753                                                         AbstractDBConnection conn = (AbstractDBConnection)((ICloneable)_command.Connection);\r
754                                                         string driverName = conn.JdbcConnection.getMetaData().getDriverName();\r
755 \r
756                                                         if (driverName.StartsWith("PostgreSQL")) {\r
757                                                                 readerCache[i] = new DateTimeReaderCacheContainer();\r
758                                                                 break;\r
759                                                         }\r
760                                                         else\r
761                                                                 goto case DbTypes.JavaSqlTypes.TIMESTAMP;\r
762                                                 case DbTypes.JavaSqlTypes.TIMESTAMP :                           \r
763                                                         readerCache[i] = new TimestampReaderCacheContainer();\r
764                                                         break;          \r
765                                                 case DbTypes.JavaSqlTypes.DECIMAL :\r
766                                                 case DbTypes.JavaSqlTypes.NUMERIC :\r
767                                                         // jdbc driver for oracle identitfies both FLOAT and NUMBEr columns as \r
768                                                         // java.sql.Types.NUMERIC (2), columnTypeName NUMBER, columnClassName java.math.BigDecimal \r
769                                                         // therefore we relay on scale\r
770                                                         int scale = ResultsMetaData.getScale(i + 1);\r
771                                                         if (scale == -127) {\r
772                                                                 // Oracle db type FLOAT\r
773                                                                 readerCache[i] = new DoubleReaderCacheContainer();\r
774                                                         }\r
775                                                         else {\r
776                                                                 readerCache[i] = new DecimalReaderCacheContainer();\r
777                                                         }\r
778                                                         break;          \r
779                                                 case DbTypes.JavaSqlTypes.DOUBLE :\r
780                                                 case DbTypes.JavaSqlTypes.FLOAT :\r
781                                                         readerCache[i] = new DoubleReaderCacheContainer();\r
782                                                         break;\r
783                                                 case DbTypes.JavaSqlTypes.INTEGER :\r
784                                                         readerCache[i] = new Int32ReaderCacheContainer();\r
785                                                         break;\r
786                                                 case DbTypes.JavaSqlTypes.LONGVARCHAR :\r
787                                                 case DbTypes.JavaSqlTypes.VARCHAR :\r
788                                                         readerCache[i] = new StringReaderCacheContainer();\r
789                                                         break;\r
790                                                 case DbTypes.JavaSqlTypes.NULL :\r
791                                                         readerCache[i] = new NullReaderCacheContainer();\r
792                                                         break;\r
793                                                 case DbTypes.JavaSqlTypes.REAL :\r
794                                                         readerCache[i] = new FloatReaderCacheContainer();\r
795                                                         break;\r
796                                                 case DbTypes.JavaSqlTypes.REF :\r
797                                                         readerCache[i] = new RefReaderCacheContainer();\r
798                                                         break;\r
799                                                 case DbTypes.JavaSqlTypes.SMALLINT :\r
800                                                         readerCache[i] = new Int16ReaderCacheContainer();\r
801                                                         break;\r
802                                                 case DbTypes.JavaSqlTypes.TINYINT :\r
803                                                         readerCache[i] = new ByteReaderCacheContainer();\r
804                                                         break;\r
805                                                 case DbTypes.JavaSqlTypes.DISTINCT :\r
806                                                 case DbTypes.JavaSqlTypes.JAVA_OBJECT :\r
807                                                 case DbTypes.JavaSqlTypes.OTHER :\r
808                                                 case DbTypes.JavaSqlTypes.STRUCT :\r
809                                                 default :\r
810                                                         readerCache[i] = new ObjectReaderCacheContainer();\r
811                                                         break;\r
812                                         }\r
813                                         //                              ((ReaderCacheContainerBase)readerCache[i])._jdbcType = (int) javaSqlType;\r
814                                 }\r
815 \r
816                                 return readerCache;\r
817                         }\r
818                         catch(SQLException e) {\r
819                                 throw CreateException(e);\r
820                         }\r
821                 }\r
822 \r
823                 public override bool IsDBNull(int columnIndex)\r
824                 {\r
825                         FillReaderCache(columnIndex);\r
826                         return ReaderCache[columnIndex].IsNull();\r
827                 }\r
828 \r
829                 public override Type GetFieldType(int i)\r
830                 {\r
831                         try {\r
832                                 int javaSqlType = ResultsMetaData.getColumnType(i + 1);\r
833                                 return DbConvert.JavaSqlTypeToClrType(javaSqlType);\r
834                         }\r
835                         catch (SQLException exp) {\r
836                                 throw new IndexOutOfRangeException(exp.Message, exp);\r
837                         }\r
838                 }\r
839 \r
840                 public IDataReader GetData(int i)\r
841                 {\r
842                         throw new NotSupportedException();\r
843                 }\r
844 \r
845                 public override DataTable GetSchemaTable()\r
846                 {\r
847                         if (SchemaTable.Rows != null && SchemaTable.Rows.Count > 0) {\r
848                                 return SchemaTable;\r
849                         }\r
850             \r
851                         ResultSetMetaData metaData;                     \r
852                         if (Behavior == CommandBehavior.SchemaOnly) {\r
853                                 try {\r
854                                         metaData = ((PreparedStatement)_command.JdbcStatement).getMetaData();\r
855                                 }\r
856                                 catch(SQLException e) {\r
857                                         throw CreateException("CommandBehaviour.SchemaOnly is not supported by the JDBC driver.",e);\r
858                                 }\r
859                         }\r
860                         else {\r
861                                 metaData = ResultsMetaData;\r
862                         }\r
863 \r
864                         if (metaData == null) {\r
865                                 return SchemaTable;\r
866                         }\r
867 \r
868                         DatabaseMetaData dbMetaData = null;\r
869                         AbstractDBConnection clonedConnection = null;\r
870                         if ((_command.Behavior & CommandBehavior.KeyInfo) != 0) {\r
871                                 clonedConnection = (AbstractDBConnection)((ICloneable)_command.Connection).Clone();\r
872 \r
873                                 try {\r
874                                         clonedConnection.Open();\r
875                                         dbMetaData = clonedConnection.JdbcConnection.getMetaData();\r
876                                 }\r
877                                 catch {\r
878                                         //suppress\r
879                                         if (clonedConnection != null) {\r
880                                                 clonedConnection.Close();\r
881                                         }\r
882                                 }                       \r
883                         }\r
884                         \r
885                         try {\r
886                                 int tmp;                                \r
887                                 for(int i = 1; i <= metaData.getColumnCount(); i++) {\r
888                                         DataRow row = SchemaTable.NewRow ();\r
889                                         string columnName = metaData.getColumnLabel(i);\r
890                                         string baseColumnName = metaData.getColumnName(i);\r
891         \r
892                                         row [(int)SCHEMA_TABLE.ColumnName] = columnName; // maybe we should use metaData.getColumnLabel(i);\r
893                                         row [(int)SCHEMA_TABLE.ColumnSize] = metaData.getColumnDisplaySize(i);\r
894                                         row [(int)SCHEMA_TABLE.ColumnOrdinal]           = i - 1;\r
895                                         try {\r
896                                                 // FIXME : workaround for Oracle JDBC driver bug\r
897                                                 // getPrecision on BLOB, CLOB, NCLOB throws NumberFormatException\r
898                                                 tmp = metaData.getPrecision(i);\r
899                                         }\r
900                                         catch(java.lang.NumberFormatException e) {\r
901                                                 // supress exception\r
902                                                 tmp = 255;\r
903                                         }\r
904                                         row [(int)SCHEMA_TABLE.NumericPrecision] = Convert.ToInt16(tmp > 255 ? 255 : tmp);\r
905                                         tmp = metaData.getScale(i);\r
906                                         row [(int)SCHEMA_TABLE.NumericScale] = Convert.ToInt16(tmp > 255 ? 255 : tmp);\r
907 \r
908                                         row [(int)SCHEMA_TABLE.BaseServerName] = DBNull.Value;\r
909                                 \r
910                                         string catalog = null;\r
911                                         try {\r
912                                                 catalog = metaData.getCatalogName(i);\r
913                                         }\r
914                                         catch (Exception e) {\r
915                                                 // supress exception\r
916                                         }\r
917                                         if (catalog != null && catalog.Length == 0)\r
918                                                 catalog =  ((AbstractDBConnection)_command.Connection).JdbcConnection.getCatalog();\r
919                                         row [(int)SCHEMA_TABLE.BaseCatalogName] = catalog;\r
920                                         row [(int)SCHEMA_TABLE.BaseColumnName] = baseColumnName;\r
921 \r
922                                         string schemaName;\r
923                                         string tableName;\r
924 \r
925                                         try {\r
926                                                 tableName = metaData.getTableName(i);\r
927                                         }\r
928                                         catch {\r
929                                                 tableName = null;\r
930                                         }\r
931 \r
932                                         try {\r
933                                                 schemaName = metaData.getSchemaName(i);\r
934                                         }\r
935                                         catch {\r
936                                                 schemaName = null;\r
937                                         }\r
938 \r
939                                         if (tableName != null && tableName.Length == 0)\r
940                                                 tableName = null;\r
941                                         if (schemaName != null && schemaName.Length == 0)\r
942                                                 schemaName = null;\r
943 \r
944                                         row [(int)SCHEMA_TABLE.BaseSchemaName] = schemaName;\r
945                                         row [(int)SCHEMA_TABLE.BaseTableName] = tableName;\r
946 \r
947 \r
948                                         row [(int)SCHEMA_TABLE.AllowDBNull] = Convert.ToBoolean(metaData.isNullable(i));\r
949                                 \r
950                                         InitKeyInfo(row, dbMetaData, catalog, schemaName, tableName);\r
951                                 \r
952                                         row [(int)SCHEMA_TABLE.IsAliased] = columnName != baseColumnName;\r
953                                         row [(int)SCHEMA_TABLE.IsExpression] = false;\r
954 \r
955                                         row [(int)SCHEMA_TABLE.IsAutoIncrement] = metaData.isAutoIncrement(i);\r
956 \r
957                                         row [(int)SCHEMA_TABLE.IsHidden] = false;\r
958                                         row [(int)SCHEMA_TABLE.IsReadOnly] = metaData.isReadOnly(i);\r
959 \r
960                                         int columnType = metaData.getColumnType(i);\r
961                                         string columnTypeName = metaData.getColumnTypeName(i);\r
962                                         if(columnType == Types.ARRAY) {\r
963                                                 row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
964                                                 row [(int)SCHEMA_TABLE.DataType] = typeof (java.sql.Array);\r
965                                                 row [(int)SCHEMA_TABLE.IsLong] = false;\r
966                                         }\r
967                                         else if(columnType == Types.BIGINT) {\r
968                                                 row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
969                                                 row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfInt64;\r
970                                                 row [(int)SCHEMA_TABLE.IsLong] = false;\r
971                                         }\r
972                                         else if(columnType == Types.BINARY) {\r
973                                                 row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
974                                                 row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfByteArray;\r
975                                                 row [(int)SCHEMA_TABLE.IsLong] = true;\r
976                                         }\r
977                                         else if(columnType == Types.BIT) {\r
978                                                 row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
979                                                 row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfBoolean;\r
980                                                 row [(int)SCHEMA_TABLE.IsLong] = false;\r
981                                         }\r
982                                         else if(columnType == Types.BLOB) {\r
983                                                 row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
984                                                 row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfByteArray;\r
985                                                 row [(int)SCHEMA_TABLE.IsLong] = true;\r
986                                         }\r
987                                         else if(columnType == Types.CHAR) {\r
988                                                 // FIXME : specific for Microsoft SQl Server driver\r
989                                                 if (columnTypeName.Equals("uniqueidentifier")) {\r
990                                                         row [(int)SCHEMA_TABLE.ProviderType] = DbType.Guid;\r
991                                                         row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfGuid;\r
992                                                         row [(int)SCHEMA_TABLE.IsLong] = false;\r
993                                                 }\r
994                                                 else {\r
995                                                         row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
996                                                         row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfString;\r
997                                                         row [(int)SCHEMA_TABLE.IsLong] = false;\r
998                                                 }\r
999                                         }\r
1000                                         else if(columnType == Types.CLOB) {\r
1001                                                 row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
1002                                                 row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfString; // instead og .java.sql.Clob\r
1003                                                 row [(int)SCHEMA_TABLE.IsLong] = true;\r
1004                                         }\r
1005                                         else if(columnType == Types.DATE) {\r
1006                                                 row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
1007                                                 row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfDateTime;\r
1008                                                 row [(int)SCHEMA_TABLE.IsLong] = false;\r
1009                                         }\r
1010                                         else if(columnType == Types.DECIMAL) {\r
1011                                                 row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
1012                                                 row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfDecimal;\r
1013                                                 row [(int)SCHEMA_TABLE.IsLong] = false;\r
1014                                         }\r
1015                                                 //                else if(columnType == Types.DISTINCT)\r
1016                                                 //                {\r
1017                                                 //                    row ["ProviderType = (int)GetProviderType(columnType);\r
1018                                                 //                    row ["DataType = typeof (?);\r
1019                                                 //                    row ["IsLong = false;\r
1020                                                 //                }\r
1021                                         else if(columnType == Types.DOUBLE) {\r
1022                                                 row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
1023                                                 row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfDouble; // was typeof(float)\r
1024                                                 row [(int)SCHEMA_TABLE.IsLong] = false;\r
1025                                         }\r
1026                                         else if(columnType == Types.FLOAT) {\r
1027                                                 row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
1028                                                 row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfDouble;\r
1029                                                 row [(int)SCHEMA_TABLE.IsLong] = false;\r
1030                                         }\r
1031                                         else if(columnType == Types.REAL) {\r
1032                                                 row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
1033                                                 row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfFloat;\r
1034                                                 row [(int)SCHEMA_TABLE.IsLong] = false;\r
1035                                         }\r
1036                                         else if(columnType == Types.INTEGER) {\r
1037                                                 row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
1038                                                 row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfInt32;\r
1039                                                 row [(int)SCHEMA_TABLE.IsLong] = false;\r
1040                                         }\r
1041                                         else if(columnType == Types.JAVA_OBJECT) {\r
1042                                                 row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
1043                                                 row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfObject;\r
1044                                                 row [(int)SCHEMA_TABLE.IsLong] = false;\r
1045                                         }\r
1046                                         else if(columnType == Types.LONGVARBINARY) {\r
1047                                                 row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
1048                                                 row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfByteArray;\r
1049                                                 row [(int)SCHEMA_TABLE.IsLong] = true;\r
1050                                         }\r
1051                                         else if(columnType == Types.LONGVARCHAR) {\r
1052                                                 row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
1053                                                 row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfString;\r
1054                                                 row [(int)SCHEMA_TABLE.IsLong] = true;\r
1055                                         }\r
1056                                         else if(columnType == Types.NUMERIC) {\r
1057                                                 row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
1058                                                 row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfDecimal;\r
1059                                                 row [(int)SCHEMA_TABLE.IsLong] = false;\r
1060                                         }\r
1061                                         else if(columnType == Types.REF) {\r
1062                                                 row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
1063                                                 row [(int)SCHEMA_TABLE.DataType] = typeof (java.sql.Ref);\r
1064                                                 row [(int)SCHEMA_TABLE.IsLong] = true;\r
1065                                         }\r
1066                                         else if(columnType == Types.SMALLINT) {\r
1067                                                 row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
1068                                                 row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfInt16;\r
1069                                                 row [(int)SCHEMA_TABLE.IsLong] = false;\r
1070                                         }\r
1071                                         else if(columnType == Types.STRUCT) {\r
1072                                                 row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
1073                                                 row [(int)SCHEMA_TABLE.DataType] = typeof (java.sql.Struct);\r
1074                                                 row [(int)SCHEMA_TABLE.IsLong] = false;\r
1075                                         }\r
1076                                         else if(columnType == Types.TIME) {\r
1077                                                 row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
1078                                                 row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfTimespan;\r
1079                                                 row [(int)SCHEMA_TABLE.IsLong] = false;\r
1080                                         }\r
1081                                         else if(columnType == Types.TIMESTAMP) {\r
1082                                                 row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
1083                                                 row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfDateTime;\r
1084                                                 row [(int)SCHEMA_TABLE.IsLong] = false;\r
1085                                         }\r
1086                                         else if(columnType == Types.TINYINT) {\r
1087                                                 row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
1088                                                 row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfByte;\r
1089                                                 row [(int)SCHEMA_TABLE.IsLong] = false;\r
1090                                         }\r
1091                                         else if(columnType == Types.VARBINARY) {\r
1092                                                 row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
1093                                                 row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfByteArray;\r
1094                                                 row [(int)SCHEMA_TABLE.IsLong] = true;\r
1095                                         }\r
1096                                         else if(columnType == Types.VARCHAR) {\r
1097                                                 // FIXME : specific for Microsoft SQl Server driver\r
1098                                                 if (columnTypeName.Equals("sql_variant")) {\r
1099                                                         row [(int)SCHEMA_TABLE.ProviderType] = DbType.Object;\r
1100                                                         row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfObject;\r
1101                                                         row [(int)SCHEMA_TABLE.IsLong] = false;\r
1102                                                 }\r
1103                                                 else {\r
1104                                                         row [(int)SCHEMA_TABLE.ProviderType] = GetProviderType(columnType);\r
1105                                                         row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfString;// (char[]);\r
1106                                                         row [(int)SCHEMA_TABLE.IsLong] = false;//true;\r
1107                                                 }\r
1108                                         }\r
1109                                         else if(columnType == -8 && columnTypeName.Equals("ROWID")) {\r
1110                                                 // FIXME : specific for Oracle JDBC driver : OracleTypes.ROWID\r
1111                                                 row [(int)SCHEMA_TABLE.ProviderType] = DbType.String;\r
1112                                                 row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfString;\r
1113                                                 row [(int)SCHEMA_TABLE.IsLong] = false;\r
1114                                         }\r
1115                                         else {\r
1116                                                 row [(int)SCHEMA_TABLE.ProviderType] = DbType.Object;\r
1117                                                 row [(int)SCHEMA_TABLE.DataType] = DbTypes.TypeOfObject;\r
1118                                                 row [(int)SCHEMA_TABLE.IsLong] = true;\r
1119                                         }\r
1120                                         SchemaTable.Rows.Add (row);\r
1121                                 }\r
1122                         }\r
1123                         catch (SQLException e) {                                \r
1124                                 throw CreateException(e);\r
1125                         }\r
1126                         finally {\r
1127                                 if (clonedConnection != null) {\r
1128                                         clonedConnection.Close();\r
1129                                 }\r
1130                         }                       \r
1131                         return SchemaTable;\r
1132                 }\r
1133 \r
1134                 private void InitKeyInfo(DataRow row, DatabaseMetaData dbMetaData, String catalog, String schema, String table) {\r
1135                         string column = (string)row [(int)SCHEMA_TABLE.BaseColumnName];\r
1136 \r
1137                         row [(int)SCHEMA_TABLE.IsUnique] = false;\r
1138                         row [(int)SCHEMA_TABLE.IsKey] = false;\r
1139                         row [(int)SCHEMA_TABLE.IsIdentity] = false;\r
1140                         row [(int)SCHEMA_TABLE.IsRowVersion] = false;\r
1141 \r
1142                         if ((_command.Behavior & CommandBehavior.KeyInfo) == 0)\r
1143                                 return;\r
1144 \r
1145                         if(table == null || column == null || dbMetaData == null)\r
1146                                 return;\r
1147 \r
1148                         ResultSet versionCol = dbMetaData.getVersionColumns(catalog, schema, table);\r
1149                         try {\r
1150                                 while(versionCol.next()) {\r
1151                                         if(versionCol.getString("COLUMN_NAME") == column) {\r
1152                                                 if (DatabaseMetaData__Finals.versionColumnPseudo == versionCol.getShort("PSEUDO_COLUMN")) {\r
1153                                                         row [(int)SCHEMA_TABLE.IsIdentity] = true;\r
1154                                                         row [(int)SCHEMA_TABLE.IsRowVersion] = true;\r
1155                                                 }\r
1156                                         }\r
1157                                 }\r
1158                         }\r
1159                         finally {\r
1160                                 versionCol.close();\r
1161                         }\r
1162 \r
1163                         ResultSet primaryKeys = dbMetaData.getPrimaryKeys(catalog,schema,table);\r
1164                         bool primaryKeyExists = false;\r
1165                         int columnCount = 0;\r
1166                         try {\r
1167                                 while(primaryKeys.next()) {\r
1168                                         columnCount++;\r
1169                                         if(primaryKeys.getString("COLUMN_NAME") == column) {\r
1170                                                 row [(int)SCHEMA_TABLE.IsKey] = true;\r
1171                                                 primaryKeyExists = true;\r
1172                                         }\r
1173                                 }\r
1174                                 // column constitutes a key by itself, so it should be marked as unique \r
1175                                 if ((columnCount == 1) && (((bool)row [(int)SCHEMA_TABLE.IsKey]) == true)) {\r
1176                                         row [(int)SCHEMA_TABLE.IsUnique] = true;\r
1177                                 }\r
1178                         }\r
1179                         finally {\r
1180                                 primaryKeys.close();\r
1181                         }\r
1182 \r
1183                         ResultSet indexInfoRes = dbMetaData.getIndexInfo(catalog,schema,table,true,false);\r
1184                         string currentIndexName = null;\r
1185                         columnCount = 0;\r
1186                         bool belongsToCurrentIndex = false;\r
1187                         bool atFirstIndex = true;\r
1188                         bool uniqueKeyExists = false;\r
1189                         try {\r
1190                                 while(indexInfoRes.next()) {\r
1191                                         if (indexInfoRes.getShort("TYPE") ==  DatabaseMetaData__Finals.tableIndexStatistic) {\r
1192                                                 // index of type tableIndexStatistic identifies table statistics - ignore it\r
1193                                                 continue;\r
1194                                         }\r
1195                                         \r
1196                                         uniqueKeyExists = true;\r
1197                                         string iname = indexInfoRes.getString("INDEX_NAME");\r
1198                                         if (currentIndexName == iname) {\r
1199                                                 // we're within the rows of the same index \r
1200                                                 columnCount++;\r
1201                                         }\r
1202                                         else {\r
1203                                                 // we jump to row of new index \r
1204                                                 if (belongsToCurrentIndex && columnCount == 1) {\r
1205                                                         // there is a constraint of type UNIQUE that applies only to this column\r
1206                                                         row [(int)SCHEMA_TABLE.IsUnique] = true;\r
1207                                                 }\r
1208 \r
1209                                                 if (currentIndexName != null) {\r
1210                                                         atFirstIndex = false;\r
1211                                                 }\r
1212                                                 currentIndexName = iname;\r
1213                                                 columnCount = 1;\r
1214                                                 belongsToCurrentIndex = false;\r
1215                                         }\r
1216 \r
1217                                         if(indexInfoRes.getString("COLUMN_NAME") == column) {\r
1218                                                 // FIXME : this will cause "spare" columns marked as IsKey. Needs future investigation.\r
1219                                                 // only the first index we met should be marked as a key\r
1220                                                 //if (atFirstIndex) {\r
1221                                                         row [(int)SCHEMA_TABLE.IsKey] = true;\r
1222                                                 //}\r
1223                                                 belongsToCurrentIndex = true;                                           \r
1224                                         }\r
1225                                 }\r
1226                                 // the column appears in the last index, which is single-column\r
1227                                 if (belongsToCurrentIndex && columnCount == 1) {\r
1228                                         // there is a constraint of type UNIQUE that applies only to this column\r
1229                                         row [(int)SCHEMA_TABLE.IsUnique] = true;\r
1230                                 }\r
1231                         }\r
1232                         finally {\r
1233                                 indexInfoRes.close();\r
1234                         }                       \r
1235 \r
1236                         if(!primaryKeyExists && !uniqueKeyExists) {\r
1237                                 ResultSet bestRowId = dbMetaData.getBestRowIdentifier(catalog, schema, table, DatabaseMetaData__Finals.bestRowTemporary, false);\r
1238                                 try {\r
1239                                         while(bestRowId.next()) {\r
1240                                                 if(bestRowId.getString("COLUMN_NAME") == column)\r
1241                                                         row [(int)SCHEMA_TABLE.IsKey] = true;\r
1242                                         }\r
1243                                 }\r
1244                                 finally {\r
1245                                         bestRowId.close();\r
1246                                 }\r
1247                         }\r
1248                 }\r
1249 \r
1250                 protected static DataTable ConstructSchemaTable ()\r
1251                 {\r
1252                         Type booleanType = DbTypes.TypeOfBoolean;\r
1253                         Type stringType = DbTypes.TypeOfString;\r
1254                         Type intType = DbTypes.TypeOfInt32;\r
1255                         Type typeType = DbTypes.TypeOfType;\r
1256                         Type shortType = DbTypes.TypeOfInt16;\r
1257 \r
1258                         DataTable schemaTable = new DataTable ("SchemaTable");\r
1259                         schemaTable.Columns.Add ("ColumnName", stringType);\r
1260                         schemaTable.Columns.Add ("ColumnOrdinal", intType);\r
1261                         schemaTable.Columns.Add ("ColumnSize", intType);\r
1262                         schemaTable.Columns.Add ("NumericPrecision", shortType);\r
1263                         schemaTable.Columns.Add ("NumericScale", shortType);\r
1264                         schemaTable.Columns.Add ("IsUnique", booleanType);\r
1265                         schemaTable.Columns.Add ("IsKey", booleanType);\r
1266                         schemaTable.Columns.Add ("BaseServerName", stringType);\r
1267                         schemaTable.Columns.Add ("BaseCatalogName", stringType);\r
1268                         schemaTable.Columns.Add ("BaseColumnName", stringType);\r
1269                         schemaTable.Columns.Add ("BaseSchemaName", stringType);\r
1270                         schemaTable.Columns.Add ("BaseTableName", stringType);\r
1271                         schemaTable.Columns.Add ("DataType", typeType);\r
1272                         schemaTable.Columns.Add ("AllowDBNull", booleanType);\r
1273                         schemaTable.Columns.Add ("ProviderType", intType);\r
1274                         schemaTable.Columns.Add ("IsAliased", booleanType);\r
1275                         schemaTable.Columns.Add ("IsExpression", booleanType);\r
1276                         schemaTable.Columns.Add ("IsIdentity", booleanType);\r
1277                         schemaTable.Columns.Add ("IsAutoIncrement", booleanType);\r
1278                         schemaTable.Columns.Add ("IsRowVersion", booleanType);\r
1279                         schemaTable.Columns.Add ("IsHidden", booleanType);\r
1280                         schemaTable.Columns.Add ("IsLong", booleanType);\r
1281                         schemaTable.Columns.Add ("IsReadOnly", booleanType);\r
1282                         return schemaTable;\r
1283                 }\r
1284 \r
1285                 #endregion // Methods\r
1286         }\r
1287 }