+2006-04-27 Francisco Figueiredo Jr. <fxjrlists@yahoo.com.br>
+
+ * Npgsql_test.dll.sources,
+ Npgsql.dll.sources,
+ Npgsql/NpgsqlError.cs,
+ Npgsql/NpgsqlCommandBuilder.resx,
+ Npgsql/NpgsqlConnectedState.cs,
+ Npgsql/NpgsqlBind.cs,
+ Npgsql/NpgsqlTransaction.cs,
+ Npgsql/NpgsqlConnectionString.cs,
+ Npgsql/NpgsqlConnection.cs,
+ Npgsql/NpgsqlClosedState.cs,
+ Npgsql/NpgsqlState.cs,
+ Npgsql/NpgsqlAsciiRow.cs,
+ Npgsql/NpgsqlConnector.cs,
+ Npgsql/NpgsqlParameterCollection.cs,
+ Npgsql/NpgsqlCancelRequest.cs,
+ Npgsql/NpgsqlCommandBuilder.cs,
+ Npgsql/NpgsqlException.cs,
+ Npgsql/NpgsqlMediator.cs,
+ Npgsql/NpgsqlReadyState.cs,
+ Npgsql/PGUtil.cs,
+ ChangeLog,
+ Npgsql.dll.resources,
+ NpgsqlTypes/NpgsqlDbType.cs,
+ NpgsqlTypes/NpgsqlTypeConverters.cs,
+ NpgsqlTypes/NpgsqlTypes.cs,
+ NpgsqlTypes/NpgsqlTypesHelper.cs,
+ Makefile: Updated to RC1 version.
+
+
Thu Feb 23 10:29:13 CET 2006 Paolo Molaro <lupus@ximian.com>
- * Npgsql/NpgsqlCommand.cs: return count for fetch and move, too,
- patch from Federico Di Gregorio <fog@initd.org>.
+ * Npgsql/NpgsqlCommand.cs: return count for fetch and move, too,
+ patch from Federico Di Gregorio <fog@initd.org>.
+
+
+2006-01-05 Francisco Figueiredo Jr. <fxjrlists@yahoo.com.br>
+
+ * PGUtil.cs: [#1000517] ServerVersion operator overload. Fixed. Thanks Otto for heads up.
+
+
+2006-01-01 Francisco Figueiredo Jr. <fxjrlists@yahoo.com.br>
+
+ * NpgsqlCommand.cs: [#1000497] Dot in parameter name causes exception. Added performance patch for GetClearCommandText() thanks Bryan Mayland.
+ * NpgsqlParameterCollection.cs: Added performance patch for GetClearCommandText() thanks Bryan Mayland.
+ * [#1000454] Getting identifier of an inserted row. Added property LastInsertedOID to NpgsqlCommand. This property only works when using a single insert command and the table was created with oids else it returns 0.
+
+2005-12-29 Francisco Figueiredo Jr. <fxjrlists@yahoo.com.br>
+
+ * NpgsqlCommand.cs: [#1000500] Fixed problem when parsing parameter names with NpgsqlCommand.Prepare(), [#1000497] Fixed problem with dot in parameter names.
+
+2005-12-25 Francisco Figueiredo Jr. <fxjrlists@yahoo.com.br>
+
+ * NpgsqlTransaction.cs: [#1000491] Fixed transaction isolation level setting. Thanks Ottó Havasvölgyi for heads up.
+ * NpgsqlTransaction.cs.resx: Removed unused key.
+
+2005-12-14 Francisco Figueiredo Jr. <fxjrlists@yahoo.com.br>
+
+ * NpgsqlAsciiRow.cs,
+ NpgsqlBind.cs,
+ NpgsqlCommand.cs,
+ NpgsqlConnector.cs,
+ NpgsqlReadyState.cs,
+ NpgsqlState.cs,
+ NpgsqlTypesHelper.cs: [#1000458] Enormous memory increase (and application crash) with large BYTEA parameter. Added binary support. Thanks Hubert FONGARNAND for patch. Improved network performance by removing some Stream.Flush() calls.
+
2005-12-13 Francisco Figueiredo Jr. <fxjrlists@yahoo.com.br>
+
* NpgsqlClosedState.cs: Added a performance improvement fix. Npgsql now use a buffered stream for all server interactions.
2005-12-12 Francisco Figueiredo Jr. <fxjrlists@yahoo.com.br>
Npgsql/NpgsqlClosedState.resources \
Npgsql/NpgsqlParameterCollection.resources \
Npgsql/NpgsqlCommand.resources \
+ Npgsql/NpgsqlCommandBuilder.resources \
Npgsql/NpgsqlConnectedState.resources \
Npgsql/NpgsqlParameter.resources \
Npgsql/NpgsqlConnection.resources \
Npgsql/NpgsqlEventLog.resources \
Npgsql/NpgsqlException.resources \
Npgsql/PGUtil.resources \
- Npgsql/NpgsqlConnectionString.resources \
+ Npgsql/NpgsqlConnectionString.resources \
NpgsqlTypes/NpgsqlTypesHelper.resources
include ../../build/library.make
-resource:Npgsql/NpgsqlParameterCollection.resources,Npgsql.NpgsqlParameterCollection.resources
-resource:Npgsql/NpgsqlParameter.resources,Npgsql.NpgsqlParameter.resources
-resource:Npgsql/NpgsqlCommand.resources,Npgsql.NpgsqlCommand.resources
+-resource:Npgsql/NpgsqlCommandBuilder.resources,Npgsql.NpgsqlCommandBuilder.resources
-resource:Npgsql/NpgsqlConnectedState.resources,Npgsql.NpgsqlConnectedState.resources
-resource:Npgsql/NpgsqlConnection.resources,Npgsql.NpgsqlConnection.resources
-resource:Npgsql/NpgsqlReadyState.resources,Npgsql.NpgsqlReadyState.resources
Npgsql/NpgsqlBackEndKeyData.cs
Npgsql/NpgsqlBinaryRow.cs
Npgsql/NpgsqlBind.cs
+Npgsql/NpgsqlCancelRequest.cs
Npgsql/NpgsqlClosedState.cs
Npgsql/NpgsqlCommand.cs
Npgsql/NpgsqlCommandBuilder.cs
}
NpgsqlRowDescriptionFieldData field_descr = row_desc[field_count];
-
+
if (row_desc[field_count].format_code == FormatCode.Text)
{
string result = ReadStringFromStream(inputStream, field_value_size, decoder);
}
else
{
- PGUtil.CheckedStreamRead(inputStream, _inputBuffer, 0, Math.Min(field_value_size, _inputBuffer.Length));
- // FIXME: _inputBuffer isn't holding all the field value. This code isn't handling binary data correctly.
- data.Add(NpgsqlTypesHelper.ConvertBackendBytesToSystemType(field_descr.type_info, _inputBuffer, encoding, field_value_size, field_descr.type_modifier));
+ Byte[] binary_data = ReadBytesFromStream(inputStream, field_value_size);
+
+ data.Add(NpgsqlTypesHelper.ConvertBackendBytesToSystemType(field_descr.type_info, binary_data, encoding,field_value_size, field_descr.type_modifier));
}
}
}
return new String(_chars, 0,charCount);
}
}
+
+ private byte[] ReadBytesFromStream(Stream inputStream, int field_value_size)
+ {
+ byte[] binary_data = new byte[field_value_size];
+ int bytes_left = field_value_size;
+ if (field_value_size > _inputBuffer.Length)
+ {
+ int i=0;
+ while (bytes_left > READ_BUFFER_SIZE)
+ {
+ PGUtil.CheckedStreamRead(inputStream, _inputBuffer, 0, READ_BUFFER_SIZE);
+ _inputBuffer.CopyTo(binary_data, i*READ_BUFFER_SIZE);
+ i++;
+ bytes_left -= READ_BUFFER_SIZE;
+ }
+ }
+ PGUtil.CheckedStreamRead(inputStream, _inputBuffer, 0, bytes_left);
+ Int32 offset = field_value_size - bytes_left;
+ Array.Copy(_inputBuffer, 0, binary_data, offset, bytes_left);
+ return binary_data;
+ }
}
}
private Int16[] _parameterFormatCodes;
private Object[] _parameterValues;
private Int16[] _resultFormatCodes;
-
+
+
public NpgsqlBind(String portalName,
String preparedStatementName,
_parameterValues = parameterValues;
_resultFormatCodes = resultFormatCodes;
+
+
}
return _portalName;
}
}
+
+ public String PreparedStatementName
+ {
+ get
+ {
+ return _preparedStatementName;
+ }
+ }
+
+ public Int16[] ResultFormatCodes
+ {
+ get
+ {
+ return _resultFormatCodes;
+ }
+ set
+ {
+ _resultFormatCodes = value;
+
+ }
+ }
public Int16[] ParameterFormatCodes
{
set
{
_parameterFormatCodes = value;
+
}
}
Int32 messageLength = 4 +
- encoding.GetByteCount(_portalName) + 1 +
- encoding.GetByteCount(_preparedStatementName) + 1 +
- 2 +
- (_parameterFormatCodes.Length * 2) +
- 2;
+ encoding.GetByteCount(_portalName) + 1 +
+ encoding.GetByteCount(_preparedStatementName) + 1 +
+ 2 +
+ (_parameterFormatCodes.Length * 2) +
+ 2;
// Get size of parameter values.
PGUtil.WriteInt32(outputStream, -1);
else
{
- String parameterValue = (String)_parameterValues[i];
- PGUtil.WriteInt32(outputStream, encoding.GetByteCount(parameterValue));
- outputStream.Write(encoding.GetBytes(parameterValue), 0, encoding.GetByteCount(parameterValue));
+ Byte[] parameterValueBytes = encoding.GetBytes((String)_parameterValues[i]);
+ PGUtil.WriteInt32(outputStream, parameterValueBytes.Length);
+ outputStream.Write(parameterValueBytes, 0, parameterValueBytes.Length);
}
}
--- /dev/null
+// Npgsql.NpgsqlCancelRequest.cs
+//
+// Author:
+// Francisco Jr. (fxjrlists@yahoo.com.br)
+//
+// Copyright (C) 2002-2006 The Npgsql Development Team
+// http://pgfoundry.org/projects/npgsql
+//
+// 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.IO;
+using System.Text;
+
+namespace Npgsql
+{
+
+ /// <summary>
+ /// This class represents the CancelRequest message sent to PostgreSQL
+ /// server.
+ /// </summary>
+ ///
+ internal sealed class NpgsqlCancelRequest
+ {
+ // Logging related values
+ private static readonly String CLASSNAME = "NpgsqlCancelRequest";
+
+
+ private static Int32 CancelRequestMessageSize = 16;
+ private static Int32 CancelRequestCode = 1234 << 16 | 5678;
+
+ private NpgsqlBackEndKeyData BackendKeydata;
+
+
+ public NpgsqlCancelRequest(NpgsqlBackEndKeyData BackendKeydata)
+ {
+ this.BackendKeydata = BackendKeydata;
+
+ }
+
+ public void WriteToStream(Stream outputStream, Encoding encoding)
+ {
+ PGUtil.WriteInt32(outputStream, CancelRequestMessageSize);
+ PGUtil.WriteInt32(outputStream, CancelRequestCode);
+ PGUtil.WriteInt32(outputStream, BackendKeydata.ProcessID);
+ PGUtil.WriteInt32(outputStream, BackendKeydata.SecretKey);
+
+ outputStream.Flush();
+
+ }
+
+ }
+}
\ No newline at end of file
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Open");
- TcpClient tcpc = new TcpClient();
+ /*TcpClient tcpc = new TcpClient();
tcpc.Connect(new IPEndPoint(ResolveIPHost(context.Host), context.Port));
- Stream stream = tcpc.GetStream();
+ Stream stream = tcpc.GetStream();*/
+
+ Socket socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
+
+ socket.Connect(new IPEndPoint(ResolveIPHost(context.Host), context.Port));
+
+ Stream stream = new NetworkStream(socket, true);
+
// If the PostgreSQL server has SSL connectors enabled Open SslClientStream if (response == 'S') {
if (response == 'S')
{
stream = new SslClientStream(
- tcpc.GetStream(),
+ stream,
context.Host,
true,
Mono.Security.Protocol.Tls.SecurityProtocolType.Default
}
context.Stream = new BufferedStream(stream);
+ context.Socket = socket;
NpgsqlEventLog.LogMsg(resman, "Log_ConnectedTo", LogLevel.Normal, context.Host, context.Port);
}
catch (Exception e)
{
- throw new NpgsqlException(e.ToString(), e);
+ throw new NpgsqlException(e.Message, e);
}
}
// NpgsqlCommandBuilder.cs
//
// Author:
-// Pedro Martínez Juliá (yoros@wanadoo.es)
+// Pedro Martínez Juliá (yoros@wanadoo.es)
//
-// Copyright (C) 2003 Pedro Martínez Juliá
+// Copyright (C) 2003 Pedro Martínez Juliá
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
using System;
+using System.Resources;
using System.Data;
using System.Data.Common;
using System.ComponentModel;
public sealed class NpgsqlCommandBuilder : Component
{
+ // Logging related values
+ private static readonly String CLASSNAME = "NpgsqlCommandBuilder";
+ private static ResourceManager resman = new ResourceManager(typeof(NpgsqlCommandBuilder));
+
bool disposed = false;
private NpgsqlCommand update_command;
private NpgsqlCommand delete_command;
- private string table_name = String.Empty;
-
private string quotePrefix = "\"";
private string quoteSuffix = "\"";
+ private DataTable select_schema;
public NpgsqlCommandBuilder ()
{}
throw new InvalidOperationException ("DataAdapter is already set");
}
data_adapter = value;
- string select_text = data_adapter.SelectCommand.CommandText;
- string[] words = select_text.Split(new char [] {' '});
- bool from_found = false;
- for (int i = 0; i < words.Length; i++)
- {
- if (from_found && (words[i] != String.Empty))
- {
- table_name = words[i];
- break;
- }
- if (words[i].ToLower() == "from")
- {
- from_found = true;
- }
- }
}
}
- private void OnRowUpdating(Object sender, NpgsqlRowUpdatingEventArgs value)
- {
+ private void OnRowUpdating(Object sender, NpgsqlRowUpdatingEventArgs value) {
switch (value.StatementType)
{
- case StatementType.Insert:
- value.Command = GetInsertCommand(value.Row);
- break;
- case StatementType.Update:
- value.Command = GetUpdateCommand(value.Row);
- break;
- case StatementType.Delete:
- value.Command = GetDeleteCommand(value.Row);
- break;
+ case StatementType.Insert:
+ value.Command = GetInsertCommand(value.Row);
+ break;
+ case StatementType.Update:
+ value.Command = GetUpdateCommand(value.Row);
+ break;
+ case StatementType.Delete:
+ value.Command = GetDeleteCommand(value.Row);
+ break;
}
DataColumnMappingCollection columnMappings = value.TableMapping.ColumnMappings;
value.Row.AcceptChanges ();
}
-
public string QuotePrefix {
get
{
}
set
{
- quotePrefix = value;
- }
+ quotePrefix = value;
+ }
}
-
public string QuoteSuffix {
get
{
}
set
{
- quoteSuffix = value;
- }
+ quoteSuffix = value;
+ }
}
- ///<summary>
- ///
- /// This method is reponsible to derive the command parameter list with values obtained from function definition.
- /// It clears the Parameters collection of command. Also, if there is any parameter type which is not supported by Npgsql, an InvalidOperationException will be thrown.
- /// Parameters name will be parameter1, parameter2, ...
- /// For while, only parameter name and NpgsqlDbType are obtained.
- ///</summary>
- /// <param name="command">NpgsqlCommand whose function parameters will be obtained.</param>
-
+ ///<summary>
+ ///
+ /// This method is reponsible to derive the command parameter list with values obtained from function definition.
+ /// It clears the Parameters collection of command. Also, if there is any parameter type which is not supported by Npgsql, an InvalidOperationException will be thrown.
+ /// Parameters name will be parameter1, parameter2, ...
+ /// For while, only parameter name and NpgsqlDbType are obtained.
+ ///</summary>
+ /// <param name="command">NpgsqlCommand whose function parameters will be obtained.</param>
public static void DeriveParameters (NpgsqlCommand command)
{
String query = "select proargtypes from pg_proc where proname = :procname";
-
+
NpgsqlCommand c = new NpgsqlCommand(query, command.Connection);
c.Parameters.Add(new NpgsqlParameter("procname", NpgsqlDbType.Text));
c.Parameters[0].Value = command.CommandText;
-
+
String types = (String) c.ExecuteScalar();
-
+
+ if (types == null)
+ throw new InvalidOperationException (String.Format(resman.GetString("Exception_InvalidFunctionName"), command.CommandText));
+
command.Parameters.Clear();
Int32 i = 1;
-
+
foreach(String s in types.Split())
{
if (!c.Connector.OidToNameMapping.ContainsOID(Int32.Parse(s)))
}
command.Parameters.Add(new NpgsqlParameter("parameter" + i++, c.Connector.OidToNameMapping[Int32.Parse(s)].NpgsqlDbType));
}
-
+
}
-
+
private string GetQuotedName(string str)
{
string result = str;
return result;
}
+
public NpgsqlCommand GetInsertCommand (DataRow row)
{
if (insert_command == null)
{
string fields = "";
string values = "";
- for (int i = 0; i < row.Table.Columns.Count; i++)
- {
- DataColumn column = row.Table.Columns[i];
- if (i != 0)
- {
- fields += ", ";
- values += ", ";
- }
- fields += GetQuotedName(column.ColumnName);
- values += ":param_" + column.ColumnName;
- }
- if (table_name == String.Empty)
- {
- table_name = row.Table.TableName;
- }
+ bool first = true;
+ if (select_schema == null)
+ {
+ BuildSchema();
+ }
+ string table_name = string.Empty;
+ foreach(DataRow schemaRow in select_schema.Rows)
+ {
+ if (!(bool)schemaRow["IsAutoIncrement"])
+ {
+ if (!first)
+ {
+ fields += ", ";
+ values += ", ";
+ }
+ else
+ {
+ table_name = (string)schemaRow["BaseTableName"];
+ if (table_name == null || table_name.Length == 0)
+ {
+ table_name = row.Table.TableName;
+ }
+ }
+ fields += GetQuotedName((string)schemaRow["BaseColumnName"]);
+ values += ":param_" + schemaRow["ColumnName"];
+ first = false;
+ }
+ }
NpgsqlCommand cmdaux = new NpgsqlCommand("insert into " + GetQuotedName(table_name) + " (" + fields + ") values (" + values + ")", data_adapter.SelectCommand.Connection);
foreach (DataColumn column in row.Table.Columns)
{
if (update_command == null)
{
string sets = "";
- string wheres = "";
- for (int i = 0; i < row.Table.Columns.Count; i++)
- {
- if (i != 0)
- {
- sets += ", ";
- wheres += " and ";
- }
- DataColumn column = row.Table.Columns[i];
- sets += String.Format(GetQuotedName("{0}") + " = :s_param_{0}", column.ColumnName);
- wheres += String.Format("((" + GetQuotedName("{0}") + " is null) or (" + GetQuotedName("{0}") + " = :w_param_{0}))", column.ColumnName);
- }
- if (table_name == String.Empty)
- {
- table_name = row.Table.TableName;
- }
+ string wheres = "";
+ bool first = true;
+ if (select_schema == null)
+ {
+ BuildSchema();
+ }
+ string table_name = string.Empty;
+ foreach(DataRow schemaRow in select_schema.Rows)
+ {
+ if (!first)
+ {
+ sets += ", ";
+ wheres += " and ";
+ }
+ else
+ {
+ table_name = (string)schemaRow["BaseTableName"];
+ if (table_name == null || table_name.Length == 0)
+ {
+ table_name = row.Table.TableName;
+ }
+ }
+ sets += String.Format("{0} = :s_param_{1}", GetQuotedName((string)schemaRow["BaseColumnName"]), schemaRow["ColumnName"]);
+ wheres += String.Format("(({0} is null) or ({0} = :w_param_{1}))", GetQuotedName((string)schemaRow["BaseColumnName"]), schemaRow["ColumnName"]);
+ first = false;
+ }
NpgsqlCommand cmdaux = new NpgsqlCommand("update " + GetQuotedName(table_name) + " set " + sets + " where ( " + wheres + " )", data_adapter.SelectCommand.Connection);
foreach (DataColumn column in row.Table.Columns)
{
{
if (delete_command == null)
{
- string wheres = "";
- for (int i = 0; i < row.Table.Columns.Count; i++)
- {
- DataColumn column = row.Table.Columns[i];
- if (i != 0)
- {
- wheres += " and ";
- }
- wheres += String.Format("((" + GetQuotedName("{0}") + " is null) or (" + GetQuotedName("{0}") + " = :param_{0}))", column.ColumnName);
- }
- if (table_name == String.Empty)
- {
- table_name = row.Table.TableName;
- }
+ string wheres = "";
+ bool first = true;
+ if (select_schema == null)
+ {
+ BuildSchema();
+ }
+ string table_name = string.Empty;
+ foreach(DataRow schemaRow in select_schema.Rows)
+ {
+ if (!first)
+ {
+ wheres += " and ";
+ }
+ else
+ {
+ table_name = (string)schemaRow["BaseTableName"];
+ if (table_name == null || table_name.Length == 0)
+ {
+ table_name = row.Table.TableName;
+ }
+ }
+ wheres += String.Format("(({0} is null) or ({0} = :param_{1}))", GetQuotedName((string)schemaRow["BaseColumnName"]), schemaRow["ColumnName"]);
+ first = false;
+ }
NpgsqlCommand cmdaux = new NpgsqlCommand("delete from " + GetQuotedName(table_name) + " where ( " + wheres + " )", data_adapter.SelectCommand.Connection);
foreach (DataColumn column in row.Table.Columns)
{
insert_command = null;
update_command = null;
delete_command = null;
+ select_schema = null;
}
protected override void Dispose (bool disposing)
}
}
+ private void BuildSchema()
+ {
+ if (select_schema == null)
+ {
+ bool openedConnection = false;
+ try
+ {
+ if ((data_adapter.SelectCommand.Connection.State & ConnectionState.Open) != ConnectionState.Open)
+ {
+ data_adapter.SelectCommand.Connection.Open();
+ openedConnection = true;
+ }
+ using (NpgsqlDataReader reader = data_adapter.SelectCommand.ExecuteReader(CommandBehavior.SchemaOnly|CommandBehavior.KeyInfo))
+ {
+ select_schema = reader.GetSchemaTable();
+ }
+ }
+ finally
+ {
+ if (openedConnection)
+ {
+ data_adapter.SelectCommand.Connection.Close();
+ }
+ }
+ }
+ }
+
/*~NpgsqlCommandBuilder ()
{
Dispose(false);
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
+ <data name="Exception_InvalidFunctionName">
+ <value>{0} does not exist in pg_proc</value>
+ </data>
</root>
startupPacket.WriteToStream( new BufferedStream(context.Stream), context.Encoding );
context.Mediator.RequireReadyForQuery = false;
+ context.Mediator.CommandTimeout = 20;
+ context.Stream.Flush();
ProcessBackendResponses( context );
}
+
+ public override void CancelRequest(NpgsqlConnector context)
+ {
+ NpgsqlCancelRequest CancelRequestMessage = new NpgsqlCancelRequest(context.BackEndKeyData);
+
+
+ CancelRequestMessage.WriteToStream(context.Stream, context.Encoding);
+
+
+ }
}
}
/// <summary>
/// Gets or sets the string used to connect to a PostgreSQL database.
/// Valid values are:
+ /// <ul>
+ /// <li>
/// Server: Address/Name of Postgresql Server;
+ /// </li>
+ /// <li>
/// Port: Port to connect to;
+ /// </li>
+ /// <li>
/// Protocol: Protocol version to use, instead of automatic; Integer 2 or 3;
+ /// </li>
+ /// <li>
/// Database: Database name. Defaults to user name if not specified;
+ /// </li>
+ /// <li>
+
/// User Id: User name;
+ /// </li>
+ /// <li>
+
/// Password: Password for clear text authentication;
+ /// </li>
+ /// <li>
+
/// SSL: True or False. Controls whether to attempt a secure connection. Default = False;
+ /// </li>
+ /// <li>
+
/// Pooling: True or False. Controls whether connection pooling is used. Default = True;
+ /// </li>
+ /// <li>
+
/// MinPoolSize: Min size of connection pool;
+ /// </li>
+ /// <li>
+
/// MaxPoolSize: Max size of connection pool;
- /// Encoding: Encoding to be used;
- /// Timeout: Time to wait for connection open in seconds.
- /// ConnectionLifeTime: Time to wait before closing unused connections in the pool.
+ /// </li>
+ /// <li>
+
+ /// Encoding: Encoding to be used; Can be ASCII or UNICODE. Default is ASCII. Use UNICODE if you are having problems with accents.
+ /// </li>
+ /// <li>
+
+ /// Timeout: Time to wait for connection open in seconds. Default is 15.
+ /// </li>
+ /// <li>
+
+ /// Sslmode: Mode for ssl connection control. Can be Prefer, Require, Allow or Disable. Default is Disable. Check user manual for explanation of values.
+ /// </li>
+
+ /// <li>
+
+ /// ConnectionLifeTime: Time to wait before closing unused connections in the pool in seconds. Default is 15.
+ /// </li>
+ /// <li>
+
+ /// SyncNotification: Specifies if Npgsql should use synchronous notifications.
+ /// </li>
+ /// </ul>
+
+
/// </summary>
/// <value>The connection string that includes the server name,
/// the database name, and other parameters needed to establish
return connection_string.ToString(ConnectionStringKeys.Database);
}
}
+
+ /// <summary>
+ /// Gets flag indicating if we are using Synchronous notification or not.
+ /// The default value is false.
+ /// </summary>
+ public Boolean SyncNotification
+ {
+ get
+ {
+ return connection_string.ToBool(ConnectionStringKeys.SyncNotification, ConnectionStringDefaults.SyncNotification);
+ }
+ }
/// <summary>
/// Gets the current state of the connection.
// Get a Connector. The connector returned is guaranteed to be connected and ready to go.
connector = NpgsqlConnectorPool.ConnectorPoolMgr.RequestConnector (this);
-
+
connector.Notice += NoticeDelegate;
connector.Notification += NotificationDelegate;
+
+ if (SyncNotification)
+ connector.AddNotificationThread();
+
}
/// <summary>
connector.Notification -= NotificationDelegate;
connector.Notice -= NoticeDelegate;
+
+ if (SyncNotification)
+ connector.RemoveNotificationThread();
NpgsqlConnectorPool.ConnectorPoolMgr.ReleaseConnector(this, connector);
+
+
+
connector = null;
}
}
public static readonly String ConnectionLifeTime = "CONNECTIONLIFETIME";
public static readonly String MinPoolSize = "MINPOOLSIZE";
public static readonly String MaxPoolSize = "MAXPOOLSIZE";
+ public static readonly String SyncNotification = "SYNCNOTIFICATION";
// A list of aliases for some of the above values. If one of these aliases is
// encountered when parsing a connection string, it's real key name will
public static readonly Int32 MaxPoolSize = 20;
public static readonly Int32 Timeout = 15; // Seconds
public static readonly Int32 ConnectionLifeTime = 15; // Seconds
+ public static readonly Boolean SyncNotification = false;
}
internal enum SslMode
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+using System.Net.Sockets;
using Mono.Security.Protocol.Tls;
// The physical network connection to the backend.
private Stream _stream;
+ private Socket _socket;
+
// Mediator which will hold data generated from backend.
private NpgsqlMediator _mediator;
private const String _planNamePrefix = "npgsqlplan";
private const String _portalNamePrefix = "npgsqlportal";
+
+ private Thread _notificationThread;
+
+ // The AutoResetEvent to synchronize processing threads.
+ internal AutoResetEvent _notificationAutoResetEvent;
+
+ // Counter of notification thread start/stop requests in order to
+ internal Int16 _notificationThreadStopCount;
+
/// <summary>
_oidToNameMapping = new NpgsqlBackendTypeMapping();
_planIndex = 0;
_portalIndex = 0;
+ _notificationThreadStopCount = -99;
+ _notificationAutoResetEvent = new AutoResetEvent(true);
}
{
CurrentState.Bind(this, bind);
}
+
+ internal void Describe (NpgsqlDescribe describe)
+ {
+ CurrentState.Describe(this, describe);
+ }
internal void Execute (NpgsqlExecute execute)
{
_stream = value;
}
}
+
+ /// <summary>
+ /// The physical connection socket to the backend.
+ /// </summary>
+
+ internal Socket Socket {
+ get
+ {
+ return _socket;
+ }
+ set
+ {
+ _socket = value;
+ }
+ }
/// <summary>
/// Reports if this connector is fully connected.
}
catch {}
}
+
+ internal void CancelRequest()
+ {
+
+ NpgsqlConnector CancelConnector = new NpgsqlConnector(ConnectionString, false, false);
+
+ CancelConnector._backend_keydata = BackEndKeyData;
+
+
+ // Get a raw connection, possibly SSL...
+ CancelConnector.CurrentState.Open(CancelConnector);
+
+ // Cancel current request.
+ CancelConnector.CurrentState.CancelRequest(CancelConnector);
+
+
+ }
- ///<summary>
- /// Returns next portal index.
- ///</summary>
- internal String NextPortalName()
+ ///<summary>
+ /// Returns next portal index.
+ ///</summary>
+ internal String NextPortalName()
{
return _portalNamePrefix + System.Threading.Interlocked.Increment(ref _portalIndex);
{
return _planNamePrefix + System.Threading.Interlocked.Increment(ref _planIndex);
}
+
+
+ internal void RemoveNotificationThread()
+ {
+ // Wait notification thread finish its work.
+ _notificationAutoResetEvent.WaitOne();
+
+ // Kill notification thread.
+ _notificationThread.Abort();
+ _notificationThread = null;
+
+ // Special case in order to not get problems with thread synchronization.
+ // It will be turned to 0 when synch thread is created.
+ _notificationThreadStopCount = -99;
+
+ }
+
+ internal void AddNotificationThread()
+ {
+
+ _notificationThreadStopCount = 0;
+ _notificationAutoResetEvent.Set();
+
+ NpgsqlContextHolder contextHolder = new NpgsqlContextHolder(this, CurrentState);
+
+ _notificationThread = new Thread(new ThreadStart(contextHolder.ProcessServerMessages));
+
+ _notificationThread.Start();
+
+
+
+ }
+
+ internal void StopNotificationThread()
+ {
+
+ _notificationThreadStopCount++;
+
+ if (_notificationThreadStopCount == 1) // If this call was the first to increment.
+ {
+
+ _notificationAutoResetEvent.WaitOne();
+
+ }
+ }
+
+ internal void ResumeNotificationThread()
+ {
+ _notificationThreadStopCount--;
+ if (_notificationThreadStopCount == 0)
+ {
+ // Release the synchronization handle.
+
+ _notificationAutoResetEvent.Set();
+ }
+
+ }
+
+ internal Boolean IsNotificationThreadRunning
+ {
+ get
+ {
+ return _notificationThreadStopCount <= 0;
+
+ }
+ }
+
+
+ internal class NpgsqlContextHolder
+ {
+
+ private NpgsqlConnector connector;
+ private NpgsqlState state;
+
+ internal NpgsqlContextHolder(NpgsqlConnector connector, NpgsqlState state)
+ {
+ this.connector = connector;
+ this.state = state;
+
+ }
+
+ internal void ProcessServerMessages()
+ {
+
+ while(true)
+ {
+ this.connector._notificationAutoResetEvent.WaitOne();
+
+ if (this.connector.Socket.Poll(1000, SelectMode.SelectRead))
+ {
+ // reset any responses just before getting new ones
+ this.connector.Mediator.ResetResponses();
+ this.state.ProcessBackendResponses(this.connector);
+ this.connector.CheckErrorsAndNotifications();
+ }
+
+ this.connector._notificationAutoResetEvent.Set();
+ }
+
+
+
+ }
+
+ }
/// This class represents the ErrorResponse and NoticeResponse
/// message sent from PostgreSQL server.
/// </summary>
+ [Serializable]
public sealed class NpgsqlError
{
// Logging related values
}
- internal NpgsqlException(String message) : base (message)
+ internal NpgsqlException(String message) : this (message, null)
{}
internal NpgsqlException(String message, Exception innerException) : base (message, innerException)
- {}
+ {
+ errors = new ArrayList();
+ errors.Add(new NpgsqlError(ProtocolVersion.Unknown, message));
+ }
override public void GetObjectData(SerializationInfo info,StreamingContext context)
private NpgsqlRowDescription _rd;
private ArrayList _rows;
private String _sqlSent;
+ private Int32 _commandTimeout;
public NpgsqlMediator()
_parameters = new ListDictionary(CaseInsensitiveComparer.Default);
_backend_key_data = null;
_sqlSent = String.Empty;
+ _commandTimeout = 0;
}
public void ResetExpectations()
_parameters.Clear();
_backend_key_data = null;
_sqlSent = String.Empty;
+ _commandTimeout = 0;
}
return _sqlSent;
}
}
+
+ public Int32 CommandTimeout
+ {
+ set
+ {
+ _commandTimeout = value;
+ }
+
+ get
+ {
+ return _commandTimeout;
+ }
+
+ }
public void AddNotification(NpgsqlNotificationEventArgs data)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IndexOf", parameterName);
// Iterate values to see what is the index of parameter.
- Int32 index = 0;
- if ( (parameterName[0] != ':') && (parameterName[0] != '@') )
- parameterName = ':' + parameterName;
-
- foreach(NpgsqlParameter parameter in this)
- {
- if (parameter.ParameterName.Remove(0, 1) == parameterName.Remove(0, 1))
- return index;
+ Int32 index = 0;\r
+ if ((parameterName[0] == ':') || (parameterName[0] == '@'))\r
+ parameterName = parameterName.Remove(0, 1);\r
+\r
+ foreach (NpgsqlParameter parameter in this)\r
+ {\r
+ if (parameter.ParameterName.Remove(0, 1) == parameterName)\r
+ return index;\r
index++;
}
return -1;
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Contains", value);
CheckType(value);
return this.InternalList.Contains(value);
- }
+ }\r
+\r
+ /// <summary>\r
+ /// Gets a value indicating whether a <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> with the specified parameter name exists in the collection.\r
+ /// </summary>\r
+ /// <param name="parameterName">The name of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object to find.</param>\r
+ /// <param name="parameter">A reference to the requested parameter is returned in this out param if it is found in the list. This value is null if the parameter is not found.</param>\r
+ /// <returns><b>true</b> if the collection contains the parameter and param will contain the parameter; otherwise, <b>false</b>.</returns>\r
+ public bool TryGetValue(string parameterName, out NpgsqlParameter parameter)\r
+ {\r
+ NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "TryGetValue", parameterName);\r
+ int index = IndexOf(parameterName);\r
+ if (index != -1)\r
+ {\r
+ parameter = this[index];\r
+ return true;\r
+ }\r
+ else\r
+ {\r
+ parameter = null;\r
+ return false;\r
+ }\r
+ }\r
/// <summary>
/// Removes all items from the collection.
NpgsqlQuery query = new NpgsqlQuery(command, context.BackendProtocolVersion);
- BufferedStream stream = new BufferedStream(context.Stream);
- query.WriteToStream(stream, context.Encoding);
- stream.Flush();
+ query.WriteToStream(context.Stream, context.Encoding);
+ context.Stream.Flush();
+
ProcessBackendResponses(context);
public override void Parse(NpgsqlConnector context, NpgsqlParse parse)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Parse");
- BufferedStream stream = new BufferedStream(context.Stream);
+
+ Stream stream = context.Stream;
parse.WriteToStream(stream, context.Encoding);
- stream.Flush();
+ //stream.Flush();
}
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Flush");
_flushMessage.WriteToStream(context.Stream, context.Encoding);
+ context.Stream.Flush();
ProcessBackendResponses(context);
}
public override void Bind(NpgsqlConnector context, NpgsqlBind bind)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Bind");
- BufferedStream stream = new BufferedStream(context.Stream);
+
+ Stream stream = context.Stream;
+
bind.WriteToStream(stream, context.Encoding);
- stream.Flush();
+ //stream.Flush();
}
+
+ public override void Describe(NpgsqlConnector context, NpgsqlDescribe describe)
+ {
+ NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Describe");
+ describe.WriteToStream(context.Stream, context.Encoding);
+ //context.Stream.Flush();
+ }
public override void Execute(NpgsqlConnector context, NpgsqlExecute execute)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Execute");
NpgsqlDescribe describe = new NpgsqlDescribe('P', execute.PortalName);
- BufferedStream stream = new BufferedStream(context.Stream);
+ Stream stream = context.Stream;
describe.WriteToStream(stream, context.Encoding);
execute.WriteToStream(stream, context.Encoding);
- stream.Flush();
+ //stream.Flush();
Sync(context);
}
using System.Text;
using System.Resources;
+
namespace Npgsql
{
///<summary> This class represents the base class for the state pattern design pattern
{
throw new InvalidOperationException("Internal Error! " + this);
}
+
+ public virtual void Describe(NpgsqlConnector context, NpgsqlDescribe describe)
+ {
+ throw new InvalidOperationException("Internal Error! " + this);
+ }
+
+ public virtual void CancelRequest(NpgsqlConnector context)
+ {
+ throw new InvalidOperationException("Internal Error! " + this);
+ }
public virtual void Close( NpgsqlConnector context )
{
/// to handle backend requests.
/// </summary>
///
- protected virtual void ProcessBackendResponses( NpgsqlConnector context )
+ internal virtual void ProcessBackendResponses( NpgsqlConnector context )
{
try
{
+
+ // Process commandTimeout behavior.
+
+ if ((context.Mediator.CommandTimeout > 0) && (!context.Socket.Poll(1000000 * context.Mediator.CommandTimeout, SelectMode.SelectRead)))
+ context.CancelRequest();
+
+
switch (context.BackendProtocolVersion)
{
case ProtocolVersion.Version2 :
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses");
- BufferedStream stream = new BufferedStream(context.Stream);
+ Stream stream = context.Stream;
NpgsqlMediator mediator = context.Mediator;
// Often used buffer
Int32 PID = PGUtil.ReadInt32(stream, inputBuffer);
String notificationResponse = PGUtil.ReadString( stream, context.Encoding );
mediator.AddNotification(new NpgsqlNotificationEventArgs(PID, notificationResponse));
+
+ if (context.IsNotificationThreadRunning)
+ readyForQuery = true;
// Wait for ReadForQuery message
break;
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses");
- BufferedStream stream = new BufferedStream(context.Stream);
+ Stream stream = context.Stream;
NpgsqlMediator mediator = context.Mediator;
// Often used buffers
// Now wait for the AsciiRow messages.
break;
+ case NpgsqlMessageTypes_Ver_3.ParameterDescription:
+
+ // Do nothing,for instance, just read...
+ int lenght = PGUtil.ReadInt32(stream, inputBuffer);
+ int nb_param = PGUtil.ReadInt16(stream, inputBuffer);
+ for (int i=0; i < nb_param; i++)
+ {
+ int typeoid = PGUtil.ReadInt32(stream, inputBuffer);
+ }
+
+ break;
case NpgsqlMessageTypes_Ver_3.DataRow:
// This is the AsciiRow message.
PGUtil.ReadString( stream, context.Encoding );
mediator.AddNotification(new NpgsqlNotificationEventArgs(PID, notificationResponse));
}
+
+ if (context.IsNotificationThreadRunning)
+ readyForQuery = true;
// Wait for ReadForQuery message
break;
resman = new System.Resources.ResourceManager(this.GetType());
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME);
- if ((isolation != IsolationLevel.ReadCommitted) &&
- (isolation != IsolationLevel.Serializable))
- throw new ArgumentOutOfRangeException(resman.GetString("Exception_UnsopportedIsolationLevel"), "isolation");
-
+
_conn = conn;
_isolation = isolation;
- StringBuilder commandText = new StringBuilder("SET TRANSACTION ISOLATION LEVEL ");
+ StringBuilder commandText = new StringBuilder("BEGIN; SET TRANSACTION ISOLATION LEVEL ");
- if (isolation == IsolationLevel.ReadCommitted)
- commandText.Append("READ COMMITTED");
- else
+ if ( (isolation == IsolationLevel.RepeatableRead) ||
+ (isolation == IsolationLevel.Serializable)
+ )
commandText.Append("SERIALIZABLE");
+ else
+ {
+ // Set isolation level default to read committed.
+ _isolation = IsolationLevel.ReadCommitted;
+ commandText.Append("READ COMMITTED");
+ }
- commandText.Append("; BEGIN");
+ commandText.Append(";");
NpgsqlCommand command = new NpgsqlCommand(commandText.ToString(), conn.Connector);
command.ExecuteNonQuery();
public static bool operator == (ServerVersion One, ServerVersion TheOther)
{
+ if (((Object)One == null) || ((Object)TheOther == null))
+ return false;
return
One._Major == TheOther._Major &&
One._Minor == TheOther._Minor &&
public static bool operator != (ServerVersion One, ServerVersion TheOther)
{
+ if (((Object)One == null) || ((Object)TheOther == null))
+ return true;
+
return ! (One == TheOther);
}
public override bool Equals(object O)
{
+ if (O == null)
+ return false;
+
return (O.GetType() == this.GetType() && this == (ServerVersion)O);
}
Time,
Timestamp,
Varchar,
- Refcursor
+ Refcursor,
+ Inet
}
return "$" + ((IFormattable)NativeData).ToString(null, CultureInfo.InvariantCulture.NumberFormat);
}
-
+ /// <summary>
+ /// Convert to a postgres inet.
+ /// </summary>
+ internal static String ToIPAddress(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
+ {
+ if (NativeData is NpgsqlInet)
+ return ((NpgsqlInet)NativeData).ToString();
+ else
+ return ((System.Net.IPAddress)NativeData).ToString();
+ }
+
}
return string.Format("({0}), {1}", Center, Radius);
}
}
+
+
+ /// <summary>
+ /// Represents a PostgreSQL inet type.
+ /// </summary>
+ public struct NpgsqlInet
+ {
+ public IPAddress addr;
+ public int mask;
+
+ public NpgsqlInet(IPAddress addr, int mask)
+ {
+ this.addr = addr;
+ this.mask = mask;
+ }
+
+ public NpgsqlInet(IPAddress addr)
+ {
+ this.addr = addr;
+ this.mask = 32;
+ }
+
+ public NpgsqlInet(string addr)
+ {
+ if (addr.IndexOf('/') > 0)
+ {
+ string[] addrbits = addr.Split('/');
+ if (addrbits.GetUpperBound(0) != 1)
+ throw new FormatException("Invalid number of parts in CIDR specification");
+ this.addr = IPAddress.Parse(addrbits[0]);
+ this.mask = int.Parse(addrbits[1]);
+ }
+ else
+ {
+ this.addr = IPAddress.Parse(addr);
+ this.mask = 32;
+ }
+ }
+
+ public override String ToString()
+ {
+ if (mask != 32)
+ return string.Format("{0}/{1}", addr.ToString(), mask);
+ else
+ return addr.ToString();
+ }
+
+ public static implicit operator IPAddress(NpgsqlInet x)
+ {
+ if (x.mask != 32)
+ throw new InvalidCastException("Cannot cast CIDR network to address");
+ else
+ return x.addr;
+ }
+ }
}
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ConvertBackendBytesToStytemType");
- /*
+
// We are never guaranteed to know about every possible data type the server can send us.
// When we encounter an unknown type, we punt and return the data without modification.
if (TypeInfo == null)
switch (TypeInfo.NpgsqlDbType)
{
- case NpgsqlDbType.Binary:
+ case NpgsqlDbType.Bytea:
return data;
- case NpgsqlDbType.Boolean:
+ /*case NpgsqlDbType.Boolean:
return BitConverter.ToBoolean(data, 0);
case NpgsqlDbType.DateTime:
return DateTime.MinValue.AddTicks(IPAddress.NetworkToHostOrder(BitConverter.ToInt64(data, 0)));
case NpgsqlDbType.String:
case NpgsqlDbType.AnsiString:
case NpgsqlDbType.StringFixedLength:
- return encoding.GetString(data, 0, fieldValueSize);
+ return encoding.GetString(data, 0, fieldValueSize);*/
default:
throw new InvalidCastException("Type not supported in binary format");
- }*/
+ }
return null;
}
new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToCircle));
NativeTypeMapping.AddTypeAlias("circle", typeof(NpgsqlCircle));
+
+ NativeTypeMapping.AddType("inet", NpgsqlDbType.Inet, DbType.Object, true,
+ new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToIPAddress));
+
+ NativeTypeMapping.AddTypeAlias("inet", typeof(IPAddress));
+ NativeTypeMapping.AddTypeAlias("inet", typeof(NpgsqlInet));
}
}
new NpgsqlBackendTypeInfo(0, "numeric", NpgsqlDbType.Numeric, DbType.Decimal, typeof(Decimal),
null),
+ new NpgsqlBackendTypeInfo(0, "inet", NpgsqlDbType.Inet, DbType.Object, typeof(NpgsqlInet),
+ null),
+
new NpgsqlBackendTypeInfo(0, "money", NpgsqlDbType.Money, DbType.Decimal, typeof(Decimal),
new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToMoney)),