1157 lines
32 KiB
C#
1157 lines
32 KiB
C#
|
//
|
||
|
// System.Data.Odbc.OdbcDataReader
|
||
|
//
|
||
|
// Author:
|
||
|
// Brian Ritchie (brianlritchie@hotmail.com)
|
||
|
// Daniel Morgan <danmorg@sc.rr.com>
|
||
|
// Sureshkumar T <tsureshkumar@novell.com> (2004)
|
||
|
//
|
||
|
// Copyright (C) Brian Ritchie, 2002
|
||
|
// Copyright (C) Daniel Morgan, 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.Globalization;
|
||
|
using System.Text;
|
||
|
|
||
|
namespace System.Data.Odbc
|
||
|
{
|
||
|
#if NET_2_0
|
||
|
public sealed class OdbcDataReader : DbDataReader
|
||
|
#else
|
||
|
public sealed class OdbcDataReader : MarshalByRefObject, IDataReader, IDisposable, IDataRecord, IEnumerable
|
||
|
#endif
|
||
|
{
|
||
|
#region Fields
|
||
|
|
||
|
private OdbcCommand command;
|
||
|
private bool open;
|
||
|
private int currentRow;
|
||
|
private OdbcColumn[] cols;
|
||
|
private IntPtr hstmt;
|
||
|
private int _recordsAffected = -1;
|
||
|
bool disposed;
|
||
|
private DataTable _dataTableSchema;
|
||
|
private CommandBehavior behavior;
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Constructors
|
||
|
|
||
|
internal OdbcDataReader (OdbcCommand command, CommandBehavior behavior)
|
||
|
{
|
||
|
this.command = command;
|
||
|
this.CommandBehavior = behavior;
|
||
|
open = true;
|
||
|
currentRow = -1;
|
||
|
hstmt = command.hStmt;
|
||
|
// Init columns array;
|
||
|
short colcount = 0;
|
||
|
libodbc.SQLNumResultCols (hstmt, ref colcount);
|
||
|
cols = new OdbcColumn [colcount];
|
||
|
GetColumns ();
|
||
|
}
|
||
|
|
||
|
internal OdbcDataReader (OdbcCommand command, CommandBehavior behavior,
|
||
|
int recordAffected) : this (command, behavior)
|
||
|
{
|
||
|
_recordsAffected = recordAffected;
|
||
|
}
|
||
|
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Properties
|
||
|
|
||
|
private CommandBehavior CommandBehavior {
|
||
|
get { return behavior; }
|
||
|
set { behavior = value; }
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
int Depth {
|
||
|
get {
|
||
|
return 0; // no nested selects supported
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
int FieldCount {
|
||
|
get {
|
||
|
if (IsClosed)
|
||
|
throw new InvalidOperationException ("The reader is closed.");
|
||
|
return cols.Length;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
bool IsClosed {
|
||
|
get {
|
||
|
return !open;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
object this [string value] {
|
||
|
get {
|
||
|
int pos = GetOrdinal (value);
|
||
|
return this [pos];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
object this [int i] {
|
||
|
get {
|
||
|
return GetValue (i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
int RecordsAffected {
|
||
|
get {
|
||
|
return _recordsAffected;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[MonoTODO]
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
bool HasRows {
|
||
|
get { throw new NotImplementedException(); }
|
||
|
}
|
||
|
|
||
|
private OdbcConnection Connection {
|
||
|
get {
|
||
|
if (command != null)
|
||
|
return command.Connection;
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Methods
|
||
|
|
||
|
private int ColIndex (string colname)
|
||
|
{
|
||
|
int i = 0;
|
||
|
foreach (OdbcColumn col in cols) {
|
||
|
if (col != null) {
|
||
|
if (col.ColumnName == colname)
|
||
|
return i;
|
||
|
if (String.Compare (col.ColumnName, colname, true) == 0)
|
||
|
return i;
|
||
|
}
|
||
|
i++;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
// Dynamically load column descriptions as needed.
|
||
|
private OdbcColumn GetColumn (int ordinal)
|
||
|
{
|
||
|
if (cols [ordinal] == null) {
|
||
|
short bufsize = 255;
|
||
|
byte [] colname_buffer = new byte [bufsize];
|
||
|
string colname;
|
||
|
short colname_size = 0;
|
||
|
uint ColSize = 0;
|
||
|
short DecDigits = 0, Nullable = 0, dt = 0;
|
||
|
OdbcReturn ret = libodbc.SQLDescribeCol (hstmt, Convert.ToUInt16 (ordinal + 1),
|
||
|
colname_buffer, bufsize, ref colname_size, ref dt, ref ColSize,
|
||
|
ref DecDigits, ref Nullable);
|
||
|
if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
|
||
|
throw Connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
|
||
|
colname = RemoveTrailingNullChar (Encoding.Unicode.GetString (colname_buffer));
|
||
|
OdbcColumn c = new OdbcColumn (colname, (SQL_TYPE) dt);
|
||
|
c.AllowDBNull = (Nullable != 0);
|
||
|
c.Digits = DecDigits;
|
||
|
if (c.IsVariableSizeType)
|
||
|
c.MaxLength = (int) ColSize;
|
||
|
cols [ordinal] = c;
|
||
|
}
|
||
|
return cols [ordinal];
|
||
|
}
|
||
|
|
||
|
// Load all column descriptions
|
||
|
private void GetColumns ()
|
||
|
{
|
||
|
for(int i = 0; i < cols.Length; i++)
|
||
|
GetColumn (i);
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
void Close ()
|
||
|
{
|
||
|
// FIXME : have to implement output parameter binding
|
||
|
open = false;
|
||
|
currentRow = -1;
|
||
|
|
||
|
this.command.FreeIfNotPrepared ();
|
||
|
|
||
|
if ((this.CommandBehavior & CommandBehavior.CloseConnection) == CommandBehavior.CloseConnection)
|
||
|
this.command.Connection.Close ();
|
||
|
}
|
||
|
|
||
|
#if ONLY_1_1
|
||
|
~OdbcDataReader ()
|
||
|
{
|
||
|
this.Dispose (false);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
bool GetBoolean (int i)
|
||
|
{
|
||
|
return (bool) GetValue (i);
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
byte GetByte (int i)
|
||
|
{
|
||
|
return Convert.ToByte (GetValue (i));
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
long GetBytes (int i, long dataIndex, byte[] buffer, int bufferIndex, int length)
|
||
|
{
|
||
|
if (IsClosed)
|
||
|
throw new InvalidOperationException ("Reader is not open.");
|
||
|
if (currentRow == -1)
|
||
|
throw new InvalidOperationException ("No data available.");
|
||
|
|
||
|
OdbcReturn ret = OdbcReturn.Error;
|
||
|
bool copyBuffer = false;
|
||
|
int returnVal = 0, outsize = 0;
|
||
|
byte [] tbuff = new byte [length+1];
|
||
|
|
||
|
if (buffer == null)
|
||
|
length = 0;
|
||
|
ret=libodbc.SQLGetData (hstmt, (ushort) (i + 1), SQL_C_TYPE.BINARY, tbuff, length,
|
||
|
ref outsize);
|
||
|
|
||
|
if (ret == OdbcReturn.NoData)
|
||
|
return 0;
|
||
|
|
||
|
if ( (ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
|
||
|
throw Connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
|
||
|
|
||
|
OdbcException odbcException = null;
|
||
|
if ( (ret == OdbcReturn.SuccessWithInfo))
|
||
|
odbcException = Connection.CreateOdbcException (
|
||
|
OdbcHandleType.Stmt, hstmt);
|
||
|
|
||
|
if (buffer == null)
|
||
|
return outsize; //if buffer is null,return length of the field
|
||
|
|
||
|
if (ret == OdbcReturn.SuccessWithInfo) {
|
||
|
if (outsize == (int) OdbcLengthIndicator.NoTotal)
|
||
|
copyBuffer = true;
|
||
|
else if (outsize == (int) OdbcLengthIndicator.NullData) {
|
||
|
copyBuffer = false;
|
||
|
returnVal = -1;
|
||
|
} else {
|
||
|
string sqlstate = odbcException.Errors [0].SQLState;
|
||
|
//SQLState: String Data, Right truncated
|
||
|
if (sqlstate != libodbc.SQLSTATE_RIGHT_TRUNC)
|
||
|
throw odbcException;
|
||
|
copyBuffer = true;
|
||
|
}
|
||
|
} else {
|
||
|
copyBuffer = outsize == -1 ? false : true;
|
||
|
returnVal = outsize;
|
||
|
}
|
||
|
|
||
|
if (copyBuffer) {
|
||
|
if (outsize == (int) OdbcLengthIndicator.NoTotal) {
|
||
|
int j = 0;
|
||
|
while (tbuff [j] != libodbc.C_NULL) {
|
||
|
buffer [bufferIndex + j] = tbuff [j];
|
||
|
j++;
|
||
|
}
|
||
|
returnVal = j;
|
||
|
} else {
|
||
|
int read_bytes = Math.Min (outsize, length);
|
||
|
for (int j = 0; j < read_bytes; j++)
|
||
|
buffer [bufferIndex + j] = tbuff [j];
|
||
|
returnVal = read_bytes;
|
||
|
}
|
||
|
}
|
||
|
return returnVal;
|
||
|
}
|
||
|
|
||
|
[MonoTODO]
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
char GetChar (int i)
|
||
|
{
|
||
|
throw new NotImplementedException ();
|
||
|
}
|
||
|
|
||
|
[MonoTODO]
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
long GetChars (int i, long dataIndex, char[] buffer, int bufferIndex, int length)
|
||
|
{
|
||
|
if (IsClosed)
|
||
|
throw new InvalidOperationException ("The reader is closed.");
|
||
|
if (currentRow == -1)
|
||
|
throw new InvalidOperationException ("No data available.");
|
||
|
if (i < 0 || i >= FieldCount)
|
||
|
throw new IndexOutOfRangeException ();
|
||
|
throw new NotImplementedException ();
|
||
|
}
|
||
|
|
||
|
[MonoTODO]
|
||
|
[EditorBrowsableAttribute (EditorBrowsableState.Never)]
|
||
|
#if ONLY_1_1
|
||
|
public
|
||
|
#endif
|
||
|
#if NET_2_0
|
||
|
new
|
||
|
#endif
|
||
|
IDataReader GetData (int i)
|
||
|
{
|
||
|
throw new NotImplementedException ();
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
string GetDataTypeName (int i)
|
||
|
{
|
||
|
if (IsClosed)
|
||
|
throw new InvalidOperationException ("The reader is closed.");
|
||
|
if (i < 0 || i >= FieldCount)
|
||
|
throw new IndexOutOfRangeException ();
|
||
|
return GetColumnAttributeStr (i + 1, FieldIdentifier.TypeName);
|
||
|
}
|
||
|
|
||
|
public DateTime GetDate (int i)
|
||
|
{
|
||
|
return GetDateTime (i);
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
DateTime GetDateTime (int i)
|
||
|
{
|
||
|
return (DateTime) GetValue (i);
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
decimal GetDecimal (int i)
|
||
|
{
|
||
|
return (decimal) GetValue (i);
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
double GetDouble (int i)
|
||
|
{
|
||
|
return (double) GetValue (i);
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
Type GetFieldType (int i)
|
||
|
{
|
||
|
if (IsClosed)
|
||
|
throw new InvalidOperationException ("The reader is closed.");
|
||
|
return GetColumn (i).DataType;
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
float GetFloat (int i)
|
||
|
{
|
||
|
return (float) GetValue (i);
|
||
|
}
|
||
|
|
||
|
[MonoTODO]
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
Guid GetGuid (int i)
|
||
|
{
|
||
|
throw new NotImplementedException ();
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
short GetInt16 (int i)
|
||
|
{
|
||
|
return (short) GetValue (i);
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
int GetInt32 (int i)
|
||
|
{
|
||
|
return (int) GetValue (i);
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
long GetInt64 (int i)
|
||
|
{
|
||
|
return (long) GetValue (i);
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
string GetName (int i)
|
||
|
{
|
||
|
if (IsClosed)
|
||
|
throw new InvalidOperationException ("The reader is closed.");
|
||
|
return GetColumn (i).ColumnName;
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
int GetOrdinal (string value)
|
||
|
{
|
||
|
if (IsClosed)
|
||
|
throw new InvalidOperationException ("The reader is closed.");
|
||
|
if (value == null)
|
||
|
throw new ArgumentNullException ("fieldName");
|
||
|
|
||
|
int i = ColIndex (value);
|
||
|
if (i == -1)
|
||
|
throw new IndexOutOfRangeException ();
|
||
|
return i;
|
||
|
}
|
||
|
|
||
|
[MonoTODO]
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
DataTable GetSchemaTable ()
|
||
|
{
|
||
|
if (IsClosed)
|
||
|
throw new InvalidOperationException ("The reader is closed.");
|
||
|
|
||
|
// FIXME :
|
||
|
// * Map OdbcType to System.Type and assign to DataType.
|
||
|
// This will eliminate the need for IsStringType in
|
||
|
// OdbcColumn
|
||
|
|
||
|
if (_dataTableSchema != null)
|
||
|
return _dataTableSchema;
|
||
|
|
||
|
DataTable dataTableSchema = null;
|
||
|
// Only Results from SQL SELECT Queries
|
||
|
// get a DataTable for schema of the result
|
||
|
// otherwise, DataTable is null reference
|
||
|
if (cols.Length > 0) {
|
||
|
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));
|
||
|
|
||
|
DataRow schemaRow;
|
||
|
|
||
|
for (int i = 0; i < cols.Length; i += 1 ) {
|
||
|
OdbcColumn col=GetColumn(i);
|
||
|
|
||
|
schemaRow = dataTableSchema.NewRow ();
|
||
|
dataTableSchema.Rows.Add (schemaRow);
|
||
|
|
||
|
schemaRow ["ColumnName"] = col.ColumnName;
|
||
|
schemaRow ["ColumnOrdinal"] = i;
|
||
|
schemaRow ["ColumnSize"] = col.MaxLength;
|
||
|
schemaRow ["NumericPrecision"] = GetColumnAttribute (i+1, FieldIdentifier.Precision);
|
||
|
schemaRow ["NumericScale"] = GetColumnAttribute (i+1, FieldIdentifier.Scale);
|
||
|
schemaRow ["BaseTableName"] = GetColumnAttributeStr (i+1, FieldIdentifier.TableName);
|
||
|
schemaRow ["BaseSchemaName"] = GetColumnAttributeStr (i+1, FieldIdentifier.SchemaName);
|
||
|
schemaRow ["BaseCatalogName"] = GetColumnAttributeStr (i+1, FieldIdentifier.CatelogName);
|
||
|
schemaRow ["BaseColumnName"] = GetColumnAttributeStr (i+1, FieldIdentifier.BaseColumnName);
|
||
|
schemaRow ["DataType"] = col.DataType;
|
||
|
schemaRow ["IsUnique"] = false;
|
||
|
schemaRow ["IsKey"] = DBNull.Value;
|
||
|
schemaRow ["AllowDBNull"] = GetColumnAttribute (i+1, FieldIdentifier.Nullable) != libodbc.SQL_NO_NULLS;
|
||
|
schemaRow ["ProviderType"] = (int) col.OdbcType;
|
||
|
schemaRow ["IsAutoIncrement"] = GetColumnAttribute (i+1, FieldIdentifier.AutoUniqueValue) == libodbc.SQL_TRUE;
|
||
|
schemaRow ["IsExpression"] = schemaRow.IsNull ("BaseTableName") || (string) schemaRow ["BaseTableName"] == String.Empty;
|
||
|
schemaRow ["IsAliased"] = (string) schemaRow ["BaseColumnName"] != (string) schemaRow ["ColumnName"];
|
||
|
schemaRow ["IsReadOnly"] = ((bool) schemaRow ["IsExpression"]
|
||
|
|| GetColumnAttribute (i+1, FieldIdentifier.Updatable) == libodbc.SQL_ATTR_READONLY);
|
||
|
|
||
|
// FIXME: all of these
|
||
|
schemaRow ["IsIdentity"] = false;
|
||
|
schemaRow ["IsRowVersion"] = false;
|
||
|
schemaRow ["IsHidden"] = false;
|
||
|
schemaRow ["IsLong"] = false;
|
||
|
|
||
|
// FIXME: according to Brian,
|
||
|
// this does not work on MS .NET
|
||
|
// however, we need it for Mono
|
||
|
// for now
|
||
|
// schemaRow.AcceptChanges();
|
||
|
}
|
||
|
|
||
|
// set primary keys
|
||
|
DataRow [] rows = dataTableSchema.Select ("BaseTableName <> ''",
|
||
|
"BaseCatalogName, BaseSchemaName, BaseTableName ASC");
|
||
|
|
||
|
string lastTableName = String.Empty,
|
||
|
lastSchemaName = String.Empty,
|
||
|
lastCatalogName = String.Empty;
|
||
|
string [] keys = null; // assumed to be sorted.
|
||
|
foreach (DataRow row in rows) {
|
||
|
string tableName = (string) row ["BaseTableName"];
|
||
|
string schemaName = (string) row ["BaseSchemaName"];
|
||
|
string catalogName = (string) row ["BaseCatalogName"];
|
||
|
|
||
|
if (tableName != lastTableName || schemaName != lastSchemaName
|
||
|
|| catalogName != lastCatalogName)
|
||
|
keys = GetPrimaryKeys (catalogName, schemaName, tableName);
|
||
|
|
||
|
if (keys != null &&
|
||
|
Array.BinarySearch (keys, (string) row ["BaseColumnName"]) >= 0) {
|
||
|
row ["IsKey"] = true;
|
||
|
row ["IsUnique"] = true;
|
||
|
row ["AllowDBNull"] = false;
|
||
|
GetColumn ( ColIndex ( (string) row ["ColumnName"])).AllowDBNull = false;
|
||
|
}
|
||
|
lastTableName = tableName;
|
||
|
lastSchemaName = schemaName;
|
||
|
lastCatalogName = catalogName;
|
||
|
}
|
||
|
dataTableSchema.AcceptChanges ();
|
||
|
}
|
||
|
return (_dataTableSchema = dataTableSchema);
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
string GetString (int i)
|
||
|
{
|
||
|
object ret = GetValue (i);
|
||
|
|
||
|
if (ret != null && ret.GetType () != typeof (string))
|
||
|
return Convert.ToString (ret);
|
||
|
else
|
||
|
return (string) GetValue (i);
|
||
|
}
|
||
|
|
||
|
[MonoTODO]
|
||
|
public TimeSpan GetTime (int i)
|
||
|
{
|
||
|
throw new NotImplementedException ();
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
object GetValue (int i)
|
||
|
{
|
||
|
if (IsClosed)
|
||
|
throw new InvalidOperationException ("The reader is closed.");
|
||
|
if (currentRow == -1)
|
||
|
throw new InvalidOperationException ("No data available.");
|
||
|
if (i > cols.Length-1 || i < 0)
|
||
|
throw new IndexOutOfRangeException ();
|
||
|
|
||
|
OdbcReturn ret;
|
||
|
int outsize = 0, bufsize;
|
||
|
byte[] buffer;
|
||
|
OdbcColumn col = GetColumn (i);
|
||
|
object DataValue = null;
|
||
|
ushort ColIndex = Convert.ToUInt16 (i + 1);
|
||
|
|
||
|
// Check cached values
|
||
|
if (col.Value == null) {
|
||
|
// odbc help file
|
||
|
// mk:@MSITStore:C:\program%20files\Microsoft%20Data%20Access%20SDK\Docs\odbc.chm::/htm/odbcc_data_types.htm
|
||
|
switch (col.OdbcType) {
|
||
|
case OdbcType.Bit:
|
||
|
short bit_data = 0;
|
||
|
ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref bit_data, 0, ref outsize);
|
||
|
if (outsize != (int) OdbcLengthIndicator.NullData)
|
||
|
DataValue = bit_data == 0 ? "False" : "True";
|
||
|
break;
|
||
|
case OdbcType.Numeric:
|
||
|
case OdbcType.Decimal:
|
||
|
bufsize = 50;
|
||
|
buffer = new byte [bufsize]; // According to sqlext.h, use SQL_CHAR for decimal.
|
||
|
// FIXME : use Numeric.
|
||
|
ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.CHAR, buffer, bufsize, ref outsize);
|
||
|
if (outsize!=-1) {
|
||
|
byte [] temp = new byte [outsize];
|
||
|
for (int j = 0; j < outsize; j++)
|
||
|
temp [j] = buffer [j];
|
||
|
DataValue = Decimal.Parse (Encoding.Default.GetString (temp),
|
||
|
CultureInfo.InvariantCulture);
|
||
|
}
|
||
|
break;
|
||
|
case OdbcType.TinyInt:
|
||
|
short short_data = 0;
|
||
|
ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref short_data, 0, ref outsize);
|
||
|
DataValue = Convert.ToByte (short_data);
|
||
|
break;
|
||
|
case OdbcType.Int:
|
||
|
int int_data = 0;
|
||
|
ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref int_data, 0, ref outsize);
|
||
|
DataValue = int_data;
|
||
|
break;
|
||
|
|
||
|
case OdbcType.SmallInt:
|
||
|
short sint_data = 0;
|
||
|
ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref sint_data, 0, ref outsize);
|
||
|
DataValue = sint_data;
|
||
|
break;
|
||
|
|
||
|
case OdbcType.BigInt:
|
||
|
long long_data = 0;
|
||
|
ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref long_data, 0, ref outsize);
|
||
|
DataValue = long_data;
|
||
|
break;
|
||
|
case OdbcType.NChar:
|
||
|
bufsize = 255;
|
||
|
buffer = new byte [bufsize];
|
||
|
ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.WCHAR, buffer, bufsize, ref outsize);
|
||
|
if (outsize != (int) OdbcLengthIndicator.NullData)
|
||
|
if (!(ret == OdbcReturn.SuccessWithInfo
|
||
|
&& outsize == (int) OdbcLengthIndicator.NoTotal))
|
||
|
DataValue = Encoding.Unicode.GetString (buffer, 0, outsize);
|
||
|
break;
|
||
|
case OdbcType.NText:
|
||
|
case OdbcType.NVarChar:
|
||
|
bufsize = (col.MaxLength < 127 ? (col.MaxLength*2+1) : 255);
|
||
|
buffer = new byte[bufsize]; // According to sqlext.h, use SQL_CHAR for both char and varchar
|
||
|
StringBuilder sb = new StringBuilder ();
|
||
|
char[] charBuffer = new char[bufsize];
|
||
|
Decoder unicodeDecoder = Encoding.Unicode.GetDecoder ();
|
||
|
do {
|
||
|
ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, buffer, bufsize, ref outsize);
|
||
|
if (ret == OdbcReturn.Error)
|
||
|
break;
|
||
|
// Fix for strance ODBC drivers (like psqlODBC)
|
||
|
if (ret == OdbcReturn.Success && outsize==-1)
|
||
|
ret = OdbcReturn.NoData;
|
||
|
|
||
|
if (ret == OdbcReturn.Success || ret == OdbcReturn.SuccessWithInfo) {
|
||
|
if (outsize >= bufsize || outsize == (int)OdbcLengthIndicator.NoTotal)
|
||
|
outsize = bufsize;
|
||
|
int charCount = unicodeDecoder.GetChars (buffer, 0, outsize, charBuffer, 0);
|
||
|
string strValue = new String (charBuffer, 0, charCount);
|
||
|
sb.Append (RemoveTrailingNullChar (strValue));
|
||
|
}
|
||
|
} while (ret != OdbcReturn.NoData);
|
||
|
DataValue = sb.ToString ();
|
||
|
charBuffer = null;
|
||
|
break;
|
||
|
case OdbcType.Text:
|
||
|
case OdbcType.VarChar:
|
||
|
bufsize = (col.MaxLength < 255 ? (col.MaxLength+1) : 255);
|
||
|
buffer = new byte[bufsize]; // According to sqlext.h, use SQL_CHAR for both char and varchar
|
||
|
StringBuilder sb1 = new StringBuilder ();
|
||
|
charBuffer = new char[bufsize];
|
||
|
Decoder defaultDecoder = Encoding.Default.GetDecoder();
|
||
|
do {
|
||
|
ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, buffer, bufsize, ref outsize);
|
||
|
if (ret == OdbcReturn.Error)
|
||
|
break;
|
||
|
// Fix for strance ODBC drivers (like psqlODBC)
|
||
|
if (ret == OdbcReturn.Success && outsize==-1)
|
||
|
ret = OdbcReturn.NoData;
|
||
|
if (ret == OdbcReturn.Success || ret == OdbcReturn.SuccessWithInfo) {
|
||
|
if (outsize >= bufsize || outsize == (int)OdbcLengthIndicator.NoTotal)
|
||
|
outsize = bufsize - 1;
|
||
|
int charCount = defaultDecoder.GetChars(buffer, 0, outsize, charBuffer, 0);
|
||
|
sb1.Append(charBuffer, 0, charCount);
|
||
|
}
|
||
|
} while (ret != OdbcReturn.NoData);
|
||
|
DataValue = sb1.ToString ();
|
||
|
break;
|
||
|
case OdbcType.Real:
|
||
|
float float_data = 0;
|
||
|
ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref float_data, 0, ref outsize);
|
||
|
DataValue = float_data;
|
||
|
break;
|
||
|
case OdbcType.Double:
|
||
|
double double_data = 0;
|
||
|
ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref double_data, 0, ref outsize);
|
||
|
DataValue = double_data;
|
||
|
break;
|
||
|
case OdbcType.Timestamp:
|
||
|
case OdbcType.DateTime:
|
||
|
case OdbcType.Date:
|
||
|
case OdbcType.Time:
|
||
|
OdbcTimestamp ts_data = new OdbcTimestamp();
|
||
|
ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref ts_data, 0, ref outsize);
|
||
|
if (outsize != -1) {// This means SQL_NULL_DATA
|
||
|
if (col.OdbcType == OdbcType.Time) {
|
||
|
// libodbc returns value in first three fields for OdbcType.Time
|
||
|
DataValue = new System.TimeSpan (ts_data.year, ts_data.month, ts_data.day);
|
||
|
} else {
|
||
|
DataValue = new DateTime(ts_data.year, ts_data.month,
|
||
|
ts_data.day, ts_data.hour, ts_data.minute,
|
||
|
ts_data.second);
|
||
|
if (ts_data.fraction != 0)
|
||
|
DataValue = ((DateTime) DataValue).AddTicks ((long)ts_data.fraction / 100);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case OdbcType.VarBinary :
|
||
|
case OdbcType.Image :
|
||
|
bufsize = (col.MaxLength < 255 && col.MaxLength > 0 ? col.MaxLength : 255);
|
||
|
buffer= new byte [bufsize];
|
||
|
ArrayList al = new ArrayList ();
|
||
|
//get the size of data to be returned.
|
||
|
ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.BINARY, buffer, 0, ref outsize);
|
||
|
if (outsize != (int) OdbcLengthIndicator.NullData) {
|
||
|
do {
|
||
|
ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.BINARY, buffer, bufsize, ref outsize);
|
||
|
if (ret == OdbcReturn.Error)
|
||
|
break;
|
||
|
if (ret != OdbcReturn.NoData && outsize != -1) {
|
||
|
if (outsize < bufsize) {
|
||
|
byte [] tmparr = new byte [outsize];
|
||
|
Array.Copy (buffer, 0, tmparr, 0, outsize);
|
||
|
al.AddRange (tmparr);
|
||
|
} else
|
||
|
al.AddRange (buffer);
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
} while (ret != OdbcReturn.NoData);
|
||
|
}
|
||
|
DataValue = al.ToArray (typeof (byte));
|
||
|
break;
|
||
|
case OdbcType.Binary :
|
||
|
bufsize = col.MaxLength;
|
||
|
buffer = new byte [bufsize];
|
||
|
GetBytes (i, 0, buffer, 0, bufsize);
|
||
|
ret = OdbcReturn.Success;
|
||
|
DataValue = buffer;
|
||
|
break;
|
||
|
default:
|
||
|
bufsize = 255;
|
||
|
buffer = new byte[bufsize];
|
||
|
ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.CHAR, buffer, bufsize, ref outsize);
|
||
|
if (outsize != (int) OdbcLengthIndicator.NullData)
|
||
|
if (! (ret == OdbcReturn.SuccessWithInfo
|
||
|
&& outsize == (int) OdbcLengthIndicator.NoTotal))
|
||
|
DataValue = Encoding.Default.GetString (buffer, 0, outsize);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo) && (ret!=OdbcReturn.NoData))
|
||
|
throw Connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
|
||
|
|
||
|
if (outsize == -1) // This means SQL_NULL_DATA
|
||
|
col.Value = DBNull.Value;
|
||
|
else
|
||
|
col.Value = DataValue;
|
||
|
}
|
||
|
return col.Value;
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
int GetValues (object [] values)
|
||
|
{
|
||
|
int numValues = 0;
|
||
|
|
||
|
if (IsClosed)
|
||
|
throw new InvalidOperationException ("The reader is closed.");
|
||
|
if (currentRow == -1)
|
||
|
throw new InvalidOperationException ("No data available.");
|
||
|
|
||
|
// copy values
|
||
|
for (int i = 0; i < values.Length; i++) {
|
||
|
if (i < FieldCount) {
|
||
|
values [i] = GetValue (i);
|
||
|
} else {
|
||
|
values [i] = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// get number of object instances in array
|
||
|
if (values.Length < FieldCount)
|
||
|
numValues = values.Length;
|
||
|
else if (values.Length == FieldCount)
|
||
|
numValues = FieldCount;
|
||
|
else
|
||
|
numValues = FieldCount;
|
||
|
|
||
|
return numValues;
|
||
|
}
|
||
|
|
||
|
#if ONLY_1_1
|
||
|
void IDisposable.Dispose ()
|
||
|
{
|
||
|
Dispose (true);
|
||
|
GC.SuppressFinalize (this);
|
||
|
}
|
||
|
|
||
|
IEnumerator IEnumerable.GetEnumerator ()
|
||
|
{
|
||
|
return new DbEnumerator (this);
|
||
|
}
|
||
|
#endif // ONLY_1_1
|
||
|
|
||
|
#if NET_2_0
|
||
|
public override IEnumerator GetEnumerator ()
|
||
|
{
|
||
|
return new DbEnumerator (this);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#if NET_2_0
|
||
|
protected override
|
||
|
#endif
|
||
|
void Dispose (bool disposing)
|
||
|
{
|
||
|
if (disposed)
|
||
|
return;
|
||
|
|
||
|
if (disposing) {
|
||
|
// dispose managed resources
|
||
|
Close ();
|
||
|
}
|
||
|
|
||
|
command = null;
|
||
|
cols = null;
|
||
|
_dataTableSchema = null;
|
||
|
disposed = true;
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
bool IsDBNull (int i)
|
||
|
{
|
||
|
return (GetValue (i) is DBNull);
|
||
|
}
|
||
|
|
||
|
/// <remarks>
|
||
|
/// Move to the next result set.
|
||
|
/// </remarks>
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
bool NextResult ()
|
||
|
{
|
||
|
OdbcReturn ret = OdbcReturn.Success;
|
||
|
ret = libodbc.SQLMoreResults (hstmt);
|
||
|
if (ret == OdbcReturn.Success) {
|
||
|
short colcount = 0;
|
||
|
libodbc.SQLNumResultCols (hstmt, ref colcount);
|
||
|
cols = new OdbcColumn [colcount];
|
||
|
_dataTableSchema = null; // force fresh creation
|
||
|
GetColumns ();
|
||
|
}
|
||
|
return (ret == OdbcReturn.Success);
|
||
|
}
|
||
|
|
||
|
/// <remarks>
|
||
|
/// Load the next row in the current result set.
|
||
|
/// </remarks>
|
||
|
private bool NextRow ()
|
||
|
{
|
||
|
OdbcReturn ret = libodbc.SQLFetch (hstmt);
|
||
|
if (ret != OdbcReturn.Success)
|
||
|
currentRow = -1;
|
||
|
else
|
||
|
currentRow++;
|
||
|
|
||
|
// Clear cached values from last record
|
||
|
foreach (OdbcColumn col in cols) {
|
||
|
if (col != null)
|
||
|
col.Value = null;
|
||
|
}
|
||
|
return (ret == OdbcReturn.Success);
|
||
|
}
|
||
|
|
||
|
private int GetColumnAttribute (int column, FieldIdentifier fieldId)
|
||
|
{
|
||
|
OdbcReturn ret = OdbcReturn.Error;
|
||
|
byte [] buffer = new byte [255];
|
||
|
short outsize = 0;
|
||
|
int val = 0;
|
||
|
ret = libodbc.SQLColAttribute (hstmt, (short)column, fieldId,
|
||
|
buffer, (short)buffer.Length,
|
||
|
ref outsize, ref val);
|
||
|
if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
|
||
|
throw Connection.CreateOdbcException (
|
||
|
OdbcHandleType.Stmt, hstmt);
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
private string GetColumnAttributeStr (int column, FieldIdentifier fieldId)
|
||
|
{
|
||
|
OdbcReturn ret = OdbcReturn.Error;
|
||
|
byte [] buffer = new byte [255];
|
||
|
short outsize = 0;
|
||
|
int val = 0;
|
||
|
ret = libodbc.SQLColAttribute (hstmt, (short)column, fieldId,
|
||
|
buffer, (short)buffer.Length,
|
||
|
ref outsize, ref val);
|
||
|
if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
|
||
|
throw Connection.CreateOdbcException (
|
||
|
OdbcHandleType.Stmt, hstmt);
|
||
|
string value = string.Empty;
|
||
|
if (outsize > 0)
|
||
|
value = Encoding.Unicode.GetString (buffer, 0, outsize);
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
private string [] GetPrimaryKeys (string catalog, string schema, string table)
|
||
|
{
|
||
|
if (cols.Length <= 0)
|
||
|
return new string [0];
|
||
|
|
||
|
ArrayList keys = null;
|
||
|
try {
|
||
|
keys = GetPrimaryKeysBySQLPrimaryKey (catalog, schema, table);
|
||
|
} catch (OdbcException) {
|
||
|
try {
|
||
|
keys = GetPrimaryKeysBySQLStatistics (catalog, schema, table);
|
||
|
} catch (OdbcException) {
|
||
|
}
|
||
|
}
|
||
|
if (keys == null)
|
||
|
return null;
|
||
|
keys.Sort ();
|
||
|
return (string []) keys.ToArray (typeof (string));
|
||
|
}
|
||
|
|
||
|
private ArrayList GetPrimaryKeysBySQLPrimaryKey (string catalog, string schema, string table)
|
||
|
{
|
||
|
ArrayList keys = new ArrayList ();
|
||
|
IntPtr handle = IntPtr.Zero;
|
||
|
OdbcReturn ret;
|
||
|
try {
|
||
|
ret=libodbc.SQLAllocHandle(OdbcHandleType.Stmt,
|
||
|
command.Connection.hDbc, ref handle);
|
||
|
if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
|
||
|
throw Connection.CreateOdbcException (
|
||
|
OdbcHandleType.Dbc, Connection.hDbc);
|
||
|
|
||
|
ret = libodbc.SQLPrimaryKeys (handle, catalog, -3,
|
||
|
schema, -3, table, -3);
|
||
|
if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
|
||
|
throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
|
||
|
|
||
|
int length = 0;
|
||
|
byte [] primaryKey = new byte [255];
|
||
|
|
||
|
ret = libodbc.SQLBindCol (handle, 4, SQL_C_TYPE.CHAR, primaryKey, primaryKey.Length, ref length);
|
||
|
if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
|
||
|
throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
|
||
|
|
||
|
while (true) {
|
||
|
ret = libodbc.SQLFetch (handle);
|
||
|
if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
|
||
|
break;
|
||
|
string pkey = Encoding.Default.GetString (primaryKey, 0, length);
|
||
|
keys.Add (pkey);
|
||
|
}
|
||
|
} finally {
|
||
|
if (handle != IntPtr.Zero) {
|
||
|
ret = libodbc.SQLFreeStmt (handle, libodbc.SQLFreeStmtOptions.Close);
|
||
|
if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
|
||
|
throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
|
||
|
|
||
|
ret = libodbc.SQLFreeHandle( (ushort) OdbcHandleType.Stmt, handle);
|
||
|
if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
|
||
|
throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
|
||
|
}
|
||
|
}
|
||
|
return keys;
|
||
|
}
|
||
|
|
||
|
private unsafe ArrayList GetPrimaryKeysBySQLStatistics (string catalog, string schema, string table)
|
||
|
{
|
||
|
ArrayList keys = new ArrayList ();
|
||
|
IntPtr handle = IntPtr.Zero;
|
||
|
OdbcReturn ret;
|
||
|
try {
|
||
|
ret=libodbc.SQLAllocHandle(OdbcHandleType.Stmt,
|
||
|
command.Connection.hDbc, ref handle);
|
||
|
if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
|
||
|
throw Connection.CreateOdbcException (
|
||
|
OdbcHandleType.Dbc, Connection.hDbc);
|
||
|
|
||
|
ret = libodbc.SQLStatistics (handle, catalog, -3,
|
||
|
schema, -3,
|
||
|
table, -3,
|
||
|
libodbc.SQL_INDEX_UNIQUE,
|
||
|
libodbc.SQL_QUICK);
|
||
|
if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
|
||
|
throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
|
||
|
|
||
|
// NON_UNIQUE
|
||
|
int nonUniqueLength = 0;
|
||
|
short nonUnique = libodbc.SQL_FALSE;
|
||
|
ret = libodbc.SQLBindCol (handle, 4, SQL_C_TYPE.SHORT, ref nonUnique, sizeof (short), ref nonUniqueLength);
|
||
|
if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
|
||
|
throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
|
||
|
|
||
|
// COLUMN_NAME
|
||
|
int length = 0;
|
||
|
byte [] colName = new byte [255];
|
||
|
ret = libodbc.SQLBindCol (handle, 9, SQL_C_TYPE.CHAR, colName, colName.Length, ref length);
|
||
|
if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
|
||
|
throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
|
||
|
|
||
|
while (true) {
|
||
|
ret = libodbc.SQLFetch (handle);
|
||
|
if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
|
||
|
break;
|
||
|
if (nonUnique == libodbc.SQL_TRUE) {
|
||
|
string pkey = Encoding.Default.GetString (colName, 0, length);
|
||
|
keys.Add (pkey);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
} finally {
|
||
|
if (handle != IntPtr.Zero) {
|
||
|
ret = libodbc.SQLFreeStmt (handle, libodbc.SQLFreeStmtOptions.Close);
|
||
|
if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
|
||
|
throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
|
||
|
|
||
|
ret = libodbc.SQLFreeHandle ((ushort) OdbcHandleType.Stmt, handle);
|
||
|
if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
|
||
|
throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
|
||
|
}
|
||
|
}
|
||
|
return keys;
|
||
|
}
|
||
|
|
||
|
public
|
||
|
#if NET_2_0
|
||
|
override
|
||
|
#endif // NET_2_0
|
||
|
bool Read ()
|
||
|
{
|
||
|
return NextRow ();
|
||
|
}
|
||
|
|
||
|
static string RemoveTrailingNullChar (string value)
|
||
|
{
|
||
|
return value.TrimEnd ('\0');
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
}
|
||
|
}
|