2 // System.Data.Common.AbstractDataReader
5 // Konstantin Triger <kostat@mainsoft.com>
6 // Boris Kirzner <borisk@mainsoft.com>
8 // (C) 2005 Mainsoft Corporation (http://www.mainsoft.com)
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:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
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.
35 using System
.Collections
;
36 using System
.Data
.Common
;
41 namespace System
.Data
.ProviderBase
43 public abstract class AbstractDataReader
: DbDataReader
, ISafeDataRecord
{
47 private ResultSetMetaData _resultsMetaData
;
48 protected AbstractDbCommand _command
;
49 private DataTable _schemaTable
;
50 private ReaderState _readerState
= ReaderState
.Uninitialized
;
52 private IReaderCacheContainer
[] _readerCache
;
53 private int _currentCacheFilledPosition
;
54 private Stack _resultSetStack
= new Stack();
55 private bool _isClosed
= false;
58 private enum ReaderState { Uninitialized = 0, Empty = 1, HasRows = 2, FirstRed = 4, Eof = 8, Fetching = 16 }
;
60 protected internal enum SCHEMA_TABLE
{ ColumnName
,
88 protected AbstractDataReader(AbstractDbCommand command
) {
90 if (_command
.Connection
!= null) {
91 ((AbstractDBConnection
)_command
.Connection
).AddReference(this);
95 #endregion // Constructors
99 public override int Depth
{
103 public override bool HasRows
{
106 throw new InvalidOperationException("Invalid attempt to HasRows when reader is closed.");
113 catch(SystemException
) {
118 return (_readerState
& ReaderState
.HasRows
) != 0;
122 public override int RecordsAffected
124 // MSDN : The RecordsAffected property is not set
125 // until all rows are read and you close the reader.
127 return _command
.RecordsAffected
;
131 public override int FieldCount
134 if (ResultsMetaData
== null)
138 return ResultsMetaData
.getColumnCount();
140 catch (SQLException exp
) {
141 throw CreateException(exp
);
147 protected internal CommandBehavior Behavior
150 return _command
.Behavior
;
154 public override Object
this[String columnName
]
158 int columnIndex
= Results
.findColumn(columnName
) - 1;
159 return this[columnIndex
];
161 catch (SQLException exp
) {
162 throw new IndexOutOfRangeException(exp
.Message
, exp
);
167 public override Object
this[int columnIndex
]
169 get { return GetValue(columnIndex); }
172 protected ResultSet Results
175 if (_readerState
== ReaderState
.Uninitialized
) {
177 if (_resultSetStack
.Count
== 0) {
178 ResultSet resultSet
= _command
.CurrentResultSet
;
179 if (resultSet
== null)
182 _resultSetStack
.Push(resultSet
);
185 _readerState
= ReaderState
.Fetching
;
188 Configuration
.BooleanSetting prefetchSchema
= Configuration
.Switches
.PrefetchSchema
;
190 if (prefetchSchema
== Configuration
.BooleanSetting
.NotSet
) {
191 AbstractDBConnection conn
= (AbstractDBConnection
)((ICloneable
)_command
.Connection
);
192 string driverName
= conn
.JdbcConnection
.getMetaData().getDriverName();
193 if (driverName
.IndexOf("DB2", StringComparison
.Ordinal
) >= 0)
194 prefetchSchema
= Configuration
.BooleanSetting
.True
;
197 if (prefetchSchema
== Configuration
.BooleanSetting
.True
)
200 ResultSet resultSet
= (ResultSet
)_resultSetStack
.Peek();
201 if (resultSet
.next()) {
202 _readerState
= (ReaderState
.HasRows
| ReaderState
.FirstRed
);
203 ResultSetMetaData rsMetaData
= ResultsMetaData
;
204 DbConvert
.JavaSqlTypes javaSqlType
= (DbConvert
.JavaSqlTypes
)rsMetaData
.getColumnType(1);
205 if (javaSqlType
== DbConvert
.JavaSqlTypes
.OTHER
) {
206 object value = GetValue(0);
207 if (value != null && value is ResultSet
) {
208 _resultsMetaData
= null;
211 _readerState
= ReaderState
.Fetching
;
212 _resultSetStack
.Push(value);
218 _readerState
= ReaderState
.Empty
;
222 catch(SQLException e
) {
223 throw CreateException(e
);
228 return (_resultSetStack
.Count
> 0) ? (ResultSet
)_resultSetStack
.Peek() : null;
232 protected ResultSetMetaData ResultsMetaData
235 ResultSet results
= Results
;
236 if (results
== null) {
239 if(_resultsMetaData
== null) {
240 _resultsMetaData
= results
.getMetaData();
242 return _resultsMetaData
;
246 protected DataTable SchemaTable
249 if (_schemaTable
== null) {
250 _schemaTable
= ConstructSchemaTable();
255 set {_schemaTable = value; }
258 internal protected IReaderCacheContainer
[] ReaderCache
261 if (_readerCache
== null) {
262 _readerCache
= CreateReaderCache();
263 _currentCacheFilledPosition
= -1;
269 public override bool IsClosed
{
270 get { return _isClosed; }
273 #endregion // Properties
277 protected abstract int GetProviderType(int jdbcType
);
279 protected abstract SystemException
CreateException(string message
, SQLException e
);
281 protected abstract SystemException
CreateException(IOException e
);
283 protected SystemException
CreateException(SQLException e
)
285 return CreateException(e
.Message
,e
);
288 private bool CloseCurrentResultSet() {
289 if (_resultSetStack
.Count
> 0) {
291 _resultsMetaData
= null;
293 _readerState
= ReaderState
.Uninitialized
;
294 ResultSet rs
= (ResultSet
)_resultSetStack
.Pop();
298 catch (SQLException exp
) {
299 throw CreateException(exp
);
306 // FIXME : add Close(bool readAllRecords) and pass this bool to skip looping over NextResult(), override AbstractDbCommand.ExecuteScalar
307 public override void Close()
313 CloseCurrentResultSet();
314 _command
.OnReaderClosed(this);
321 internal void CloseInternal()
323 _resultsMetaData
= null;
328 public override IEnumerator
GetEnumerator ()
330 bool closeReader
= (Behavior
& CommandBehavior
.CloseConnection
) != 0;
331 return new DbEnumerator (this , closeReader
);
334 public override bool NextResult()
336 CloseCurrentResultSet();
338 if ((_command
.Behavior
& CommandBehavior
.SingleResult
) != 0) {
339 while (CloseCurrentResultSet());
340 while (_command
.NextResultSet());
345 while (_resultSetStack
.Count
> 0) {
346 ResultSet rs
= (ResultSet
)_resultSetStack
.Peek();
349 CloseCurrentResultSet();
353 // must be a ResultSet
354 object childRs
= rs
.getObject(1);
355 if (childRs
!= null) {
357 _resultSetStack
.Push(childRs
);
362 catch (SQLException exp
) {
363 throw CreateException(exp
);
366 if (_command
.NextResultSet()) {
373 public override bool Read()
375 if(null == Results
||
376 (_readerState
& (ReaderState
.HasRows
| ReaderState
.Eof
)) != ReaderState
.HasRows
)
379 bool firstRead
= false;
382 if ((_readerState
& ReaderState
.FirstRed
) != 0) {
384 _readerState
&= ~ReaderState
.FirstRed
;
388 bool next
= Results
.next();
391 _readerState
|= ReaderState
.Eof
;
396 catch (SQLException exp
) {
397 // suppress exception as .Net does
401 // in case of first read we could sampled the first value
402 // to see whether there is a resultset, so _currentCacheFilledPosition
403 // might be already inited
405 _currentCacheFilledPosition
= -1;
409 public override bool GetBoolean(int columnIndex
)
411 FillReaderCache(columnIndex
);
412 return ((BooleanReaderCacheContainer
)ReaderCache
[columnIndex
]).GetBoolean();
415 public bool GetBooleanSafe(int columnIndex
)
417 if (ReaderCache
[columnIndex
] is BooleanReaderCacheContainer
) {
418 return GetBoolean(columnIndex
);
421 return Convert
.ToBoolean(GetValue(columnIndex
));
425 public override byte GetByte(int columnIndex
)
427 FillReaderCache(columnIndex
);
428 return ((ByteReaderCacheContainer
)ReaderCache
[columnIndex
]).GetByte();
431 public byte GetByteSafe(int columnIndex
)
433 if (ReaderCache
[columnIndex
] is ByteReaderCacheContainer
) {
434 return GetByte(columnIndex
);
437 return Convert
.ToByte(GetValue(columnIndex
));
441 public override long GetBytes(
448 FillReaderCache(columnIndex
);
449 return ((BytesReaderCacheContainer
)ReaderCache
[columnIndex
])
450 .GetBytes(dataIndex
, buffer
, bufferIndex
, length
);
453 public virtual byte[] GetBytes(int columnIndex
)
455 FillReaderCache(columnIndex
);
456 return ((BytesReaderCacheContainer
)ReaderCache
[columnIndex
]).GetBytes();
459 public override char GetChar(int columnIndex
)
461 FillReaderCache(columnIndex
);
462 string s
= ((StringReaderCacheContainer
)ReaderCache
[columnIndex
]).GetString();
469 public char GetCharSafe(int columnIndex
)
471 if (ReaderCache
[columnIndex
] is StringReaderCacheContainer
) {
472 return GetChar(columnIndex
);
475 return Convert
.ToChar(GetValue(columnIndex
));
479 public override long GetChars(
486 FillReaderCache(columnIndex
);
487 return ((CharsReaderCacheContainer
)ReaderCache
[columnIndex
])
488 .GetChars(dataIndex
, buffer
, bufferIndex
, length
);
491 public override string GetDataTypeName(int columnIndex
)
494 if (ResultsMetaData
== null) {
497 return ResultsMetaData
.getColumnTypeName(columnIndex
+ 1);
499 catch (SQLException exp
) {
500 throw CreateException(exp
);
504 public override DateTime
GetDateTime(int columnIndex
)
506 return GetDateTimeUnsafe(columnIndex
);
509 DateTime
GetDateTimeUnsafe(int columnIndex
)
511 FillReaderCache(columnIndex
);
512 return ((DateTimeReaderCacheContainer
)ReaderCache
[columnIndex
]).GetDateTime();
515 public DateTime
GetDateTimeSafe(int columnIndex
)
517 if (ReaderCache
[columnIndex
] is DateTimeReaderCacheContainer
) {
518 return GetDateTimeUnsafe(columnIndex
);
521 return Convert
.ToDateTime(GetValue(columnIndex
));
525 public virtual TimeSpan
GetTimeSpan(int columnIndex
)
527 FillReaderCache(columnIndex
);
528 return ((TimeSpanReaderCacheContainer
)ReaderCache
[columnIndex
]).GetTimeSpan();
531 public override Guid
GetGuid(int columnIndex
)
533 FillReaderCache(columnIndex
);
534 return ((GuidReaderCacheContainer
)ReaderCache
[columnIndex
]).GetGuid();
537 public override decimal GetDecimal(int columnIndex
)
539 return GetDecimalUnsafe(columnIndex
);
542 decimal GetDecimalUnsafe(int columnIndex
)
544 FillReaderCache(columnIndex
);
545 return ((DecimalReaderCacheContainer
)ReaderCache
[columnIndex
]).GetDecimal();
548 public decimal GetDecimalSafe(int columnIndex
)
550 if (ReaderCache
[columnIndex
] is DecimalReaderCacheContainer
) {
551 return GetDecimalUnsafe(columnIndex
);
554 return Convert
.ToDecimal(GetValue(columnIndex
));
558 public override double GetDouble(int columnIndex
)
560 return GetDoubleUnsafe(columnIndex
);
563 double GetDoubleUnsafe(int columnIndex
)
565 FillReaderCache(columnIndex
);
566 return ((DoubleReaderCacheContainer
)ReaderCache
[columnIndex
]).GetDouble();
569 public double GetDoubleSafe(int columnIndex
)
571 if (ReaderCache
[columnIndex
] is DoubleReaderCacheContainer
) {
572 return GetDoubleUnsafe(columnIndex
);
575 return Convert
.ToDouble(GetValue(columnIndex
));
579 public override float GetFloat(int columnIndex
)
581 return GetFloatUnsafe(columnIndex
);
584 float GetFloatUnsafe(int columnIndex
)
586 FillReaderCache(columnIndex
);
587 return ((FloatReaderCacheContainer
)ReaderCache
[columnIndex
]).GetFloat();
590 public float GetFloatSafe(int columnIndex
)
592 if (ReaderCache
[columnIndex
] is FloatReaderCacheContainer
) {
593 return GetFloatUnsafe(columnIndex
);
596 return Convert
.ToSingle(GetValue(columnIndex
));
600 public override short GetInt16(int columnIndex
)
602 return GetInt16Unsafe(columnIndex
);
605 short GetInt16Unsafe(int columnIndex
)
607 FillReaderCache(columnIndex
);
608 return ((Int16ReaderCacheContainer
)ReaderCache
[columnIndex
]).GetInt16();
611 public short GetInt16Safe(int columnIndex
)
613 if (ReaderCache
[columnIndex
] is Int16ReaderCacheContainer
) {
614 return GetInt16Unsafe(columnIndex
);
617 return Convert
.ToInt16(GetValue(columnIndex
));
621 public override int GetInt32(int columnIndex
)
623 return GetInt32Unsafe(columnIndex
);
626 int GetInt32Unsafe(int columnIndex
)
628 FillReaderCache(columnIndex
);
629 return ((Int32ReaderCacheContainer
)ReaderCache
[columnIndex
]).GetInt32();
632 public int GetInt32Safe(int columnIndex
)
634 if (ReaderCache
[columnIndex
] is Int32ReaderCacheContainer
) {
635 return GetInt32Unsafe(columnIndex
);
638 return Convert
.ToInt32(GetValue(columnIndex
));
642 public override long GetInt64(int columnIndex
)
644 return GetInt64Unsafe(columnIndex
);
647 long GetInt64Unsafe(int columnIndex
)
649 FillReaderCache(columnIndex
);
650 return ((Int64ReaderCacheContainer
)ReaderCache
[columnIndex
]).GetInt64();
653 public long GetInt64Safe(int columnIndex
)
655 if (ReaderCache
[columnIndex
] is Int64ReaderCacheContainer
) {
656 return GetInt64Unsafe(columnIndex
);
659 return Convert
.ToInt64(GetValue(columnIndex
));
663 public override string GetName(int columnIndex
)
666 if (ResultsMetaData
== null) {
669 return ResultsMetaData
.getColumnName(columnIndex
+ 1);
671 catch (SQLException exp
) {
672 throw new IndexOutOfRangeException(exp
.Message
, exp
);
676 public override int GetOrdinal(String columnName
)
679 int retVal
= Results
.findColumn(columnName
);
685 catch (SQLException exp
) {
686 throw new IndexOutOfRangeException(exp
.Message
, exp
);
690 public override string GetString(int columnIndex
)
692 return GetStringUnsafe(columnIndex
);
695 string GetStringUnsafe(int columnIndex
)
697 FillReaderCache(columnIndex
);
698 return ((StringReaderCacheContainer
)ReaderCache
[columnIndex
]).GetString();
701 public string GetStringSafe(int columnIndex
) {
702 if (ReaderCache
[columnIndex
] is StringReaderCacheContainer
) {
703 return GetStringUnsafe(columnIndex
);
706 return Convert
.ToString(GetValue(columnIndex
));
710 public override object GetValue(int columnIndex
)
712 FillReaderCache(columnIndex
);
713 if (ReaderCache
[columnIndex
].IsNull()) {
716 return ReaderCache
[columnIndex
].GetValue();
719 public override int GetValues(Object
[] values
)
721 int columnCount
= FieldCount
;
723 for (; i
< values
.Length
&& i
< columnCount
; i
++) {
724 values
[i
] = GetValue(i
);
729 private void FillReaderCache(int columnIndex
)
732 IReaderCacheContainer
[] readerCache
= ReaderCache
;
733 if ((Behavior
& CommandBehavior
.SequentialAccess
) == 0) {
734 while (_currentCacheFilledPosition
< columnIndex
) {
735 _currentCacheFilledPosition
++;
736 readerCache
[_currentCacheFilledPosition
].Fetch(Results
,_currentCacheFilledPosition
, false);
740 readerCache
[columnIndex
].Fetch(Results
,columnIndex
, true);
743 catch(SQLException e
) {
744 _currentCacheFilledPosition
= -1;
745 throw CreateException(e
);
747 catch (IOException e
) {
748 _currentCacheFilledPosition
= -1;
749 throw CreateException(e
);
753 protected virtual IReaderCacheContainer
CreateReaderCacheContainer(int jdbcType
, int columnIndex
) {
754 switch ((DbConvert
.JavaSqlTypes
)jdbcType
) {
755 case DbConvert
.JavaSqlTypes
.ARRAY
:
756 return new ArrayReaderCacheContainer();
757 case DbConvert
.JavaSqlTypes
.BIGINT
:
758 return new Int64ReaderCacheContainer();
759 case DbConvert
.JavaSqlTypes
.BINARY
:
760 case DbConvert
.JavaSqlTypes
.VARBINARY
:
761 case DbConvert
.JavaSqlTypes
.LONGVARBINARY
:
762 return new BytesReaderCacheContainer();
763 case DbConvert
.JavaSqlTypes
.BIT
:
764 return new BooleanReaderCacheContainer();
765 case DbConvert
.JavaSqlTypes
.BLOB
:
766 return new BlobReaderCacheContainer();
767 case DbConvert
.JavaSqlTypes
.VARCHAR
:
768 case DbConvert
.JavaSqlTypes
.CHAR
:
769 if (String
.CompareOrdinal("uniqueidentifier", ResultsMetaData
.getColumnTypeName(columnIndex
)) == 0) {
770 return new GuidReaderCacheContainer();
773 return new StringReaderCacheContainer();
775 case DbConvert
.JavaSqlTypes
.CLOB
:
776 return new ClobReaderCacheContainer();
777 case DbConvert
.JavaSqlTypes
.TIME
:
778 return new TimeSpanReaderCacheContainer();
779 case DbConvert
.JavaSqlTypes
.DATE
:
780 AbstractDBConnection conn
= (AbstractDBConnection
)((ICloneable
)_command
.Connection
);
781 string driverName
= conn
.JdbcConnection
.getMetaData().getDriverName();
783 if (driverName
.StartsWith("PostgreSQL")) {
784 return new DateTimeReaderCacheContainer();
787 goto case DbConvert
.JavaSqlTypes
.TIMESTAMP
;
788 case DbConvert
.JavaSqlTypes
.TIMESTAMP
:
789 return new TimestampReaderCacheContainer();
790 case DbConvert
.JavaSqlTypes
.DECIMAL
:
791 case DbConvert
.JavaSqlTypes
.NUMERIC
:
792 // jdbc driver for oracle identitfies both FLOAT and NUMBEr columns as
793 // java.sql.Types.NUMERIC (2), columnTypeName NUMBER, columnClassName java.math.BigDecimal
794 // therefore we relay on scale
795 int scale
= ResultsMetaData
.getScale(columnIndex
);
797 // Oracle db type FLOAT
798 return new DoubleReaderCacheContainer();
801 return new DecimalReaderCacheContainer();
803 case DbConvert
.JavaSqlTypes
.DOUBLE
:
804 case DbConvert
.JavaSqlTypes
.FLOAT
:
805 return new DoubleReaderCacheContainer();
806 case DbConvert
.JavaSqlTypes
.INTEGER
:
807 return new Int32ReaderCacheContainer();
808 case DbConvert
.JavaSqlTypes
.LONGVARCHAR
:
809 return new StringReaderCacheContainer();
810 case DbConvert
.JavaSqlTypes
.NULL
:
811 return new NullReaderCacheContainer();
812 case DbConvert
.JavaSqlTypes
.REAL
:
813 return new FloatReaderCacheContainer();
814 case DbConvert
.JavaSqlTypes
.REF
:
815 return new RefReaderCacheContainer();
816 case DbConvert
.JavaSqlTypes
.SMALLINT
:
817 return new Int16ReaderCacheContainer();
818 case DbConvert
.JavaSqlTypes
.TINYINT
:
819 return new ByteReaderCacheContainer();
820 case DbConvert
.JavaSqlTypes
.DISTINCT
:
821 case DbConvert
.JavaSqlTypes
.JAVA_OBJECT
:
822 case DbConvert
.JavaSqlTypes
.OTHER
:
823 case DbConvert
.JavaSqlTypes
.STRUCT
:
825 return new ObjectReaderCacheContainer();
829 private IReaderCacheContainer
[] CreateReaderCache()
832 IReaderCacheContainer
[] readerCache
= new IReaderCacheContainer
[FieldCount
];
833 for(int i
=1; i
<= readerCache
.Length
; i
++)
834 readerCache
[i
-1] = CreateReaderCacheContainer(ResultsMetaData
.getColumnType(i
), i
);
838 catch(SQLException e
) {
839 throw CreateException(e
);
843 protected bool IsNumeric(int columnIndex
)
845 return ReaderCache
[columnIndex
].IsNumeric();
848 public override bool IsDBNull(int columnIndex
)
850 FillReaderCache(columnIndex
);
851 return ReaderCache
[columnIndex
].IsNull();
854 public override Type
GetFieldType(int i
)
857 int javaSqlType
= ResultsMetaData
.getColumnType(i
+ 1);
858 return DbConvert
.JavaSqlTypeToClrType(javaSqlType
);
860 catch (SQLException exp
) {
861 throw new IndexOutOfRangeException(exp
.Message
, exp
);
865 public IDataReader
GetData(int i
)
867 throw new NotSupportedException();
870 protected virtual void SetSchemaType(DataRow schemaRow
, ResultSetMetaData metaData
, int columnIndex
) {
871 DbConvert
.JavaSqlTypes columnType
= (DbConvert
.JavaSqlTypes
)metaData
.getColumnType(columnIndex
);
873 switch (columnType
) {
874 case DbConvert
.JavaSqlTypes
.ARRAY
: {
875 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
876 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = typeof (java
.sql
.Array
);
877 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = false;
880 case DbConvert
.JavaSqlTypes
.BIGINT
: {
881 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
882 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfInt64
;
883 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = false;
886 case DbConvert
.JavaSqlTypes
.BINARY
: {
887 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
888 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfByteArray
;
889 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = true;
892 case DbConvert
.JavaSqlTypes
.BIT
: {
893 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
894 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfBoolean
;
895 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = false;
898 case DbConvert
.JavaSqlTypes
.BLOB
: {
899 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
900 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfByteArray
;
901 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = true;
904 case DbConvert
.JavaSqlTypes
.VARCHAR
:
905 case DbConvert
.JavaSqlTypes
.CHAR
: {
906 // FIXME : specific for Microsoft SQl Server driver
907 if (String
.CompareOrdinal(metaData
.getColumnTypeName(columnIndex
), "uniqueidentifier") == 0) {
908 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = DbType
.Guid
;
909 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfGuid
;
910 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = false;
913 if (String
.CompareOrdinal(metaData
.getColumnTypeName(columnIndex
), "sql_variant") == 0) {
914 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = DbType
.Object
;
915 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfObject
;
916 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = false;
919 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
920 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfString
;
921 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = false;
925 case DbConvert
.JavaSqlTypes
.CLOB
: {
926 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
927 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfString
; // instead og .java.sql.Clob
928 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = true;
931 case DbConvert
.JavaSqlTypes
.DATE
: {
932 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
933 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfDateTime
;
934 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = false;
937 // else if(DbConvert.JavaSqlTypes.DISTINCT)
939 // schemaRow ["ProviderType = (int)GetProviderType((int)columnType);
940 // schemaRow ["DataType = typeof (?);
941 // schemaRow ["IsLong = false;
943 case DbConvert
.JavaSqlTypes
.DOUBLE
: {
944 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
945 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfDouble
; // was typeof(float)
946 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = false;
949 case DbConvert
.JavaSqlTypes
.FLOAT
: {
950 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
951 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfDouble
;
952 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = false;
955 case DbConvert
.JavaSqlTypes
.REAL
: {
956 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
957 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfFloat
;
958 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = false;
961 case DbConvert
.JavaSqlTypes
.INTEGER
: {
962 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
963 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfInt32
;
964 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = false;
967 case DbConvert
.JavaSqlTypes
.JAVA_OBJECT
: {
968 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
969 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfObject
;
970 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = false;
973 case DbConvert
.JavaSqlTypes
.LONGVARBINARY
: {
974 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
975 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfByteArray
;
976 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = true;
979 case DbConvert
.JavaSqlTypes
.LONGVARCHAR
: {
980 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
981 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfString
;
982 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = true;
985 case DbConvert
.JavaSqlTypes
.DECIMAL
:
986 case DbConvert
.JavaSqlTypes
.NUMERIC
: {
987 int scale
= ResultsMetaData
.getScale(columnIndex
);
989 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
990 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfDouble
;
991 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = false;
994 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
995 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfDecimal
;
996 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = false;
1000 case DbConvert
.JavaSqlTypes
.REF
: {
1001 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
1002 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = typeof (java
.sql
.Ref
);
1003 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = true;
1006 case DbConvert
.JavaSqlTypes
.SMALLINT
: {
1007 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
1008 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfInt16
;
1009 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = false;
1012 case DbConvert
.JavaSqlTypes
.STRUCT
: {
1013 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
1014 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = typeof (java
.sql
.Struct
);
1015 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = false;
1018 case DbConvert
.JavaSqlTypes
.TIME
: {
1019 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
1020 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfTimespan
;
1021 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = false;
1024 case DbConvert
.JavaSqlTypes
.TIMESTAMP
: {
1025 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
1026 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfDateTime
;
1027 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = false;
1030 case DbConvert
.JavaSqlTypes
.TINYINT
: {
1031 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
1032 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfByte
;
1033 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = false;
1036 case DbConvert
.JavaSqlTypes
.VARBINARY
: {
1037 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = GetProviderType((int)columnType
);
1038 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfByteArray
;
1039 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = true;
1043 schemaRow
[(int)SCHEMA_TABLE
.ProviderType
] = DbType
.Object
;
1044 schemaRow
[(int)SCHEMA_TABLE
.DataType
] = DbTypes
.TypeOfObject
;
1045 schemaRow
[(int)SCHEMA_TABLE
.IsLong
] = true;
1051 public override DataTable
GetSchemaTable()
1053 if (SchemaTable
.Rows
!= null && SchemaTable
.Rows
.Count
> 0) {
1057 ResultSetMetaData metaData
;
1058 if (Behavior
== CommandBehavior
.SchemaOnly
) {
1060 metaData
= ((PreparedStatement
)_command
.Statement
).getMetaData();
1062 catch(SQLException e
) {
1063 throw CreateException("CommandBehaviour.SchemaOnly is not supported by the JDBC driver.",e
);
1067 metaData
= ResultsMetaData
;
1070 if (metaData
== null) {
1074 DatabaseMetaData dbMetaData
= null;
1075 AbstractDBConnection clonedConnection
= null;
1076 if ((_command
.Behavior
& CommandBehavior
.KeyInfo
) != 0) {
1077 clonedConnection
= (AbstractDBConnection
)((ICloneable
)_command
.Connection
).Clone();
1080 clonedConnection
.Open();
1081 dbMetaData
= clonedConnection
.JdbcConnection
.getMetaData();
1085 if (clonedConnection
!= null) {
1086 clonedConnection
.Close();
1093 for(int i
= 1; i
<= metaData
.getColumnCount(); i
++) {
1094 DataRow row
= SchemaTable
.NewRow ();
1095 string columnName
= metaData
.getColumnLabel(i
);
1096 string baseColumnName
= metaData
.getColumnName(i
);
1098 row
[(int)SCHEMA_TABLE
.ColumnName
] = columnName
; // maybe we should use metaData.getColumnLabel(i);
1099 row
[(int)SCHEMA_TABLE
.ColumnSize
] = metaData
.getColumnDisplaySize(i
);
1100 row
[(int)SCHEMA_TABLE
.ColumnOrdinal
] = i
- 1;
1102 // FIXME : workaround for Oracle JDBC driver bug
1103 // getPrecision on BLOB, CLOB, NCLOB throws NumberFormatException
1104 tmp
= metaData
.getPrecision(i
);
1106 catch(java
.lang
.NumberFormatException e
) {
1107 // supress exception
1110 row
[(int)SCHEMA_TABLE
.NumericPrecision
] = Convert
.ToInt16(tmp
> 255 ? 255 : tmp
);
1111 tmp
= metaData
.getScale(i
);
1112 row
[(int)SCHEMA_TABLE
.NumericScale
] = Convert
.ToInt16(tmp
> 255 ? 255 : tmp
);
1114 row
[(int)SCHEMA_TABLE
.BaseServerName
] = DBNull
.Value
;
1116 string catalog
= null;
1118 catalog
= metaData
.getCatalogName(i
);
1120 catch (Exception e
) {
1121 // supress exception
1123 if (catalog
!= null && catalog
.Length
== 0)
1124 catalog
= ((AbstractDBConnection
)_command
.Connection
).JdbcConnection
.getCatalog();
1125 row
[(int)SCHEMA_TABLE
.BaseCatalogName
] = catalog
;
1126 row
[(int)SCHEMA_TABLE
.BaseColumnName
] = baseColumnName
;
1132 tableName
= metaData
.getTableName(i
);
1139 schemaName
= metaData
.getSchemaName(i
);
1145 if (tableName
!= null && tableName
.Length
== 0)
1147 if (schemaName
!= null && schemaName
.Length
== 0)
1150 row
[(int)SCHEMA_TABLE
.BaseSchemaName
] = schemaName
;
1151 row
[(int)SCHEMA_TABLE
.BaseTableName
] = tableName
;
1154 row
[(int)SCHEMA_TABLE
.AllowDBNull
] = Convert
.ToBoolean(metaData
.isNullable(i
));
1156 InitKeyInfo(row
, dbMetaData
, catalog
, schemaName
, tableName
);
1158 row
[(int)SCHEMA_TABLE
.IsAliased
] = columnName
!= baseColumnName
;
1159 row
[(int)SCHEMA_TABLE
.IsExpression
] = false;
1161 row
[(int)SCHEMA_TABLE
.IsAutoIncrement
] = metaData
.isAutoIncrement(i
);
1163 row
[(int)SCHEMA_TABLE
.IsHidden
] = false;
1164 row
[(int)SCHEMA_TABLE
.IsReadOnly
] = metaData
.isReadOnly(i
);
1166 SetSchemaType(row
, metaData
, i
);
1168 SchemaTable
.Rows
.Add (row
);
1171 catch (SQLException e
) {
1172 throw CreateException(e
);
1175 if (clonedConnection
!= null) {
1176 clonedConnection
.Close();
1182 private void InitKeyInfo(DataRow row
, DatabaseMetaData dbMetaData
, String catalog
, String schema
, String table
) {
1183 string column
= (string)row
[(int)SCHEMA_TABLE
.BaseColumnName
];
1185 row
[(int)SCHEMA_TABLE
.IsUnique
] = false;
1186 row
[(int)SCHEMA_TABLE
.IsKey
] = false;
1187 row
[(int)SCHEMA_TABLE
.IsIdentity
] = false;
1188 row
[(int)SCHEMA_TABLE
.IsRowVersion
] = false;
1190 if ((_command
.Behavior
& CommandBehavior
.KeyInfo
) == 0)
1193 if(table
== null || column
== null || dbMetaData
== null)
1196 ResultSet versionCol
= dbMetaData
.getVersionColumns(catalog
, schema
, table
);
1198 while(versionCol
.next()) {
1199 if(versionCol
.getString("COLUMN_NAME") == column
) {
1200 if (DatabaseMetaData__Finals
.versionColumnPseudo
== versionCol
.getShort("PSEUDO_COLUMN")) {
1201 row
[(int)SCHEMA_TABLE
.IsIdentity
] = true;
1202 row
[(int)SCHEMA_TABLE
.IsRowVersion
] = true;
1211 ResultSet primaryKeys
= dbMetaData
.getPrimaryKeys(catalog
,schema
,table
);
1212 bool primaryKeyExists
= false;
1213 int columnCount
= 0;
1215 while(primaryKeys
.next()) {
1217 if(primaryKeys
.getString("COLUMN_NAME") == column
) {
1218 row
[(int)SCHEMA_TABLE
.IsKey
] = true;
1219 primaryKeyExists
= true;
1222 // column constitutes a key by itself, so it should be marked as unique
1223 if ((columnCount
== 1) && (((bool)row
[(int)SCHEMA_TABLE
.IsKey
]) == true)) {
1224 row
[(int)SCHEMA_TABLE
.IsUnique
] = true;
1228 primaryKeys
.close();
1231 ResultSet indexInfoRes
= dbMetaData
.getIndexInfo(catalog
,schema
,table
,true,false);
1232 string currentIndexName
= null;
1234 bool belongsToCurrentIndex
= false;
1235 bool atFirstIndex
= true;
1236 bool uniqueKeyExists
= false;
1238 while(indexInfoRes
.next()) {
1239 if (indexInfoRes
.getShort("TYPE") == DatabaseMetaData__Finals
.tableIndexStatistic
) {
1240 // index of type tableIndexStatistic identifies table statistics - ignore it
1244 uniqueKeyExists
= true;
1245 string iname
= indexInfoRes
.getString("INDEX_NAME");
1246 if (currentIndexName
== iname
) {
1247 // we're within the rows of the same index
1251 // we jump to row of new index
1252 if (belongsToCurrentIndex
&& columnCount
== 1) {
1253 // there is a constraint of type UNIQUE that applies only to this column
1254 row
[(int)SCHEMA_TABLE
.IsUnique
] = true;
1257 if (currentIndexName
!= null) {
1258 atFirstIndex
= false;
1260 currentIndexName
= iname
;
1262 belongsToCurrentIndex
= false;
1265 if(indexInfoRes
.getString("COLUMN_NAME") == column
) {
1266 // FIXME : this will cause "spare" columns marked as IsKey. Needs future investigation.
1267 // only the first index we met should be marked as a key
1268 //if (atFirstIndex) {
1269 row
[(int)SCHEMA_TABLE
.IsKey
] = true;
1271 belongsToCurrentIndex
= true;
1274 // the column appears in the last index, which is single-column
1275 if (belongsToCurrentIndex
&& columnCount
== 1) {
1276 // there is a constraint of type UNIQUE that applies only to this column
1277 row
[(int)SCHEMA_TABLE
.IsUnique
] = true;
1281 indexInfoRes
.close();
1284 if(!primaryKeyExists
&& !uniqueKeyExists
) {
1285 ResultSet bestRowId
= dbMetaData
.getBestRowIdentifier(catalog
, schema
, table
, DatabaseMetaData__Finals
.bestRowTemporary
, false);
1287 while(bestRowId
.next()) {
1288 if(bestRowId
.getString("COLUMN_NAME") == column
)
1289 row
[(int)SCHEMA_TABLE
.IsKey
] = true;
1298 protected static DataTable
ConstructSchemaTable ()
1300 Type booleanType
= DbTypes
.TypeOfBoolean
;
1301 Type stringType
= DbTypes
.TypeOfString
;
1302 Type intType
= DbTypes
.TypeOfInt32
;
1303 Type typeType
= DbTypes
.TypeOfType
;
1304 Type shortType
= DbTypes
.TypeOfInt16
;
1306 DataTable schemaTable
= new DataTable ("SchemaTable");
1307 schemaTable
.Columns
.Add ("ColumnName", stringType
);
1308 schemaTable
.Columns
.Add ("ColumnOrdinal", intType
);
1309 schemaTable
.Columns
.Add ("ColumnSize", intType
);
1310 schemaTable
.Columns
.Add ("NumericPrecision", shortType
);
1311 schemaTable
.Columns
.Add ("NumericScale", shortType
);
1312 schemaTable
.Columns
.Add ("IsUnique", booleanType
);
1313 schemaTable
.Columns
.Add ("IsKey", booleanType
);
1314 schemaTable
.Columns
.Add ("BaseServerName", stringType
);
1315 schemaTable
.Columns
.Add ("BaseCatalogName", stringType
);
1316 schemaTable
.Columns
.Add ("BaseColumnName", stringType
);
1317 schemaTable
.Columns
.Add ("BaseSchemaName", stringType
);
1318 schemaTable
.Columns
.Add ("BaseTableName", stringType
);
1319 schemaTable
.Columns
.Add ("DataType", typeType
);
1320 schemaTable
.Columns
.Add ("AllowDBNull", booleanType
);
1321 schemaTable
.Columns
.Add ("ProviderType", intType
);
1322 schemaTable
.Columns
.Add ("IsAliased", booleanType
);
1323 schemaTable
.Columns
.Add ("IsExpression", booleanType
);
1324 schemaTable
.Columns
.Add ("IsIdentity", booleanType
);
1325 schemaTable
.Columns
.Add ("IsAutoIncrement", booleanType
);
1326 schemaTable
.Columns
.Add ("IsRowVersion", booleanType
);
1327 schemaTable
.Columns
.Add ("IsHidden", booleanType
);
1328 schemaTable
.Columns
.Add ("IsLong", booleanType
);
1329 schemaTable
.Columns
.Add ("IsReadOnly", booleanType
);
1333 #endregion // Methods