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