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 {
31 public sealed class OracleDataReader : MarshalByRefObject, IDataReader, IDisposable, IDataRecord, IEnumerable
35 OracleCommand command;
36 ArrayList dataTypeNames;
37 bool disposed = false;
40 DataTable schemaTable;
41 CommandBehavior behavior;
43 int recordsAffected = -1;
44 OciStatementType statementType;
45 OciStatementHandle statement;
51 internal OracleDataReader (OracleCommand command, OciStatementHandle statement, bool extHasRows, CommandBehavior behavior)
53 this.command = command;
54 this.hasRows = extHasRows;
55 this.isClosed = false;
56 this.schemaTable = ConstructSchemaTable ();
57 this.statement = statement;
58 this.statementType = statement.GetStatementType ();
59 this.behavior = behavior;
67 #endregion // Constructors
75 public int FieldCount {
76 get { return statement.ColumnCount; }
80 get { return hasRows; }
83 public bool IsClosed {
84 get { return isClosed; }
87 public object this [string name] {
88 get { return GetValue (GetOrdinal (name)); }
91 public object this [int i] {
92 get { return GetValue (i); }
95 public int RecordsAffected {
97 return GetRecordsAffected ();
101 #endregion // Properties
108 GetRecordsAffected ();
110 command.CloseDataReader ();
112 if (statement != null) {
119 private static DataTable ConstructSchemaTable ()
121 Type booleanType = Type.GetType ("System.Boolean");
122 Type stringType = Type.GetType ("System.String");
123 Type intType = Type.GetType ("System.Int32");
124 Type typeType = Type.GetType ("System.Type");
125 Type shortType = Type.GetType ("System.Int16");
127 DataTable schemaTable = new DataTable ("SchemaTable");
128 schemaTable.Columns.Add ("ColumnName", stringType);
129 schemaTable.Columns.Add ("ColumnOrdinal", intType);
130 schemaTable.Columns.Add ("ColumnSize", intType);
131 schemaTable.Columns.Add ("NumericPrecision", shortType);
132 schemaTable.Columns.Add ("NumericScale", shortType);
133 schemaTable.Columns.Add ("DataType", typeType);
134 schemaTable.Columns.Add ("ProviderType", intType);
135 schemaTable.Columns.Add ("IsLong", booleanType);
136 schemaTable.Columns.Add ("AllowDBNull", booleanType);
137 schemaTable.Columns.Add ("IsAliased", booleanType);
\r
138 schemaTable.Columns.Add ("IsExpression", booleanType);
139 schemaTable.Columns.Add ("IsKey", booleanType);
140 schemaTable.Columns.Add ("IsUnique", booleanType);
141 schemaTable.Columns.Add ("BaseSchemaName", stringType);
142 schemaTable.Columns.Add ("BaseTableName", stringType);
143 schemaTable.Columns.Add ("BaseColumnName", stringType);
148 private void Dispose (bool disposing)
152 schemaTable.Dispose ();
159 public void Dispose ()
162 GC.SuppressFinalize (this);
165 public bool GetBoolean (int i)
167 throw new NotSupportedException ();
170 public byte GetByte (int i)
172 throw new NotSupportedException ();
175 public long GetBytes (int i, long fieldOffset, byte[] buffer2, int bufferoffset, int length)
177 object value = GetValue (i);
178 if (!(value is byte[]))
179 throw new InvalidCastException ();
181 if ( buffer2 == null )
182 return ((byte []) value).Length; // Return length of data
184 // Copy data into buffer
185 long lobLength = ((byte []) value).Length;
186 if ( (lobLength - fieldOffset) < length)
187 length = (int) (lobLength - fieldOffset);
188 Array.Copy ( (byte[]) value, (int) fieldOffset, buffer2, bufferoffset, length);
189 return length; // return actual read count
192 public char GetChar (int i)
194 throw new NotSupportedException ();
197 public long GetChars (int i, long fieldOffset, char[] buffer2, int bufferoffset, int length)
199 object value = GetValue (i);
200 if (!(value is char[]))
201 throw new InvalidCastException ();
202 Array.Copy ((char[]) value, (int) fieldOffset, buffer2, bufferoffset, length);
203 return ((char[]) value).Length - fieldOffset;
207 public IDataReader GetData (int i)
209 throw new NotImplementedException ();
212 public string GetDataTypeName (int i)
214 return dataTypeNames [i].ToString ().ToUpper ();
217 public DateTime GetDateTime (int i)
219 IConvertible c = (IConvertible) GetValue (i);
220 return c.ToDateTime (CultureInfo.CurrentCulture);
223 public decimal GetDecimal (int i)
225 IConvertible c = (IConvertible) GetValue (i);
226 return c.ToDecimal (CultureInfo.CurrentCulture);
229 public double GetDouble (int i)
231 IConvertible c = (IConvertible) GetValue (i);
232 return c.ToDouble (CultureInfo.CurrentCulture);
235 public Type GetFieldType (int i)
237 OciDefineHandle defineHandle = (OciDefineHandle) statement.Values [i];
238 return defineHandle.FieldType;
241 public float GetFloat (int i)
243 IConvertible c = (IConvertible) GetValue (i);
244 return c.ToSingle (CultureInfo.CurrentCulture);
247 public Guid GetGuid (int i)
249 throw new NotSupportedException ();
252 public short GetInt16 (int i)
254 throw new NotSupportedException ();
257 public int GetInt32 (int i)
259 IConvertible c = (IConvertible) GetValue (i);
260 return c.ToInt32 (CultureInfo.CurrentCulture);
263 public long GetInt64 (int i)
265 IConvertible c = (IConvertible) GetValue (i);
266 return c.ToInt64 (CultureInfo.CurrentCulture);
269 public string GetName (int i)
271 return statement.GetParameter (i).GetName ();
275 public OracleBFile GetOracleBFile (int i)
277 throw new NotImplementedException ();
281 public OracleBinary GetOracleBinary (int i)
284 throw new InvalidOperationException("The value is null");
286 return new OracleBinary ((byte[]) GetValue (i));
289 public OracleLob GetOracleLob (int i)
292 throw new InvalidOperationException("The value is null");
294 OracleLob output = (OracleLob) ((OciDefineHandle) statement.Values [i]).GetValue();
295 output.connection = command.Connection;
299 public OracleNumber GetOracleNumber (int i)
302 throw new InvalidOperationException("The value is null");
304 return new OracleNumber (GetDecimal (i));
307 public OracleDateTime GetOracleDateTime (int i)
310 throw new InvalidOperationException("The value is null");
312 return new OracleDateTime (GetDateTime (i));
316 public OracleMonthSpan GetOracleMonthSpan (int i)
318 throw new NotImplementedException ();
321 public OracleString GetOracleString (int i)
324 throw new InvalidOperationException("The value is null");
326 return new OracleString (GetString (i));
329 public object GetOracleValue (int i)
331 OciDefineHandle defineHandle = (OciDefineHandle) statement.Values [i];
333 switch (defineHandle.DataType) {
334 case OciDataType.Raw:
335 return GetOracleBinary (i);
336 case OciDataType.Date:
337 return GetOracleDateTime (i);
338 case OciDataType.Clob:
339 case OciDataType.Blob:
340 return GetOracleLob (i);
341 case OciDataType.Integer:
342 case OciDataType.Number:
343 case OciDataType.Float:
344 return GetOracleNumber (i);
345 case OciDataType.VarChar2:
346 case OciDataType.String:
347 case OciDataType.VarChar:
348 case OciDataType.Char:
349 case OciDataType.CharZ:
350 case OciDataType.OciString:
351 case OciDataType.LongVarChar:
352 case OciDataType.Long:
353 case OciDataType.RowIdDescriptor:
354 return GetOracleString (i);
356 throw new NotImplementedException ();
360 public int GetOracleValues (object[] values)
362 int len = values.Length;
363 int count = statement.ColumnCount;
371 for (int i = 0; i < retval; i += 1)
372 values [i] = GetOracleValue (i);
378 public OracleTimeSpan GetOracleTimeSpan (int i)
380 return new OracleTimeSpan (GetTimeSpan (i));
383 public int GetOrdinal (string name)
385 int i = GetOrdinalInternal (name);
387 throw new IndexOutOfRangeException ();
391 private int GetOrdinalInternal (string name)
395 for (i = 0; i < statement.ColumnCount; i += 1) {
396 if (String.Compare (statement.GetParameter(i).GetName(), name, false) == 0)
400 for (i = 0; i < statement.ColumnCount; i += 1) {
401 if (String.Compare (statement.GetParameter(i).GetName(), name, true) == 0)
408 private int GetRecordsAffected ()
410 if (statementType == OciStatementType.Select)
413 if (this.isClosed == false) {
414 if (recordsAffected == -1)
415 if (statement != null)
416 recordsAffected = statement.GetAttributeInt32 (OciAttributeType.RowCount, command.ErrorHandle);
420 return recordsAffected;
423 // get the KeyInfo about table columns (primary key)
424 private StringCollection GetKeyInfo (out string ownerName, out string tableName)
426 ArrayList tables = new ArrayList ();
427 ParseSql (command.CommandText, ref tables);
428 // TODO: handle multiple tables
429 GetOwnerAndName ((string)tables[0], out ownerName, out tableName);
430 return GetKeyColumns (ownerName, tableName);
433 // get the columns in a table that have a primary key
434 private StringCollection GetKeyColumns(string owner, string table)
436 OracleCommand cmd = command.Connection.CreateCommand ();
438 StringCollection columns = new StringCollection ();
440 if (command.Transaction != null)
\r
441 cmd.Transaction = command.Transaction;
\r
443 cmd.CommandText = "select col.column_name " +
\r
444 "from all_constraints pk, all_cons_columns col " +
\r
445 "where pk.owner = '" + owner + "' " +
\r
446 "and pk.table_name = '" + table + "' " +
\r
447 "and pk.constraint_type = 'P' " +
\r
448 "and pk.owner = col.owner " +
\r
449 "and pk.table_name = col.table_name " +
\r
450 "and pk.constraint_name = col.constraint_name";
\r
452 OracleDataReader rdr = cmd.ExecuteReader ();
454 columns.Add (rdr.GetString (0));
464 // parse the list of table names in the SQL
465 // TODO: parse the column aliases and table aliases too
466 // and determine if a column is a true table column
468 private void ParseSql (string sql, ref ArrayList tables) {
\r
469 if (sql == String.Empty)
\r
472 char[] chars = sql.ToCharArray ();
\r
473 StringBuilder wb = new StringBuilder ();
\r
475 bool bFromFound = false;
\r
478 bool bTableFound = false;
\r
480 for (; bEnd == false && i < chars.Length; i++) {
\r
481 char ch = chars[i];
\r
483 if (Char.IsLetter (ch)) {
\r
486 else if (Char.IsWhiteSpace (ch)) {
\r
487 if (wb.Length > 0) {
\r
488 if (bFromFound == false) {
\r
489 string word = wb.ToString ().ToUpper ();
\r
490 if (word.Equals ("FROM")) {
\r
494 wb = new StringBuilder ();
\r
495 bTableFound = false;
\r
498 switch (wb.ToString ().ToUpper ()) {
\r
503 bTableFound = false;
\r
506 if (bTableFound == true)
\r
507 bTableFound = false; // this is done in case of a table alias
\r
509 bTableFound = true;
\r
510 tables.Add (wb.ToString ().ToUpper ());
\r
513 wb = new StringBuilder ();
\r
519 else if (bFromFound == true) {
\r
522 if (bTableFound == true)
\r
523 bTableFound = false;
\r
525 tables.Add (wb.ToString ().ToUpper ());
\r
528 wb = new StringBuilder ();
\r
538 if (bEnd == false) {
\r
539 if (wb.Length > 0) {
\r
540 if (bFromFound == false && wb.ToString ().ToUpper ().Equals ("FROM"))
\r
542 if (bFromFound == true) {
\r
543 switch(wb.ToString ().ToUpper ()) {
\r
550 if (bTableFound == false) {
\r
551 tables.Add (wb.ToString ().ToUpper ());
\r
560 // takes a object name like "owner.name" and parses it into "owner" and "name" strings
\r
561 // if object name is only "name", then it gets the username as the owner and returns
\r
563 private void GetOwnerAndName (string objectName, out string owner, out string name)
\r
565 int idx = objectName.IndexOf (".");
\r
567 OracleCommand cmd = command.Connection.CreateCommand ();
568 if (command.Transaction != null)
\r
569 cmd.Transaction = command.Transaction;
\r
571 cmd.CommandText = "SELECT USER FROM DUAL";
572 owner = (string) cmd.ExecuteScalar();
578 owner = objectName.Substring (0, idx);
579 name = objectName.Substring (idx + 1);
\r
583 public DataTable GetSchemaTable ()
585 StringCollection keyinfo = null;
587 if (schemaTable.Rows != null && schemaTable.Rows.Count > 0)
590 string owner = String.Empty;
591 string table = String.Empty;
592 if ((behavior & CommandBehavior.KeyInfo) != 0)
593 keyinfo = GetKeyInfo (out owner, out table);
595 dataTypeNames = new ArrayList ();
597 for (int i = 0; i < statement.ColumnCount; i += 1) {
598 DataRow row = schemaTable.NewRow ();
600 OciParameterDescriptor parameter = statement.GetParameter (i);
602 dataTypeNames.Add (parameter.GetDataTypeName ());
604 row ["ColumnName"] = parameter.GetName ();
605 row ["ColumnOrdinal"] = i + 1;
606 row ["ColumnSize"] = parameter.GetDataSize ();
607 row ["NumericPrecision"] = parameter.GetPrecision ();
608 row ["NumericScale"] = parameter.GetScale ();
610 string sDataTypeName = parameter.GetDataTypeName ();
611 row ["DataType"] = parameter.GetFieldType (sDataTypeName);
613 OciDataType ociType = parameter.GetDataType();
614 OracleType oraType = OciParameterDescriptor.OciDataTypeToOracleType (ociType);
615 row ["ProviderType"] = (int) oraType;
617 if (ociType == OciDataType.Blob || ociType == OciDataType.Clob)
618 row ["IsLong"] = true;
620 row ["IsLong"] = false;
622 row ["AllowDBNull"] = parameter.GetIsNull ();
624 row ["IsAliased"] = DBNull.Value; // TODO:
\r
625 row ["IsExpression"] = DBNull.Value; // TODO:
627 if ((behavior & CommandBehavior.KeyInfo) != 0) {
628 if (keyinfo.IndexOf ((string)row ["ColumnName"]) >= 0)
629 row ["IsKey"] = true;
631 row ["IsKey"] = false;
633 row ["IsUnique"] = DBNull.Value; // TODO: only set this if CommandBehavior.KeyInfo, otherwise, null
634 row ["BaseSchemaName"] = owner;
635 row ["BaseTableName"] = table;
636 row ["BaseColumnName"] = row ["ColumnName"];
639 row ["IsKey"] = DBNull.Value;
640 row ["IsUnique"] = DBNull.Value;
641 row ["BaseSchemaName"] = DBNull.Value;
642 row ["BaseTableName"] = DBNull.Value;
643 row ["BaseColumnName"] = DBNull.Value;
646 schemaTable.Rows.Add (row);
652 public string GetString (int i)
654 object value = GetValue (i);
655 if (!(value is string))
656 throw new InvalidCastException ();
657 return (string) value;
660 public TimeSpan GetTimeSpan (int i)
662 object value = GetValue (i);
663 if (!(value is TimeSpan))
664 throw new InvalidCastException ();
665 return (TimeSpan) value;
668 public object GetValue (int i)
670 OciDefineHandle defineHandle = (OciDefineHandle) statement.Values [i];
672 if (defineHandle.IsNull)
675 switch (defineHandle.DataType) {
676 case OciDataType.Blob:
677 case OciDataType.Clob:
678 OracleLob lob = GetOracleLob (i);
679 object value = lob.Value;
683 return defineHandle.GetValue ();
687 public int GetValues (object[] values)
689 int len = values.Length;
690 int count = statement.ColumnCount;
698 for (int i = 0; i < retval; i += 1)
699 values [i] = GetValue (i);
704 IEnumerator IEnumerable.GetEnumerator ()
706 return new DbEnumerator (this);
709 public bool IsDBNull (int i)
711 OciDefineHandle defineHandle = (OciDefineHandle) statement.Values [i];
712 return defineHandle.IsNull;
716 public bool NextResult ()
718 // FIXME: get next result
725 bool retval = statement.Fetch ();
732 #endregion // Methods