// Npgsql.NpgsqlDataReader.cs // // Author: // Francisco Jr. (fxjrlists@yahoo.com.br) // // Copyright (C) 2002 The Npgsql Development Team // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using System; using System.Data; using System.Collections; using System.Text; using NpgsqlTypes; namespace Npgsql { /// /// Provides a means of reading a forward-only stream of rows from a PostgreSQL backend. This class cannot be inherited. /// public sealed class NpgsqlDataReader : IDataReader, IEnumerable { private NpgsqlConnection _connection; private ArrayList _resultsets; private ArrayList _responses; private Int32 _rowIndex; private Int32 _resultsetIndex; private NpgsqlResultSet _currentResultset; private DataTable _currentResultsetSchema; private CommandBehavior _behavior; private Boolean _isClosed; private NpgsqlCommand _command; // Logging related values private static readonly String CLASSNAME = "NpgsqlDataReader"; internal NpgsqlDataReader( ArrayList resultsets, ArrayList responses, CommandBehavior behavior, NpgsqlCommand command) { _resultsets = resultsets; _responses = responses; _connection = command.Connection; _rowIndex = -1; _resultsetIndex = 0; if (_resultsets.Count > 0) _currentResultset = (NpgsqlResultSet)_resultsets[_resultsetIndex]; _behavior = behavior; _isClosed = false; _command = command; } private Boolean HaveResultSet() { return (_currentResultset != null); } private Boolean HaveRow() { return (HaveResultSet() && _rowIndex >= 0 && _rowIndex < _currentResultset.Count); } private void CheckHaveResultSet() { if (! HaveResultSet()) { throw new InvalidOperationException("Cannot read data. No result set."); } } private void CheckHaveRow() { CheckHaveResultSet(); if (_rowIndex < 0) { throw new InvalidOperationException("DataReader positioned before beginning of result set. Did you call Read()?"); } else if (_rowIndex >= _currentResultset.Count) { throw new InvalidOperationException("DataReader positioned beyond end of result set."); } } /// /// Releases the resources used by the NpgsqlCommand. /// public void Dispose() { Dispose(true); } /// /// Releases the resources used by the NpgsqlCommand. /// protected void Dispose (bool disposing) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Dispose"); if (disposing) { this.Close(); } } /// /// Gets a value indicating the depth of nesting for the current row. Always returns zero. /// public Int32 Depth { get { NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "Depth"); return 0; } } /// /// Gets a value indicating whether the data reader is closed. /// public Boolean IsClosed { get { NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "IsClosed"); return _isClosed; } } /// /// Gets the number of rows changed, inserted, or deleted by execution of the SQL statement. /// public Int32 RecordsAffected { get { NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "RecordsAffected"); if (HaveResultSet()) { return -1; } String[] _returnStringTokens = ((String)_responses[_resultsetIndex]).Split(null); // whitespace separator. try { return Int32.Parse(_returnStringTokens[_returnStringTokens.Length - 1]); } catch (FormatException) { return -1; } } } /// /// Indicates if NpgsqlDatareader has rows to be read. /// public Boolean HasRows { get { return (HaveResultSet() ? _currentResultset.Count > 0 : false); } } /// /// Closes the data reader object. /// public void Close() { if ((_behavior & CommandBehavior.CloseConnection) == CommandBehavior.CloseConnection) { _connection.Close(); } _isClosed = true; if (this.ReaderClosed != null) this.ReaderClosed(this, EventArgs.Empty); } /// /// Is raised whenever Close() is called. /// public event EventHandler ReaderClosed; /// /// Advances the data reader to the next result, when multiple result sets were returned by the PostgreSQL backend. /// /// True if the reader was advanced, otherwise false. public Boolean NextResult() { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "NextResult"); if((_resultsetIndex + 1) < _resultsets.Count) { _resultsetIndex++; _rowIndex = -1; _currentResultset = (NpgsqlResultSet)_resultsets[_resultsetIndex]; return true; } else return false; } /// /// Advances the data reader to the next row. /// /// True if the reader was advanced, otherwise false. public Boolean Read() { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Read"); if (!HaveResultSet()) return false; if (_rowIndex < _currentResultset.Count) { _rowIndex++; return (_rowIndex < _currentResultset.Count); } else { return false; } } /// /// Returns a System.Data.DataTable that describes the column metadata of the DataReader. /// public DataTable GetSchemaTable() { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetSchemaTable"); if(_currentResultsetSchema == null) _currentResultsetSchema = GetResultsetSchema(); return _currentResultsetSchema; } /// /// Gets the number of columns in the current row. /// public Int32 FieldCount { get { NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "FieldCount"); if (! HaveResultSet()) //Executed a non return rows query. return -1; else return _currentResultset.RowDescription.NumFields; } } /// /// Return the column name of the column at index . /// public String GetName(Int32 Index) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetName"); CheckHaveResultSet(); return _currentResultset.RowDescription[Index].name; } /// /// Return the data type OID of the column at index . /// /// FIXME: Why this method returns String? public String GetDataTypeOID(Int32 Index) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDataTypeName"); CheckHaveResultSet(); NpgsqlBackendTypeInfo TI = GetTypeInfo(Index); return _currentResultset.RowDescription[Index].type_oid.ToString(); } /// /// Return the data type name of the column at index . /// public String GetDataTypeName(Int32 Index) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDataTypeName"); CheckHaveResultSet(); NpgsqlBackendTypeInfo TI = GetTypeInfo(Index); if (TI == null) { return _currentResultset.RowDescription[Index].type_oid.ToString(); } else { return TI.Name; } } /// /// Return the data type of the column at index . /// public Type GetFieldType(Int32 Index) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFieldType"); CheckHaveResultSet(); NpgsqlBackendTypeInfo TI = GetTypeInfo(Index); if (TI == null) { return typeof(String); //Default type is string. } else { return TI.Type; } } /// /// Return the data DbType of the column at index . /// public DbType GetFieldDbType(Int32 Index) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFieldType"); CheckHaveResultSet(); NpgsqlBackendTypeInfo TI = GetTypeInfo(Index); if (TI == null) { return DbType.String; } else { //return TI.DBType; return DbType.String; } } /// /// Return the data NpgsqlDbType of the column at index . /// public NpgsqlDbType GetFieldNpgsqlDbType(Int32 Index) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFieldType"); CheckHaveResultSet(); NpgsqlBackendTypeInfo TI = GetTypeInfo(Index); if (TI == null) { return NpgsqlDbType.Text; } else { return TI.NpgsqlDbType; } } /// /// Return the value of the column at index . /// public Object GetValue(Int32 Index) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetValue"); if (Index < 0 || Index >= _currentResultset.RowDescription.NumFields) { throw new IndexOutOfRangeException("Column index out of range"); } CheckHaveRow(); return ((NpgsqlAsciiRow)_currentResultset[_rowIndex])[Index]; } /// /// Copy values from each column in the current row into . /// /// The number of column values copied. public Int32 GetValues(Object[] Values) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetValues"); CheckHaveRow(); // Only the number of elements in the array are filled. // It's also possible to pass an array with more that FieldCount elements. Int32 maxColumnIndex = (Values.Length < FieldCount) ? Values.Length : FieldCount; for (Int32 i = 0; i < maxColumnIndex; i++) { Values[i] = GetValue(i); } return maxColumnIndex; } /// /// Return the column name of the column named . /// public Int32 GetOrdinal(String Name) { CheckHaveResultSet(); return _currentResultset.RowDescription.FieldIndex(Name); } /// /// Gets the value of a column in its native format. /// public Object this [ Int32 i ] { get { NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, i); return GetValue(i); } } /// /// Gets the value of a column in its native format. /// public Object this [ String name ] { get { NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, name); Int32 fieldIndex = _currentResultset.RowDescription.FieldIndex(name); if (fieldIndex == -1) throw new IndexOutOfRangeException("Field not found"); return GetValue(fieldIndex); } } /// /// Gets the value of a column as Boolean. /// public Boolean GetBoolean(Int32 i) { // Should this be done using the GetValue directly and not by converting to String // and parsing from there? NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetBoolean"); return (Boolean) GetValue(i); } /// /// Gets the value of a column as Byte. Not implemented. /// public Byte GetByte(Int32 i) { throw new NotImplementedException(); } /// /// Gets raw data from a column. /// public Int64 GetBytes(Int32 i, Int64 fieldOffset, Byte[] buffer, Int32 bufferoffset, Int32 length) { Byte[] result; result = (Byte[]) GetValue(i); if (buffer == null) return result.Length; // We just support read all the field for while. So, any fieldOffset value other than 0 will not read // anything and return 0. if (fieldOffset != 0) return 0; // [TODO] Implement blob support. result.CopyTo(buffer, 0); return result.Length; } /// /// Gets the value of a column as Char. Not implemented. /// public Char GetChar(Int32 i) { throw new NotImplementedException(); } /// /// Gets raw data from a column. /// public Int64 GetChars(Int32 i, Int64 fieldoffset, Char[] buffer, Int32 bufferoffset, Int32 length) { String str; str = GetString(i); if (buffer == null) return str.Length; str.ToCharArray(bufferoffset, length).CopyTo(buffer, 0); return buffer.GetLength(0); } /// /// Gets the value of a column converted to a Guid. Not implemented. /// public Guid GetGuid(Int32 i) { throw new NotImplementedException(); } /// /// Gets the value of a column as Int16. /// public Int16 GetInt16(Int32 i) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetInt16"); return (Int16) GetValue(i); } /// /// Gets the value of a column as Int32. /// public Int32 GetInt32(Int32 i) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetInt32"); return (Int32) GetValue(i); } /// /// Gets the value of a column as Int64. /// public Int64 GetInt64(Int32 i) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetInt64"); return (Int64) GetValue(i); } /// /// Gets the value of a column as Single. /// public Single GetFloat(Int32 i) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFloat"); return (Single) GetValue(i); } /// /// Gets the value of a column as Double. /// public Double GetDouble(Int32 i) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDouble"); return (Double) GetValue(i); } /// /// Gets the value of a column as String. /// public String GetString(Int32 i) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetString"); return (String) GetValue(i); } /// /// Gets the value of a column as Decimal. /// public Decimal GetDecimal(Int32 i) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDecimal"); return (Decimal) GetValue(i); } /// /// Gets the value of a column as DateTime. /// public DateTime GetDateTime(Int32 i) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDateTime"); return (DateTime) GetValue(i); } /// /// Not implemented. /// public IDataReader GetData(Int32 i) { throw new NotImplementedException(); } /// /// Report whether the value in a column is DBNull. /// public Boolean IsDBNull(Int32 i) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IsDBNull"); return (GetValue(i) == DBNull.Value); } internal NpgsqlBackendTypeInfo GetTypeInfo(Int32 FieldIndex) { return _currentResultset.RowDescription[FieldIndex].type_info; } private DataTable GetResultsetSchema() { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetResultsetSchema"); DataTable result = null; NpgsqlRowDescription rd = _currentResultset.RowDescription; Int16 numFields = rd.NumFields; if(numFields > 0) { result = new DataTable("SchemaTable"); result.Columns.Add ("ColumnName", typeof (string)); result.Columns.Add ("ColumnOrdinal", typeof (int)); result.Columns.Add ("ColumnSize", typeof (int)); result.Columns.Add ("NumericPrecision", typeof (int)); result.Columns.Add ("NumericScale", typeof (int)); result.Columns.Add ("IsUnique", typeof (bool)); result.Columns.Add ("IsKey", typeof (bool)); result.Columns.Add ("BaseCatalogName", typeof (string)); result.Columns.Add ("BaseColumnName", typeof (string)); result.Columns.Add ("BaseSchemaName", typeof (string)); result.Columns.Add ("BaseTableName", typeof (string)); result.Columns.Add ("DataType", typeof(Type)); result.Columns.Add ("AllowDBNull", typeof (bool)); result.Columns.Add ("ProviderType", typeof (string)); result.Columns.Add ("IsAliased", typeof (bool)); result.Columns.Add ("IsExpression", typeof (bool)); result.Columns.Add ("IsIdentity", typeof (bool)); result.Columns.Add ("IsAutoIncrement", typeof (bool)); result.Columns.Add ("IsRowVersion", typeof (bool)); result.Columns.Add ("IsHidden", typeof (bool)); result.Columns.Add ("IsLong", typeof (bool)); result.Columns.Add ("IsReadOnly", typeof (bool)); if (_connection.Connector.BackendProtocolVersion == ProtocolVersion.Version2) { FillSchemaTable_v2(result); } else if (_connection.Connector.BackendProtocolVersion == ProtocolVersion.Version3) { FillSchemaTable_v3(result); } } return result; } private void FillSchemaTable_v2(DataTable schema) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "FillSchemaTable_v2"); NpgsqlRowDescription rd = _currentResultset.RowDescription; ArrayList keyList = null; if ((_behavior & CommandBehavior.KeyInfo) == CommandBehavior.KeyInfo) { keyList = GetPrimaryKeys(GetTableNameFromQuery()); } DataRow row; for (Int16 i = 0; i < rd.NumFields; i++) { row = schema.NewRow(); row["ColumnName"] = GetName(i); row["ColumnOrdinal"] = i + 1; if (rd[i].type_modifier != -1 && rd[i].type_info != null && (rd[i].type_info.Name == "varchar" || rd[i].type_info.Name == "bpchar")) row["ColumnSize"] = rd[i].type_modifier - 4; else if (rd[i].type_modifier != -1 && rd[i].type_info != null && (rd[i].type_info.Name == "bit" || rd[i].type_info.Name == "varbit")) row["ColumnSize"] = rd[i].type_modifier; else row["ColumnSize"] = (int) rd[i].type_size; if (rd[i].type_modifier != -1 && rd[i].type_info != null && rd[i].type_info.Name == "numeric") { row["NumericPrecision"] = ((rd[i].type_modifier-4)>>16)&ushort.MaxValue; row["NumericScale"] = (rd[i].type_modifier-4)&ushort.MaxValue; } else { row["NumericPrecision"] = 0; row["NumericScale"] = 0; } row["IsUnique"] = false; row["IsKey"] = IsKey(GetName(i), keyList); row["BaseCatalogName"] = ""; row["BaseSchemaName"] = ""; row["BaseTableName"] = ""; row["BaseColumnName"] = GetName(i); row["DataType"] = GetFieldType(i); row["AllowDBNull"] = IsNullable(null, i); if (rd[i].type_info != null) { row["ProviderType"] = rd[i].type_info.Name; } row["IsAliased"] = false; row["IsExpression"] = false; row["IsIdentity"] = false; row["IsAutoIncrement"] = false; row["IsRowVersion"] = false; row["IsHidden"] = false; row["IsLong"] = false; row["IsReadOnly"] = false; schema.Rows.Add(row); } } private void FillSchemaTable_v3(DataTable schema) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "FillSchemaTable_v3"); NpgsqlRowDescription rd = _currentResultset.RowDescription; Hashtable oidTableLookup = null; KeyLookup keyLookup = new KeyLookup(); Hashtable columnLookup = null; if ((_behavior & CommandBehavior.KeyInfo) == CommandBehavior.KeyInfo) { ArrayList tableOids = new ArrayList(); for(short i=0; i>16)&ushort.MaxValue; row["NumericScale"] = (rd[i].type_modifier-4)&ushort.MaxValue; } else { row["NumericPrecision"] = 0; row["NumericScale"] = 0; } row["IsUnique"] = IsUnique(keyLookup, baseColumnName); row["IsKey"] = IsKey(keyLookup, baseColumnName); if (rd[i].table_oid != 0 && oidTableLookup != null) { row["BaseCatalogName"] = ((object[])oidTableLookup[rd[i].table_oid])[Tables.table_catalog]; row["BaseSchemaName"] = ((object[])oidTableLookup[rd[i].table_oid])[Tables.table_schema]; row["BaseTableName"] = ((object[])oidTableLookup[rd[i].table_oid])[Tables.table_name]; } else { row["BaseCatalogName"] = ""; row["BaseSchemaName"] = ""; row["BaseTableName"] = ""; } row["BaseColumnName"] = baseColumnName; row["DataType"] = GetFieldType(i); row["AllowDBNull"] = IsNullable(columnLookup, i); if (rd[i].type_info != null) { row["ProviderType"] = rd[i].type_info.Name; } row["IsAliased"] = string.CompareOrdinal((string)row["ColumnName"], baseColumnName) != 0; row["IsExpression"] = false; row["IsIdentity"] = false; row["IsAutoIncrement"] = IsAutoIncrement(columnLookup, i); row["IsRowVersion"] = false; row["IsHidden"] = false; row["IsLong"] = false; row["IsReadOnly"] = false; schema.Rows.Add(row); } } private Boolean IsKey(String ColumnName, ArrayList ListOfKeys) { if (ListOfKeys == null || ListOfKeys.Count == 0) return false; foreach(String s in ListOfKeys) { if (s == ColumnName) return true; } return false; } private ArrayList GetPrimaryKeys(String tablename) { if (tablename == String.Empty) return null; String getPKColumns = "select a.attname from pg_catalog.pg_class ct, pg_catalog.pg_class ci, pg_catalog.pg_attribute a, pg_catalog.pg_index i WHERE ct.oid=i.indrelid AND ci.oid=i.indexrelid AND a.attrelid=ci.oid AND i.indisprimary AND ct.relname = :tablename"; ArrayList result = new ArrayList(); NpgsqlConnection metadataConn = _connection.Clone(); NpgsqlCommand c = new NpgsqlCommand(getPKColumns, metadataConn); c.Parameters.Add(new NpgsqlParameter("tablename", NpgsqlDbType.Text)); c.Parameters["tablename"].Value = tablename; NpgsqlDataReader dr = c.ExecuteReader(); while (dr.Read()) result.Add(dr[0]); metadataConn.Close(); return result; } private bool IsKey(KeyLookup keyLookup, string fieldName) { if (keyLookup.primaryKey == null || keyLookup.primaryKey.Count == 0) return false; for (int i=0; i /// Contains the column names as the keys /// public ArrayList primaryKey; /// /// Contains all unique columns /// public ArrayList uniqueColumns; } private KeyLookup GetKeys(Int32 tableOid) { string getKeys = "select a.attname, ci.relname, i.indisprimary from pg_catalog.pg_class ct, pg_catalog.pg_class ci, pg_catalog.pg_attribute a, pg_catalog.pg_index i WHERE ct.oid=i.indrelid AND ci.oid=i.indexrelid AND a.attrelid=ci.oid AND i.indisunique AND ct.oid = :tableOid order by ci.relname"; KeyLookup lookup = new KeyLookup(); lookup.primaryKey = new ArrayList(); lookup.uniqueColumns = new ArrayList(); using (NpgsqlConnection metadataConn = _connection.Clone()) { NpgsqlCommand c = new NpgsqlCommand(getKeys, metadataConn); c.Parameters.Add(new NpgsqlParameter("tableOid", NpgsqlDbType.Integer)).Value = tableOid; using (NpgsqlDataReader dr = c.ExecuteReader()) { string previousKeyName = null; string possiblyUniqueColumn = null; string columnName; string currentKeyName; // loop through adding any column that is primary to the primary key list // add any column that is the only column for that key to the unique list // unique here doesn't mean general unique constraint (with possibly multiple columns) // it means all values in this single column must be unique while (dr.Read()) { columnName = dr.GetString(0); currentKeyName = dr.GetString(1); // if i.indisprimary if (dr.GetBoolean(2)) { // add column name as part of the primary key lookup.primaryKey.Add(columnName); } if (currentKeyName != previousKeyName) { if (possiblyUniqueColumn != null) { lookup.uniqueColumns.Add(possiblyUniqueColumn); } possiblyUniqueColumn = columnName; } else { possiblyUniqueColumn = null; } previousKeyName = currentKeyName; } // if finished reading and have a possiblyUniqueColumn name that is // not null, then it is the name of a unique column if (possiblyUniqueColumn != null) lookup.uniqueColumns.Add(possiblyUniqueColumn); } } return lookup; } private Boolean IsNullable(Hashtable columnLookup, Int32 FieldIndex) { if (columnLookup == null || _currentResultset.RowDescription[FieldIndex].table_oid == 0) return true; string lookupKey = _currentResultset.RowDescription[FieldIndex].table_oid.ToString() + "," + _currentResultset.RowDescription[FieldIndex].column_attribute_number; object[] row = (object[])columnLookup[lookupKey]; if (row != null) return !(bool)row[Columns.column_notnull]; else return true; } private string GetBaseColumnName(Hashtable columnLookup, Int32 FieldIndex) { if (columnLookup == null || _currentResultset.RowDescription[FieldIndex].table_oid == 0) return GetName(FieldIndex); string lookupKey = _currentResultset.RowDescription[FieldIndex].table_oid.ToString() + "," + _currentResultset.RowDescription[FieldIndex].column_attribute_number; object[] row = (object[])columnLookup[lookupKey]; if (row != null) return (string)row[Columns.column_name]; else return GetName(FieldIndex); } private bool IsAutoIncrement(Hashtable columnLookup, Int32 FieldIndex) { if (columnLookup == null || _currentResultset.RowDescription[FieldIndex].table_oid == 0) return false; string lookupKey = _currentResultset.RowDescription[FieldIndex].table_oid.ToString() + "," + _currentResultset.RowDescription[FieldIndex].column_attribute_number; object[] row = (object[])columnLookup[lookupKey]; if (row != null) return row[Columns.column_default].ToString().StartsWith("nextval("); else return true; } /// /// This methods parses the command text and tries to get the tablename /// from it. /// private String GetTableNameFromQuery() { Int32 fromClauseIndex = _command.CommandText.ToLower().IndexOf("from"); String tableName = _command.CommandText.Substring(fromClauseIndex + 4).Trim(); if (tableName == String.Empty) return String.Empty; /*if (tableName.EndsWith(".")); return String.Empty; */ foreach (Char c in tableName.Substring (0, tableName.Length - 1)) if (!Char.IsLetterOrDigit (c) && c != '_' && c != '.') return String.Empty; return tableName; } private struct Tables { public const int table_catalog = 0; public const int table_schema = 1; public const int table_name = 2; public const int table_id = 3; } private Hashtable GetTablesFromOids(ArrayList oids) { if (oids.Count == 0) return null; StringBuilder sb = new StringBuilder(); // the column index is used to find data. // any changes to the order of the columns needs to be reflected in struct Tables sb.Append("SELECT current_database() AS table_catalog, nc.nspname AS table_schema, c.relname AS table_name, c.oid as table_id"); sb.Append(" FROM pg_namespace nc, pg_class c WHERE c.relnamespace = nc.oid AND (c.relkind = 'r' OR c.relkind = 'v') AND c.oid IN ("); bool first = true; foreach(int oid in oids) { if (!first) sb.Append(','); sb.Append(oid); first = false; } sb.Append(')'); using (NpgsqlConnection connection = _connection.Clone()) using (NpgsqlCommand command = new NpgsqlCommand(sb.ToString(), connection)) using (NpgsqlDataReader reader = command.ExecuteReader()) { Hashtable oidLookup = new Hashtable(); int columnCount = reader.FieldCount; while (reader.Read()) { object[] values = new object[columnCount]; reader.GetValues(values); oidLookup[Convert.ToInt32(reader[Tables.table_id])] = values; } return oidLookup; } } private struct Columns { public const int column_name = 0; public const int column_notnull = 1; public const int table_id = 2; public const int column_num = 3; public const int column_default = 4; } private Hashtable GetColumns() { StringBuilder sb = new StringBuilder(); // the column index is used to find data. // any changes to the order of the columns needs to be reflected in struct Columns sb.Append("SELECT a.attname AS column_name, a.attnotnull AS column_notnull, a.attrelid AS table_id, a.attnum AS column_num, d.adsrc as column_default"); sb.Append(" FROM pg_attribute a LEFT OUTER JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum WHERE a.attnum > 0 AND ("); bool first = true; for(int i=0; i<_currentResultset.RowDescription.NumFields; ++i) { if (_currentResultset.RowDescription[i].table_oid != 0) { if (!first) sb.Append(" OR "); sb.AppendFormat("(a.attrelid={0} AND a.attnum={1})", _currentResultset.RowDescription[i].table_oid, _currentResultset.RowDescription[i].column_attribute_number); first = false; } } sb.Append(')'); // if the loop ended without setting first to false, then there will be no results from the query if (first) return null; using (NpgsqlConnection connection = _connection.Clone()) using (NpgsqlCommand command = new NpgsqlCommand(sb.ToString(), connection)) using (NpgsqlDataReader reader = command.ExecuteReader()) { Hashtable columnLookup = new Hashtable(); int columnCount = reader.FieldCount; while(reader.Read()) { object[] values = new object[columnCount]; reader.GetValues(values); columnLookup[reader[Columns.table_id].ToString() + "," + reader[Columns.column_num].ToString()] = values; } return columnLookup; } } IEnumerator IEnumerable.GetEnumerator () { return new System.Data.Common.DbEnumerator (this); } } }