4 // Part of the Mono class libraries at
5 // mcs/class/System.Data.OracleClient/System.Data.OracleClient
7 // Assembly: System.Data.OracleClient.dll
8 // Namespace: System.Data.OracleClient
10 // Authors: Tim Coleman <tim@timcoleman.com>
11 // Daniel Morgan <danmorg@sc.rr.com>
13 // Copyright (C) Tim Coleman, 2003
14 // Copyright (C) Daniel Morgan, 2003, 2005
16 // Licensed under the MIT/X11 License.
20 using System.Collections;
21 using System.Collections.Specialized;
22 using System.ComponentModel;
24 using System.Data.Common;
25 using System.Data.OracleClient.Oci;
26 using System.Globalization;
27 using System.Runtime.InteropServices;
30 namespace System.Data.OracleClient
32 public sealed class OracleDataReader :
36 MarshalByRefObject, IDataReader, IDisposable, IDataRecord, IEnumerable
41 OracleCommand command;
42 ArrayList dataTypeNames;
48 DataTable schemaTable;
49 CommandBehavior behavior;
51 int recordsAffected = -1;
52 OciStatementType statementType;
53 OciStatementHandle statement;
59 internal OracleDataReader (OracleCommand command, OciStatementHandle statement, bool extHasRows, CommandBehavior behavior)
61 this.command = command;
62 this.hasRows = extHasRows;
63 this.schemaTable = ConstructSchemaTable ();
64 this.statement = statement;
65 this.statementType = statement.GetStatementType ();
66 this.behavior = behavior;
74 #endregion // Constructors
91 get { return statement.ColumnCount; }
99 get { return hasRows; }
107 get { return isClosed; }
114 object this [string name] {
115 get { return GetValue (GetOrdinal (name)); }
122 object this [int i] {
123 get { return GetValue (i); }
130 int RecordsAffected {
132 return GetRecordsAffected ();
136 #endregion // Properties
147 GetRecordsAffected ();
149 command.CloseDataReader ();
151 if (statement != null) {
156 if (schemaTable != null) {
157 schemaTable.Dispose ();
164 private static DataTable ConstructSchemaTable ()
166 Type booleanType = typeof (bool);
167 Type stringType = typeof (string);
168 Type intType = typeof (int);
169 Type typeType = typeof (Type);
170 Type shortType = typeof (short);
172 DataTable schemaTable = new DataTable ("SchemaTable");
173 schemaTable.Columns.Add ("ColumnName", stringType);
174 schemaTable.Columns.Add ("ColumnOrdinal", intType);
175 schemaTable.Columns.Add ("ColumnSize", intType);
176 schemaTable.Columns.Add ("NumericPrecision", shortType);
177 schemaTable.Columns.Add ("NumericScale", shortType);
178 schemaTable.Columns.Add ("DataType", typeType);
179 schemaTable.Columns.Add ("ProviderType", intType);
180 schemaTable.Columns.Add ("IsLong", booleanType);
181 schemaTable.Columns.Add ("AllowDBNull", booleanType);
182 schemaTable.Columns.Add ("IsAliased", booleanType);
183 schemaTable.Columns.Add ("IsExpression", booleanType);
184 schemaTable.Columns.Add ("IsKey", booleanType);
185 schemaTable.Columns.Add ("IsUnique", booleanType);
186 schemaTable.Columns.Add ("BaseSchemaName", stringType);
187 schemaTable.Columns.Add ("BaseTableName", stringType);
188 schemaTable.Columns.Add ("BaseColumnName", stringType);
194 private void Dispose (bool disposing)
203 public void Dispose ()
206 GC.SuppressFinalize (this);
214 bool GetBoolean (int i)
216 throw new NotSupportedException ();
225 throw new NotSupportedException ();
232 long GetBytes (int i, long fieldOffset, byte[] buffer2, int bufferoffset, int length)
234 byte[] value = (byte[]) GetValue (i);
237 return value.Length; // Return length of data
239 // Copy data into buffer
240 long lobLength = value.Length;
241 if ((lobLength - fieldOffset) < length)
242 length = (int) (lobLength - fieldOffset);
243 Array.Copy (value, (int) fieldOffset, buffer2,
244 bufferoffset, length);
245 return length; // return actual read count
254 throw new NotSupportedException ();
261 long GetChars (int i, long fieldOffset, char[] buffer2, int bufferoffset, int length)
263 char [] value = (char[]) GetValue (i);
264 Array.Copy (value, (int) fieldOffset, buffer2,
265 bufferoffset, length);
266 return (value.Length - fieldOffset);
271 public IDataReader GetData (int i)
273 throw new NotImplementedException ();
281 string GetDataTypeName (int i)
283 return dataTypeNames [i].ToString ().ToUpper ();
290 DateTime GetDateTime (int i)
292 IConvertible c = (IConvertible) GetValue (i);
293 return c.ToDateTime (CultureInfo.CurrentCulture);
300 decimal GetDecimal (int i)
302 IConvertible c = (IConvertible) GetValue (i);
303 return c.ToDecimal (CultureInfo.CurrentCulture);
310 double GetDouble (int i)
312 IConvertible c = (IConvertible) GetValue (i);
313 return c.ToDouble (CultureInfo.CurrentCulture);
320 Type GetFieldType (int i)
322 OciDefineHandle defineHandle = (OciDefineHandle) statement.Values [i];
323 return defineHandle.FieldType;
330 float GetFloat (int i)
332 IConvertible c = (IConvertible) GetValue (i);
333 return c.ToSingle (CultureInfo.CurrentCulture);
342 throw new NotSupportedException ();
349 short GetInt16 (int i)
351 throw new NotSupportedException ();
360 IConvertible c = (IConvertible) GetValue (i);
361 return c.ToInt32 (CultureInfo.CurrentCulture);
368 long GetInt64 (int i)
370 IConvertible c = (IConvertible) GetValue (i);
371 return c.ToInt64 (CultureInfo.CurrentCulture);
378 string GetName (int i)
380 return statement.GetParameter (i).GetName ();
384 public OracleBFile GetOracleBFile (int i)
386 throw new NotImplementedException ();
390 public OracleBinary GetOracleBinary (int i)
393 throw new InvalidOperationException("The value is null");
395 return new OracleBinary ((byte[]) GetValue (i));
398 public OracleLob GetOracleLob (int i)
401 throw new InvalidOperationException("The value is null");
403 OracleLob output = (OracleLob) ((OciDefineHandle) statement.Values [i]).GetValue (
404 command.Connection.SessionFormatProvider, command.Connection);
405 output.connection = command.Connection;
409 public OracleNumber GetOracleNumber (int i)
412 throw new InvalidOperationException("The value is null");
414 return new OracleNumber (GetDecimal (i));
417 public OracleDateTime GetOracleDateTime (int i)
420 throw new InvalidOperationException("The value is null");
422 return new OracleDateTime (GetDateTime (i));
425 public OracleMonthSpan GetOracleMonthSpan (int i)
428 throw new InvalidOperationException("The value is null");
430 OracleMonthSpan output = (OracleMonthSpan) ((OciDefineHandle) statement.Values [i]).GetValue (
431 command.Connection.SessionFormatProvider, command.Connection);
435 public OracleString GetOracleString (int i)
438 throw new InvalidOperationException("The value is null");
440 return new OracleString (GetString (i));
443 public object GetOracleValue (int i)
445 OciDefineHandle defineHandle = (OciDefineHandle) statement.Values [i];
447 switch (defineHandle.DataType) {
448 case OciDataType.Raw:
449 return GetOracleBinary (i);
450 case OciDataType.Date:
451 return GetOracleDateTime (i);
452 case OciDataType.Clob:
453 case OciDataType.Blob:
454 return GetOracleLob (i);
455 case OciDataType.Integer:
456 case OciDataType.Number:
457 case OciDataType.Float:
458 return GetOracleNumber (i);
459 case OciDataType.VarChar2:
460 case OciDataType.String:
461 case OciDataType.VarChar:
462 case OciDataType.Char:
463 case OciDataType.CharZ:
464 case OciDataType.OciString:
465 case OciDataType.LongVarChar:
466 case OciDataType.Long:
467 case OciDataType.RowIdDescriptor:
468 return GetOracleString (i);
469 case OciDataType.IntervalDayToSecond:
470 return GetOracleTimeSpan (i);
471 case OciDataType.IntervalYearToMonth:
472 return GetOracleMonthSpan (i);
474 throw new NotImplementedException ();
478 public int GetOracleValues (object[] values)
480 int len = values.Length;
481 int count = statement.ColumnCount;
489 for (int i = 0; i < retval; i += 1)
490 values [i] = GetOracleValue (i);
495 public OracleTimeSpan GetOracleTimeSpan (int i)
497 return new OracleTimeSpan (GetTimeSpan (i));
504 int GetOrdinal (string name)
506 int i = GetOrdinalInternal (name);
508 throw new IndexOutOfRangeException ();
512 private int GetOrdinalInternal (string name)
516 for (i = 0; i < statement.ColumnCount; i += 1) {
517 if (String.Compare (statement.GetParameter(i).GetName(), name, false) == 0)
521 for (i = 0; i < statement.ColumnCount; i += 1) {
522 if (String.Compare (statement.GetParameter(i).GetName(), name, true) == 0)
529 private int GetRecordsAffected ()
531 if (statementType == OciStatementType.Select)
535 if (recordsAffected == -1)
536 if (statement != null)
537 recordsAffected = statement.GetAttributeInt32 (OciAttributeType.RowCount, command.ErrorHandle);
541 return recordsAffected;
544 // get the KeyInfo about table columns (primary key)
545 private StringCollection GetKeyInfo (out string ownerName, out string tableName)
547 ArrayList tables = new ArrayList ();
548 ParseSql (command.CommandText, ref tables);
549 // TODO: handle multiple tables
550 GetOwnerAndName ((string)tables[0], out ownerName, out tableName);
551 return GetKeyColumns (ownerName, tableName);
554 // get the columns in a table that have a primary key
555 private StringCollection GetKeyColumns(string owner, string table)
557 OracleCommand cmd = command.Connection.CreateCommand ();
559 StringCollection columns = new StringCollection ();
561 if (command.Transaction != null)
562 cmd.Transaction = command.Transaction;
564 cmd.CommandText = "select col.column_name " +
565 "from all_constraints pk, all_cons_columns col " +
566 "where pk.owner = '" + owner + "' " +
567 "and pk.table_name = '" + table + "' " +
568 "and pk.constraint_type = 'P' " +
569 "and pk.owner = col.owner " +
570 "and pk.table_name = col.table_name " +
571 "and pk.constraint_name = col.constraint_name";
573 OracleDataReader rdr = cmd.ExecuteReader ();
575 columns.Add (rdr.GetString (0));
585 // parse the list of table names in the SQL
586 // TODO: parse the column aliases and table aliases too
587 // and determine if a column is a true table column
589 private void ParseSql (string sql, ref ArrayList tables) {
590 if (sql == String.Empty)
593 char[] chars = sql.ToCharArray ();
594 StringBuilder wb = new StringBuilder ();
596 bool bFromFound = false;
599 bool bTableFound = false;
601 for (; !bEnd && i < chars.Length; i++) {
604 if (Char.IsLetter (ch)) {
606 } else if (Char.IsWhiteSpace (ch)) {
609 string word = wb.ToString ().ToUpper ();
610 if (word.Equals ("FROM")) {
614 wb = new StringBuilder ();
617 switch (wb.ToString ().ToUpper ()) {
626 bTableFound = false; // this is done in case of a table alias
629 tables.Add (wb.ToString ().ToUpper ());
632 wb = new StringBuilder ();
637 } else if (bFromFound) {
643 tables.Add (wb.ToString ().ToUpper ());
645 wb = new StringBuilder ();
657 if (!bFromFound && wb.ToString ().ToUpper ().Equals ("FROM"))
660 switch(wb.ToString ().ToUpper ()) {
668 tables.Add (wb.ToString ().ToUpper ());
676 // takes a object name like "owner.name" and parses it into "owner" and "name" strings
677 // if object name is only "name", then it gets the username as the owner and returns
679 private void GetOwnerAndName (string objectName, out string owner, out string name)
681 int idx = objectName.IndexOf (".");
683 OracleCommand cmd = command.Connection.CreateCommand ();
684 if (command.Transaction != null)
685 cmd.Transaction = command.Transaction;
687 cmd.CommandText = "SELECT USER FROM DUAL";
688 owner = (string) cmd.ExecuteScalar();
693 owner = objectName.Substring (0, idx);
694 name = objectName.Substring (idx + 1);
698 [MonoTODO("Implement this properly, with all needed information.")]
703 DataTable GetSchemaTable ()
705 StringCollection keyinfo = null;
707 if (schemaTable.Rows != null && schemaTable.Rows.Count > 0)
710 string owner = String.Empty;
711 string table = String.Empty;
712 if ((behavior & CommandBehavior.KeyInfo) != 0)
713 keyinfo = GetKeyInfo (out owner, out table);
715 dataTypeNames = new ArrayList ();
717 for (int i = 0; i < statement.ColumnCount; i += 1) {
718 DataRow row = schemaTable.NewRow ();
720 OciParameterDescriptor parameter = statement.GetParameter (i);
722 dataTypeNames.Add (parameter.GetDataTypeName ());
724 row ["ColumnName"] = parameter.GetName ();
725 row ["ColumnOrdinal"] = i + 1;
726 row ["ColumnSize"] = parameter.GetDataSize ();
727 row ["NumericPrecision"] = parameter.GetPrecision ();
728 row ["NumericScale"] = parameter.GetScale ();
730 string sDataTypeName = parameter.GetDataTypeName ();
731 row ["DataType"] = parameter.GetFieldType (sDataTypeName);
733 OciDataType ociType = parameter.GetDataType();
734 OracleType oraType = OciParameterDescriptor.OciDataTypeToOracleType (ociType);
735 row ["ProviderType"] = (int) oraType;
737 if (ociType == OciDataType.Blob || ociType == OciDataType.Clob)
738 row ["IsLong"] = true;
740 row ["IsLong"] = false;
742 row ["AllowDBNull"] = parameter.GetIsNull ();
744 row ["IsAliased"] = DBNull.Value; // TODO:
745 row ["IsExpression"] = DBNull.Value; // TODO:
747 if ((behavior & CommandBehavior.KeyInfo) != 0) {
748 if (keyinfo.IndexOf ((string)row ["ColumnName"]) >= 0)
749 row ["IsKey"] = true;
751 row ["IsKey"] = false;
753 row ["IsUnique"] = DBNull.Value; // TODO: only set this if CommandBehavior.KeyInfo, otherwise, null
754 row ["BaseSchemaName"] = owner;
755 row ["BaseTableName"] = table;
756 row ["BaseColumnName"] = row ["ColumnName"];
758 row ["IsKey"] = DBNull.Value;
759 row ["IsUnique"] = DBNull.Value;
760 row ["BaseSchemaName"] = DBNull.Value;
761 row ["BaseTableName"] = DBNull.Value;
762 row ["BaseColumnName"] = DBNull.Value;
765 schemaTable.Rows.Add (row);
775 string GetString (int i)
777 return (string) GetValue (i);
780 public TimeSpan GetTimeSpan (int i)
782 return (TimeSpan) GetValue (i);
789 object GetValue (int i)
791 OciDefineHandle defineHandle = (OciDefineHandle) statement.Values [i];
793 if (defineHandle.IsNull)
796 switch (defineHandle.DataType) {
797 case OciDataType.Blob:
798 case OciDataType.Clob:
799 OracleLob lob = GetOracleLob (i);
800 object value = lob.Value;
804 return defineHandle.GetValue (command.Connection.SessionFormatProvider, command.Connection);
812 int GetValues (object [] values)
814 int len = values.Length;
815 int count = statement.ColumnCount;
823 for (int i = 0; i < retval; i += 1)
824 values [i] = GetValue (i);
830 public override IEnumerator GetEnumerator ()
832 IEnumerator IEnumerable.GetEnumerator ()
835 return new DbEnumerator (this);
842 bool IsDBNull (int i)
844 OciDefineHandle defineHandle = (OciDefineHandle) statement.Values [i];
845 return defineHandle.IsNull;
848 void ValidateState ()
851 throw new InvalidOperationException ("Invalid attempt to read data when reader is closed");
862 if (statement == null)
865 statement.Dispose ();
868 statement = command.GetNextResult ();
870 if (statement == null)
885 bool retval = statement.Fetch ();
894 public override Type GetProviderSpecificFieldType (int i)
896 return GetOracleValue (i).GetType ();
900 public override object GetProviderSpecificValue (int i)
902 return GetOracleValue (i);
906 public override int GetProviderSpecificValues (object [] values)
908 return GetOracleValues (values);
912 #endregion // Methods