//
// Author:
// Rodrigo Moya (rodrigo@ximian.com)
+// Tim Coleman (tim@timcoleman.com)
//
// Copyright (C) Rodrigo Moya, 2002
+// Copyright (C) Tim Coleman, 2002
+//
+
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Data.Common;
+using System.Runtime.InteropServices;
namespace System.Data.OleDb
{
- public sealed class OleDbDataReader : MarshalByRefObject,
- IDataReader, IDisposable, IDataRecord, IEnumerable
+ public sealed class OleDbDataReader : MarshalByRefObject, IDataReader, IDisposable, IDataRecord, IEnumerable
{
+ #region Fields
+
+ private OleDbCommand command;
+ private bool open;
+ private ArrayList gdaResults;
+ private int currentResult;
+ private int currentRow;
+ private bool disposed = false;
+
+ #endregion
+
+ #region Constructors
+
+ internal OleDbDataReader (OleDbCommand command, ArrayList results)
+ {
+ this.command = command;
+ open = true;
+ if (results != null)
+ gdaResults = results;
+ else
+ gdaResults = new ArrayList ();
+ currentResult = -1;
+ currentRow = -1;
+ }
+
+ #endregion
+
+ #region Properties
+
+ public int Depth {
+ get {
+ return 0; // no nested selects supported
+ }
+ }
+
+ public int FieldCount {
+ get {
+ if (currentResult < 0 ||
+ currentResult >= gdaResults.Count)
+ return 0;
+
+ return libgda.gda_data_model_get_n_columns (
+ (IntPtr) gdaResults[currentResult]);
+ }
+ }
+
+ public bool IsClosed {
+ get {
+ return !open;
+ }
+ }
+
+ public object this[string name] {
+ get {
+ int pos;
+
+ if (currentResult == -1)
+ throw new InvalidOperationException ();
+
+ pos = libgda.gda_data_model_get_column_position (
+ (IntPtr) gdaResults[currentResult],
+ name);
+ if (pos == -1)
+ throw new IndexOutOfRangeException ();
+
+ return this[pos];
+ }
+ }
+
+ public object this[int index] {
+ get {
+ return (object) GetValue (index);
+ }
+ }
+
+ public int RecordsAffected {
+ get {
+ int total_rows;
+
+ if (currentResult < 0 ||
+ currentResult >= gdaResults.Count)
+ return 0;
+
+ total_rows = libgda.gda_data_model_get_n_rows (
+ (IntPtr) gdaResults[currentResult]);
+ if (total_rows > 0) {
+ if (FieldCount > 0) {
+ // It's a SELECT statement
+ return -1;
+ }
+ }
+
+ return FieldCount > 0 ? -1 : total_rows;
+ }
+ }
+
+ [MonoTODO]
+ public bool HasRows {
+ get {
+ throw new NotImplementedException ();
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ public void Close ()
+ {
+ for (int i = 0; i < gdaResults.Count; i++) {
+ IntPtr obj = (IntPtr) gdaResults[i];
+ libgda.FreeObject (obj);
+ }
+
+ gdaResults.Clear ();
+ gdaResults = null;
+
+ open = false;
+ currentResult = -1;
+ currentRow = -1;
+ }
+
+ public bool GetBoolean (int ordinal)
+ {
+ IntPtr value;
+
+ if (currentResult == -1)
+ throw new InvalidCastException ();
+
+ value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
+ ordinal, currentRow);
+ if (value == IntPtr.Zero)
+ throw new InvalidCastException ();
+
+ if (libgda.gda_value_get_type (value) != GdaValueType.Boolean)
+ throw new InvalidCastException ();
+ return libgda.gda_value_get_boolean (value);
+ }
+
+ public byte GetByte (int ordinal)
+ {
+ IntPtr value;
+
+ if (currentResult == -1)
+ throw new InvalidCastException ();
+
+ value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
+ ordinal, currentRow);
+ if (value == IntPtr.Zero)
+ throw new InvalidCastException ();
+
+ if (libgda.gda_value_get_type (value) != GdaValueType.Tinyint)
+ throw new InvalidCastException ();
+ return libgda.gda_value_get_tinyint (value);
+ }
+
+ [MonoTODO]
+ public long GetBytes (int ordinal, long dataIndex, byte[] buffer, int bufferIndex, int length)
+ {
+ throw new NotImplementedException ();
+ }
+
+ [EditorBrowsableAttribute (EditorBrowsableState.Never)]
+ public char GetChar (int ordinal)
+ {
+ IntPtr value;
+
+ if (currentResult == -1)
+ throw new InvalidCastException ();
+
+ value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
+ ordinal, currentRow);
+ if (value == IntPtr.Zero)
+ throw new InvalidCastException ();
+
+ if (libgda.gda_value_get_type (value) != GdaValueType.Tinyint)
+ throw new InvalidCastException ();
+ return (char) libgda.gda_value_get_tinyint (value);
+ }
+
+ [MonoTODO]
+ public long GetChars (int ordinal, long dataIndex, char[] buffer, int bufferIndex, int length)
+ {
+ throw new NotImplementedException ();
+ }
+
+ [MonoTODO]
+ public OleDbDataReader GetData (int ordinal)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public string GetDataTypeName (int index)
+ {
+ IntPtr attrs;
+ GdaValueType type;
+
+ if (currentResult == -1)
+ return "unknown";
+
+
+ attrs = libgda.gda_data_model_describe_column ((IntPtr) gdaResults[currentResult],
+ index);
+ if (attrs == IntPtr.Zero)
+ return "unknown";
+
+ type = libgda.gda_field_attributes_get_gdatype (attrs);
+ libgda.gda_field_attributes_free (attrs);
+
+ return libgda.gda_type_to_string (type);
+ }
+
+ public DateTime GetDateTime (int ordinal)
+ {
+ IntPtr value;
+ DateTime dt;
+
+ if (currentResult == -1)
+ throw new InvalidCastException ();
+
+ value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
+ ordinal, currentRow);
+ if (value == IntPtr.Zero)
+ throw new InvalidCastException ();
+
+ if (libgda.gda_value_get_type (value) == GdaValueType.Date) {
+ GdaDate gdt;
+
+ gdt = (GdaDate) Marshal.PtrToStructure (libgda.gda_value_get_date (value),
+ typeof (GdaDate));
+ return new DateTime ((int) gdt.year, (int) gdt.month, (int) gdt.day);
+ } else if (libgda.gda_value_get_type (value) == GdaValueType.Time) {
+ GdaTime gdt;
+
+ gdt = (GdaTime) Marshal.PtrToStructure (libgda.gda_value_get_time (value),
+ typeof (GdaTime));
+ return new DateTime (0, 0, 0, (int) gdt.hour, (int) gdt.minute, (int) gdt.second, 0);
+ } else if (libgda.gda_value_get_type (value) == GdaValueType.Timestamp) {
+ GdaTimestamp gdt;
+
+ gdt = (GdaTimestamp) Marshal.PtrToStructure (libgda.gda_value_get_timestamp (value),
+ typeof (GdaTimestamp));
+
+ return new DateTime ((int) gdt.year, (int) gdt.month, (int) gdt.day,
+ (int) gdt.hour, (int) gdt.minute, (int) gdt.second,
+ (int) gdt.fraction);
+ }
+
+ throw new InvalidCastException ();
+ }
+
+ [MonoTODO]
+ public decimal GetDecimal (int ordinal)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public double GetDouble (int ordinal)
+ {
+ IntPtr value;
+
+ if (currentResult == -1)
+ throw new InvalidCastException ();
+
+ value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
+ ordinal, currentRow);
+ if (value == IntPtr.Zero)
+ throw new InvalidCastException ();
+
+ if (libgda.gda_value_get_type (value) != GdaValueType.Double)
+ throw new InvalidCastException ();
+ return libgda.gda_value_get_double (value);
+ }
+
+ [MonoTODO]
+ public Type GetFieldType (int index)
+ {
+ IntPtr value;
+ GdaValueType type;
+
+ if (currentResult == -1)
+ throw new IndexOutOfRangeException ();
+
+ value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
+ index, currentRow);
+ if (value == IntPtr.Zero)
+ throw new IndexOutOfRangeException ();
+
+ type = libgda.gda_value_get_type (value);
+ switch (type) {
+ case GdaValueType.Bigint : return typeof (long);
+ case GdaValueType.Boolean : return typeof (bool);
+ case GdaValueType.Date : return typeof (DateTime);
+ case GdaValueType.Double : return typeof (double);
+ case GdaValueType.Integer : return typeof (int);
+ case GdaValueType.Single : return typeof (float);
+ case GdaValueType.Smallint : return typeof (byte);
+ case GdaValueType.String : return typeof (string);
+ case GdaValueType.Time : return typeof (DateTime);
+ case GdaValueType.Timestamp : return typeof (DateTime);
+ case GdaValueType.Tinyint : return typeof (byte);
+ }
+
+ return typeof(string); // default
+ }
+
+ public float GetFloat (int ordinal)
+ {
+ IntPtr value;
+
+ if (currentResult == -1)
+ throw new InvalidCastException ();
+
+ value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
+ ordinal, currentRow);
+ if (value == IntPtr.Zero)
+ throw new InvalidCastException ();
+
+ if (libgda.gda_value_get_type (value) != GdaValueType.Single)
+ throw new InvalidCastException ();
+ return libgda.gda_value_get_single (value);
+ }
+
+ [MonoTODO]
+ public Guid GetGuid (int ordinal)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public short GetInt16 (int ordinal)
+ {
+ IntPtr value;
+
+ if (currentResult == -1)
+ throw new InvalidCastException ();
+
+ value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
+ ordinal, currentRow);
+ if (value == IntPtr.Zero)
+ throw new InvalidCastException ();
+
+ if (libgda.gda_value_get_type (value) != GdaValueType.Smallint)
+ throw new InvalidCastException ();
+ return (short) libgda.gda_value_get_smallint (value);
+ }
+
+ public int GetInt32 (int ordinal)
+ {
+ IntPtr value;
+
+ if (currentResult == -1)
+ throw new InvalidCastException ();
+
+ value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
+ ordinal, currentRow);
+ if (value == IntPtr.Zero)
+ throw new InvalidCastException ();
+
+ if (libgda.gda_value_get_type (value) != GdaValueType.Integer)
+ throw new InvalidCastException ();
+ return libgda.gda_value_get_integer (value);
+ }
+
+ public long GetInt64 (int ordinal)
+ {
+ IntPtr value;
+
+ if (currentResult == -1)
+ throw new InvalidCastException ();
+
+ value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
+ ordinal, currentRow);
+ if (value == IntPtr.Zero)
+ throw new InvalidCastException ();
+
+ if (libgda.gda_value_get_type (value) != GdaValueType.Bigint)
+ throw new InvalidCastException ();
+ return libgda.gda_value_get_bigint (value);
+ }
+
+ public string GetName (int index)
+ {
+ if (currentResult == -1)
+ return null;
+
+ return libgda.gda_data_model_get_column_title (
+ (IntPtr) gdaResults[currentResult], index);
+ }
+
+ public int GetOrdinal (string name)
+ {
+ if (currentResult == -1)
+ throw new IndexOutOfRangeException ();
+
+ for (int i = 0; i < FieldCount; i++) {
+ if (GetName (i) == name)
+ return i;
+ }
+
+ throw new IndexOutOfRangeException ();
+ }
+
+ public DataTable GetSchemaTable ()
+ {
+ DataTable dataTableSchema = null;
+ // Only Results from SQL SELECT Queries
+ // get a DataTable for schema of the result
+ // otherwise, DataTable is null reference
+ if(this.FieldCount > 0) {
+
+ IntPtr attrs;
+ GdaValueType gdaType;
+ long columnSize = 0;
+
+ if (currentResult == -1) {
+ // FIXME: throw an exception?
+#if DEBUG_OleDbDataReader
+ Console.WriteLine("Error: current result -1");
+#endif
+ return null;
+ }
+
+ dataTableSchema = new DataTable ();
+
+ dataTableSchema.Columns.Add ("ColumnName", typeof (string));
+ dataTableSchema.Columns.Add ("ColumnOrdinal", typeof (int));
+ dataTableSchema.Columns.Add ("ColumnSize", typeof (int));
+ dataTableSchema.Columns.Add ("NumericPrecision", typeof (int));
+ dataTableSchema.Columns.Add ("NumericScale", typeof (int));
+ dataTableSchema.Columns.Add ("IsUnique", typeof (bool));
+ dataTableSchema.Columns.Add ("IsKey", typeof (bool));
+ DataColumn dc = dataTableSchema.Columns["IsKey"];
+ dc.AllowDBNull = true; // IsKey can have a DBNull
+ dataTableSchema.Columns.Add ("BaseCatalogName", typeof (string));
+ dataTableSchema.Columns.Add ("BaseColumnName", typeof (string));
+ dataTableSchema.Columns.Add ("BaseSchemaName", typeof (string));
+ dataTableSchema.Columns.Add ("BaseTableName", typeof (string));
+ dataTableSchema.Columns.Add ("DataType", typeof(Type));
+ dataTableSchema.Columns.Add ("AllowDBNull", typeof (bool));
+ dataTableSchema.Columns.Add ("ProviderType", typeof (int));
+ dataTableSchema.Columns.Add ("IsAliased", typeof (bool));
+ dataTableSchema.Columns.Add ("IsExpression", typeof (bool));
+ dataTableSchema.Columns.Add ("IsIdentity", typeof (bool));
+ dataTableSchema.Columns.Add ("IsAutoIncrement", typeof (bool));
+ dataTableSchema.Columns.Add ("IsRowVersion", typeof (bool));
+ dataTableSchema.Columns.Add ("IsHidden", typeof (bool));
+ dataTableSchema.Columns.Add ("IsLong", typeof (bool));
+ dataTableSchema.Columns.Add ("IsReadOnly", typeof (bool));
+\r
+ DataRow schemaRow;
+ DbType dbType;
+ Type typ;
+
+ for (int i = 0; i < this.FieldCount; i += 1 ) {
+
+ schemaRow = dataTableSchema.NewRow ();
+
+ attrs = libgda.gda_data_model_describe_column ((IntPtr) gdaResults[currentResult],
+ i);
+ if (attrs == IntPtr.Zero){
+ // FIXME: throw exception
+#if DEBUG_OleDbDataReader
+ Console.WriteLine("Error: attrs null");
+#endif
+ return null;
+ }
+
+ gdaType = libgda.gda_field_attributes_get_gdatype (attrs);
+ columnSize = libgda.gda_field_attributes_get_defined_size (attrs);
+ libgda.gda_field_attributes_free (attrs);
+
+ schemaRow["ColumnName"] = this.GetName(i);
+ schemaRow["ColumnOrdinal"] = i + 1;
+
+ schemaRow["ColumnSize"] = (int) columnSize;
+ schemaRow["NumericPrecision"] = 0;
+ schemaRow["NumericScale"] = 0;
+ // TODO: need to get KeyInfo
+ //if((cmdBehavior & CommandBehavior.KeyInfo) == CommandBehavior.KeyInfo) {
+ // bool IsUnique, IsKey;
+ // GetKeyInfo(field[i].Name, out IsUnique, out IsKey);
+ //}
+ //else {
+ schemaRow["IsUnique"] = false;
+ schemaRow["IsKey"] = DBNull.Value;
+ //}
+ schemaRow["BaseCatalogName"] = "";
+
+ schemaRow["BaseColumnName"] = this.GetName(i);
+ schemaRow["BaseSchemaName"] = "";
+ schemaRow["BaseTableName"] = "";
+
+ schemaRow["DataType"] = this.GetFieldType(i);\r
+
+ schemaRow["AllowDBNull"] = false;
+
+ schemaRow["ProviderType"] = (int) gdaType;
+ schemaRow["IsAliased"] = false;
+ schemaRow["IsExpression"] = false;
+ schemaRow["IsIdentity"] = false;
+ schemaRow["IsAutoIncrement"] = false;
+ schemaRow["IsRowVersion"] = false;
+ schemaRow["IsHidden"] = false;
+ schemaRow["IsLong"] = false;
+ schemaRow["IsReadOnly"] = false;
+
+ schemaRow.AcceptChanges();
+
+ dataTableSchema.Rows.Add (schemaRow);
+ }
+
+#if DEBUG_OleDbDataReader
+ Console.WriteLine("********** DEBUG Table Schema BEGIN ************");
+ foreach (DataRow myRow in dataTableSchema.Rows) {\r
+ foreach (DataColumn myCol in dataTableSchema.Columns)\r
+ Console.WriteLine(myCol.ColumnName + " = " + myRow[myCol]);\r
+ Console.WriteLine();\r
+ }
+ Console.WriteLine("********** DEBUG Table Schema END ************");
+#endif // DEBUG_OleDbDataReader
+
+ }
+
+ return dataTableSchema;
+ }
+
+ public string GetString (int ordinal)
+ {
+ IntPtr value;
+
+ if (currentResult == -1)
+ throw new InvalidCastException ();
+
+ value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
+ ordinal, currentRow);
+ if (value == IntPtr.Zero)
+ throw new InvalidCastException ();
+
+ if (libgda.gda_value_get_type (value) != GdaValueType.String)
+ throw new InvalidCastException ();
+ return libgda.gda_value_get_string (value);
+ }
+
+ [MonoTODO]
+ public TimeSpan GetTimeSpan (int ordinal)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public object GetValue (int ordinal)
+ {
+ IntPtr value;
+ GdaValueType type;
+
+ if (currentResult == -1)
+ throw new IndexOutOfRangeException ();
+
+ value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
+ ordinal, currentRow);
+ if (value == IntPtr.Zero)
+ throw new IndexOutOfRangeException ();
+
+ type = libgda.gda_value_get_type (value);
+ switch (type) {
+ case GdaValueType.Bigint : return GetInt64 (ordinal);
+ case GdaValueType.Boolean : return GetBoolean (ordinal);
+ case GdaValueType.Date : return GetDateTime (ordinal);
+ case GdaValueType.Double : return GetDouble (ordinal);
+ case GdaValueType.Integer : return GetInt32 (ordinal);
+ case GdaValueType.Single : return GetFloat (ordinal);
+ case GdaValueType.Smallint : return GetByte (ordinal);
+ case GdaValueType.String : return GetString (ordinal);
+ case GdaValueType.Time : return GetDateTime (ordinal);
+ case GdaValueType.Timestamp : return GetDateTime (ordinal);
+ case GdaValueType.Tinyint : return GetByte (ordinal);
+ }
+
+ return (object) libgda.gda_value_stringify (value);
+ }
+
+ [MonoTODO]
+ public int GetValues (object[] values)
+ {
+ throw new NotImplementedException ();
+ }
+
+ [MonoTODO]
+ IDataReader IDataRecord.GetData (int ordinal)
+ {
+ throw new NotImplementedException ();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator ()
+ {
+ return new DbEnumerator (this);
+ }
+
+ public bool IsDBNull (int ordinal)
+ {
+ IntPtr value;
+
+ if (currentResult == -1)
+ throw new IndexOutOfRangeException ();
+
+ value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
+ ordinal, currentRow);
+ if (value == IntPtr.Zero)
+ throw new IndexOutOfRangeException ();
+
+ return libgda.gda_value_is_null (value);
+ }
+
+ public bool NextResult ()
+ {
+ int i = currentResult + 1;
+ if (i >= 0 && i < gdaResults.Count) {
+ currentResult++;
+ return true;
+ }
+
+ return false;
+ }
+
+ public bool Read ()
+ {
+ if (currentResult < 0 ||
+ currentResult >= gdaResults.Count)
+ return false;
+
+ currentRow++;
+ if (currentRow <
+ libgda.gda_data_model_get_n_rows ((IntPtr) gdaResults[currentResult]))
+ return true;
+
+ return false;
+ }
+
+ #endregion
+
+ #region Destructors
+
+ private void Dispose (bool disposing) {
+ if (!this.disposed) {
+ if (disposing) {
+ // release any managed resources
+ command = null;
+ }
+ // release any unmanaged resources
+ if (gdaResults != null) {
+ gdaResults.Clear ();
+ gdaResults = null;
+ }
+
+ // close any handles
+ if (open)
+ Close ();
+
+ this.disposed = true;
+ }
+ }
+
+ void IDisposable.Dispose() {
+ Dispose (true);
+ }
+
+ ~OleDbDataReader() {
+ Dispose (false);
+ }
+
+ #endregion // Destructors
+
}
}