diff options
author | Francisco Figueiredo Jr. <fxjr@mono-cvs.ximian.com> | 2008-11-06 06:08:03 +0300 |
---|---|---|
committer | Francisco Figueiredo Jr. <fxjr@mono-cvs.ximian.com> | 2008-11-06 06:08:03 +0300 |
commit | a3bc867dd9c3237bfac9f359a35f04d03256d109 (patch) | |
tree | 5347626555ca06711601d00ff3d86dcde7ed997f /mcs/class/Npgsql | |
parent | 7b867dfd1859e28eef7b42f5c7f3dcb559c67aa3 (diff) |
2008-11-06 Francisco Figueiredo Jr. <francisco@npgsql.org>
Reverted last update because it broke the svn trunk.
svn path=/trunk/mcs/; revision=118072
Diffstat (limited to 'mcs/class/Npgsql')
56 files changed, 12141 insertions, 14190 deletions
diff --git a/mcs/class/Npgsql/ChangeLog b/mcs/class/Npgsql/ChangeLog index f5099b6d136..6d3ec4026af 100644 --- a/mcs/class/Npgsql/ChangeLog +++ b/mcs/class/Npgsql/ChangeLog @@ -1,4 +1,8 @@ 2008-11-05 Francisco Figueiredo Jr. <francisco@npgsql.org> + + Undone update as it breaks the svn tree. + +2008-11-05 Francisco Figueiredo Jr. <francisco@npgsql.org> Updated Npgsql to Npgsql2 2.0.1. Now Npgsql requires .Net 2.0 or above diff --git a/mcs/class/Npgsql/Makefile b/mcs/class/Npgsql/Makefile index f05c133588d..1dafe2da7d1 100755 --- a/mcs/class/Npgsql/Makefile +++ b/mcs/class/Npgsql/Makefile @@ -9,7 +9,6 @@ LIBRARY_SNK = Npgsql/Npgsql.snk LIB_MCS_FLAGS = /r:$(corlib) /r:System.dll /r:System.Xml.dll \ /r:System.Data.dll \ /r:Mono.Security.dll \ - /r:System.Transactions.dll \ @Npgsql.dll.resources TEST_MCS_FLAGS = /r:$(corlib) /r:System.dll /r:System.Xml.dll \ diff --git a/mcs/class/Npgsql/Npgsql.dll.sources b/mcs/class/Npgsql/Npgsql.dll.sources index a866a3dba22..29c1d0c19e8 100755 --- a/mcs/class/Npgsql/Npgsql.dll.sources +++ b/mcs/class/Npgsql/Npgsql.dll.sources @@ -1,30 +1,22 @@ Npgsql/AssemblyInfo.cs ../../build/common/Consts.cs -Npgsql/Cache.cs Npgsql/HashAlgorithm.cs Npgsql/MD5.cs Npgsql/MD5CryptoServiceProvider.cs Npgsql/NpgsqlAsciiRow.cs Npgsql/NpgsqlBackEndKeyData.cs Npgsql/NpgsqlBind.cs +Npgsql/NpgsqlBinaryRow.cs Npgsql/NpgsqlCancelRequest.cs Npgsql/NpgsqlClosedState.cs Npgsql/NpgsqlCommand.cs Npgsql/NpgsqlCommandBuilder.cs Npgsql/NpgsqlConnectedState.cs Npgsql/NpgsqlConnection.cs -Npgsql/NpgsqlConnectionStringBuilder.cs +Npgsql/NpgsqlConnectionString.cs Npgsql/NpgsqlConnector.cs Npgsql/NpgsqlConnectorPool.cs -Npgsql/NpgsqlCopyFormat.cs -Npgsql/NpgsqlCopyIn.cs -Npgsql/NpgsqlCopyInState.cs -Npgsql/NpgsqlCopyInStream.cs -Npgsql/NpgsqlCopyOut.cs -Npgsql/NpgsqlCopyOutState.cs -Npgsql/NpgsqlCopyOutStream.cs -Npgsql/NpgsqlCopySerializer.cs Npgsql/NpgsqlDataAdapter.cs Npgsql/NpgsqlDataReader.cs Npgsql/NpgsqlDescribe.cs @@ -32,7 +24,6 @@ Npgsql/NpgsqlError.cs Npgsql/NpgsqlEventLog.cs Npgsql/NpgsqlException.cs Npgsql/NpgsqlExecute.cs -Npgsql/NpgsqlFactory.cs Npgsql/NpgsqlFlush.cs Npgsql/NpgsqlMediator.cs Npgsql/NpgsqlMessageTypes.cs @@ -42,27 +33,20 @@ Npgsql/NpgsqlParameterCollection.cs Npgsql/NpgsqlParameterStatus.cs Npgsql/NpgsqlParse.cs Npgsql/NpgsqlPasswordPacket.cs -Npgsql/NpgsqlPromotableSinglePhaseNotification.cs -Npgsql/NpgsqlProviderManifest.cs Npgsql/NpgsqlQuery.cs Npgsql/NpgsqlReadyState.cs -Npgsql/NpgsqlResourceManager.cs +Npgsql/NpgsqlResultSet.cs Npgsql/NpgsqlRow.cs Npgsql/NpgsqlRowDescription.cs Npgsql/NpgsqlSchema.cs -Npgsql/NpgsqlServices.cs Npgsql/NpgsqlStartupPacket.cs Npgsql/NpgsqlStartupState.cs Npgsql/NpgsqlState.cs Npgsql/NpgsqlSync.cs Npgsql/NpgsqlTransaction.cs -Npgsql/NpgsqlTransactionCallbacks.cs Npgsql/PGUtil.cs -Npgsql/SSPIHandler.cs -NpgsqlTypes/ArrayHandling.cs NpgsqlTypes/LargeObjectManager.cs -NpgsqlTypes/DateDatatypes.cs NpgsqlTypes/NpgsqlDbType.cs NpgsqlTypes/FastPath.cs NpgsqlTypes/NpgsqlTypeConverters.cs diff --git a/mcs/class/Npgsql/Npgsql/HashAlgorithm.cs b/mcs/class/Npgsql/Npgsql/HashAlgorithm.cs index f624ee68c9b..ad05a0ef0db 100644 --- a/mcs/class/Npgsql/Npgsql/HashAlgorithm.cs +++ b/mcs/class/Npgsql/Npgsql/HashAlgorithm.cs @@ -16,95 +16,98 @@ using System; using System.IO; + namespace Npgsql { - // Comment: Removed the ICryptoTransform implementation as this interface may be not supported by - // all platforms. - - internal abstract class HashAlgorithm : IDisposable - { - protected byte[] HashValue; // Caches the hash after it is calculated. Accessed through the Hash property. - protected int HashSizeValue; // The size of the hash in bits. - protected int State; // nonzero when in use; zero when not in use - private bool disposed; - - /// <summary> - /// Called from constructor of derived class. - /// </summary> - protected HashAlgorithm() - { - disposed = false; - } - - /// <summary> - /// Finalizer for HashAlgorithm - /// </summary> - ~HashAlgorithm() - { - Dispose(false); - } - - /// <summary> - /// Get whether or not the hash can transform multiple blocks at a time. - /// Note: MUST be overriden if descendant can transform multiple block - /// on a single call! - /// </summary> - public virtual bool CanTransformMultipleBlocks - { - get { return true; } - } - - public virtual bool CanReuseTransform - { - get { return true; } - } - - public void Clear() - { - // same as System.IDisposable.Dispose() which is documented - Dispose(true); - } - - /// <summary> - /// Computes the entire hash of all the bytes in the byte array. - /// </summary> - public byte[] ComputeHash(byte[] input) - { - return ComputeHash(input, 0, input.Length); - } - - public byte[] ComputeHash(byte[] buffer, int offset, int count) - { - if (disposed) - { - throw new ObjectDisposedException("HashAlgorithm"); - } - - HashCore(buffer, offset, count); - HashValue = HashFinal(); - Initialize(); - - return HashValue; - } - - public byte[] ComputeHash(Stream inputStream) - { - // don't read stream unless object is ready to use - if (disposed) - { - throw new ObjectDisposedException("HashAlgorithm"); - } - - int l = (int) (inputStream.Length - inputStream.Position); - byte[] buffer = new byte[l]; - inputStream.Read(buffer, 0, l); - - return ComputeHash(buffer, 0, l); - } - - // Commented out because it uses the CryptoConfig which can't be available in all platforms - - /* + + + // Comment: Removed the ICryptoTransform implementation as this interface may be not supported by + // all platforms. + + internal abstract class HashAlgorithm : IDisposable + { + protected byte[] HashValue; // Caches the hash after it is calculated. Accessed through the Hash property. + protected int HashSizeValue; // The size of the hash in bits. + protected int State; // nonzero when in use; zero when not in use + private bool disposed; + + /// <summary> + /// Called from constructor of derived class. + /// </summary> + protected HashAlgorithm () + { + disposed = false; + } + + /// <summary> + /// Finalizer for HashAlgorithm + /// </summary> + ~HashAlgorithm () + { + Dispose(false); + } + + /// <summary> + /// Get whether or not the hash can transform multiple blocks at a time. + /// Note: MUST be overriden if descendant can transform multiple block + /// on a single call! + /// </summary> + public virtual bool CanTransformMultipleBlocks { + get + { + return true; + } + } + + public virtual bool CanReuseTransform { + get + { + return true; + } + } + + public void Clear() + { + // same as System.IDisposable.Dispose() which is documented + Dispose (true); + } + + /// <summary> + /// Computes the entire hash of all the bytes in the byte array. + /// </summary> + public byte[] ComputeHash (byte[] input) + { + return ComputeHash (input, 0, input.Length); + } + + public byte[] ComputeHash (byte[] buffer, int offset, int count) + { + if (disposed) + throw new ObjectDisposedException ("HashAlgorithm"); + + HashCore (buffer, offset, count); + HashValue = HashFinal (); + Initialize (); + + return HashValue; + } + + public byte[] ComputeHash (Stream inputStream) + { + // don't read stream unless object is ready to use + if (disposed) + throw new ObjectDisposedException ("HashAlgorithm"); + + int l = (int) (inputStream.Length - inputStream.Position); + byte[] buffer = new byte [l]; + inputStream.Read (buffer, 0, l); + + return ComputeHash (buffer, 0, l); + } + + // Commented out because it uses the CryptoConfig which can't be available in all platforms + + /* /// <summary> /// Creates the default implementation of the default hash algorithm (SHA1). /// </summary> @@ -113,7 +116,7 @@ namespace Npgsql return Create ("System.Security.Cryptography.HashAlgorithm"); }*/ - /* + /* /// <summary> /// Creates a specific implementation of the general hash idea. /// </summary> @@ -124,109 +127,114 @@ namespace Npgsql }*/ - // Changed Exception type because it uses the CryptographicUnexpectedOperationException - // which can't be available in all platforms. - /// <summary> - /// Gets the previously computed hash. - /// </summary> - public virtual byte[] Hash - { - get - { - if (HashValue == null) - { - throw new NullReferenceException("HashValue is null"); - } - return HashValue; - } - } - - /// <summary> - /// When overridden in a derived class, drives the hashing function. - /// </summary> - /// <param name="rgb"></param> - /// <param name="start"></param> - /// <param name="size"></param> - protected abstract void HashCore(byte[] rgb, int start, int size); - - /// <summary> - /// When overridden in a derived class, this pads and hashes whatever data might be left in the buffers and then returns the hash created. - /// </summary> - protected abstract byte[] HashFinal(); - - /// <summary> - /// Returns the size in bits of the hash. - /// </summary> - public virtual int HashSize - { - get { return HashSizeValue; } - } - - /// <summary> - /// When overridden in a derived class, initializes the object to prepare for hashing. - /// </summary> - public abstract void Initialize(); - - protected virtual void Dispose(bool disposing) - { - disposed = true; - } - - /// <summary> - /// Must be overriden if not 1 - /// </summary> - public virtual int InputBlockSize - { - get { return 1; } - } - - /// <summary> - /// Must be overriden if not 1 - /// </summary> - public virtual int OutputBlockSize - { - get { return 1; } - } - - void IDisposable.Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); // Finalization is now unnecessary - } - - /// <summary> - /// Used for stream chaining. Computes hash as data passes through it. - /// </summary> - /// <param name="inputBuffer">The buffer from which to grab the data to be copied.</param> - /// <param name="inputOffset">The offset into the input buffer to start reading at.</param> - /// <param name="inputCount">The number of bytes to be copied.</param> - /// <param name="outputBuffer">The buffer to write the copied data to.</param> - /// <param name="outputOffset">At what point in the outputBuffer to write the data at.</param> - public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - Buffer.BlockCopy(inputBuffer, inputOffset, outputBuffer, outputOffset, inputCount); - HashCore(inputBuffer, inputOffset, inputCount); - - return inputCount; - } - - /// <summary> - /// Used for stream chaining. Computes hash as data passes through it. Finishes off the hash. - /// </summary> - /// <param name="inputBuffer">The buffer from which to grab the data to be copied.</param> - /// <param name="inputOffset">The offset into the input buffer to start reading at.</param> - /// <param name="inputCount">The number of bytes to be copied.</param> - public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) - { - byte[] outputBuffer = new byte[inputCount]; - - Buffer.BlockCopy(inputBuffer, inputOffset, outputBuffer, 0, inputCount); - - HashCore(inputBuffer, inputOffset, inputCount); - HashValue = HashFinal(); - Initialize(); - - return outputBuffer; - } - } -}
\ No newline at end of file + + // Changed Exception type because it uses the CryptographicUnexpectedOperationException + // which can't be available in all platforms. + /// <summary> + /// Gets the previously computed hash. + /// </summary> + public virtual byte[] Hash { + get + { + if (HashValue == null) + throw new NullReferenceException("HashValue is null"); + return HashValue; + } + } + + /// <summary> + /// When overridden in a derived class, drives the hashing function. + /// </summary> + /// <param name="rgb"></param> + /// <param name="start"></param> + /// <param name="size"></param> + protected abstract void HashCore (byte[] rgb, int start, int size); + + /// <summary> + /// When overridden in a derived class, this pads and hashes whatever data might be left in the buffers and then returns the hash created. + /// </summary> + protected abstract byte[] HashFinal (); + + /// <summary> + /// Returns the size in bits of the hash. + /// </summary> + public virtual int HashSize { + get + { + return HashSizeValue; + } + } + + /// <summary> + /// When overridden in a derived class, initializes the object to prepare for hashing. + /// </summary> + public abstract void Initialize (); + + protected virtual void Dispose (bool disposing) + { + disposed = true; + } + + /// <summary> + /// Must be overriden if not 1 + /// </summary> + public virtual int InputBlockSize { + get + { + return 1; + } + } + + /// <summary> + /// Must be overriden if not 1 + /// </summary> + public virtual int OutputBlockSize { + get + { + return 1; + } + } + + void IDisposable.Dispose () + { + Dispose (true); + GC.SuppressFinalize (this); // Finalization is now unnecessary + } + + /// <summary> + /// Used for stream chaining. Computes hash as data passes through it. + /// </summary> + /// <param name="inputBuffer">The buffer from which to grab the data to be copied.</param> + /// <param name="inputOffset">The offset into the input buffer to start reading at.</param> + /// <param name="inputCount">The number of bytes to be copied.</param> + /// <param name="outputBuffer">The buffer to write the copied data to.</param> + /// <param name="outputOffset">At what point in the outputBuffer to write the data at.</param> + public int TransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + Buffer.BlockCopy (inputBuffer, inputOffset, outputBuffer, outputOffset, inputCount); + HashCore (inputBuffer, inputOffset, inputCount); + + return inputCount; + } + + /// <summary> + /// Used for stream chaining. Computes hash as data passes through it. Finishes off the hash. + /// </summary> + /// <param name="inputBuffer">The buffer from which to grab the data to be copied.</param> + /// <param name="inputOffset">The offset into the input buffer to start reading at.</param> + /// <param name="inputCount">The number of bytes to be copied.</param> + public byte[] TransformFinalBlock (byte[] inputBuffer, int inputOffset, int inputCount) + { + byte[] outputBuffer = new byte[inputCount]; + + Buffer.BlockCopy (inputBuffer, inputOffset, outputBuffer, 0, inputCount); + + HashCore (inputBuffer, inputOffset, inputCount); + HashValue = HashFinal (); + Initialize (); + + return outputBuffer; + } + } + +} diff --git a/mcs/class/Npgsql/Npgsql/MD5.cs b/mcs/class/Npgsql/Npgsql/MD5.cs index 628a2030311..7391466adc3 100644 --- a/mcs/class/Npgsql/Npgsql/MD5.cs +++ b/mcs/class/Npgsql/Npgsql/MD5.cs @@ -4,25 +4,23 @@ // // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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 // + // // System.Security.Cryptography MD5 Class implementation // @@ -40,32 +38,37 @@ // + + + namespace Npgsql { - /// <summary> - /// Common base class for all derived MD5 implementations. - /// </summary> - internal abstract class MD5 : HashAlgorithm - { - /// <summary> - /// Called from constructor of derived class. - /// </summary> - // Why is it protected when others abstract hash classes are public ? - protected MD5() - { - HashSizeValue = 128; - } - /// <summary> - /// Creates the default derived class. - /// </summary> - public static MD5 Create() - { - //return Create ("System.Security.Cryptography.MD5"); - return new MD5CryptoServiceProvider(); - } - /* + /// <summary> + /// Common base class for all derived MD5 implementations. + /// </summary> + internal abstract class MD5 : HashAlgorithm + { + /// <summary> + /// Called from constructor of derived class. + /// </summary> + // Why is it protected when others abstract hash classes are public ? + protected MD5 () + { + HashSizeValue = 128; + } + + /// <summary> + /// Creates the default derived class. + /// </summary> + public static MD5 Create () + { + //return Create ("System.Security.Cryptography.MD5"); + return new MD5CryptoServiceProvider(); + } + + /* // Commented out because it uses the CryptoConfig which can't be available in all // platforms. /// <summary> @@ -76,5 +79,6 @@ namespace Npgsql { return (MD5) CryptoConfig.CreateFromName (hashName); }*/ - } -}
\ No newline at end of file + + } +} diff --git a/mcs/class/Npgsql/Npgsql/MD5CryptoServiceProvider.cs b/mcs/class/Npgsql/Npgsql/MD5CryptoServiceProvider.cs index ced30403391..06ebc4d1f0c 100644 --- a/mcs/class/Npgsql/Npgsql/MD5CryptoServiceProvider.cs +++ b/mcs/class/Npgsql/Npgsql/MD5CryptoServiceProvider.cs @@ -14,549 +14,508 @@ using System; + namespace Npgsql { - /// <summary> - /// C# implementation of the MD5 cryptographic hash function. - /// </summary> + /// <summary> + /// C# implementation of the MD5 cryptographic hash function. + /// </summary> #if USE_VERSION_1_0 internal class MD5CryptoServiceProvider : MD5 { #else - internal sealed class MD5CryptoServiceProvider : MD5 - { + internal sealed class MD5CryptoServiceProvider : MD5 + { #endif - private const int BLOCK_SIZE_BYTES = 64; - private const int HASH_SIZE_BYTES = 16; - private const int HASH_SIZE_BITS = 128; - private readonly uint[] _H; - private uint count; - private readonly byte[] _ProcessingBuffer; // Used to start data when passed less than a block worth. - private int _ProcessingBufferCount; // Counts how much data we have stored that still needs processed. - - /// <summary> - /// Creates a new MD5CryptoServiceProvider. - /// </summary> - public MD5CryptoServiceProvider() - { - _H = new uint[4]; - HashSizeValue = HASH_SIZE_BITS; - _ProcessingBuffer = new byte[BLOCK_SIZE_BYTES]; - - Initialize(); - } - - ~MD5CryptoServiceProvider() - { - Dispose(false); - } - - protected override void Dispose(bool disposing) - { - // nothing to do (managed implementation) - } - - /// <summary> - /// Drives the hashing function. - /// </summary> - /// <param name="rgb">Byte array containing the data to hash.</param> - /// <param name="start">Where in the input buffer to start.</param> - /// <param name="size">Size in bytes of the data in the buffer to hash.</param> - protected override void HashCore(byte[] rgb, int start, int size) - { - int i; - State = 1; - - if (_ProcessingBufferCount != 0) - { - if (size < (BLOCK_SIZE_BYTES - _ProcessingBufferCount)) - { - Buffer.BlockCopy(rgb, start, _ProcessingBuffer, _ProcessingBufferCount, size); - _ProcessingBufferCount += size; - return; - } - else - { - i = (BLOCK_SIZE_BYTES - _ProcessingBufferCount); - Buffer.BlockCopy(rgb, start, _ProcessingBuffer, _ProcessingBufferCount, i); - ProcessBlock(_ProcessingBuffer, 0); - _ProcessingBufferCount = 0; - start += i; - size -= i; - } - } - - for (i = 0; i < size - size%BLOCK_SIZE_BYTES; i += BLOCK_SIZE_BYTES) - { - ProcessBlock(rgb, start + i); - } - - if (size%BLOCK_SIZE_BYTES != 0) - { - Buffer.BlockCopy(rgb, size - size%BLOCK_SIZE_BYTES + start, _ProcessingBuffer, 0, size%BLOCK_SIZE_BYTES); - _ProcessingBufferCount = size%BLOCK_SIZE_BYTES; - } - } - - /// <summary> - /// This finalizes the hash. Takes the data from the chaining variables and returns it. - /// </summary> - protected override byte[] HashFinal() - { - byte[] hash = new byte[16]; - int i, j; - - ProcessFinalBlock(_ProcessingBuffer, 0, _ProcessingBufferCount); - - for (i = 0; i < 4; i++) - { - for (j = 0; j < 4; j++) - { - hash[i*4 + j] = (byte) (_H[i] >> j*8); - } - } - - return hash; - } - - /// <summary> - /// Resets the class after use. Called automatically after hashing is done. - /// </summary> - public override void Initialize() - { - count = 0; - _ProcessingBufferCount = 0; - - _H[0] = 0x67452301; - _H[1] = 0xefcdab89; - _H[2] = 0x98badcfe; - _H[3] = 0x10325476; - } - - /// <summary> - /// This is the meat of the hash function. It is what processes each block one at a time. - /// </summary> - /// <param name="inputBuffer">Byte array to process data from.</param> - /// <param name="inputOffset">Where in the byte array to start processing.</param> - private void ProcessBlock(byte[] inputBuffer, int inputOffset) - { - uint[] buff = new uint[16]; - uint a, b, c, d; - int i; - - count += BLOCK_SIZE_BYTES; - - for (i = 0; i < 16; i++) - { - buff[i] = (inputBuffer[inputOffset + 4*i]) | (((uint) (inputBuffer[inputOffset + 4*i + 1])) << 8) | - (((uint) (inputBuffer[inputOffset + 4*i + 2])) << 16) | - (((uint) (inputBuffer[inputOffset + 4*i + 3])) << 24); - } - - a = _H[0]; - b = _H[1]; - c = _H[2]; - d = _H[3]; - - // This function was unrolled because it seems to be doubling our performance with current compiler/VM. - // Possibly roll up if this changes. - - - // ---- Round 1 -------- - - a += (((c ^ d) & b) ^ d) + (uint) Constants.C0 + buff[0]; - a = (a << 7) | (a >> 25); - a += b; - - d += (((b ^ c) & a) ^ c) + (uint) Constants.C1 + buff[1]; - d = (d << 12) | (d >> 20); - d += a; - - c += (((a ^ b) & d) ^ b) + (uint) Constants.C2 + buff[2]; - c = (c << 17) | (c >> 15); - c += d; - - b += (((d ^ a) & c) ^ a) + (uint) Constants.C3 + buff[3]; - b = (b << 22) | (b >> 10); - b += c; - - a += (((c ^ d) & b) ^ d) + (uint) Constants.C4 + buff[4]; - a = (a << 7) | (a >> 25); - a += b; - - d += (((b ^ c) & a) ^ c) + (uint) Constants.C5 + buff[5]; - d = (d << 12) | (d >> 20); - d += a; - - c += (((a ^ b) & d) ^ b) + (uint) Constants.C6 + buff[6]; - c = (c << 17) | (c >> 15); - c += d; - - b += (((d ^ a) & c) ^ a) + (uint) Constants.C7 + buff[7]; - b = (b << 22) | (b >> 10); - b += c; - - a += (((c ^ d) & b) ^ d) + (uint) Constants.C8 + buff[8]; - a = (a << 7) | (a >> 25); - a += b; - - d += (((b ^ c) & a) ^ c) + (uint) Constants.C9 + buff[9]; - d = (d << 12) | (d >> 20); - d += a; + private const int BLOCK_SIZE_BYTES = 64; + private const int HASH_SIZE_BYTES = 16; + private const int HASH_SIZE_BITS = 128; + [CLSCompliant(false)] private uint[] _H; + [CLSCompliant(false)] private uint count; + private byte[] _ProcessingBuffer; // Used to start data when passed less than a block worth. + private int _ProcessingBufferCount; // Counts how much data we have stored that still needs processed. + + /// <summary> + /// Creates a new MD5CryptoServiceProvider. + /// </summary> + public MD5CryptoServiceProvider () + { + _H = new uint[4]; + HashSizeValue = HASH_SIZE_BITS; + _ProcessingBuffer = new byte[BLOCK_SIZE_BYTES]; + + Initialize(); + } + + ~MD5CryptoServiceProvider () + { + Dispose (false); + } + + protected override void Dispose (bool disposing) + { + // nothing to do (managed implementation) + } + + /// <summary> + /// Drives the hashing function. + /// </summary> + /// <param name="rgb">Byte array containing the data to hash.</param> + /// <param name="start">Where in the input buffer to start.</param> + /// <param name="size">Size in bytes of the data in the buffer to hash.</param> + protected override void HashCore (byte[] rgb, int start, int size) + { + int i; + State = 1; + + if (_ProcessingBufferCount != 0) + { + if (size < (BLOCK_SIZE_BYTES - _ProcessingBufferCount)) + { + System.Buffer.BlockCopy (rgb, start, _ProcessingBuffer, _ProcessingBufferCount, size); + _ProcessingBufferCount += size; + return; + } + else + { + i = (BLOCK_SIZE_BYTES - _ProcessingBufferCount); + System.Buffer.BlockCopy (rgb, start, _ProcessingBuffer, _ProcessingBufferCount, i); + ProcessBlock (_ProcessingBuffer, 0); + _ProcessingBufferCount = 0; + start += i; + size -= i; + } + } + + for (i=0; i<size-size%BLOCK_SIZE_BYTES; i += BLOCK_SIZE_BYTES) + { + ProcessBlock (rgb, start+i); + } + + if (size%BLOCK_SIZE_BYTES != 0) + { + System.Buffer.BlockCopy (rgb, size-size%BLOCK_SIZE_BYTES+start, _ProcessingBuffer, 0, size%BLOCK_SIZE_BYTES); + _ProcessingBufferCount = size%BLOCK_SIZE_BYTES; + } + } + + /// <summary> + /// This finalizes the hash. Takes the data from the chaining variables and returns it. + /// </summary> + protected override byte[] HashFinal () + { + byte[] hash = new byte[16]; + int i, j; + + ProcessFinalBlock(_ProcessingBuffer, 0, _ProcessingBufferCount); + + for (i=0; i<4; i++) + { + for (j=0; j<4; j++) + { + hash[i*4+j] = (byte)(_H[i] >> j*8); + } + } + + return hash; + } + + /// <summary> + /// Resets the class after use. Called automatically after hashing is done. + /// </summary> + public override void Initialize () + { + count = 0; + _ProcessingBufferCount = 0; + + _H[0] = 0x67452301; + _H[1] = 0xefcdab89; + _H[2] = 0x98badcfe; + _H[3] = 0x10325476; + } + + /// <summary> + /// This is the meat of the hash function. It is what processes each block one at a time. + /// </summary> + /// <param name="inputBuffer">Byte array to process data from.</param> + /// <param name="inputOffset">Where in the byte array to start processing.</param> + private void ProcessBlock (byte[] inputBuffer, int inputOffset) + { + uint[] buff = new uint[16]; + uint a, b, c, d; + int i; + + count += BLOCK_SIZE_BYTES; + + for (i=0; i<16; i++) + { + buff[i] = (uint)(inputBuffer[inputOffset+4*i]) + | (((uint)(inputBuffer[inputOffset+4*i+1])) << 8) + | (((uint)(inputBuffer[inputOffset+4*i+2])) << 16) + | (((uint)(inputBuffer[inputOffset+4*i+3])) << 24); + } + + a = _H[0]; + b = _H[1]; + c = _H[2]; + d = _H[3]; + + // This function was unrolled because it seems to be doubling our performance with current compiler/VM. + // Possibly roll up if this changes. + + + // ---- Round 1 -------- + + a += (((c ^ d) & b) ^ d) + (uint) Constants.C0 + buff [0]; + a = (a << 7) | (a >> 25); + a += b; + + d += (((b ^ c) & a) ^ c) + (uint) Constants.C1 + buff [1]; + d = (d << 12) | (d >> 20); + d += a; + + c += (((a ^ b) & d) ^ b) + (uint) Constants.C2 + buff [2]; + c = (c << 17) | (c >> 15); + c += d; + + b += (((d ^ a) & c) ^ a) + (uint) Constants.C3 + buff [3]; + b = (b << 22) | (b >> 10); + b += c; + + a += (((c ^ d) & b) ^ d) + (uint) Constants.C4 + buff [4]; + a = (a << 7) | (a >> 25); + a += b; + + d += (((b ^ c) & a) ^ c) + (uint) Constants.C5 + buff [5]; + d = (d << 12) | (d >> 20); + d += a; + + c += (((a ^ b) & d) ^ b) + (uint) Constants.C6 + buff [6]; + c = (c << 17) | (c >> 15); + c += d; + + b += (((d ^ a) & c) ^ a) + (uint) Constants.C7 + buff [7]; + b = (b << 22) | (b >> 10); + b += c; + + a += (((c ^ d) & b) ^ d) + (uint) Constants.C8 + buff [8]; + a = (a << 7) | (a >> 25); + a += b; + + d += (((b ^ c) & a) ^ c) + (uint) Constants.C9 + buff [9]; + d = (d << 12) | (d >> 20); + d += a; - c += (((a ^ b) & d) ^ b) + (uint) Constants.C10 + buff[10]; - c = (c << 17) | (c >> 15); - c += d; + c += (((a ^ b) & d) ^ b) + (uint) Constants.C10 + buff [10]; + c = (c << 17) | (c >> 15); + c += d; + + b += (((d ^ a) & c) ^ a) + (uint) Constants.C11 + buff [11]; + b = (b << 22) | (b >> 10); + b += c; - b += (((d ^ a) & c) ^ a) + (uint) Constants.C11 + buff[11]; - b = (b << 22) | (b >> 10); - b += c; + a += (((c ^ d) & b) ^ d) + (uint) Constants.C12 + buff [12]; + a = (a << 7) | (a >> 25); + a += b; - a += (((c ^ d) & b) ^ d) + (uint) Constants.C12 + buff[12]; - a = (a << 7) | (a >> 25); - a += b; + d += (((b ^ c) & a) ^ c) + (uint) Constants.C13 + buff [13]; + d = (d << 12) | (d >> 20); + d += a; - d += (((b ^ c) & a) ^ c) + (uint) Constants.C13 + buff[13]; - d = (d << 12) | (d >> 20); - d += a; + c += (((a ^ b) & d) ^ b) + (uint) Constants.C14 + buff [14]; + c = (c << 17) | (c >> 15); + c += d; - c += (((a ^ b) & d) ^ b) + (uint) Constants.C14 + buff[14]; - c = (c << 17) | (c >> 15); - c += d; + b += (((d ^ a) & c) ^ a) + (uint) Constants.C15 + buff [15]; + b = (b << 22) | (b >> 10); + b += c; - b += (((d ^ a) & c) ^ a) + (uint) Constants.C15 + buff[15]; - b = (b << 22) | (b >> 10); - b += c; + // ---- Round 2 -------- - // ---- Round 2 -------- + a += (((b ^ c) & d) ^ c) + (uint) Constants.C16 + buff [1]; + a = (a << 5) | (a >> 27); + a += b; - a += (((b ^ c) & d) ^ c) + (uint) Constants.C16 + buff[1]; - a = (a << 5) | (a >> 27); - a += b; + d += (((a ^ b) & c) ^ b) + (uint) Constants.C17 + buff [6]; + d = (d << 9) | (d >> 23); + d += a; - d += (((a ^ b) & c) ^ b) + (uint) Constants.C17 + buff[6]; - d = (d << 9) | (d >> 23); - d += a; + c += (((d ^ a) & b) ^ a) + (uint) Constants.C18 + buff [11]; + c = (c << 14) | (c >> 18); + c += d; - c += (((d ^ a) & b) ^ a) + (uint) Constants.C18 + buff[11]; - c = (c << 14) | (c >> 18); - c += d; + b += (((c ^ d) & a) ^ d) + (uint) Constants.C19 + buff [0]; + b = (b << 20) | (b >> 12); + b += c; - b += (((c ^ d) & a) ^ d) + (uint) Constants.C19 + buff[0]; - b = (b << 20) | (b >> 12); - b += c; + a += (((b ^ c) & d) ^ c) + (uint) Constants.C20 + buff [5]; + a = (a << 5) | (a >> 27); + a += b; - a += (((b ^ c) & d) ^ c) + (uint) Constants.C20 + buff[5]; - a = (a << 5) | (a >> 27); - a += b; + d += (((a ^ b) & c) ^ b) + (uint) Constants.C21 + buff [10]; + d = (d << 9) | (d >> 23); + d += a; - d += (((a ^ b) & c) ^ b) + (uint) Constants.C21 + buff[10]; - d = (d << 9) | (d >> 23); - d += a; + c += (((d ^ a) & b) ^ a) + (uint) Constants.C22 + buff [15]; + c = (c << 14) | (c >> 18); + c += d; - c += (((d ^ a) & b) ^ a) + (uint) Constants.C22 + buff[15]; - c = (c << 14) | (c >> 18); - c += d; + b += (((c ^ d) & a) ^ d) + (uint) Constants.C23 + buff [4]; + b = (b << 20) | (b >> 12); + b += c; - b += (((c ^ d) & a) ^ d) + (uint) Constants.C23 + buff[4]; - b = (b << 20) | (b >> 12); - b += c; + a += (((b ^ c) & d) ^ c) + (uint) Constants.C24 + buff [9]; + a = (a << 5) | (a >> 27); + a += b; - a += (((b ^ c) & d) ^ c) + (uint) Constants.C24 + buff[9]; - a = (a << 5) | (a >> 27); - a += b; + d += (((a ^ b) & c) ^ b) + (uint) Constants.C25 + buff [14]; + d = (d << 9) | (d >> 23); + d += a; - d += (((a ^ b) & c) ^ b) + (uint) Constants.C25 + buff[14]; - d = (d << 9) | (d >> 23); - d += a; + c += (((d ^ a) & b) ^ a) + (uint) Constants.C26 + buff [3]; + c = (c << 14) | (c >> 18); + c += d; - c += (((d ^ a) & b) ^ a) + (uint) Constants.C26 + buff[3]; - c = (c << 14) | (c >> 18); - c += d; + b += (((c ^ d) & a) ^ d) + (uint) Constants.C27 + buff [8]; + b = (b << 20) | (b >> 12); + b += c; - b += (((c ^ d) & a) ^ d) + (uint) Constants.C27 + buff[8]; - b = (b << 20) | (b >> 12); - b += c; + a += (((b ^ c) & d) ^ c) + (uint) Constants.C28 + buff [13]; + a = (a << 5) | (a >> 27); + a += b; - a += (((b ^ c) & d) ^ c) + (uint) Constants.C28 + buff[13]; - a = (a << 5) | (a >> 27); - a += b; + d += (((a ^ b) & c) ^ b) + (uint) Constants.C29 + buff [2]; + d = (d << 9) | (d >> 23); + d += a; - d += (((a ^ b) & c) ^ b) + (uint) Constants.C29 + buff[2]; - d = (d << 9) | (d >> 23); - d += a; + c += (((d ^ a) & b) ^ a) + (uint) Constants.C30 + buff [7]; + c = (c << 14) | (c >> 18); + c += d; - c += (((d ^ a) & b) ^ a) + (uint) Constants.C30 + buff[7]; - c = (c << 14) | (c >> 18); - c += d; + b += (((c ^ d) & a) ^ d) + (uint) Constants.C31 + buff [12]; + b = (b << 20) | (b >> 12); + b += c; - b += (((c ^ d) & a) ^ d) + (uint) Constants.C31 + buff[12]; - b = (b << 20) | (b >> 12); - b += c; + // ---- Round 3 -------- - // ---- Round 3 -------- + a += (b ^ c ^ d) + (uint) Constants.C32 + buff [5]; + a = (a << 4) | (a >> 28); + a += b; - a += (b ^ c ^ d) + (uint) Constants.C32 + buff[5]; - a = (a << 4) | (a >> 28); - a += b; + d += (a ^ b ^ c) + (uint) Constants.C33 + buff [8]; + d = (d << 11) | (d >> 21); + d += a; - d += (a ^ b ^ c) + (uint) Constants.C33 + buff[8]; - d = (d << 11) | (d >> 21); - d += a; + c += (d ^ a ^ b) + (uint) Constants.C34 + buff [11]; + c = (c << 16) | (c >> 16); + c += d; - c += (d ^ a ^ b) + (uint) Constants.C34 + buff[11]; - c = (c << 16) | (c >> 16); - c += d; + b += (c ^ d ^ a) + (uint) Constants.C35 + buff [14]; + b = (b << 23) | (b >> 9); + b += c; - b += (c ^ d ^ a) + (uint) Constants.C35 + buff[14]; - b = (b << 23) | (b >> 9); - b += c; + a += (b ^ c ^ d) + (uint) Constants.C36 + buff [1]; + a = (a << 4) | (a >> 28); + a += b; - a += (b ^ c ^ d) + (uint) Constants.C36 + buff[1]; - a = (a << 4) | (a >> 28); - a += b; + d += (a ^ b ^ c) + (uint) Constants.C37 + buff [4]; + d = (d << 11) | (d >> 21); + d += a; - d += (a ^ b ^ c) + (uint) Constants.C37 + buff[4]; - d = (d << 11) | (d >> 21); - d += a; + c += (d ^ a ^ b) + (uint) Constants.C38 + buff [7]; + c = (c << 16) | (c >> 16); + c += d; - c += (d ^ a ^ b) + (uint) Constants.C38 + buff[7]; - c = (c << 16) | (c >> 16); - c += d; + b += (c ^ d ^ a) + (uint) Constants.C39 + buff [10]; + b = (b << 23) | (b >> 9); + b += c; - b += (c ^ d ^ a) + (uint) Constants.C39 + buff[10]; - b = (b << 23) | (b >> 9); - b += c; + a += (b ^ c ^ d) + (uint) Constants.C40 + buff [13]; + a = (a << 4) | (a >> 28); + a += b; - a += (b ^ c ^ d) + (uint) Constants.C40 + buff[13]; - a = (a << 4) | (a >> 28); - a += b; + d += (a ^ b ^ c) + (uint) Constants.C41 + buff [0]; + d = (d << 11) | (d >> 21); + d += a; - d += (a ^ b ^ c) + (uint) Constants.C41 + buff[0]; - d = (d << 11) | (d >> 21); - d += a; + c += (d ^ a ^ b) + (uint) Constants.C42 + buff [3]; + c = (c << 16) | (c >> 16); + c += d; - c += (d ^ a ^ b) + (uint) Constants.C42 + buff[3]; - c = (c << 16) | (c >> 16); - c += d; + b += (c ^ d ^ a) + (uint) Constants.C43 + buff [6]; + b = (b << 23) | (b >> 9); + b += c; - b += (c ^ d ^ a) + (uint) Constants.C43 + buff[6]; - b = (b << 23) | (b >> 9); - b += c; + a += (b ^ c ^ d) + (uint) Constants.C44 + buff [9]; + a = (a << 4) | (a >> 28); + a += b; - a += (b ^ c ^ d) + (uint) Constants.C44 + buff[9]; - a = (a << 4) | (a >> 28); - a += b; + d += (a ^ b ^ c) + (uint) Constants.C45 + buff [12]; + d = (d << 11) | (d >> 21); + d += a; - d += (a ^ b ^ c) + (uint) Constants.C45 + buff[12]; - d = (d << 11) | (d >> 21); - d += a; + c += (d ^ a ^ b) + (uint) Constants.C46 + buff [15]; + c = (c << 16) | (c >> 16); + c += d; - c += (d ^ a ^ b) + (uint) Constants.C46 + buff[15]; - c = (c << 16) | (c >> 16); - c += d; + b += (c ^ d ^ a) + (uint) Constants.C47 + buff [2]; + b = (b << 23) | (b >> 9); + b += c; - b += (c ^ d ^ a) + (uint) Constants.C47 + buff[2]; - b = (b << 23) | (b >> 9); - b += c; + // ---- Round 4 -------- - // ---- Round 4 -------- + a += (((~d) | b) ^ c) + (uint) Constants.C48 + buff [0]; + a = (a << 6) | (a >> 26); + a += b; - a += (((~d) | b) ^ c) + (uint) Constants.C48 + buff[0]; - a = (a << 6) | (a >> 26); - a += b; + d += (((~c) | a) ^ b) + (uint) Constants.C49 + buff [7]; + d = (d << 10) | (d >> 22); + d += a; - d += (((~c) | a) ^ b) + (uint) Constants.C49 + buff[7]; - d = (d << 10) | (d >> 22); - d += a; + c += (((~b) | d) ^ a) + (uint) Constants.C50 + buff [14]; + c = (c << 15) | (c >> 17); + c += d; - c += (((~b) | d) ^ a) + (uint) Constants.C50 + buff[14]; - c = (c << 15) | (c >> 17); - c += d; + b += (((~a) | c) ^ d) + (uint) Constants.C51 + buff [5]; + b = (b << 21) | (b >> 11); + b += c; - b += (((~a) | c) ^ d) + (uint) Constants.C51 + buff[5]; - b = (b << 21) | (b >> 11); - b += c; + a += (((~d) | b) ^ c) + (uint) Constants.C52 + buff [12]; + a = (a << 6) | (a >> 26); + a += b; - a += (((~d) | b) ^ c) + (uint) Constants.C52 + buff[12]; - a = (a << 6) | (a >> 26); - a += b; + d += (((~c) | a) ^ b) + (uint) Constants.C53 + buff [3]; + d = (d << 10) | (d >> 22); + d += a; - d += (((~c) | a) ^ b) + (uint) Constants.C53 + buff[3]; - d = (d << 10) | (d >> 22); - d += a; + c += (((~b) | d) ^ a) + (uint) Constants.C54 + buff [10]; + c = (c << 15) | (c >> 17); + c += d; - c += (((~b) | d) ^ a) + (uint) Constants.C54 + buff[10]; - c = (c << 15) | (c >> 17); - c += d; + b += (((~a) | c) ^ d) + (uint) Constants.C55 + buff [1]; + b = (b << 21) | (b >> 11); + b += c; + + a += (((~d) | b) ^ c) + (uint) Constants.C56 + buff [8]; + a = (a << 6) | (a >> 26); + a += b; + + d += (((~c) | a) ^ b) + (uint) Constants.C57 + buff [15]; + d = (d << 10) | (d >> 22); + d += a; + + c += (((~b) | d) ^ a) + (uint) Constants.C58 + buff [6]; + c = (c << 15) | (c >> 17); + c += d; + + b += (((~a) | c) ^ d) + (uint) Constants.C59 + buff [13]; + b = (b << 21) | (b >> 11); + b += c; + + a += (((~d) | b) ^ c) + (uint) Constants.C60 + buff [4]; + a = (a << 6) | (a >> 26); + a += b; + + d += (((~c) | a) ^ b) + (uint) Constants.C61 + buff [11]; + d = (d << 10) | (d >> 22); + d += a; + + c += (((~b) | d) ^ a) + (uint) Constants.C62 + buff [2]; + c = (c << 15) | (c >> 17); + c += d; + + b += (((~a) | c) ^ d) + (uint) Constants.C63 + buff [9]; + b = (b << 21) | (b >> 11); + b += c; + + + _H[0] += a; + _H[1] += b; + _H[2] += c; + _H[3] += d; + } + + /// <summary> + /// Pads and then processes the final block. + /// </summary> + /// <param name="inputBuffer">Buffer to grab data from.</param> + /// <param name="inputOffset">Position in buffer in bytes to get data from.</param> + /// <param name="inputCount">How much data in bytes in the buffer to use.</param> + private void ProcessFinalBlock (byte[] inputBuffer, int inputOffset, int inputCount) + { + byte[] fooBuffer; + int paddingSize; + int i; + uint size; + + paddingSize = (int)(56 - (inputCount + count) % BLOCK_SIZE_BYTES); + + if (paddingSize < 1) + paddingSize += BLOCK_SIZE_BYTES; + + + fooBuffer = new byte[inputCount+paddingSize+8]; + + for (i=0; i<inputCount; i++) + { + fooBuffer[i] = inputBuffer[i+inputOffset]; + } + + fooBuffer[inputCount] = 0x80; + for (i=inputCount+1; i<inputCount+paddingSize; i++) + { + fooBuffer[i] = 0x00; + } + + size = (uint)(count+inputCount); + size *= 8; + fooBuffer[inputCount+paddingSize] = (byte)((size) >> 0); + fooBuffer[inputCount+paddingSize+1] = (byte)((size) >> 8); + fooBuffer[inputCount+paddingSize+2] = (byte)((size) >> 16); + fooBuffer[inputCount+paddingSize+3] = (byte)((size) >> 24); + + fooBuffer[inputCount+paddingSize+4] = 0x00; + fooBuffer[inputCount+paddingSize+5] = 0x00; + fooBuffer[inputCount+paddingSize+6] = 0x00; + fooBuffer[inputCount+paddingSize+7] = 0x00; + + ProcessBlock(fooBuffer, 0); + + if (inputCount+paddingSize+8 == 128) + { + ProcessBlock(fooBuffer, 64); + } + } + + private enum Constants : + uint { + C0 = 0xd76aa478, C1 = 0xe8c7b756, C2 = 0x242070db, + C3 = 0xc1bdceee, C4 = 0xf57c0faf, C5 = 0x4787c62a, + C6 = 0xa8304613, C7 = 0xfd469501, C8 = 0x698098d8, + C9 = 0x8b44f7af,C10 = 0xffff5bb1,C11 = 0x895cd7be, + C12 = 0x6b901122,C13 = 0xfd987193,C14 = 0xa679438e, + C15 = 0x49b40821,C16 = 0xf61e2562,C17 = 0xc040b340, + C18 = 0x265e5a51,C19 = 0xe9b6c7aa,C20 = 0xd62f105d, + C21 = 0x02441453,C22 = 0xd8a1e681,C23 = 0xe7d3fbc8, + C24 = 0x21e1cde6,C25 = 0xc33707d6,C26 = 0xf4d50d87, + C27 = 0x455a14ed,C28 = 0xa9e3e905,C29 = 0xfcefa3f8, + C30 = 0x676f02d9,C31 = 0x8d2a4c8a,C32 = 0xfffa3942, + C33 = 0x8771f681,C34 = 0x6d9d6122,C35 = 0xfde5380c, + C36 = 0xa4beea44,C37 = 0x4bdecfa9,C38 = 0xf6bb4b60, + C39 = 0xbebfbc70,C40 = 0x289b7ec6,C41 = 0xeaa127fa, + C42 = 0xd4ef3085,C43 = 0x04881d05,C44 = 0xd9d4d039, + C45 = 0xe6db99e5,C46 = 0x1fa27cf8,C47 = 0xc4ac5665, + C48 = 0xf4292244,C49 = 0x432aff97,C50 = 0xab9423a7, + C51 = 0xfc93a039,C52 = 0x655b59c3,C53 = 0x8f0ccc92, + C54 = 0xffeff47d,C55 = 0x85845dd1,C56 = 0x6fa87e4f, + C57 = 0xfe2ce6e0,C58 = 0xa3014314,C59 = 0x4e0811a1, + C60 = 0xf7537e82,C61 = 0xbd3af235,C62 = 0x2ad7d2bb, + C63 = 0xeb86d391 + } + + } +} - b += (((~a) | c) ^ d) + (uint) Constants.C55 + buff[1]; - b = (b << 21) | (b >> 11); - b += c; - - a += (((~d) | b) ^ c) + (uint) Constants.C56 + buff[8]; - a = (a << 6) | (a >> 26); - a += b; - - d += (((~c) | a) ^ b) + (uint) Constants.C57 + buff[15]; - d = (d << 10) | (d >> 22); - d += a; - - c += (((~b) | d) ^ a) + (uint) Constants.C58 + buff[6]; - c = (c << 15) | (c >> 17); - c += d; - - b += (((~a) | c) ^ d) + (uint) Constants.C59 + buff[13]; - b = (b << 21) | (b >> 11); - b += c; - - a += (((~d) | b) ^ c) + (uint) Constants.C60 + buff[4]; - a = (a << 6) | (a >> 26); - a += b; - - d += (((~c) | a) ^ b) + (uint) Constants.C61 + buff[11]; - d = (d << 10) | (d >> 22); - d += a; - - c += (((~b) | d) ^ a) + (uint) Constants.C62 + buff[2]; - c = (c << 15) | (c >> 17); - c += d; - - b += (((~a) | c) ^ d) + (uint) Constants.C63 + buff[9]; - b = (b << 21) | (b >> 11); - b += c; - - - _H[0] += a; - _H[1] += b; - _H[2] += c; - _H[3] += d; - } - - /// <summary> - /// Pads and then processes the final block. - /// </summary> - /// <param name="inputBuffer">Buffer to grab data from.</param> - /// <param name="inputOffset">Position in buffer in bytes to get data from.</param> - /// <param name="inputCount">How much data in bytes in the buffer to use.</param> - private void ProcessFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) - { - byte[] fooBuffer; - int paddingSize; - int i; - uint size; - - paddingSize = (int) (56 - (inputCount + count)%BLOCK_SIZE_BYTES); - - if (paddingSize < 1) - { - paddingSize += BLOCK_SIZE_BYTES; - } - - - fooBuffer = new byte[inputCount + paddingSize + 8]; - - for (i = 0; i < inputCount; i++) - { - fooBuffer[i] = inputBuffer[i + inputOffset]; - } - - fooBuffer[inputCount] = 0x80; - for (i = inputCount + 1; i < inputCount + paddingSize; i++) - { - fooBuffer[i] = 0x00; - } - - size = (uint) (count + inputCount); - size *= 8; - fooBuffer[inputCount + paddingSize] = (byte) ((size) >> 0); - fooBuffer[inputCount + paddingSize + 1] = (byte) ((size) >> 8); - fooBuffer[inputCount + paddingSize + 2] = (byte) ((size) >> 16); - fooBuffer[inputCount + paddingSize + 3] = (byte) ((size) >> 24); - - fooBuffer[inputCount + paddingSize + 4] = 0x00; - fooBuffer[inputCount + paddingSize + 5] = 0x00; - fooBuffer[inputCount + paddingSize + 6] = 0x00; - fooBuffer[inputCount + paddingSize + 7] = 0x00; - - ProcessBlock(fooBuffer, 0); - - if (inputCount + paddingSize + 8 == 128) - { - ProcessBlock(fooBuffer, 64); - } - } - - private enum Constants : - uint - { - C0 = 0xd76aa478, - C1 = 0xe8c7b756, - C2 = 0x242070db, - C3 = 0xc1bdceee, - C4 = 0xf57c0faf, - C5 = 0x4787c62a, - C6 = 0xa8304613, - C7 = 0xfd469501, - C8 = 0x698098d8, - C9 = 0x8b44f7af, - C10 = 0xffff5bb1, - C11 = 0x895cd7be, - C12 = 0x6b901122, - C13 = 0xfd987193, - C14 = 0xa679438e, - C15 = 0x49b40821, - C16 = 0xf61e2562, - C17 = 0xc040b340, - C18 = 0x265e5a51, - C19 = 0xe9b6c7aa, - C20 = 0xd62f105d, - C21 = 0x02441453, - C22 = 0xd8a1e681, - C23 = 0xe7d3fbc8, - C24 = 0x21e1cde6, - C25 = 0xc33707d6, - C26 = 0xf4d50d87, - C27 = 0x455a14ed, - C28 = 0xa9e3e905, - C29 = 0xfcefa3f8, - C30 = 0x676f02d9, - C31 = 0x8d2a4c8a, - C32 = 0xfffa3942, - C33 = 0x8771f681, - C34 = 0x6d9d6122, - C35 = 0xfde5380c, - C36 = 0xa4beea44, - C37 = 0x4bdecfa9, - C38 = 0xf6bb4b60, - C39 = 0xbebfbc70, - C40 = 0x289b7ec6, - C41 = 0xeaa127fa, - C42 = 0xd4ef3085, - C43 = 0x04881d05, - C44 = 0xd9d4d039, - C45 = 0xe6db99e5, - C46 = 0x1fa27cf8, - C47 = 0xc4ac5665, - C48 = 0xf4292244, - C49 = 0x432aff97, - C50 = 0xab9423a7, - C51 = 0xfc93a039, - C52 = 0x655b59c3, - C53 = 0x8f0ccc92, - C54 = 0xffeff47d, - C55 = 0x85845dd1, - C56 = 0x6fa87e4f, - C57 = 0xfe2ce6e0, - C58 = 0xa3014314, - C59 = 0x4e0811a1, - C60 = 0xf7537e82, - C61 = 0xbd3af235, - C62 = 0x2ad7d2bb, - C63 = 0xeb86d391 - } - } -}
\ No newline at end of file diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlAsciiRow.cs b/mcs/class/Npgsql/Npgsql/NpgsqlAsciiRow.cs index d8f10f8aef3..995e4881550 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlAsciiRow.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlAsciiRow.cs @@ -9,232 +9,216 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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.IO; +using System.Text; +using System.Net; + using NpgsqlTypes; namespace Npgsql { - /// <summary> - /// Implements <see cref="RowReader"/> for version 3 of the protocol. - /// </summary> - internal sealed class StringRowReaderV3 : RowReader - { - private readonly int _messageSize; - private int? _nextFieldSize = null; - - public StringRowReaderV3(NpgsqlRowDescription rowDesc, Stream inputStream) - : base(rowDesc, inputStream) - { - _messageSize = PGUtil.ReadInt32(inputStream); - if (PGUtil.ReadInt16(inputStream) != rowDesc.NumFields) - { - throw new DataException(); - } - } - protected override object ReadNext() - { - int fieldSize = GetThisFieldCount(); - if (fieldSize >= _messageSize) - { - AbandonShip(); - } - _nextFieldSize = null; + /// <summary> + /// This class represents the AsciiRow (version 2) and DataRow (version 3+) + /// message sent from the PostgreSQL server. + /// </summary> + internal sealed class NpgsqlAsciiRow : NpgsqlRow + { + // Logging related values + private static readonly String CLASSNAME = "NpgsqlAsciiRow"; - // Check if this field is null - if (fieldSize == -1) // Null value - { - return DBNull.Value; - } + private readonly Int16 READ_BUFFER_SIZE = 300; //[FIXME] Is this enough?? + private byte[] _inputBuffer; + private char[] _chars; - NpgsqlRowDescription.FieldData field_descr = FieldData; + public NpgsqlAsciiRow(NpgsqlRowDescription rowDesc, ProtocolVersion protocolVersion, byte[] inputBuffer, char[] chars) + : base(rowDesc, protocolVersion) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME); + _inputBuffer = inputBuffer; + _chars = chars; + } - byte[] buffer = new byte[fieldSize]; - PGUtil.CheckedStreamRead(Stream, buffer, 0, fieldSize); + public override void ReadFromStream(Stream inputStream, Encoding encoding) + { + switch (protocol_version) + { + case ProtocolVersion.Version2 : + ReadFromStream_Ver_2(inputStream, encoding); + break; - try - { - if (field_descr.FormatCode == FormatCode.Text) - { - char[] charBuffer = new char[UTF8Encoding.GetCharCount(buffer, 0, buffer.Length)]; - UTF8Encoding.GetChars(buffer, 0, buffer.Length, charBuffer, 0); - return - NpgsqlTypesHelper.ConvertBackendStringToSystemType(field_descr.TypeInfo, new string(charBuffer), - field_descr.TypeSize, field_descr.TypeModifier); - } - else - { - return - NpgsqlTypesHelper.ConvertBackendBytesToSystemType(field_descr.TypeInfo, buffer, fieldSize, - field_descr.TypeModifier); - } - } - catch (InvalidCastException ice) - { - return ice; - } - catch (Exception ex) - { - return new InvalidCastException(ex.Message, ex); - } - } + case ProtocolVersion.Version3 : + ReadFromStream_Ver_3(inputStream, encoding); + break; - private void AbandonShip() - { - //field size will always be smaller than message size - //but if we fall out of sync with the stream due to an error then we will probably hit - //such a situation soon as bytes from elsewhere in the stream get interpreted as a size. - //so if we see this happens, we know we've lost the stream - our best option is to just give up on it, - //and have the connector recovered later. - try - { - Stream.WriteByte((byte) FrontEndMessageCode.Termination); - PGUtil.WriteInt32(Stream, 4); - Stream.Flush(); - } - catch - { - } - try - { - Stream.Close(); - } - catch - { - } - throw new DataException(); - } + } + } - protected override void SkipOne() - { - int fieldSize = GetThisFieldCount(); - if (fieldSize >= _messageSize) - { - AbandonShip(); - } - _nextFieldSize = null; - PGUtil.EatStreamBytes(Stream, fieldSize); - } + private void ReadFromStream_Ver_2(Stream inputStream, Encoding encoding) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_2"); - public override bool IsNextDBNull - { - get { return GetThisFieldCount() == -1; } - } + Byte[] null_map_array = new Byte[(row_desc.NumFields + 7)/8]; - private int GetThisFieldCount() - { - return (_nextFieldSize = _nextFieldSize ?? PGUtil.ReadInt32(Stream)).Value; - } + Array.Clear(null_map_array, 0, null_map_array.Length); - protected override int GetNextFieldCount() - { - int ret = GetThisFieldCount(); - _nextFieldSize = null; - return ret; - } - } - - /// <summary> - /// Implements <see cref="RowReader"/> for version 2 of the protocol. - /// </summary> - internal sealed class StringRowReaderV2 : RowReader - { - /// <summary> - /// Encapsulates the null mapping bytes sent at the start of a version 2 - /// datarow message, and the process of identifying the nullity of the data - /// at a particular index - /// </summary> - private sealed class NullMap - { - private readonly byte[] _map; - public NullMap(NpgsqlRowDescription desc, Stream inputStream) - { - _map = new byte[(desc.NumFields + 7)/8]; - PGUtil.CheckedStreamRead(inputStream, _map, 0, _map.Length); - } + // Decoders used to get decoded chars when using unicode like encodings which may have chars crossing the byte buffer bounds. - public bool IsNull(int index) - { - // Get the byte that holds the bit index position. - // Then check the bit that in MSB order corresponds - // to the index position. - return (_map[index/8] & (0x80 >> (index%8))) == 0; - } - } + Decoder decoder = encoding.GetDecoder(); - private readonly NullMap _nullMap; + // Read the null fields bitmap. + PGUtil.CheckedStreamRead(inputStream, null_map_array, 0, null_map_array.Length ); - public StringRowReaderV2(NpgsqlRowDescription rowDesc, Stream inputStream) - : base(rowDesc, inputStream) - { - _nullMap = new NullMap(rowDesc, inputStream); - } + // Get the data. + for (Int16 field_count = 0; field_count < row_desc.NumFields; field_count++) + { + // Check if this field is null + if (IsBackendNull(null_map_array, field_count)) + { + data.Add(DBNull.Value); + continue; + } - protected override object ReadNext() - { - if (_nullMap.IsNull(CurrentField)) - { - return DBNull.Value; - } + // Read the first data of the first row. - NpgsqlRowDescription.FieldData field_descr = FieldData; - Int32 field_value_size = PGUtil.ReadInt32(Stream) - 4; - byte[] buffer = new byte[field_value_size]; - PGUtil.CheckedStreamRead(Stream, buffer, 0, field_value_size); - char[] charBuffer = new char[UTF8Encoding.GetCharCount(buffer, 0, buffer.Length)]; - UTF8Encoding.GetChars(buffer, 0, buffer.Length, charBuffer, 0); - try - { - return - NpgsqlTypesHelper.ConvertBackendStringToSystemType(field_descr.TypeInfo, new string(charBuffer), - field_descr.TypeSize, field_descr.TypeModifier); - } - catch (InvalidCastException ice) - { - return ice; - } - catch (Exception ex) + PGUtil.CheckedStreamRead(inputStream, _inputBuffer, 0, 4); + + NpgsqlRowDescriptionFieldData field_descr = row_desc[field_count]; + Int32 field_value_size = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(_inputBuffer, 0)); + field_value_size -= 4; + + string result = ReadStringFromStream(inputStream, field_value_size, decoder); + // Add them to the AsciiRow data. + data.Add(NpgsqlTypesHelper.ConvertBackendStringToSystemType(field_descr.type_info, result, field_descr.type_size, field_descr.type_modifier)); + + } + } + + private void ReadFromStream_Ver_3(Stream inputStream, Encoding encoding) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_3"); + + PGUtil.ReadInt32(inputStream, _inputBuffer); + Int16 numCols = PGUtil.ReadInt16(inputStream, _inputBuffer); + + Decoder decoder = encoding.GetDecoder(); + + for (Int16 field_count = 0; field_count < numCols; field_count++) { - return new InvalidCastException(ex.Message, ex); - } - } + Int32 field_value_size = PGUtil.ReadInt32(inputStream, _inputBuffer); + + // Check if this field is null + if (field_value_size == -1) // Null value + { + data.Add(DBNull.Value); + continue; + } - public override bool IsNextDBNull + NpgsqlRowDescriptionFieldData field_descr = row_desc[field_count]; + + if (row_desc[field_count].format_code == FormatCode.Text) + { + string result = ReadStringFromStream(inputStream, field_value_size, decoder); + // Add them to the AsciiRow data. + data.Add(NpgsqlTypesHelper.ConvertBackendStringToSystemType(field_descr.type_info, result, field_descr.type_size, field_descr.type_modifier)); + } + else + { + 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)); + } + } + } + + // Using the given null field map (provided by the backend), + // determine if the given field index is mapped null by the backend. + // We only need to do this for version 2 protocol. + private static Boolean IsBackendNull(Byte[] null_map_array, Int32 index) + { + // Get the byte that holds the bit index position. + Byte test_byte = null_map_array[index/8]; + + // Now, check if index bit is set. + // To do this, get its position in the byte, shift to + // MSB and test it with the byte 10000000. + return (((test_byte << (index%8)) & 0x80) == 0); + } + + private int GetCharsFromStream(Stream inputStream, int count, Decoder decoder, char[] chars) { - get { return _nullMap.IsNull(CurrentField + 1); } + // Now, read just the field value. + PGUtil.CheckedStreamRead(inputStream, _inputBuffer, 0, count); + int charCount = decoder.GetCharCount(_inputBuffer, 0, count); + decoder.GetChars(_inputBuffer, 0, count, chars, 0); + return charCount; } - protected override void SkipOne() + private string ReadStringFromStream(Stream inputStream, int field_value_size, Decoder decoder) { - if (!_nullMap.IsNull(CurrentField)) + int bytes_left = field_value_size; + int charCount; + + if (field_value_size > _inputBuffer.Length) { - PGUtil.EatStreamBytes(Stream, PGUtil.ReadInt32(Stream) - 4); + StringBuilder result = new StringBuilder(); + + while (bytes_left > READ_BUFFER_SIZE) + { + charCount = GetCharsFromStream(inputStream, READ_BUFFER_SIZE, decoder, _chars); + result.Append(_chars, 0,charCount); + bytes_left -= READ_BUFFER_SIZE; + } + + charCount = GetCharsFromStream(inputStream, bytes_left, decoder, _chars); + result.Append(_chars, 0,charCount); + + return result.ToString(); } - } + else + { + charCount = GetCharsFromStream(inputStream, bytes_left, decoder, _chars); - protected override int GetNextFieldCount() - { - return _nullMap.IsNull(CurrentField) ? -1 : PGUtil.ReadInt32(Stream) - 4; + return new String(_chars, 0,charCount); + } } - } -}
\ No newline at end of file + + 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; + } + } +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlBackEndKeyData.cs b/mcs/class/Npgsql/Npgsql/NpgsqlBackEndKeyData.cs index a684695b170..693c5c88de0 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlBackEndKeyData.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlBackEndKeyData.cs @@ -10,48 +10,84 @@ // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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; +using System.Net; namespace Npgsql { - /// <summary> - /// This class represents a BackEndKeyData message received - /// from PostgreSQL - /// </summary> - internal sealed class NpgsqlBackEndKeyData - { - public readonly int ProcessID; - public readonly int SecretKey; - - public NpgsqlBackEndKeyData(ProtocolVersion protocolVersion, Stream stream) - { - // Read the BackendKeyData message contents. Two Int32 integers = 8 Bytes. - // For protocol version 3.0 they are three integers. The first one is just the size of message - // so, just read it. - if (protocolVersion >= ProtocolVersion.Version3) - { - PGUtil.EatStreamBytes(stream, 4); - } - ProcessID = PGUtil.ReadInt32(stream); - SecretKey = PGUtil.ReadInt32(stream); - } - } -}
\ No newline at end of file + /// <summary> + /// This class represents a BackEndKeyData message received + /// from PostgreSQL + /// </summary> + internal sealed class NpgsqlBackEndKeyData + { + // Logging related values + private static readonly String CLASSNAME = "NpgsqlBackEndKeyData"; + + private Int32 _processId; + private Int32 _secretKey; + + private ProtocolVersion _protocolVersion; + + public NpgsqlBackEndKeyData(ProtocolVersion protocolVersion) + { + _protocolVersion = protocolVersion; + _processId = -1; + _secretKey = -1; + } + + + public void ReadFromStream(Stream inputStream) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME); + + Byte[] inputBuffer = new Byte[8]; + + // Read the BackendKeyData message contents. Two Int32 integers = 8 Bytes. + // For protocol version 3.0 they are three integers. The first one is just the size of message + // so, just read it. + if (_protocolVersion >= ProtocolVersion.Version3) + inputStream.Read(inputBuffer, 0, 4); + + inputStream.Read(inputBuffer, 0, 8); + _processId = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(inputBuffer, 0)); + _secretKey = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(inputBuffer, 4)); + + } + + public Int32 ProcessID + { + get + { + NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "ProcessID"); + return _processId; + } + } + + public Int32 SecretKey + { + get + { + NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "SecretKey"); + return _secretKey; + } + } + } +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlBind.cs b/mcs/class/Npgsql/Npgsql/NpgsqlBind.cs index 35de60c056b..91056afc587 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlBind.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlBind.cs @@ -9,174 +9,210 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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; +using System.Data; + namespace Npgsql { - /// <summary> - /// This class represents the Bind message sent to PostgreSQL - /// server. - /// </summary> - /// - internal sealed class NpgsqlBind : ClientMessage - { - private readonly String _portalName; - private readonly String _preparedStatementName; - private Int16[] _parameterFormatCodes; - private Object[] _parameterValues; - private Int16[] _resultFormatCodes; - - - public NpgsqlBind(String portalName, String preparedStatementName, Int16[] parameterFormatCodes, - Object[] parameterValues, Int16[] resultFormatCodes) - { - _portalName = portalName; - _preparedStatementName = preparedStatementName; - _parameterFormatCodes = parameterFormatCodes; - _parameterValues = parameterValues; - _resultFormatCodes = resultFormatCodes; - } - - public String PortalName - { - get { return _portalName; } - } - - public String PreparedStatementName - { - get { return _preparedStatementName; } - } - - public Int16[] ResultFormatCodes - { - get { return _resultFormatCodes; } - set { _resultFormatCodes = value; } - } - - public Int16[] ParameterFormatCodes - { - get { return _parameterFormatCodes; } - - set { _parameterFormatCodes = value; } - } - - public Object[] ParameterValues - { - get { return _parameterValues; } - - set { _parameterValues = value; } - } - - - public override void WriteToStream(Stream outputStream) - { - Int32 messageLength = 4 + UTF8Encoding.GetByteCount(_portalName) + 1 + - UTF8Encoding.GetByteCount(_preparedStatementName) + 1 + 2 + (_parameterFormatCodes.Length*2) + - 2; - - - // Get size of parameter values. - Int32 i; - - if (_parameterValues != null) - { - for (i = 0; i < _parameterValues.Length; i++) - { - messageLength += 4; - if (_parameterValues[i] != null) - { - if (((_parameterFormatCodes.Length == 1) && (_parameterFormatCodes[0] == (Int16) FormatCode.Binary)) || - ((_parameterFormatCodes.Length != 1) && (_parameterFormatCodes[i] == (Int16) FormatCode.Binary))) - { - messageLength += ((Byte[]) _parameterValues[i]).Length; - } - else - { - messageLength += UTF8Encoding.GetByteCount((String) _parameterValues[i]); - } - } - } - } - - messageLength += 2 + (_resultFormatCodes.Length*2); - - - outputStream.WriteByte((byte) FrontEndMessageCode.Bind); - - PGUtil.WriteInt32(outputStream, messageLength); - PGUtil.WriteString(_portalName, outputStream); - PGUtil.WriteString(_preparedStatementName, outputStream); - - PGUtil.WriteInt16(outputStream, (Int16) _parameterFormatCodes.Length); - - for (i = 0; i < _parameterFormatCodes.Length; i++) - { - PGUtil.WriteInt16(outputStream, _parameterFormatCodes[i]); - } - - if (_parameterValues != null) - { - PGUtil.WriteInt16(outputStream, (Int16) _parameterValues.Length); - - for (i = 0; i < _parameterValues.Length; i++) - { - if (((_parameterFormatCodes.Length == 1) && (_parameterFormatCodes[0] == (Int16) FormatCode.Binary)) || - ((_parameterFormatCodes.Length != 1) && (_parameterFormatCodes[i] == (Int16) FormatCode.Binary))) - { - Byte[] parameterValue = (Byte[]) _parameterValues[i]; - if (parameterValue == null) - { - PGUtil.WriteInt32(outputStream, -1); - } - else - { - PGUtil.WriteInt32(outputStream, parameterValue.Length); - outputStream.Write(parameterValue, 0, parameterValue.Length); - } - } - else - { - if ((_parameterValues[i] == null)) - { - PGUtil.WriteInt32(outputStream, -1); - } - else - { - Byte[] parameterValueBytes = UTF8Encoding.GetBytes((String) _parameterValues[i]); - PGUtil.WriteInt32(outputStream, parameterValueBytes.Length); - outputStream.Write(parameterValueBytes, 0, parameterValueBytes.Length); - } - } - } - } - else - { - PGUtil.WriteInt16(outputStream, 0); - } - - PGUtil.WriteInt16(outputStream, (Int16) _resultFormatCodes.Length); - for (i = 0; i < _resultFormatCodes.Length; i++) - { - PGUtil.WriteInt16(outputStream, _resultFormatCodes[i]); - } - } - } -}
\ No newline at end of file + + /// <summary> + /// This class represents the Bind message sent to PostgreSQL + /// server. + /// </summary> + /// + internal sealed class NpgsqlBind + { + + // Logging related values + private static readonly String CLASSNAME = "NpgsqlBind"; + + private String _portalName; + private String _preparedStatementName; + private Int16[] _parameterFormatCodes; + private Object[] _parameterValues; + private Int16[] _resultFormatCodes; + + + + public NpgsqlBind(String portalName, + String preparedStatementName, + Int16[] parameterFormatCodes, + Object[] parameterValues, + Int16[] resultFormatCodes) + { + + _portalName = portalName; + _preparedStatementName = preparedStatementName; + _parameterFormatCodes = parameterFormatCodes; + _parameterValues = parameterValues; + _resultFormatCodes = resultFormatCodes; + + + + + } + + public String PortalName + { + get + { + return _portalName; + } + } + + public String PreparedStatementName + { + get + { + return _preparedStatementName; + } + } + + public Int16[] ResultFormatCodes + { + get + { + return _resultFormatCodes; + } + set + { + _resultFormatCodes = value; + + } + } + + public Int16[] ParameterFormatCodes + { + get + { + return _parameterFormatCodes; + } + + set + { + _parameterFormatCodes = value; + + } + } + + public Object[] ParameterValues + { + get + { + return _parameterValues; + } + + set + { + _parameterValues = value; + } + } + + + public void WriteToStream(Stream outputStream, Encoding encoding) + { + + + + Int32 messageLength = 4 + + encoding.GetByteCount(_portalName) + 1 + + encoding.GetByteCount(_preparedStatementName) + 1 + + 2 + + (_parameterFormatCodes.Length * 2) + + 2; + + + // Get size of parameter values. + Int32 i; + + if (_parameterValues != null) + for (i = 0; i < _parameterValues.Length; i++) + { + messageLength += 4; + if ( _parameterValues[i] != null) + if ( ((_parameterFormatCodes.Length == 1) && (_parameterFormatCodes[0] == (Int16) FormatCode.Binary)) || + ((_parameterFormatCodes.Length != 1) && (_parameterFormatCodes[i] == (Int16) FormatCode.Binary)) ) + messageLength += ((Byte[])_parameterValues[i]).Length; + else + messageLength += encoding.GetByteCount((String)_parameterValues[i]); + + } + + messageLength += 2 + (_resultFormatCodes.Length * 2); + + + outputStream.WriteByte((Byte)'B'); + + PGUtil.WriteInt32(outputStream, messageLength); + PGUtil.WriteString(_portalName, outputStream, encoding); + PGUtil.WriteString(_preparedStatementName, outputStream, encoding); + + PGUtil.WriteInt16(outputStream, (Int16)_parameterFormatCodes.Length); + + for (i = 0; i < _parameterFormatCodes.Length; i++) + PGUtil.WriteInt16(outputStream, _parameterFormatCodes[i]); + + if (_parameterValues != null) + { + PGUtil.WriteInt16(outputStream, (Int16)_parameterValues.Length); + + for (i = 0; i < _parameterValues.Length; i++) + { + if ( ((_parameterFormatCodes.Length == 1) && (_parameterFormatCodes[0] == (Int16) FormatCode.Binary)) || + ((_parameterFormatCodes.Length != 1) && (_parameterFormatCodes[i] == (Int16) FormatCode.Binary)) ) + { + + Byte[] parameterValue = (Byte[])_parameterValues[i]; + if (parameterValue == null) + PGUtil.WriteInt32(outputStream, -1); + else + { + PGUtil.WriteInt32(outputStream, parameterValue.Length); + outputStream.Write(parameterValue, 0, parameterValue.Length); + } + } + else + { + if ((_parameterValues[i] == null)) + PGUtil.WriteInt32(outputStream, -1); + else + { + Byte[] parameterValueBytes = encoding.GetBytes((String)_parameterValues[i]); + PGUtil.WriteInt32(outputStream, parameterValueBytes.Length); + outputStream.Write(parameterValueBytes, 0, parameterValueBytes.Length); + } + } + + } + } + else + PGUtil.WriteInt16(outputStream, 0); + + PGUtil.WriteInt16(outputStream, (Int16)_resultFormatCodes.Length); + for (i = 0; i < _resultFormatCodes.Length; i++) + PGUtil.WriteInt16(outputStream, _resultFormatCodes[i]); + + + + } + + } +} + diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlCancelRequest.cs b/mcs/class/Npgsql/Npgsql/NpgsqlCancelRequest.cs index 776fc58bfdc..9b0c02a5a01 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlCancelRequest.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlCancelRequest.cs @@ -6,58 +6,60 @@ // Copyright (C) 2002-2006 The Npgsql Development Team // http://pgfoundry.org/projects/npgsql // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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 : ClientMessage - { - // Logging related values - //private static readonly String CLASSNAME = "NpgsqlCancelRequest"; - - - private static readonly Int32 CancelRequestMessageSize = 16; - private static readonly Int32 CancelRequestCode = 1234 << 16 | 5678; - - private readonly NpgsqlBackEndKeyData BackendKeydata; - - - public NpgsqlCancelRequest(NpgsqlBackEndKeyData BackendKeydata) - { - this.BackendKeydata = BackendKeydata; - } - - public override void WriteToStream(Stream outputStream) - { - PGUtil.WriteInt32(outputStream, CancelRequestMessageSize); - PGUtil.WriteInt32(outputStream, CancelRequestCode); - PGUtil.WriteInt32(outputStream, BackendKeydata.ProcessID); - PGUtil.WriteInt32(outputStream, BackendKeydata.SecretKey); - - outputStream.Flush(); - } - } + + /// <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 diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlClosedState.cs b/mcs/class/Npgsql/Npgsql/NpgsqlClosedState.cs index 795046c31d4..e6cd113e836 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlClosedState.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlClosedState.cs @@ -1,88 +1,96 @@ // Npgsql.NpgsqlClosedState.cs // -// Authors: -// Dave Joyner <d4ljoyn@yahoo.com> -// Daniel Nauck <dna(at)mono-project.de> +// Author: +// Dave Joyner <d4ljoyn@yahoo.com> // // Copyright (C) 2002 The Npgsql Development Team // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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.Net; using System.Net.Sockets; +using System.Collections; using System.Threading; + using Mono.Security.Protocol.Tls; -using SecurityProtocolType=Mono.Security.Protocol.Tls.SecurityProtocolType; namespace Npgsql { - internal sealed class NpgsqlClosedState : NpgsqlState - { - private static readonly NpgsqlClosedState _instance = new NpgsqlClosedState(); - private static readonly String CLASSNAME = "NpgsqlClosedState"; - - - private NpgsqlClosedState() - : base() - { - } - - public static NpgsqlClosedState Instance - { - get - { - NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "Instance"); - return _instance; - } - } - - - /// <summary> - /// Resolve a host name or IP address. - /// This is needed because if you call Dns.Resolve() with an IP address, it will attempt - /// to resolve it as a host name, when it should just convert it to an IP address. - /// </summary> - /// <param name="HostName"></param> - private static IPAddress[] ResolveIPHost(String HostName) - { - return Dns.GetHostAddresses(HostName); - } - - public override void Open(NpgsqlConnector context) - { - try - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Open"); - - /*TcpClient tcpc = new TcpClient(); + + internal sealed class NpgsqlClosedState : NpgsqlState + { + + private static NpgsqlClosedState _instance = new NpgsqlClosedState(); + private static readonly String CLASSNAME = "NpgsqlClosedState"; + + + private NpgsqlClosedState() : base() + { } + + public static NpgsqlClosedState Instance { + get + { + NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "Instance"); + return _instance; + } + } + + + + /// <summary> + /// Resolve a host name or IP address. + /// This is needed because if you call Dns.Resolve() with an IP address, it will attempt + /// to resolve it as a host name, when it should just convert it to an IP address. + /// </summary> + /// <param name="HostName"></param> + private static IPAddress ResolveIPHost(String HostName) + { + + try + { + // Is it a raw IP address? + return IPAddress.Parse(HostName); + } + catch (FormatException) + { + // Not an IP, must be a host name... + return Dns.Resolve(HostName).AddressList[0]; + } + } + + public override void Open(NpgsqlConnector context) + { + + try + { + + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Open"); + + /*TcpClient tcpc = new TcpClient(); tcpc.Connect(new IPEndPoint(ResolveIPHost(context.Host), context.Port)); Stream stream = tcpc.GetStream();*/ + + Socket socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); + + /*socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.SendTimeout, context.ConnectionTimeout*1000);*/ - /*socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.SendTimeout, context.ConnectionTimeout*1000);*/ - - //socket.Connect(new IPEndPoint(ResolveIPHost(context.Host), context.Port)); - - - /*Socket socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); + //socket.Connect(new IPEndPoint(ResolveIPHost(context.Host), context.Port)); IAsyncResult result = socket.BeginConnect(new IPEndPoint(ResolveIPHost(context.Host), context.Port), null, null); @@ -96,105 +104,57 @@ namespace Npgsql { socket.EndConnect(result); } - catch (Exception) + catch (Exception ex) { socket.Close(); throw; } - */ - - IPAddress[] ips = ResolveIPHost(context.Host); - Socket socket = null; - - // try every ip address of the given hostname, use the first reachable one - foreach (IPAddress ip in ips) - { - NpgsqlEventLog.LogMsg(resman, "Log_ConnectingTo", LogLevel.Debug, ip); - - IPEndPoint ep = new IPEndPoint(ip, context.Port); - socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp); - - try - { - IAsyncResult result = socket.BeginConnect(ep, null, null); - - if (!result.AsyncWaitHandle.WaitOne(context.ConnectionTimeout*1000, true)) - { - socket.Close(); - throw new Exception(resman.GetString("Exception_ConnectionTimeout")); - } - - socket.EndConnect(result); - - // connect was successful, leave the loop - break; - } - catch (Exception) - { - NpgsqlEventLog.LogMsg(resman, "Log_FailedConnection", LogLevel.Normal, ip); - socket.Close(); - } - } - - if (socket == null || !socket.Connected) - { - throw new Exception(string.Format(resman.GetString("Exception_FailedConnection"), context.Host)); - } - - Stream stream = new NetworkStream(socket, true); - - - // If the PostgreSQL server has SSL connectors enabled Open SslClientStream if (response == 'S') { - if (context.SSL || (context.SslMode == SslMode.Require) || (context.SslMode == SslMode.Prefer)) - { - PGUtil.WriteInt32(stream, 8); - PGUtil.WriteInt32(stream, 80877103); - // Receive response - - Char response = (Char) stream.ReadByte(); - if (response == 'S') - { - stream = new SslClientStream(stream, context.Host, true, SecurityProtocolType.Default); - - ((SslClientStream) stream).ClientCertSelectionDelegate = - new CertificateSelectionCallback(context.DefaultCertificateSelectionCallback); - ((SslClientStream) stream).ServerCertValidationDelegate = - new CertificateValidationCallback(context.DefaultCertificateValidationCallback); - ((SslClientStream) stream).PrivateKeyCertSelectionDelegate = - new PrivateKeySelectionCallback(context.DefaultPrivateKeySelectionCallback); - } - else if (context.SslMode == SslMode.Require) - { - throw new InvalidOperationException(resman.GetString("Exception_Ssl_RequestError")); - } - } - - context.Stream = new BufferedStream(stream); - context.Socket = socket; - - - NpgsqlEventLog.LogMsg(resman, "Log_ConnectedTo", LogLevel.Normal, context.Host, context.Port); - ChangeState(context, NpgsqlConnectedState.Instance); - } - //FIXME: Exceptions that come from what we are handling should be wrapped - e.g. an error connecting to - //the server should definitely be presented to the uesr as an NpgsqlError. Exceptions from userland should - //be passed untouched - e.g. ThreadAbortException because the user started this in a thread they created and - //then aborted should be passed through. - //Are there any others that should be pass through? Alternatively, are there a finite number that should - //be wrapped? - catch (ThreadAbortException) - { - throw; - } - catch (Exception e) - { - throw new NpgsqlException(e.Message, e); - } - } - - public override void Close(NpgsqlConnector context) - { - //DO NOTHING. - } - } -}
\ No newline at end of file + + Stream stream = new NetworkStream(socket, true); + + + + // If the PostgreSQL server has SSL connectors enabled Open SslClientStream if (response == 'S') { + if (context.SSL || (context.SslMode == SslMode.Require) || (context.SslMode == SslMode.Prefer)) + { + PGUtil.WriteInt32(stream, 8); + PGUtil.WriteInt32(stream,80877103); + // Receive response + + Char response = (Char)stream.ReadByte(); + if (response == 'S') + { + stream = new SslClientStream( + stream, + context.Host, + true, + Mono.Security.Protocol.Tls.SecurityProtocolType.Default + ); + + ((SslClientStream)stream).ClientCertSelectionDelegate = new CertificateSelectionCallback(context.DefaultCertificateSelectionCallback); + ((SslClientStream)stream).ServerCertValidationDelegate = new CertificateValidationCallback(context.DefaultCertificateValidationCallback); + ((SslClientStream)stream).PrivateKeyCertSelectionDelegate = new PrivateKeySelectionCallback(context.DefaultPrivateKeySelectionCallback); + } + else if (context.SslMode == SslMode.Require) + throw new InvalidOperationException(resman.GetString("Exception_Ssl_RequestError")); + + } + + context.Stream = new BufferedStream(stream); + context.Socket = socket; + + + NpgsqlEventLog.LogMsg(resman, "Log_ConnectedTo", LogLevel.Normal, context.Host, context.Port); + ChangeState(context, NpgsqlConnectedState.Instance); + + + } + catch (Exception e) + { + throw new NpgsqlException(e.Message, e); + } + } + + } + +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlCommand.cs b/mcs/class/Npgsql/Npgsql/NpgsqlCommand.cs index a0cf749c417..4a536daec4e 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlCommand.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlCommand.cs @@ -3,42 +3,40 @@ // Npgsql.NpgsqlCommand.cs // // Author: -// Francisco Jr. (fxjrlists@yahoo.com.br) +// 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 +// Copyright (C) 2002 The Npgsql Development Team +// npgsql-general@gborg.postgresql.org +// http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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.ComponentModel; using System.Data; -using System.Data.Common; -using System.IO; -using System.Reflection; -using System.Resources; using System.Text; -using System.Text.RegularExpressions; +using System.Resources; +using System.ComponentModel; +using System.Collections; +using System.IO; + using NpgsqlTypes; -#if WITHDESIGN +using System.Text.RegularExpressions; +#if WITHDESIGN +using Npgsql.Design; #endif namespace Npgsql @@ -47,62 +45,54 @@ namespace Npgsql /// Represents a SQL statement or function (stored procedure) to execute /// against a PostgreSQL database. This class cannot be inherited. /// </summary> -#if WITHDESIGN + #if WITHDESIGN [System.Drawing.ToolboxBitmapAttribute(typeof(NpgsqlCommand)), ToolboxItem(true)] -#endif - - public sealed class NpgsqlCommand : DbCommand, ICloneable + #endif + public sealed class NpgsqlCommand : Component, IDbCommand, ICloneable { // Logging related values private static readonly String CLASSNAME = "NpgsqlCommand"; - private static ResourceManager resman = new ResourceManager(MethodBase.GetCurrentMethod().DeclaringType); - private readonly Regex parameterReplace = new Regex(@"([:@][\w\.]*)", RegexOptions.Singleline); + private static ResourceManager resman = new ResourceManager(typeof(NpgsqlCommand)); + private static readonly Regex parameterReplace = new Regex(@"([:@][\w\.]*)", RegexOptions.Singleline); + + private NpgsqlConnection connection; + private NpgsqlConnector connector; + private NpgsqlTransaction transaction; + private String text; + private Int32 timeout; + private CommandType type; + private NpgsqlParameterCollection parameters; + private String planName; - private NpgsqlConnection connection; - private NpgsqlConnector m_Connector; //renamed to account for hiding it in a local function - //if all locals were named with this prefix, it would solve LOTS of issues. - private NpgsqlTransaction transaction; - private String text; - private Int32 timeout; - private CommandType type; - private readonly NpgsqlParameterCollection parameters; - private String planName; - private Boolean designTimeVisible; + private NpgsqlParse parse; + private NpgsqlBind bind; - private NpgsqlParse parse; - private NpgsqlBind bind; + private Boolean invalidTransactionDetected = false; + + private CommandBehavior commandBehavior; - private Int64 lastInsertedOID = 0; + private Int64 lastInsertedOID = 0; // Constructors /// <summary> /// Initializes a new instance of the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see> class. /// </summary> - public NpgsqlCommand() - : this(String.Empty, null, null) - { - } - + public NpgsqlCommand() : this(String.Empty, null, null) + {} /// <summary> /// Initializes a new instance of the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see> class with the text of the query. /// </summary> /// <param name="cmdText">The text of the query.</param> - public NpgsqlCommand(String cmdText) - : this(cmdText, null, null) - { - } - + public NpgsqlCommand(String cmdText) : this(cmdText, null, null) + {} /// <summary> /// Initializes a new instance of the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see> class with the text of the query and a <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see>. /// </summary> /// <param name="cmdText">The text of the query.</param> /// <param name="connection">A <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see> that represents the connection to a PostgreSQL server.</param> - public NpgsqlCommand(String cmdText, NpgsqlConnection connection) - : this(cmdText, connection, null) - { - } - + public NpgsqlCommand(String cmdText, NpgsqlConnection connection) : this(cmdText, connection, null) + {} /// <summary> /// Initializes a new instance of the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see> class with the text of the query, a <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see>, and the <see cref="Npgsql.NpgsqlTransaction">NpgsqlTransaction</see>. /// </summary> @@ -116,17 +106,18 @@ namespace Npgsql planName = String.Empty; text = cmdText; this.connection = connection; - + if (this.connection != null) - { - this.m_Connector = connection.Connector; - } + this.connector = connection.Connector; parameters = new NpgsqlParameterCollection(); type = CommandType.Text; this.Transaction = transaction; + commandBehavior = CommandBehavior.Default; SetCommandTimeout(); + + } /// <summary> @@ -134,15 +125,16 @@ namespace Npgsql /// </summary> internal NpgsqlCommand(String cmdText, NpgsqlConnector connector) { - resman = new ResourceManager(this.GetType()); + resman = new System.Resources.ResourceManager(this.GetType()); NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME); - + planName = String.Empty; text = cmdText; - this.m_Connector = connector; + this.connector = connector; type = CommandType.Text; - + commandBehavior = CommandBehavior.Default; + parameters = new NpgsqlParameterCollection(); // Internal commands aren't affected by command timeout value provided by user. @@ -155,9 +147,11 @@ namespace Npgsql /// </summary> /// <value>The Transact-SQL statement or stored procedure to execute. The default is an empty string.</value> [Category("Data"), DefaultValue("")] - public override String CommandText - { - get { return text; } + public String CommandText { + get + { + return text; + } set { @@ -167,6 +161,7 @@ namespace Npgsql planName = String.Empty; parse = null; bind = null; + commandBehavior = CommandBehavior.Default; } } @@ -177,16 +172,17 @@ namespace Npgsql /// <value>The time (in seconds) to wait for the command to execute. /// The default is 20 seconds.</value> [DefaultValue(20)] - public override Int32 CommandTimeout + public Int32 CommandTimeout { - get { return timeout; } + get + { + return timeout; + } set { if (value < 0) - { throw new ArgumentOutOfRangeException(resman.GetString("Exception_CommandTimeoutLessZero")); - } timeout = value; NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "CommandTimeout", value); @@ -199,9 +195,12 @@ namespace Npgsql /// </summary> /// <value>One of the <see cref="System.Data.CommandType">CommandType</see> values. The default is <see cref="System.Data.CommandType">CommandType.Text</see>.</value> [Category("Data"), DefaultValue(CommandType.Text)] - public override CommandType CommandType + public CommandType CommandType { - get { return type; } + get + { + return type; + } set { @@ -210,14 +209,17 @@ namespace Npgsql } } - protected override DbConnection DbConnection + IDbConnection IDbCommand.Connection { - get { return Connection; } + get + { + return Connection; + } set { - Connection = (NpgsqlConnection)value; - NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "DbConnection", value); + Connection = (NpgsqlConnection) value; + NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "IDbCommand.Connection", value); } } @@ -227,7 +229,7 @@ namespace Npgsql /// </summary> /// <value>The connection to a data source. The default value is a null reference.</value> [Category("Behavior"), DefaultValue(null)] - public new NpgsqlConnection Connection + public NpgsqlConnection Connection { get { @@ -238,31 +240,22 @@ namespace Npgsql set { if (this.Connection == value) - { return; - } //if (this.transaction != null && this.transaction.Connection == null) - // this.transaction = null; - - // All this checking needs revising. It should be simpler. - // This this.Connector != null check was added to remove the nullreferenceexception in case - // of the previous connection has been closed which makes Connector null and so the last check would fail. - // See bug 1000581 for more details. - if (this.transaction != null && this.connection != null && this.Connector != null && this.Connector.Transaction != null) - { + // this.transaction = null; + + if (this.transaction != null && this.connection != null && this.Connector.Transaction != null) throw new InvalidOperationException(resman.GetString("Exception_SetConnectionInTransaction")); - } + this.connection = value; Transaction = null; if (this.connection != null) - { - m_Connector = this.connection.Connector; - } + connector = this.connection.Connector; SetCommandTimeout(); - + NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "Connection", value); } } @@ -272,28 +265,28 @@ namespace Npgsql get { if (this.connection != null) - { - m_Connector = this.connection.Connector; - } + connector = this.connection.Connector; - return m_Connector; + return connector; } } - protected override DbParameterCollection DbParameterCollection - { - get { return Parameters; } + IDataParameterCollection IDbCommand.Parameters { + get + { + return Parameters; + } } /// <summary> /// Gets the <see cref="Npgsql.NpgsqlParameterCollection">NpgsqlParameterCollection</see>. /// </summary> /// <value>The parameters of the SQL statement or function (stored procedure). The default is an empty collection.</value> -#if WITHDESIGN + #if WITHDESIGN [Category("Data"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] -#endif - - public new NpgsqlParameterCollection Parameters + #endif + + public NpgsqlParameterCollection Parameters { get { @@ -302,29 +295,32 @@ namespace Npgsql } } - - protected override DbTransaction DbTransaction + + IDbTransaction IDbCommand.Transaction { - get { return Transaction; } + get + { + return Transaction; + } + set { - Transaction = (NpgsqlTransaction)value; + Transaction = (NpgsqlTransaction) value; NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "IDbCommand.Transaction", value); } } - + /// <summary> /// Gets or sets the <see cref="Npgsql.NpgsqlTransaction">NpgsqlTransaction</see> /// within which the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see> executes. /// </summary> /// <value>The <see cref="Npgsql.NpgsqlTransaction">NpgsqlTransaction</see>. /// The default value is a null reference.</value> -#if WITHDESIGN + #if WITHDESIGN [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] -#endif - - public new NpgsqlTransaction Transaction - { + #endif + + public NpgsqlTransaction Transaction { get { NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "Transaction"); @@ -338,40 +334,46 @@ namespace Npgsql set { - NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "Transaction", value); + NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "Transaction" ,value); - this.transaction = value; + this.transaction = (NpgsqlTransaction) value; } } /// <summary> /// Gets or sets how command results are applied to the <see cref="System.Data.DataRow">DataRow</see> - /// when used by the <see cref="System.Data.Common.DbDataAdapter.Update(DataSet)">Update</see> + /// when used by the <see cref="System.Data.Common.DbDataAdapter.Update">Update</see> /// method of the <see cref="System.Data.Common.DbDataAdapter">DbDataAdapter</see>. /// </summary> /// <value>One of the <see cref="System.Data.UpdateRowSource">UpdateRowSource</see> values.</value> -#if WITHDESIGN + #if WITHDESIGN [Category("Behavior"), DefaultValue(UpdateRowSource.Both)] -#endif - - public override UpdateRowSource UpdatedRowSource - { + #endif + + public UpdateRowSource UpdatedRowSource { get { + NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "UpdatedRowSource"); return UpdateRowSource.Both; } - set { } + set + { + } } /// <summary> /// Returns oid of inserted row. This is only updated when using executenonQuery and when command inserts just a single row. If table is created without oids, this will always be 0. /// </summary> - public Int64 LastInsertedOID + + public Int64 LastInsertedOID { - get { return lastInsertedOID; } + get + { + return lastInsertedOID; + } } @@ -379,7 +381,7 @@ namespace Npgsql /// Attempts to cancel the execution of a <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see>. /// </summary> /// <remarks>This Method isn't implemented yet.</remarks> - public override void Cancel() + public void Cancel() { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Cancel"); @@ -395,13 +397,13 @@ namespace Npgsql catch (IOException) { Connection.ClearPool(); - } + } catch (NpgsqlException) { // Cancel documentation says the Cancel doesn't throw on failure } } - + /// <summary> /// Create a new command based on this one. /// </summary> @@ -419,33 +421,25 @@ namespace Npgsql { // TODO: Add consistency checks. - NpgsqlCommand clone = new NpgsqlCommand(CommandText, Connection, Transaction); - clone.CommandTimeout = CommandTimeout; - clone.CommandType = CommandType; - clone.DesignTimeVisible = DesignTimeVisible; - foreach (NpgsqlParameter parameter in Parameters) - { - clone.Parameters.Add(((ICloneable)parameter).Clone()); - } - return clone; + return new NpgsqlCommand(CommandText, Connection, Transaction); } /// <summary> - /// Creates a new instance of an <see cref="System.Data.Common.DbParameter">DbParameter</see> object. + /// Creates a new instance of an <see cref="System.Data.IDbDataParameter">IDbDataParameter</see> object. /// </summary> - /// <returns>An <see cref="System.Data.Common.DbParameter">DbParameter</see> object.</returns> - protected override DbParameter CreateDbParameter() + /// <returns>An <see cref="System.Data.IDbDataParameter">IDbDataParameter</see> object.</returns> + IDbDataParameter IDbCommand.CreateParameter() { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CreateDbParameter"); + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IDbCommand.CreateParameter"); - return CreateParameter(); + return (NpgsqlParameter) CreateParameter(); } /// <summary> /// Creates a new instance of a <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object. /// </summary> /// <returns>A <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object.</returns> - public new NpgsqlParameter CreateParameter() + public NpgsqlParameter CreateParameter() { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CreateParameter"); @@ -453,38 +447,143 @@ namespace Npgsql } /// <summary> - /// Slightly optimised version of ExecuteNonQuery() for internal ues in cases where the number - /// of affected rows is of no interest. - /// </summary> - internal void ExecuteBlind() - { - GetReader(CommandBehavior.SequentialAccess).Dispose(); - } - - /// <summary> /// Executes a SQL statement against the connection and returns the number of rows affected. /// </summary> - /// <returns>The number of rows affected if known; -1 otherwise.</returns> - public override Int32 ExecuteNonQuery() + /// <returns>The number of rows affected.</returns> + public Int32 ExecuteNonQuery() { - //We treat this as a simple wrapper for calling ExecuteReader() and then - //update the records affected count at every call to NextResult(); NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ExecuteNonQuery"); - int? ret = null; - using (NpgsqlDataReader rdr = GetReader(CommandBehavior.SequentialAccess)) + + // Initialize lastInsertOID + lastInsertedOID = 0; + + ExecuteCommand(); + + UpdateOutputParameters(); + + + // If nothing is returned, just return -1. + if(Connector.Mediator.CompletedResponses.Count == 0) { - do + return -1; + } + + // Check if the response is available. + String firstCompletedResponse = (String)Connector.Mediator.CompletedResponses[0]; + + if (firstCompletedResponse == null) + return -1; + + String[] ret_string_tokens = firstCompletedResponse.Split(null); // whitespace separator. + + + // Check if the command was insert, delete, update, fetch or move. + // Only theses commands return rows affected. + // [FIXME] Is there a better way to check this?? + if ((String.Compare(ret_string_tokens[0], "INSERT", true) == 0) || + (String.Compare(ret_string_tokens[0], "UPDATE", true) == 0) || + (String.Compare(ret_string_tokens[0], "DELETE", true) == 0) || + (String.Compare(ret_string_tokens[0], "FETCH", true) == 0) || + (String.Compare(ret_string_tokens[0], "MOVE", true) == 0)) + + + { + if (String.Compare(ret_string_tokens[0], "INSERT", true) == 0) + // Get oid of inserted row. + lastInsertedOID = Int32.Parse(ret_string_tokens[1]); + + // The number of rows affected is in the third token for insert queries + // and in the second token for update and delete queries. + // In other words, it is the last token in the 0-based array. + + return Int32.Parse(ret_string_tokens[ret_string_tokens.Length - 1]); + } + else + return -1; + } + + + + private void UpdateOutputParameters() + { + // Check if there was some resultset returned. If so, put the result in output parameters. + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "UpdateOutputParameters"); + + // Get ResultSets. + ArrayList resultSets = Connector.Mediator.ResultSets; + + if (resultSets.Count != 0) + { + NpgsqlResultSet nrs = (NpgsqlResultSet)resultSets[0]; + + if ((nrs != null) && (nrs.Count > 0)) { - int thisRecord = rdr.RecordsAffected; - if (thisRecord != -1) + NpgsqlAsciiRow nar = (NpgsqlAsciiRow)nrs[0]; + + Int32 i = 0; + Boolean hasMapping = false; + + // First check if there is any mapping between parameter name and resultset name. + // If so, just update output parameters which has mapping. + + foreach (NpgsqlParameter p in Parameters) + { + if (nrs.RowDescription.FieldIndex(p.ParameterName.Substring(1)) > -1) + { + hasMapping = true; + break; + } + + } + + + if (hasMapping) { - ret = (ret ?? 0) + thisRecord; + foreach (NpgsqlParameter p in Parameters) + { + if (((p.Direction == ParameterDirection.Output) || + (p.Direction == ParameterDirection.InputOutput)) && (i < nrs.RowDescription.NumFields )) + { + Int32 fieldIndex = nrs.RowDescription.FieldIndex(p.ParameterName.Substring(1)); + + if (fieldIndex > -1) + { + p.Value = nar[fieldIndex]; + i++; + } + + } + } + } - lastInsertedOID = rdr.LastInsertedOID ?? lastInsertedOID; + else + foreach (NpgsqlParameter p in Parameters) + { + if (((p.Direction == ParameterDirection.Output) || + (p.Direction == ParameterDirection.InputOutput)) && (i < nrs.RowDescription.NumFields )) + { + p.Value = nar[i]; + i++; + } + } } - while (rdr.NextResult()); - } - return ret ?? -1; + + } + + + } + + /// <summary> + /// Sends the <see cref="Npgsql.NpgsqlCommand.CommandText">CommandText</see> to + /// the <see cref="Npgsql.NpgsqlConnection">Connection</see> and builds a + /// <see cref="Npgsql.NpgsqlDataReader">NpgsqlDataReader</see>. + /// </summary> + /// <returns>A <see cref="Npgsql.NpgsqlDataReader">NpgsqlDataReader</see> object.</returns> + IDataReader IDbCommand.ExecuteReader() + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IDbCommand.ExecuteReader"); + + return (NpgsqlDataReader) ExecuteReader(); } /// <summary> @@ -493,11 +592,13 @@ namespace Npgsql /// <see cref="Npgsql.NpgsqlDataReader">NpgsqlDataReader</see> /// using one of the <see cref="System.Data.CommandBehavior">CommandBehavior</see> values. /// </summary> - /// <param name="behavior">One of the <see cref="System.Data.CommandBehavior">CommandBehavior</see> values.</param> + /// <param name="cb">One of the <see cref="System.Data.CommandBehavior">CommandBehavior</see> values.</param> /// <returns>A <see cref="Npgsql.NpgsqlDataReader">NpgsqlDataReader</see> object.</returns> - protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) + IDataReader IDbCommand.ExecuteReader(CommandBehavior cb) { - return ExecuteReader(behavior); + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IDbCommand.ExecuteReader", cb); + + return (NpgsqlDataReader) ExecuteReader(cb); } /// <summary> @@ -506,7 +607,7 @@ namespace Npgsql /// <see cref="Npgsql.NpgsqlDataReader">NpgsqlDataReader</see>. /// </summary> /// <returns>A <see cref="Npgsql.NpgsqlDataReader">NpgsqlDataReader</see> object.</returns> - public new NpgsqlDataReader ExecuteReader() + public NpgsqlDataReader ExecuteReader() { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ExecuteReader"); @@ -522,70 +623,19 @@ namespace Npgsql /// <param name="cb">One of the <see cref="System.Data.CommandBehavior">CommandBehavior</see> values.</param> /// <returns>A <see cref="Npgsql.NpgsqlDataReader">NpgsqlDataReader</see> object.</returns> /// <remarks>Currently the CommandBehavior parameter is ignored.</remarks> - public new NpgsqlDataReader ExecuteReader(CommandBehavior cb) + public NpgsqlDataReader ExecuteReader(CommandBehavior cb) { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ExecuteReader", cb); - - // Close connection if requested even when there is an error. - - try - { - if (connection != null) - { - if (connection.PreloadReader) - { - //Adjust behaviour so source reader is sequential access - for speed - and doesn't close the connection - or it'll do so at the wrong time. - CommandBehavior adjusted = (cb | CommandBehavior.SequentialAccess) & ~CommandBehavior.CloseConnection; - return new CachingDataReader(GetReader(adjusted), cb); - } - } - return GetReader(cb); - } - catch (Exception) - { - if ((cb & CommandBehavior.CloseConnection) == CommandBehavior.CloseConnection) - { - connection.Close(); - } - throw; - } - - } - - internal ForwardsOnlyDataReader GetReader(CommandBehavior cb) - { - try - { - CheckConnectionState(); - - // reset any responses just before getting new ones - Connector.Mediator.ResetResponses(); - - // Set command timeout. - m_Connector.Mediator.CommandTimeout = CommandTimeout; + // [FIXME] No command behavior handling. + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ExecuteReader", cb); + commandBehavior = cb; - using (m_Connector.BlockNotificationThread()) - { - if (parse == null) - { - return new ForwardsOnlyDataReader(m_Connector.QueryEnum(this), cb, this, m_Connector.BlockNotificationThread(), false); - } - //return new ForwardsOnlyDataReader(m_Connector.QueryEnum(this), cb, this, m_Connector.BlockNotificationThread(), false); - else - { - BindParameters(); - return - new ForwardsOnlyDataReader(m_Connector.ExecuteEnum(new NpgsqlExecute(bind.PortalName, 0)), cb, this, - m_Connector.BlockNotificationThread(), true); - //return new ForwardsOnlyDataReader(m_Connector.ExecuteEnum(new NpgsqlExecute(bind.PortalName, 0)), cb, this, m_Connector.BlockNotificationThread(), true); - } - } - } - catch (IOException ex) - { - throw ClearPoolAndCreateException(ex); - } + ExecuteCommand(); + + UpdateOutputParameters(); + + // Get the resultsets and create a Datareader with them. + return new NpgsqlDataReader(Connector.Mediator.ResultSets, Connector.Mediator.CompletedResponses, cb, this); } ///<summary> @@ -594,34 +644,29 @@ namespace Npgsql /// </summary> private void BindParameters() { + if (parameters.Count != 0) { Object[] parameterValues = new Object[parameters.Count]; Int16[] parameterFormatCodes = bind.ParameterFormatCodes; - + for (Int32 i = 0; i < parameters.Count; i++) { // Do not quote strings, or escape existing quotes - this will be handled by the backend. // DBNull or null values are returned as null. // TODO: Would it be better to remove this null special handling out of ConvertToBackend?? - + // Do special handling of bytea values. They will be send in binary form. // TODO: Add binary format support for all supported types. Not only bytea. if (parameters[i].TypeInfo.NpgsqlDbType != NpgsqlDbType.Bytea) { + parameterValues[i] = parameters[i].TypeInfo.ConvertToBackend(parameters[i].Value, true); } else { - if (parameters[i].Value != DBNull.Value) - { - parameterFormatCodes[i] = (Int16)FormatCode.Binary; - parameterValues[i] = (byte[])parameters[i].Value; - } - else - { - parameterValues[i] = parameters[i].TypeInfo.ConvertToBackend(parameters[i].Value, true); - } + parameterFormatCodes[i] = (Int16) FormatCode.Binary; + parameterValues[i]=(byte[])parameters[i].Value; } } bind.ParameterValues = parameterValues; @@ -629,8 +674,13 @@ namespace Npgsql } Connector.Bind(bind); - + + // See Prepare() method for a discussion of this. + Connector.Mediator.RequireReadyForQuery = false; Connector.Flush(); + + + connector.CheckErrorsAndNotifications(); } /// <summary> @@ -639,120 +689,150 @@ namespace Npgsql /// </summary> /// <returns>The first column of the first row in the result set, /// or a null reference if the result set is empty.</returns> - public override Object ExecuteScalar() + public Object ExecuteScalar() { - using ( - NpgsqlDataReader reader = - GetReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow)) + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ExecuteScalar"); + + ExecuteCommand(); + + + // Now get the results. + // Only the first column of the first row must be returned. + + // Get ResultSets. + ArrayList resultSets = Connector.Mediator.ResultSets; + + // First data is the RowDescription object. + // Check all resultsets as insert commands could have been sent along + // with resultset queries. The insert commands return null and and some queries + // may return empty resultsets, so, if we find one of these, skip to next resultset. + // If no resultset is found, return null as per specification. + + NpgsqlAsciiRow ascii_row = null; + foreach( NpgsqlResultSet nrs in resultSets ) { - return reader.Read() && reader.FieldCount != 0 ? reader.GetValue(0) : null; + if( (nrs != null) && (nrs.Count > 0) ) + { + ascii_row = (NpgsqlAsciiRow) nrs[0]; + return ascii_row[0]; + } } + + return null; } /// <summary> /// Creates a prepared version of the command on a PostgreSQL server. /// </summary> - public override void Prepare() + public void Prepare() { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Prepare"); // Check the connection state. CheckConnectionState(); - + // reset any responses just before getting new ones Connector.Mediator.ResetResponses(); - + // Set command timeout. - m_Connector.Mediator.CommandTimeout = CommandTimeout; + connector.Mediator.CommandTimeout = CommandTimeout; - if (!m_Connector.SupportsPrepare) + if (! connector.SupportsPrepare) { - return; // Do nothing. + return; // Do nothing. } - if (m_Connector.BackendProtocolVersion == ProtocolVersion.Version2) + if (connector.BackendProtocolVersion == ProtocolVersion.Version2) { - using (NpgsqlCommand command = new NpgsqlCommand(GetPrepareCommandText(), m_Connector)) - { - command.ExecuteBlind(); - } + NpgsqlCommand command = new NpgsqlCommand(GetPrepareCommandText(), connector ); + command.ExecuteNonQuery(); } else { - using (m_Connector.BlockNotificationThread()) + try { - try + + connector.StopNotificationThread(); + + // Use the extended query parsing... + planName = connector.NextPlanName(); + String portalName = connector.NextPortalName(); + + parse = new NpgsqlParse(planName, GetParseCommandText(), new Int32[] {}); + + connector.Parse(parse); + + // We need that because Flush() doesn't cause backend to send + // ReadyForQuery on error. Without ReadyForQuery, we don't return + // from query extended processing. + + // We could have used Connector.Flush() which sends us back a + // ReadyForQuery, but on postgresql server below 8.1 there is an error + // with extended query processing which hinders us from using it. + connector.Mediator.RequireReadyForQuery = false; + connector.Flush(); + + // Check for errors and/or notifications and do the Right Thing. + connector.CheckErrorsAndNotifications(); + + + // Description... + NpgsqlDescribe describe = new NpgsqlDescribe('S', planName); + + + connector.Describe(describe); + + connector.Sync(); + + Npgsql.NpgsqlRowDescription returnRowDesc = connector.Mediator.LastRowDescription; + + Int16[] resultFormatCodes; + + + if (returnRowDesc != null) { - // Use the extended query parsing... - planName = m_Connector.NextPlanName(); - String portalName = m_Connector.NextPortalName(); - - parse = new NpgsqlParse(planName, GetParseCommandText(), new Int32[] { }); - - m_Connector.Parse(parse); - - // We need that because Flush() doesn't cause backend to send - // ReadyForQuery on error. Without ReadyForQuery, we don't return - // from query extended processing. - - // We could have used Connector.Flush() which sends us back a - // ReadyForQuery, but on postgresql server below 8.1 there is an error - // with extended query processing which hinders us from using it. - m_Connector.RequireReadyForQuery = false; - m_Connector.Flush(); - - - // Description... - NpgsqlDescribe describe = new NpgsqlDescribe('S', planName); - - - m_Connector.Describe(describe); - - NpgsqlRowDescription returnRowDesc = m_Connector.Sync(); - - Int16[] resultFormatCodes; - - - if (returnRowDesc != null) + resultFormatCodes = new Int16[returnRowDesc.NumFields]; + + for (int i=0; i < returnRowDesc.NumFields; i++) { - resultFormatCodes = new Int16[returnRowDesc.NumFields]; - - for (int i = 0; i < returnRowDesc.NumFields; i++) + Npgsql.NpgsqlRowDescriptionFieldData returnRowDescData = returnRowDesc[i]; + + + if (returnRowDescData.type_info != null && returnRowDescData.type_info.NpgsqlDbType == NpgsqlTypes.NpgsqlDbType.Bytea) { - NpgsqlRowDescription.FieldData returnRowDescData = returnRowDesc[i]; - - - if (returnRowDescData.TypeInfo != null && returnRowDescData.TypeInfo.NpgsqlDbType == NpgsqlDbType.Bytea) - { - // Binary format - resultFormatCodes[i] = (Int16)FormatCode.Binary; - } - else - { - // Text Format - resultFormatCodes[i] = (Int16)FormatCode.Text; - } + // Binary format + resultFormatCodes[i] = (Int16)FormatCode.Binary; } + else + // Text Format + resultFormatCodes[i] = (Int16)FormatCode.Text; } - else - { - resultFormatCodes = new Int16[] { 0 }; - } - - bind = new NpgsqlBind("", planName, new Int16[Parameters.Count], null, resultFormatCodes); - } - catch (IOException e) - { - throw ClearPoolAndCreateException(e); - } - catch - { - // See ExecuteCommand method for a discussion of this. - m_Connector.Sync(); - - throw; + + } + else + resultFormatCodes = new Int16[]{0}; + + bind = new NpgsqlBind("", planName, new Int16[Parameters.Count], null, resultFormatCodes); + } + catch (IOException e) + { + ClearPoolAndThrowException(e); + } + catch + { + // See ExecuteCommand method for a discussion of this. + connector.Sync(); + + throw; } + finally + { + connector.ResumeNotificationThread(); + } + + + } } @@ -788,15 +868,10 @@ namespace Npgsql // Check the connection state. - if (Connector == null || Connector.State == ConnectionState.Closed) + if (Connector == null || Connector.State != ConnectionState.Open) { throw new InvalidOperationException(resman.GetString("Exception_ConnectionNotOpen")); } - if (Connector.State != ConnectionState.Open) - { - throw new InvalidOperationException( - "There is already an open DataReader associated with this Command which must be closed first."); - } } /// <summary> @@ -809,37 +884,31 @@ namespace Npgsql { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetCommandText"); - if (string.IsNullOrEmpty(planName))//== String.Empty) - { + if (planName == String.Empty) return GetClearCommandText(); - } - return GetPreparedCommandText(); - + else + return GetPreparedCommandText(); } private String GetClearCommandText() { if (NpgsqlEventLog.Level == LogLevel.Debug) - { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetClearCommandText"); - } - Boolean addProcedureParenthesis = false; // Do not add procedure parenthesis by default. + Boolean addProcedureParenthesis = false; // Do not add procedure parenthesis by default. - Boolean functionReturnsRecord = false; // Functions don't return record by default. + Boolean functionReturnsRecord = false; // Functions don't return record by default. - Boolean functionReturnsRefcursor = false; // Functions don't return refcursor by default. + Boolean functionReturnsRefcursor = false; // Functions don't return refcursor by default. String result = text; - if (type == CommandType.StoredProcedure) { + if (Parameters.Count > 0) - { - functionReturnsRecord = !CheckFunctionHasOutParameters() && CheckFunctionReturn("record"); - } + functionReturnsRecord = CheckFunctionReturn("record"); functionReturnsRefcursor = CheckFunctionReturn("refcursor"); @@ -851,40 +920,33 @@ namespace Npgsql } if (Connector.SupportsPrepare) - { result = "select * from " + result; // This syntax is only available in 7.3+ as well SupportsPrepare. - } else - { - result = "select " + result; //Only a single result return supported. 7.2 and earlier. - } + result = "select " + result; //Only a single result return supported. 7.2 and earlier. } else if (type == CommandType.TableDirect) - { - return "select * from " + result; // There is no parameter support on table direct. - } + return "select * from " + result; // There is no parameter support on table direct. if (parameters == null || parameters.Count == 0) { if (addProcedureParenthesis) - { result += ")"; - } // If function returns ref cursor just process refcursor-result function call // and return command which will be used to return data from refcursor. if (functionReturnsRefcursor) - { return ProcessRefcursorFunctionReturn(result); - } if (functionReturnsRecord) - { result = AddFunctionReturnsRecordSupport(result); - } + + + result = AddSingleRowBehaviorSupport(result); + + result = AddSchemaOnlyBehaviorSupport(result); return result; } @@ -908,36 +970,35 @@ namespace Npgsql foreach (String s in queryparts) { if (s == string.Empty) - { continue; - } - if ((s[0] == ':' || s[0] == '@') && Parameters.TryGetValue(s, out p)) + if ((s[0] == ':' || s[0] == '@') && + Parameters.TryGetValue(s, out p)) { - // It's a parameter. Lets handle it. - if ((p.Direction == ParameterDirection.Input) || (p.Direction == ParameterDirection.InputOutput)) + if ((p.Direction == ParameterDirection.Input) || + (p.Direction == ParameterDirection.InputOutput)) { - + // FIXME DEBUG ONLY + // adding the '::<datatype>' on the end of a parameter is a highly + // questionable practice, but it is great for debugging! sb.Append(p.TypeInfo.ConvertToBackend(p.Value, false)); - - if (p.UseCast) + + // Only add data type info if we are calling an stored procedure. + + if (type == CommandType.StoredProcedure) { sb.Append("::"); - sb.Append(p.TypeInfo.CastName); - + sb.Append(p.TypeInfo.Name); - if (p.TypeInfo.UseSize && (p.Size > 0)) - { - sb.Append("(").Append(p.Size).Append(")"); - } - } + if (p.TypeInfo.UseSize && (p.Size > 0)) + sb.Append("(").Append(p.Size).Append(")"); + } } + } else - { sb.Append(s); - } } result = sb.ToString(); @@ -945,70 +1006,49 @@ namespace Npgsql else { + for (Int32 i = 0; i < parameters.Count; i++) { NpgsqlParameter Param = parameters[i]; - if ((Param.Direction == ParameterDirection.Input) || (Param.Direction == ParameterDirection.InputOutput)) - { - if (Param.UseCast) - result += Param.TypeInfo.ConvertToBackend(Param.Value, false) + "::" + Param.TypeInfo.CastName + ","; - else - result += Param.TypeInfo.ConvertToBackend(Param.Value, false) + ","; - } + if ((Param.Direction == ParameterDirection.Input) || + (Param.Direction == ParameterDirection.InputOutput)) + + + result += Param.TypeInfo.ConvertToBackend(Param.Value, false) + "::" + Param.TypeInfo.Name + ","; } // Remove a trailing comma added from parameter handling above. If any. // Maybe there are only output parameters. If so, there will be no comma. if (result.EndsWith(",")) - { result = result.Remove(result.Length - 1, 1); - } result += ")"; } if (functionReturnsRecord) - { result = AddFunctionReturnsRecordSupport(result); - } // If function returns ref cursor just process refcursor-result function call // and return command which will be used to return data from refcursor. if (functionReturnsRefcursor) - { return ProcessRefcursorFunctionReturn(result); - } + + result = AddSingleRowBehaviorSupport(result); + + result = AddSchemaOnlyBehaviorSupport(result); + return result; } - - - private Boolean CheckFunctionHasOutParameters() + + + + private Boolean CheckFunctionReturn(String ReturnType) { - // Check if this function has output parameters. - // This is used to enable or not the colum definition list - // when calling functions which return record. - // Functions which has out or inout parameters have return record - // but doesn't allow column definition list. - // See http://pgfoundry.org/forum/forum.php?thread_id=1075&forum_id=519 - // for discussion about that. - - - // inout parameters are only supported from 8.1+ versions. - if (Connection.PostgreSqlVersion < new Version(8, 1, 0)) - { - return false; - } - - - //String outParameterExistanceQuery = - // "select count(*) > 0 from pg_proc where proname=:proname and ('o' = any (proargmodes) OR 'b' = any(proargmodes))"; - - // Updated after 0.99.3 to support the optional existence of a name qualifying schema and allow for case insensitivity // when the schema or procedure name do not contain a quote. // The hard-coded schema name 'public' was replaced with code that uses schema as a qualifier, only if it is provided. @@ -1017,30 +1057,30 @@ namespace Npgsql StringBuilder parameterTypes = new StringBuilder(""); - + // Process parameters - - foreach (NpgsqlParameter p in Parameters) + + foreach(NpgsqlParameter p in Parameters) { - if ((p.Direction == ParameterDirection.Input) || (p.Direction == ParameterDirection.InputOutput)) + if ((p.Direction == ParameterDirection.Input) || + (p.Direction == ParameterDirection.InputOutput)) { parameterTypes.Append(Connection.Connector.OidToNameMapping[p.TypeInfo.Name].OID + " "); } } - + // Process schema name. - + String schemaName = String.Empty; String procedureName = String.Empty; - - + + String[] fullName = CommandText.Split('.'); - + if (fullName.Length == 2) { - returnRecordQuery = - "select count(*) > 0 from pg_proc p left join pg_namespace n on p.pronamespace = n.oid where prorettype = ( select oid from pg_type where typname = 'record' ) and proargtypes=:proargtypes and proname=:proname and n.nspname=:nspname and ('o' = any (proargmodes) OR 'b' = any(proargmodes))"; + returnRecordQuery = "select count(*) > 0 from pg_proc p left join pg_namespace n on p.pronamespace = n.oid where prorettype = ( select oid from pg_type where typname = :typename ) and proargtypes=:proargtypes and proname=:proname and n.nspname=:nspname"; schemaName = (fullName[0].IndexOf("\"") != -1) ? fullName[0] : fullName[0].ToLower(); procedureName = (fullName[1].IndexOf("\"") != -1) ? fullName[1] : fullName[1].ToLower(); @@ -1049,144 +1089,74 @@ namespace Npgsql { // Instead of defaulting don't use the nspname, as an alternative, query pg_proc and pg_namespace to try and determine the nspname. //schemaName = "public"; // This was removed after build 0.99.3 because the assumption that a function is in public is often incorrect. - returnRecordQuery = - "select count(*) > 0 from pg_proc p where prorettype = ( select oid from pg_type where typname = 'record' ) and proargtypes=:proargtypes and proname=:proname and ('o' = any (proargmodes) OR 'b' = any(proargmodes))"; - + returnRecordQuery = "select count(*) > 0 from pg_proc p where prorettype = ( select oid from pg_type where typname = :typename ) and proargtypes=:proargtypes and proname=:proname"; + procedureName = (CommandText.IndexOf("\"") != -1) ? CommandText : CommandText.ToLower(); } - + + + NpgsqlCommand c = new NpgsqlCommand(returnRecordQuery, Connection); - - c.Parameters.Add(new NpgsqlParameter("proargtypes", NpgsqlDbType.Oidvector)); + + c.Parameters.Add(new NpgsqlParameter("typename", NpgsqlDbType.Text)); + c.Parameters.Add(new NpgsqlParameter("proargtypes", NpgsqlDbType.Text)); c.Parameters.Add(new NpgsqlParameter("proname", NpgsqlDbType.Text)); - - c.Parameters[0].Value = parameterTypes.ToString(); - c.Parameters[1].Value = procedureName; + + c.Parameters[0].Value = ReturnType; + c.Parameters[1].Value = parameterTypes.ToString(); + c.Parameters[2].Value = procedureName; if (schemaName != null && schemaName.Length > 0) { c.Parameters.Add(new NpgsqlParameter("nspname", NpgsqlDbType.Text)); - c.Parameters[2].Value = schemaName; + c.Parameters[3].Value = schemaName; } + - - Boolean ret = (Boolean)c.ExecuteScalar(); + Boolean ret = (Boolean) c.ExecuteScalar(); // reset any responses just before getting new ones - m_Connector.Mediator.ResetResponses(); - + connector.Mediator.ResetResponses(); + // Set command timeout. - m_Connector.Mediator.CommandTimeout = CommandTimeout; - + connector.Mediator.CommandTimeout = CommandTimeout; + return ret; - } - private Boolean CheckFunctionReturn(String ReturnType) - { - // Updated after 0.99.3 to support the optional existence of a name qualifying schema and allow for case insensitivity - // when the schema or procedure name do not contain a quote. - // The hard-coded schema name 'public' was replaced with code that uses schema as a qualifier, only if it is provided. - - String returnRecordQuery; - - StringBuilder parameterTypes = new StringBuilder(""); - - - // Process parameters - - foreach (NpgsqlParameter p in Parameters) - { - if ((p.Direction == ParameterDirection.Input) || (p.Direction == ParameterDirection.InputOutput)) - { - parameterTypes.Append(Connection.Connector.OidToNameMapping[p.TypeInfo.Name].OID + " "); - } - } - - - // Process schema name. - - String schemaName = String.Empty; - String procedureName = String.Empty; - - - String[] fullName = CommandText.Split('.'); - if (fullName.Length == 2) - { - returnRecordQuery = - "select count(*) > 0 from pg_proc p left join pg_namespace n on p.pronamespace = n.oid where prorettype = ( select oid from pg_type where typname = :typename ) and proargtypes=:proargtypes and proname=:proname and n.nspname=:nspname"; - - schemaName = (fullName[0].IndexOf("\"") != -1) ? fullName[0] : fullName[0].ToLower(); - procedureName = (fullName[1].IndexOf("\"") != -1) ? fullName[1] : fullName[1].ToLower(); - } - else - { - // Instead of defaulting don't use the nspname, as an alternative, query pg_proc and pg_namespace to try and determine the nspname. - //schemaName = "public"; // This was removed after build 0.99.3 because the assumption that a function is in public is often incorrect. - returnRecordQuery = - "select count(*) > 0 from pg_proc p where prorettype = ( select oid from pg_type where typname = :typename ) and proargtypes=:proargtypes and proname=:proname"; - - procedureName = (CommandText.IndexOf("\"") != -1) ? CommandText : CommandText.ToLower(); - } - - - bool ret; - - using (NpgsqlCommand c = new NpgsqlCommand(returnRecordQuery, Connection)) - { - c.Parameters.Add(new NpgsqlParameter("typename", NpgsqlDbType.Text)); - c.Parameters.Add(new NpgsqlParameter("proargtypes", NpgsqlDbType.Oidvector)); - c.Parameters.Add(new NpgsqlParameter("proname", NpgsqlDbType.Text)); - - c.Parameters[0].Value = ReturnType; - c.Parameters[1].Value = parameterTypes.ToString(); - c.Parameters[2].Value = procedureName; - - if (schemaName != null && schemaName.Length > 0) - { - c.Parameters.Add(new NpgsqlParameter("nspname", NpgsqlDbType.Text)); - c.Parameters[3].Value = schemaName; - } - - - ret = (Boolean)c.ExecuteScalar(); - } - - // reset any responses just before getting new ones - m_Connector.Mediator.ResetResponses(); - - // Set command timeout. - m_Connector.Mediator.CommandTimeout = CommandTimeout; - - return ret; } - - + + private String AddFunctionReturnsRecordSupport(String OriginalResult) { + StringBuilder sb = new StringBuilder(OriginalResult); - + sb.Append(" as ("); - - foreach (NpgsqlParameter p in Parameters) + + foreach(NpgsqlParameter p in Parameters) { - if ((p.Direction == ParameterDirection.Output) || (p.Direction == ParameterDirection.InputOutput)) + if ((p.Direction == ParameterDirection.Output) || + (p.Direction == ParameterDirection.InputOutput)) { - sb.Append(String.Format("{0} {1}, ", p.CleanName, p.TypeInfo.Name)); + sb.Append(String.Format("{0} {1}, ", p.ParameterName.Substring(1), p.TypeInfo.Name)); } } - + String result = sb.ToString(); - + result = result.Remove(result.Length - 2, 1); - + result += ")"; - - + + + return result; + + } - + ///<summary> /// This methods takes a string with a function call witch returns a refcursor or a set of /// refcursor. It will return the names of the open cursors/portals which will hold @@ -1194,40 +1164,42 @@ namespace Npgsql /// in form of one resultset for each cursor open. This way, clients don't need to do anything /// else besides calling function normally to get results in this way. ///</summary> + private String ProcessRefcursorFunctionReturn(String FunctionCall) { + NpgsqlCommand c = new NpgsqlCommand(FunctionCall, Connection); + + NpgsqlDataReader dr = c.ExecuteReader(); + StringBuilder sb = new StringBuilder(); - using (NpgsqlCommand c = new NpgsqlCommand(FunctionCall, Connection)) + + while (dr.Read()) { - using (NpgsqlDataReader dr = c.GetReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) - { - while (dr.Read()) - { - sb.Append("fetch all from \"").Append(dr.GetString(0)).Append("\";"); - } - } + sb.Append("fetch all from \"").Append(dr.GetString(0)).Append("\";"); + } - + sb.Append(";"); // Just in case there is no response from refcursor function return. - + // reset any responses just before getting new ones - m_Connector.Mediator.ResetResponses(); - + connector.Mediator.ResetResponses(); + // Set command timeout. - m_Connector.Mediator.CommandTimeout = CommandTimeout; - + connector.Mediator.CommandTimeout = CommandTimeout; + return sb.ToString(); + + } + private String GetPreparedCommandText() { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetPreparedCommandText"); if (parameters.Count == 0) - { - return string.Format("execute {0}", planName); - } + return "execute " + planName; StringBuilder result = new StringBuilder("execute " + planName + '('); @@ -1235,46 +1207,40 @@ namespace Npgsql for (Int32 i = 0; i < parameters.Count; i++) { - if (parameters[i].UseCast) - result.Append(string.Format("{0}::{1},", parameters[i].TypeInfo.ConvertToBackend(parameters[i].Value, false), parameters[i].TypeInfo.CastName)); - else - result.Append(parameters[i].TypeInfo.ConvertToBackend(parameters[i].Value, false) + ','); - + result.Append(parameters[i].TypeInfo.ConvertToBackend(parameters[i].Value, false) + ','); } result = result.Remove(result.Length - 1, 1); result.Append(')'); return result.ToString(); + } + private String GetParseCommandText() { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetParseCommandText"); - Boolean addProcedureParenthesis = false; // Do not add procedure parenthesis by default. - + Boolean addProcedureParenthesis = false; // Do not add procedure parenthesis by default. + String parseCommand = text; if (type == CommandType.StoredProcedure) { // Check if just procedure name was passed. If so, does not replace parameter names and just pass parameter values in order they were added in parameters collection. - if (!parseCommand.Trim().EndsWith(")")) + if (!parseCommand.Trim().EndsWith(")")) { addProcedureParenthesis = true; parseCommand += "("; } - - parseCommand = string.Format("select * from {0}", parseCommand); // This syntax is only available in 7.3+ as well SupportsPrepare. - } - else - { - if (type == CommandType.TableDirect) - { - return string.Format("select * from {0}", parseCommand); // There is no parameter support on TableDirect. - } + + parseCommand = "select * from " + parseCommand; // This syntax is only available in 7.3+ as well SupportsPrepare. } + else if (type == CommandType.TableDirect) + return "select * from " + parseCommand; // There is no parameter support on TableDirect. + if (parameters.Count > 0) { // The ReplaceParameterValue below, also checks if the parameter is present. @@ -1285,43 +1251,28 @@ namespace Npgsql for (i = 0; i < parameters.Count; i++) { if ((parameters[i].Direction == ParameterDirection.Input) || - (parameters[i].Direction == ParameterDirection.InputOutput)) + (parameters[i].Direction == ParameterDirection.InputOutput)) { + if (!addProcedureParenthesis) { //result = result.Replace(":" + parameterName, parameters[i].Value.ToString()); - parameterName = parameters[i].CleanName; + parameterName = parameters[i].ParameterName; //textCommand = textCommand.Replace(':' + parameterName, "$" + (i+1)); - - // Just add typecast if needed. - if (parameters[i].UseCast) - parseCommand = ReplaceParameterValue(parseCommand, parameterName, string.Format("${0}::{1}", (i + 1), parameters[i].TypeInfo.CastName)); - else - parseCommand = ReplaceParameterValue(parseCommand, parameterName, string.Format("${0}", (i + 1))); + parseCommand = ReplaceParameterValue(parseCommand, parameterName, "$" + (i+1) + "::" + parameters[i].TypeInfo.Name); } else - { - if (parameters[i].UseCast) - parseCommand += string.Format("${0}::{1}", (i + 1), parameters[i].TypeInfo.CastName); - else - parseCommand += string.Format("${0}", (i + 1)); - } + parseCommand += "$" + (i+1) + "::" + parameters[i].TypeInfo.Name; } + } } + if (addProcedureParenthesis) + return parseCommand + ")"; + else + return parseCommand; - return string.Format("{0}{1}", parseCommand, addProcedureParenthesis ? ")" : string.Empty); - - - //if (addProcedureParenthesis) - //{ - // return parseCommand + ")"; - //} - //else - //{ - // return parseCommand; - //} } @@ -1329,7 +1280,7 @@ namespace Npgsql { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetPrepareCommandText"); - Boolean addProcedureParenthesis = false; // Do not add procedure parenthesis by default. + Boolean addProcedureParenthesis = false; // Do not add procedure parenthesis by default. planName = Connector.NextPlanName(); @@ -1340,18 +1291,16 @@ namespace Npgsql if (type == CommandType.StoredProcedure) { // Check if just procedure name was passed. If so, does not replace parameter names and just pass parameter values in order they were added in parameters collection. - if (!textCommand.Trim().EndsWith(")")) + if (!textCommand.Trim().EndsWith(")")) { addProcedureParenthesis = true; textCommand += "("; } - + textCommand = "select * from " + textCommand; } else if (type == CommandType.TableDirect) - { return "select * from " + textCommand; // There is no parameter support on TableDirect. - } if (parameters.Count > 0) @@ -1364,22 +1313,22 @@ namespace Npgsql for (i = 0; i < parameters.Count; i++) { if ((parameters[i].Direction == ParameterDirection.Input) || - (parameters[i].Direction == ParameterDirection.InputOutput)) + (parameters[i].Direction == ParameterDirection.InputOutput)) { + if (!addProcedureParenthesis) { //result = result.Replace(":" + parameterName, parameters[i].Value.ToString()); - parameterName = parameters[i].CleanName; + parameterName = parameters[i].ParameterName; // The space in front of '$' fixes a parsing problem in 7.3 server // which gives errors of operator when finding the caracters '=$' in // prepare text - textCommand = ReplaceParameterValue(textCommand, parameterName, " $" + (i + 1)); + textCommand = ReplaceParameterValue(textCommand, parameterName, " $" + (i+1)); } else - { - textCommand += " $" + (i + 1); - } + textCommand += " $" + (i+1); } + } //[TODO] Check if there are any missing parameters in the query. @@ -1390,106 +1339,204 @@ namespace Npgsql for (i = 0; i < parameters.Count; i++) { // command.Append(NpgsqlTypesHelper.GetDefaultTypeInfo(parameters[i].DbType)); - if (parameters[i].UseCast) - command.Append(parameters[i].TypeInfo.Name); - else - command.Append("unknown"); + command.Append(parameters[i].TypeInfo.Name); command.Append(','); } command = command.Remove(command.Length - 1, 1); command.Append(')'); - } + } + if (addProcedureParenthesis) - { textCommand += ")"; - } command.Append(" as "); command.Append(textCommand); return command.ToString(); + } private static String ReplaceParameterValue(String result, String parameterName, String paramVal) { + String quote_pattern = @"['][^']*[']"; - string parameterMarker = string.Empty; - // search parameter marker since it is not part of the name - String pattern = "[- |\n\r\t,)(;=+/<>][:|@]" + parameterMarker + parameterName + "([- |\n\r\t,)(;=+/<>]|$)"; + String pattern = "[- |\n\r\t,)(;=+/]" + parameterName + "([- |\n\r\t,)(;=+/]|$)"; Int32 start, end; String withoutquote = result; Boolean found = false; // First of all // Suppress quoted string from query (because we ave to ignore them) - MatchCollection results = Regex.Matches(result, quote_pattern); + MatchCollection results = Regex.Matches(result,quote_pattern); foreach (Match match in results) { start = match.Index; end = match.Index + match.Length; - String spaces = new String(' ', match.Length - 2); - withoutquote = withoutquote.Substring(0, start + 1) + spaces + withoutquote.Substring(end - 1); + String spaces = new String(' ', match.Length-2); + withoutquote = withoutquote.Substring(0,start + 1) + spaces + withoutquote.Substring(end - 1); } do { // Now we look for the searched parameters on the "withoutquote" string - results = Regex.Matches(withoutquote, pattern); + results = Regex.Matches(withoutquote,pattern); if (results.Count == 0) - { - // If no parameter is found, go out! + // If no parameter is found, go out! break; - } // We take the first parameter found found = true; Match match = results[0]; start = match.Index; - if ((match.Length - parameterName.Length) == 3) - { + if ((match.Length - parameterName.Length) == 2) // If the found string is not the end of the string end = match.Index + match.Length - 1; - } else - { // If the found string is the end of the string end = match.Index + match.Length; - } result = result.Substring(0, start + 1) + paramVal + result.Substring(end); - withoutquote = withoutquote.Substring(0, start + 1) + paramVal + withoutquote.Substring(end); + withoutquote = withoutquote.Substring(0,start + 1) + paramVal + withoutquote.Substring(end); } while (true); if (!found) + throw new IndexOutOfRangeException (String.Format(resman.GetString("Exception_ParamNotInQuery"), + parameterName)); + return result; + } + + + private String AddSingleRowBehaviorSupport(String ResultCommandText) + { + + ResultCommandText = ResultCommandText.Trim(); + + // Do not add SingleRowBehavior if SchemaOnly behavior is set. + + if ((commandBehavior & CommandBehavior.SchemaOnly) == CommandBehavior.SchemaOnly) + return ResultCommandText; + + if ((commandBehavior & CommandBehavior.SingleRow) == CommandBehavior.SingleRow) { - throw new IndexOutOfRangeException(String.Format(resman.GetString("Exception_ParamNotInQuery"), parameterName)); + if (ResultCommandText.EndsWith(";")) + ResultCommandText = ResultCommandText.Substring(0, ResultCommandText.Length - 1); + ResultCommandText += " limit 1;"; + } - return result; + + + + return ResultCommandText; + + } + + private String AddSchemaOnlyBehaviorSupport(String ResultCommandText) + { + + ResultCommandText = ResultCommandText.Trim(); + + if ((commandBehavior & CommandBehavior.SchemaOnly) == CommandBehavior.SchemaOnly) + { + if (ResultCommandText.EndsWith(";")) + ResultCommandText = ResultCommandText.Substring(0, ResultCommandText.Length - 1); + ResultCommandText += " limit 0;"; + + } + + + return ResultCommandText; + } - private void SetCommandTimeout() + + private void ExecuteCommand() { - if (Connection != null) + try { - timeout = Connection.CommandTimeout; + + // Check the connection state first. + CheckConnectionState(); + + // reset any responses just before getting new ones + Connector.Mediator.ResetResponses(); + + // Set command timeout. + connector.Mediator.CommandTimeout = CommandTimeout; + + + connector.StopNotificationThread(); + + + if (parse == null) + { + connector.Query(this); + + + connector.ResumeNotificationThread(); + + // Check for errors and/or notifications and do the Right Thing. + connector.CheckErrorsAndNotifications(); + + + } else { - timeout = (int)NpgsqlConnectionStringBuilder.GetDefaultValue(Keywords.CommandTimeout); + try + { + + BindParameters(); + + connector.Execute(new NpgsqlExecute(bind.PortalName, 0)); + + // Check for errors and/or notifications and do the Right Thing. + connector.CheckErrorsAndNotifications(); + } + catch + { + // As per documentation: + // "[...] When an error is detected while processing any extended-query message, + // the backend issues ErrorResponse, then reads and discards messages until a + // Sync is reached, then issues ReadyForQuery and returns to normal message processing.[...]" + // So, send a sync command if we get any problems. + + connector.Sync(); + + throw; + } + finally + { + connector.ResumeNotificationThread(); + } + } + + } + + catch(IOException e) + { + ClearPoolAndThrowException(e); } + } - internal NpgsqlException ClearPoolAndCreateException(Exception e) + private void SetCommandTimeout() { - Connection.ClearPool(); - return new NpgsqlException(resman.GetString("Exception_ConnectionBroken"), e); + if (Connector != null) + timeout = Connector.CommandTimeout; + else + timeout = ConnectionStringDefaults.CommandTimeout; } - public override bool DesignTimeVisible + private void ClearPoolAndThrowException(Exception e) { - get { return designTimeVisible; } - set { designTimeVisible = value; } + Connection.ClearPool(); + throw new NpgsqlException(resman.GetString("Exception_ConnectionBroken"), e); + } + + + + } } diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlCommandBuilder.cs b/mcs/class/Npgsql/Npgsql/NpgsqlCommandBuilder.cs index 9935d2e8ab6..4628d91ce0a 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlCommandBuilder.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlCommandBuilder.cs @@ -5,286 +5,488 @@ // // 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 +// "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: // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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; +using System.Resources; using System.Data; using System.Data.Common; -using System.Globalization; -using System.Resources; +using System.ComponentModel; using NpgsqlTypes; namespace Npgsql { - ///<summary> - /// This class is responsible to create database commands for automatic insert, update and delete operations. - ///</summary> - public sealed class NpgsqlCommandBuilder : DbCommandBuilder - { - // Logging related values - //private static readonly String CLASSNAME = "NpgsqlCommandBuilder"; - private readonly static ResourceManager resman = new ResourceManager(typeof (NpgsqlCommandBuilder)); - private NpgsqlRowUpdatingEventHandler rowUpdatingHandler; - - - public NpgsqlCommandBuilder() - : this(null) - { - } - public NpgsqlCommandBuilder(NpgsqlDataAdapter adapter) - : base() - { - DataAdapter = adapter; - this.QuotePrefix = "\""; - this.QuoteSuffix = "\""; - } + ///<summary> + /// This class is responsible to create database commands for automatic insert, update and delete operations. + ///</summary> + 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 NpgsqlDataAdapter data_adapter; + private NpgsqlCommand insert_command; + private NpgsqlCommand update_command; + private NpgsqlCommand delete_command; + + private string quotePrefix = "\""; + private string quoteSuffix = "\""; + private DataTable select_schema; + + public NpgsqlCommandBuilder () + {} + + public NpgsqlCommandBuilder (NpgsqlDataAdapter adapter) + { + DataAdapter = adapter; + } + + public NpgsqlDataAdapter DataAdapter { + get + { + return data_adapter; + } + set + { + if (data_adapter != null) + { + throw new InvalidOperationException ("DataAdapter is already set"); + } + data_adapter = value; + data_adapter.RowUpdating += new NpgsqlRowUpdatingEventHandler(OnRowUpdating); + } + } + + private void OnRowUpdating(Object sender, NpgsqlRowUpdatingEventArgs value) { + switch (value.StatementType) + { + case StatementType.Insert: + value.Command = GetInsertCommand(value.Row, false); + break; + case StatementType.Update: + value.Command = GetUpdateCommand(value.Row, false); + break; + case StatementType.Delete: + value.Command = GetDeleteCommand(value.Row, false); + break; + } + + DataColumnMappingCollection columnMappings = value.TableMapping.ColumnMappings; + foreach (IDataParameter parameter in value.Command.Parameters) + { + + string dsColumnName = parameter.SourceColumn; + if (columnMappings.Contains(parameter.SourceColumn)) + { + DataColumnMapping mapping = columnMappings[parameter.SourceColumn]; + if (mapping != null) + { + dsColumnName = mapping.DataSetColumn; + } + } + + DataRowVersion rowVersion = DataRowVersion.Default; + if (value.StatementType == StatementType.Update) + rowVersion = parameter.SourceVersion; + if (value.StatementType == StatementType.Delete) + rowVersion = DataRowVersion.Original; + parameter.Value = value.Row [dsColumnName, rowVersion]; + } + } + + public string QuotePrefix { + get + { + return quotePrefix; + } + set + { + quotePrefix = value; + } + } + + public string QuoteSuffix { + get + { + return quoteSuffix; + } + set + { + quoteSuffix = value; + } + } - public override string QuotePrefix - { - get { return base.QuotePrefix; } - set - { - if (String.IsNullOrEmpty(value)) - { - base.QuotePrefix = value; - } - else + ///<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) + { + + // Updated after 0.99.3 to support the optional existence of a name qualifying schema and case insensitivity when the schema ror procedure name do not contain a quote. + // This fixed an incompatibility with NpgsqlCommand.CheckFunctionReturn(String ReturnType) + String query = null; + string procedureName = null; + string schemaName = null; + string[] fullName = command.CommandText.Split('.'); + if (fullName.Length > 1 && fullName[0].Length > 0) + { + query = "select proargtypes from pg_proc p left join pg_namespace n on p.pronamespace = n.oid where proname=:proname and n.nspname=:nspname"; + schemaName = (fullName[0].IndexOf("\"") != -1) ? fullName[0] : fullName[0].ToLower(); + procedureName = (fullName[1].IndexOf("\"") != -1) ? fullName[1] : fullName[1].ToLower(); + } + else + { + query = "select proargtypes from pg_proc where proname = :proname"; + procedureName = (fullName[0].IndexOf("\"") != -1) ? fullName[0] : fullName[0].ToLower(); + } + + NpgsqlCommand c = new NpgsqlCommand(query, command.Connection); + c.Parameters.Add(new NpgsqlParameter("proname", NpgsqlDbType.Text)); + + + c.Parameters[0].Value = procedureName.Replace("\"", "").Trim(); + + if (fullName.Length > 1 && schemaName.Length > 0) + { + NpgsqlParameter prm = c.Parameters.Add(new NpgsqlParameter("nspname", NpgsqlDbType.Text)); + prm.Value = schemaName.Replace("\"", "").Trim(); + } + + 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.Clear(); + throw new InvalidOperationException(String.Format("Invalid parameter type: {0}", s)); + } + command.Parameters.Add(new NpgsqlParameter("parameter" + i++, c.Connector.OidToNameMapping[Int32.Parse(s)].NpgsqlDbType)); + } + + } + + private string GetQuotedName(string str) + { + string result = str; + if ((QuotePrefix != string.Empty) && !str.StartsWith(QuotePrefix)) + { + result = QuotePrefix + result; + } + if ((QuoteSuffix != string.Empty) && !str.EndsWith(QuoteSuffix)) + { + result = result + QuoteSuffix; + } + return result; + } + + + public NpgsqlCommand GetInsertCommand (DataRow row) + { + return GetInsertCommand(row, true); + } + + private NpgsqlCommand GetInsertCommand(DataRow row, bool setParameterValues) + { + if (insert_command == null) + { + string fields = ""; + string values = ""; + bool first = true; + if (select_schema == null) { - base.QuotePrefix = "\""; + BuildSchema(); } - } - } - - public override string QuoteSuffix - { - get { return base.QuoteSuffix; } - set - { - if (String.IsNullOrEmpty(value)) + string schema_name = string.Empty; + string table_name = string.Empty; + string quotedName; + NpgsqlCommand cmdaux = new NpgsqlCommand(); + foreach(DataRow schemaRow in select_schema.Rows) { - base.QuoteSuffix = value; + if (!(bool)schemaRow["IsAutoIncrement"]) + { + if (!first) + { + fields += ", "; + values += ", "; + } + else + { + schema_name = (string)schemaRow["BaseSchemaName"]; + table_name = (string)schemaRow["BaseTableName"]; + if (table_name == null || table_name.Length == 0) + { + table_name = row.Table.TableName; + } + } + quotedName = GetQuotedName((string)schemaRow["BaseColumnName"]); + DataColumn column = row.Table.Columns[(string)schemaRow["ColumnName"]]; + + fields += quotedName; + values += ":param_" + column.ColumnName; + first = false; + + NpgsqlParameter aux = new NpgsqlParameter("param_" + column.ColumnName, NpgsqlTypesHelper.GetNativeTypeInfo(column.DataType)); + aux.Direction = ParameterDirection.Input; + aux.SourceColumn = column.ColumnName; + cmdaux.Parameters.Add(aux); + } } - else + cmdaux.CommandText = "insert into " + QualifiedTableName(schema_name, table_name) + " (" + fields + ") values (" + values + ")"; + cmdaux.Connection = data_adapter.SelectCommand.Connection; + insert_command = cmdaux; + } + if (setParameterValues) + { + SetParameterValuesFromRow(insert_command, row); + } + return insert_command; + } + + public NpgsqlCommand GetUpdateCommand (DataRow row) + { + return GetUpdateCommand(row, true); + } + + private NpgsqlCommand GetUpdateCommand(DataRow row, bool setParameterValues) + { + if (update_command == null) + { + string sets = ""; + string wheres = ""; + bool first = true; + if (select_schema == null) { - base.QuoteSuffix = "\""; + BuildSchema(); } - } - } - - ///<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) - { - // Updated after 0.99.3 to support the optional existence of a name qualifying schema and case insensitivity when the schema ror procedure name do not contain a quote. - // This fixed an incompatibility with NpgsqlCommand.CheckFunctionReturn(String ReturnType) - String query = null; - string procedureName = null; - string schemaName = null; - string[] fullName = command.CommandText.Split('.'); - if (fullName.Length > 1 && fullName[0].Length > 0) - { - query = - "select proargtypes from pg_proc p left join pg_namespace n on p.pronamespace = n.oid where proname=:proname and n.nspname=:nspname"; - schemaName = (fullName[0].IndexOf("\"") != -1) ? fullName[0] : fullName[0].ToLower(); - procedureName = (fullName[1].IndexOf("\"") != -1) ? fullName[1] : fullName[1].ToLower(); - } - else - { - query = "select proargtypes from pg_proc where proname = :proname"; - procedureName = (fullName[0].IndexOf("\"") != -1) ? fullName[0] : fullName[0].ToLower(); - } - - using (NpgsqlCommand c = new NpgsqlCommand(query, command.Connection)) - { - c.Parameters.Add(new NpgsqlParameter("proname", NpgsqlDbType.Text)); - c.Parameters[0].Value = procedureName.Replace("\"", "").Trim(); - if (fullName.Length > 1 && !String.IsNullOrEmpty(schemaName)) + string schema_name = string.Empty; + string table_name = string.Empty; + string quotedName; + NpgsqlCommand cmdaux = new NpgsqlCommand(); + foreach(DataRow schemaRow in select_schema.Rows) { - NpgsqlParameter prm = c.Parameters.Add(new NpgsqlParameter("nspname", NpgsqlDbType.Text)); - prm.Value = schemaName.Replace("\"", "").Trim(); + if (!first) + { + sets += ", "; + wheres += " and "; + } + else + { + schema_name = (string)schemaRow["BaseSchemaName"]; + table_name = (string)schemaRow["BaseTableName"]; + if (table_name == null || table_name.Length == 0) + { + table_name = row.Table.TableName; + } + } + quotedName = GetQuotedName((string)schemaRow["BaseColumnName"]); + DataColumn column = row.Table.Columns[(string)schemaRow["ColumnName"]]; + sets += String.Format("{0} = :s_param_{1}", quotedName, column.ColumnName); + wheres += String.Format("(({0} is null) or ({0} = :w_param_{1}))", quotedName, column.ColumnName); + first = false; + + NpgsqlNativeTypeInfo typeInfo = NpgsqlTypesHelper.GetNativeTypeInfo(column.DataType); + NpgsqlParameter aux_set = new NpgsqlParameter("s_param_" + column.ColumnName, typeInfo); + aux_set.Direction = ParameterDirection.Input; + aux_set.SourceColumn = column.ColumnName; + aux_set.SourceVersion = DataRowVersion.Current; + cmdaux.Parameters.Add(aux_set); + + NpgsqlParameter aux_where = new NpgsqlParameter("w_param_" + column.ColumnName, typeInfo); + aux_where.Direction = ParameterDirection.Input; + aux_where.SourceColumn = column.ColumnName; + aux_where.SourceVersion = DataRowVersion.Original; + cmdaux.Parameters.Add(aux_where); } - String types = (String) c.ExecuteScalar(); - if (types == null) + cmdaux.CommandText = "update " + QualifiedTableName(schema_name, table_name) + " set " + sets + " where ( " + wheres + " )"; + cmdaux.Connection = data_adapter.SelectCommand.Connection; + update_command = cmdaux; + + } + if (setParameterValues) + { + SetParameterValuesFromRow(update_command, row); + } + return update_command; + } + + public NpgsqlCommand GetDeleteCommand (DataRow row) + { + return GetDeleteCommand(row, true); + } + + private NpgsqlCommand GetDeleteCommand(DataRow row, bool setParameterValues) + { + if (delete_command == null) + { + string wheres = ""; + bool first = true; + if (select_schema == null) { - throw new InvalidOperationException( - String.Format(resman.GetString("Exception_InvalidFunctionName"), command.CommandText)); + BuildSchema(); } - - command.Parameters.Clear(); - Int32 i = 1; - foreach (String s in types.Split()) + string schema_name = string.Empty; + string table_name = string.Empty; + string quotedName; + NpgsqlCommand cmdaux = new NpgsqlCommand(); + foreach(DataRow schemaRow in select_schema.Rows) { - NpgsqlBackendTypeInfo typeInfo = null; - if (!c.Connector.OidToNameMapping.TryGetValue(int.Parse(s), out typeInfo)) + if (!first) { - command.Parameters.Clear(); - throw new InvalidOperationException(String.Format("Invalid parameter type: {0}", s)); + wheres += " and "; } - command.Parameters.Add(new NpgsqlParameter("parameter" + i++, typeInfo.NpgsqlDbType)); + else + { + schema_name = (string)schemaRow["BaseSchemaName"]; + table_name = (string)schemaRow["BaseTableName"]; + if (table_name == null || table_name.Length == 0) + { + table_name = row.Table.TableName; + } + } + + quotedName = GetQuotedName((string)schemaRow["BaseColumnName"]); + DataColumn column = row.Table.Columns[(string)schemaRow["ColumnName"]]; + + wheres += String.Format("(({0} is null) or ({0} = :param_{1}))", quotedName , column.ColumnName); + first = false; + + NpgsqlParameter aux = new NpgsqlParameter("param_" + column.ColumnName, NpgsqlTypesHelper.GetNativeTypeInfo(column.DataType)); + aux.Direction = ParameterDirection.Input; + aux.SourceColumn = column.ColumnName; + aux.SourceVersion = DataRowVersion.Original; + cmdaux.Parameters.Add(aux); } - } - } - - public new NpgsqlCommand GetInsertCommand() - { - return (NpgsqlCommand) base.GetInsertCommand(); - } - - public new NpgsqlCommand GetInsertCommand(bool useColumnsForParameterNames) - { - return (NpgsqlCommand) base.GetInsertCommand(useColumnsForParameterNames); - } - - public new NpgsqlCommand GetUpdateCommand() - { - return (NpgsqlCommand) base.GetUpdateCommand(); - } - - public new NpgsqlCommand GetUpdateCommand(bool useColumnsForParameterNames) - { - return (NpgsqlCommand) base.GetUpdateCommand(useColumnsForParameterNames); - } - - public new NpgsqlCommand GetDeleteCommand() - { - return (NpgsqlCommand) base.GetDeleteCommand(); - } - - public new NpgsqlCommand GetDeleteCommand(bool useColumnsForParameterNames) - { - return (NpgsqlCommand) base.GetDeleteCommand(useColumnsForParameterNames); - } - - //never used - //private string QualifiedTableName(string schema, string tableName) - //{ - // if (schema == null || schema.Length == 0) - // { - // return tableName; - // } - // else - // { - // return schema + "." + tableName; - // } - //} - -/* - private static void SetParameterValuesFromRow(NpgsqlCommand command, DataRow row) - { - foreach (NpgsqlParameter parameter in command.Parameters) - { - parameter.Value = row[parameter.SourceColumn, parameter.SourceVersion]; - } - } -*/ - - protected override void ApplyParameterInfo(DbParameter p, DataRow row, StatementType statementType, bool whereClause) - { - NpgsqlParameter parameter = (NpgsqlParameter) p; - parameter.DbType = NpgsqlTypesHelper.GetNativeTypeInfo((Type) row[SchemaTableColumn.DataType]).DbType; - } - - protected override string GetParameterName(int parameterOrdinal) - { - return String.Format(CultureInfo.InvariantCulture, "@p{0}", parameterOrdinal); - } - - protected override string GetParameterName(string parameterName) - { - return String.Format(CultureInfo.InvariantCulture, "@{0}", parameterName); - } - - protected override string GetParameterPlaceholder(int parameterOrdinal) - { - return GetParameterName(parameterOrdinal); - } - - protected override void SetRowUpdatingHandler(DbDataAdapter adapter) + cmdaux.CommandText = "delete from " + QualifiedTableName(schema_name, table_name) + " where ( " + wheres + " )"; + cmdaux.Connection = data_adapter.SelectCommand.Connection; + delete_command = cmdaux; + } + if (setParameterValues) + { + SetParameterValuesFromRow(delete_command, row); + } + return delete_command; + } + + public void RefreshSchema () + { + insert_command = null; + update_command = null; + delete_command = null; + select_schema = null; + } + + protected override void Dispose (bool disposing) + { + if (!disposed) + { + if (disposing) + { + if (insert_command != null) + { + insert_command.Dispose(); + } + if (update_command != null) + { + update_command.Dispose(); + } + if (delete_command != null) + { + delete_command.Dispose(); + } + + data_adapter.RowUpdating -= new NpgsqlRowUpdatingEventHandler(OnRowUpdating); + } + } + base.Dispose(disposing); + } + + private void BuildSchema() { - if (!(adapter is NpgsqlDataAdapter)) - { - throw new InvalidOperationException("adapter needs to be a NpgsqlDataAdapter"); - } - - - this.rowUpdatingHandler = new NpgsqlRowUpdatingEventHandler(this.RowUpdatingHandler); - - ((NpgsqlDataAdapter) adapter).RowUpdating += this.rowUpdatingHandler; - } - - - private void RowUpdatingHandler(object sender, NpgsqlRowUpdatingEventArgs e) - - { - base.RowUpdatingHandler(e); - } - - - public override string QuoteIdentifier(string unquotedIdentifier) - - { - if (unquotedIdentifier == null) - + if (select_schema == null) { - throw new ArgumentNullException("Unquoted identifier parameter cannot be 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(); + } + } } - - - return String.Format("{0}{1}{2}", this.QuotePrefix, unquotedIdentifier, this.QuoteSuffix); } - - public override string UnquoteIdentifier(string quotedIdentifier) - - { - if (quotedIdentifier == null) - - { - throw new ArgumentNullException("Quoted identifier parameter cannot be null"); - } - - - string unquotedIdentifier = quotedIdentifier.Trim(); - - - if (unquotedIdentifier.StartsWith(this.QuotePrefix)) - - { - unquotedIdentifier = unquotedIdentifier.Remove(0, 1); - } - - if (unquotedIdentifier.EndsWith(this.QuoteSuffix)) - - { - unquotedIdentifier = unquotedIdentifier.Remove(unquotedIdentifier.Length - 1, 1); - } - - - return unquotedIdentifier; - } - } -}
\ No newline at end of file + /*~NpgsqlCommandBuilder () + { + Dispose(false); + }*/ + + private string QualifiedTableName(string schema, string tableName) + { + if (schema == null || schema.Length == 0) + { + return GetQuotedName(tableName); + } + else + { + return GetQuotedName(schema) + "." + GetQuotedName(tableName); + } + } + + private static void SetParameterValuesFromRow(NpgsqlCommand command, DataRow row) + { + foreach (NpgsqlParameter parameter in command.Parameters) + { + parameter.Value = row[parameter.SourceColumn, parameter.SourceVersion]; + } + } + } + +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlConnectedState.cs b/mcs/class/Npgsql/Npgsql/NpgsqlConnectedState.cs index 67b3e197d93..a32d89430fb 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlConnectedState.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlConnectedState.cs @@ -8,54 +8,72 @@ // http://gborg.postgresql.org/project/npgsql/projdisplay.php // // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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; + namespace Npgsql { - internal sealed class NpgsqlConnectedState : NpgsqlState - { - public static readonly NpgsqlConnectedState Instance = new NpgsqlConnectedState(); - - private NpgsqlConnectedState() - { - } - - public override void Startup(NpgsqlConnector context) - { - NpgsqlStartupPacket startupPacket = new NpgsqlStartupPacket(296, //Not used. - context.BackendProtocolVersion, context.Database, - context.UserName, "", "", ""); - - startupPacket.WriteToStream(new BufferedStream(context.Stream)); - context.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); - } - } -}
\ No newline at end of file + internal sealed class NpgsqlConnectedState : NpgsqlState + { + + private static NpgsqlConnectedState _instance = null; + + private NpgsqlConnectedState() + {} + + public static NpgsqlConnectedState Instance + { + get + { + if ( _instance == null ) + { + _instance = new NpgsqlConnectedState(); + } + return _instance; + } + } + + public override void Startup(NpgsqlConnector context) + { + NpgsqlStartupPacket startupPacket = new NpgsqlStartupPacket(296, //Not used. + context.BackendProtocolVersion, + context.Database, + context.UserName, + "", + "", + ""); + + 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); + + + } + + } +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlConnection.cs b/mcs/class/Npgsql/Npgsql/NpgsqlConnection.cs index 56b5949e959..cbc595c351f 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlConnection.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlConnection.cs @@ -9,969 +9,881 @@ // http://gborg.postgresql.org/project/npgsql/projdisplay.php // // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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.ComponentModel; using System.Data; -using System.Data.Common; +using System.Text; +using System.Collections; using System.Resources; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; -using System.Transactions; + using Mono.Security.Protocol.Tls; -using IsolationLevel = System.Data.IsolationLevel; -#if WITHDESIGN +using NpgsqlTypes; +#if WITHDESIGN +using Npgsql.Design; #endif namespace Npgsql { - /// <summary> - /// Represents the method that handles the <see cref="Npgsql.NpgsqlConnection.Notification">Notice</see> events. - /// </summary> - /// <param name="e">A <see cref="Npgsql.NpgsqlNoticeEventArgs">NpgsqlNoticeEventArgs</see> that contains the event data.</param> - public delegate void NoticeEventHandler(Object sender, NpgsqlNoticeEventArgs e); - - /// <summary> - /// Represents the method that handles the <see cref="Npgsql.NpgsqlConnection.Notification">Notification</see> events. - /// </summary> - /// <param name="sender">The source of the event.</param> - /// <param name="e">A <see cref="Npgsql.NpgsqlNotificationEventArgs">NpgsqlNotificationEventArgs</see> that contains the event data.</param> - public delegate void NotificationEventHandler(Object sender, NpgsqlNotificationEventArgs e); - - /// <summary> - /// This class represents a connection to a - /// PostgreSQL server. - /// </summary> -#if WITHDESIGN + /// <summary> + /// Represents the method that handles the <see cref="Npgsql.NpgsqlConnection.Notification">Notice</see> events. + /// </summary> + /// <param name="e">A <see cref="Npgsql.NpgsqlNoticeEventArgs">NpgsqlNoticeEventArgs</see> that contains the event data.</param> + public delegate void NoticeEventHandler(Object sender, NpgsqlNoticeEventArgs e); + + /// <summary> + /// Represents the method that handles the <see cref="Npgsql.NpgsqlConnection.Notification">Notification</see> events. + /// </summary> + /// <param name="sender">The source of the event.</param> + /// <param name="e">A <see cref="Npgsql.NpgsqlNotificationEventArgs">NpgsqlNotificationEventArgs</see> that contains the event data.</param> + public delegate void NotificationEventHandler(Object sender, NpgsqlNotificationEventArgs e); + + /// <summary> + /// This class represents a connection to a + /// PostgreSQL server. + /// </summary> + #if WITHDESIGN [System.Drawing.ToolboxBitmapAttribute(typeof(NpgsqlConnection))] -#endif + #endif + + public sealed class NpgsqlConnection : Component, IDbConnection, ICloneable + { + // Logging related values + private static readonly String CLASSNAME = "NpgsqlConnection"; + private static ResourceManager resman = new System.Resources.ResourceManager(typeof(NpgsqlConnection)); - public sealed class NpgsqlConnection : DbConnection, ICloneable - { - // Logging related values - private static readonly String CLASSNAME = "NpgsqlConnection"; - private static readonly ResourceManager resman = new ResourceManager(typeof(NpgsqlConnection)); - - // Parsed connection string cache - private static readonly Cache<NpgsqlConnectionStringBuilder> cache = new Cache<NpgsqlConnectionStringBuilder>(); - - /// <summary> - /// Occurs on NoticeResponses from the PostgreSQL backend. - /// </summary> - public event NoticeEventHandler Notice; - - internal NoticeEventHandler NoticeDelegate; - - /// <summary> - /// Occurs on NotificationResponses from the PostgreSQL backend. - /// </summary> - public event NotificationEventHandler Notification; - - internal NotificationEventHandler NotificationDelegate; - - /// <summary> - /// Mono.Security.Protocol.Tls.CertificateSelectionCallback delegate. - /// </summary> - public event CertificateSelectionCallback CertificateSelectionCallback; - - internal CertificateSelectionCallback CertificateSelectionCallbackDelegate; - - /// <summary> - /// Mono.Security.Protocol.Tls.CertificateValidationCallback delegate. - /// </summary> - public event CertificateValidationCallback CertificateValidationCallback; - - internal CertificateValidationCallback CertificateValidationCallbackDelegate; - - /// <summary> - /// Mono.Security.Protocol.Tls.PrivateKeySelectionCallback delegate. - /// </summary> - public event PrivateKeySelectionCallback PrivateKeySelectionCallback; - - internal PrivateKeySelectionCallback PrivateKeySelectionCallbackDelegate; - - // Set this when disposed is called. - private bool disposed = false; - - // Used when we closed the connector due to an error, but are pretending it's open. - private bool _fakingOpen; - - // Strong-typed ConnectionString values - private NpgsqlConnectionStringBuilder settings; - - // Connector being used for the active connection. - private NpgsqlConnector connector = null; - - private readonly NpgsqlPromotableSinglePhaseNotification promotable = null; - - /// <summary> - /// Initializes a new instance of the - /// <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see> class. - /// </summary> - public NpgsqlConnection() - : this(String.Empty) - { - } - - /// <summary> - /// Initializes a new instance of the - /// <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see> class - /// and sets the <see cref="Npgsql.NpgsqlConnection.ConnectionString">ConnectionString</see>. - /// </summary> - /// <param name="ConnectionString">The connection used to open the PostgreSQL database.</param> - public NpgsqlConnection(String ConnectionString) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME, "NpgsqlConnection()"); - - NpgsqlConnectionStringBuilder builder = cache[ConnectionString]; - if (builder == null) - { - settings = new NpgsqlConnectionStringBuilder(ConnectionString); - } - else - { - settings = builder.Clone(); - } - - LogConnectionString(); - - NoticeDelegate = new NoticeEventHandler(OnNotice); - NotificationDelegate = new NotificationEventHandler(OnNotification); - - CertificateValidationCallbackDelegate = new CertificateValidationCallback(DefaultCertificateValidationCallback); - CertificateSelectionCallbackDelegate = new CertificateSelectionCallback(DefaultCertificateSelectionCallback); - PrivateKeySelectionCallbackDelegate = new PrivateKeySelectionCallback(DefaultPrivateKeySelectionCallback); - - // Fix authentication problems. See https://bugzilla.novell.com/show_bug.cgi?id=MONO77559 and - // http://pgfoundry.org/forum/message.php?msg_id=1002377 for more info. - RSACryptoServiceProvider.UseMachineKeyStore = true; - - promotable = new NpgsqlPromotableSinglePhaseNotification(this); - } - - /// <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; - /// </li> - /// <li> - /// Timeout: Time to wait for connection open in seconds. Default is 15. - /// </li> - /// <li> - /// CommandTimeout: Time to wait for command to finish execution before throw an exception. In seconds. Default is 20. - /// </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> - /// <li> - /// SearchPath: Changes search path to specified and public schemas. - /// </li> - /// </ul> - /// </summary> - /// <value>The connection string that includes the server name, - /// the database name, and other parameters needed to establish - /// the initial connection. The default value is an empty string. - /// </value> + /// <summary> + /// Occurs on NoticeResponses from the PostgreSQL backend. + /// </summary> + public event NoticeEventHandler Notice; + internal NoticeEventHandler NoticeDelegate; -#if WITHDESIGN + /// <summary> + /// Occurs on NotificationResponses from the PostgreSQL backend. + /// </summary> + public event NotificationEventHandler Notification; + internal NotificationEventHandler NotificationDelegate; + + /// <summary> + /// Mono.Security.Protocol.Tls.CertificateSelectionCallback delegate. + /// </summary> + public event CertificateSelectionCallback CertificateSelectionCallback; + internal CertificateSelectionCallback CertificateSelectionCallbackDelegate; + + /// <summary> + /// Mono.Security.Protocol.Tls.CertificateValidationCallback delegate. + /// </summary> + public event CertificateValidationCallback CertificateValidationCallback; + internal CertificateValidationCallback CertificateValidationCallbackDelegate; + + /// <summary> + /// Mono.Security.Protocol.Tls.PrivateKeySelectionCallback delegate. + /// </summary> + public event PrivateKeySelectionCallback PrivateKeySelectionCallback; + internal PrivateKeySelectionCallback PrivateKeySelectionCallbackDelegate; + + // Set this when disposed is called. + private bool disposed = false; + + // Connection string values. + private NpgsqlConnectionString connection_string; + + // Connector being used for the active connection. + private NpgsqlConnector connector = null; + + + /// <summary> + /// Initializes a new instance of the + /// <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see> class. + /// </summary> + public NpgsqlConnection() : this(String.Empty) + {} + + /// <summary> + /// Initializes a new instance of the + /// <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see> class + /// and sets the <see cref="Npgsql.NpgsqlConnection.ConnectionString">ConnectionString</see>. + /// </summary> + /// <param name="ConnectionString">The connection used to open the PostgreSQL database.</param> + public NpgsqlConnection(String ConnectionString) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME, "NpgsqlConnection()"); + + connection_string = NpgsqlConnectionString.ParseConnectionString(ConnectionString); + LogConnectionString(); + + NoticeDelegate = new NoticeEventHandler(OnNotice); + NotificationDelegate = new NotificationEventHandler(OnNotification); + + CertificateValidationCallbackDelegate = new CertificateValidationCallback(DefaultCertificateValidationCallback); + CertificateSelectionCallbackDelegate = new CertificateSelectionCallback(DefaultCertificateSelectionCallback); + PrivateKeySelectionCallbackDelegate = new PrivateKeySelectionCallback(DefaultPrivateKeySelectionCallback); + } + + /// <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; + /// </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> + + /// CommandTimeout: Time to wait for command to finish execution before throw an exception. In seconds. Default is 20. + /// </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 + /// the initial connection. The default value is an empty string. + /// </value> + + #if WITHDESIGN [RefreshProperties(RefreshProperties.All), DefaultValue(""), RecommendedAsConfigurable(true)] [NpgsqlSysDescription("Description_ConnectionString", typeof(NpgsqlConnection)), Category("Data")] [Editor(typeof(ConnectionStringEditor), typeof(System.Drawing.Design.UITypeEditor))] -#endif + #endif + + public String ConnectionString { + get + { + return connection_string.ToString(); + } + set + { + // Connection string is used as the key to the connector. Because of this, + // we cannot change it while we own a connector. + CheckConnectionClosed(); + NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "ConnectionString", value); + connection_string = NpgsqlConnectionString.ParseConnectionString(value); + LogConnectionString(); + } + } - public override String ConnectionString - { - get { return settings.ConnectionString; } - set - { - // Connection string is used as the key to the connector. Because of this, - // we cannot change it while we own a connector. - CheckConnectionClosed(); - NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "ConnectionString", value); - NpgsqlConnectionStringBuilder builder = cache[value]; - if (builder == null) - { - settings = new NpgsqlConnectionStringBuilder(value); - } - else - { - settings = builder.Clone(); - } - LogConnectionString(); - } - } - - /// <summary> - /// Backend server host name. - /// </summary> - [Browsable(true)] - public String Host - { - get { return settings.Host; } - } - - /// <summary> - /// Backend server port. - /// </summary> - [Browsable(true)] - public Int32 Port - { - get { return settings.Port; } - } - - /// <summary> - /// If true, the connection will attempt to use SSL. - /// </summary> - [Browsable(true)] - public Boolean SSL - { - get { return settings.SSL; } - } - - /// <summary> - /// Gets the time to wait while trying to establish a connection - /// before terminating the attempt and generating an error. - /// </summary> - /// <value>The time (in seconds) to wait for a connection to open. The default value is 15 seconds.</value> + /// <summary> + /// Backend server host name. + /// </summary> + [Browsable(true)] + public String Host { + get + { + return connection_string.ToString(ConnectionStringKeys.Host); + } + } -#if WITHDESIGN + /// <summary> + /// Backend server port. + /// </summary> + [Browsable(true)] + public Int32 Port { + get + { + return connection_string.ToInt32(ConnectionStringKeys.Port, ConnectionStringDefaults.Port); + } + } + + /// <summary> + /// If true, the connection will attempt to use SSL. + /// </summary> + [Browsable(true)] + public Boolean SSL { + get + { + return connection_string.ToBool(ConnectionStringKeys.SSL); + } + } + + /// <summary> + /// Gets the time to wait while trying to establish a connection + /// before terminating the attempt and generating an error. + /// </summary> + /// <value>The time (in seconds) to wait for a connection to open. The default value is 15 seconds.</value> + + #if WITHDESIGN [NpgsqlSysDescription("Description_ConnectionTimeout", typeof(NpgsqlConnection))] -#endif + #endif + + public Int32 ConnectionTimeout { + get + { + return connection_string.ToInt32(ConnectionStringKeys.Timeout, ConnectionStringDefaults.Timeout); + } + } + + /// <summary> + /// Gets the time to wait before closing unused connections in the pool if the count + /// of all connections exeeds MinPoolSize. + /// </summary> + /// <remarks> + /// If connection pool contains unused connections for ConnectionLifeTime seconds, + /// the half of them will be closed. If there will be unused connections in a second + /// later then again the half of them will be closed and so on. + /// This strategy provide smooth change of connection count in the pool. + /// </remarks> + /// <value>The time (in seconds) to wait. The default value is 15 seconds.</value> + public Int32 ConnectionLifeTime { + get + { + return connection_string.ToInt32(ConnectionStringKeys.ConnectionLifeTime, ConnectionStringDefaults.ConnectionLifeTime); + } + } - public override Int32 ConnectionTimeout - { - get { return settings.Timeout; } - } - - /// <summary> - /// Gets the time to wait while trying to execute a command - /// before terminating the attempt and generating an error. - /// </summary> - /// <value>The time (in seconds) to wait for a command to complete. The default value is 20 seconds.</value> - public Int32 CommandTimeout - { - get { return settings.CommandTimeout; } - } - - /// <summary> - /// Gets the time to wait before closing unused connections in the pool if the count - /// of all connections exeeds MinPoolSize. - /// </summary> - /// <remarks> - /// If connection pool contains unused connections for ConnectionLifeTime seconds, - /// the half of them will be closed. If there will be unused connections in a second - /// later then again the half of them will be closed and so on. - /// This strategy provide smooth change of connection count in the pool. - /// </remarks> - /// <value>The time (in seconds) to wait. The default value is 15 seconds.</value> - public Int32 ConnectionLifeTime - { - get { return settings.ConnectionLifeTime; } - } - - ///<summary> - /// Gets the name of the current database or the database to be used after a connection is opened. - /// </summary> - /// <value>The name of the current database or the name of the database to be - /// used after a connection is opened. The default value is the empty string.</value> -#if WITHDESIGN + ///<summary> + /// Gets the name of the current database or the database to be used after a connection is opened. + /// </summary> + /// <value>The name of the current database or the name of the database to be + /// used after a connection is opened. The default value is the empty string.</value> + #if WITHDESIGN [NpgsqlSysDescription("Description_Database", typeof(NpgsqlConnection))] -#endif - - public override String Database - { - get { return settings.Database; } - } - - /// <summary> - /// Whether datareaders are loaded in their entirety (for compatibility with earlier code). - /// </summary> - public bool PreloadReader - { - get { return settings.PreloadReader; } - } - - /// <summary> - /// Gets the database server name. - /// </summary> - public override string DataSource - { - get { return settings.Host; } - } - - /// <summary> - /// Gets flag indicating if we are using Synchronous notification or not. - /// The default value is false. - /// </summary> - public Boolean SyncNotification - { - get { return settings.SyncNotification; } - } - - /// <summary> - /// Gets the current state of the connection. - /// </summary> - /// <value>A bitwise combination of the <see cref="System.Data.ConnectionState">ConnectionState</see> values. The default is <b>Closed</b>.</value> - [Browsable(false)] - public ConnectionState FullState - { - get - { - CheckNotDisposed(); - - if (connector != null) - { - return connector.State; - } - else - { - return ConnectionState.Closed; - } - } - } - - /// <summary> - /// Gets whether the current state of the connection is Open or Closed - /// </summary> - /// <value>ConnectionState.Open or ConnectionState.Closed</value> - [Browsable(false)] - public override ConnectionState State + #endif + + public String Database { + get + { + 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 (FullState & ConnectionState.Open) == ConnectionState.Open ? ConnectionState.Open : ConnectionState.Closed; - } - } - - /// <summary> - /// Version of the PostgreSQL backend. - /// This can only be called when there is an active connection. - /// </summary> - [Browsable(false)] - public Version PostgreSqlVersion - { - get - { - CheckConnectionOpen(); - return connector.ServerVersion; - } - } - - public override string ServerVersion - { - get { return PostgreSqlVersion.ToString(); } - } - - /// <summary> - /// Protocol version in use. - /// This can only be called when there is an active connection. - /// </summary> - [Browsable(false)] - public ProtocolVersion BackendProtocolVersion - { - get - { - CheckConnectionOpen(); - return connector.BackendProtocolVersion; - } - } - - /// <summary> - /// Process id of backend server. - /// This can only be called when there is an active connection. - /// </summary> - [Browsable(false)] - public Int32 ProcessID - { - get - { - CheckConnectionOpen(); - return connector.BackEndKeyData.ProcessID; - } - } - - /// <summary> - /// Begins a database transaction with the specified isolation level. - /// </summary> - /// <param name="isolationLevel">The <see cref="System.Data.IsolationLevel">isolation level</see> under which the transaction should run.</param> - /// <returns>An <see cref="System.Data.Common.DbTransaction">DbTransaction</see> - /// object representing the new transaction.</returns> - /// <remarks> - /// Currently the IsolationLevel ReadCommitted and Serializable are supported by the PostgreSQL backend. - /// There's no support for nested transactions. - /// </remarks> - protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "BeginDbTransaction", isolationLevel); - - return BeginTransaction(isolationLevel); - } - - /// <summary> - /// Begins a database transaction. - /// </summary> - /// <returns>A <see cref="Npgsql.NpgsqlTransaction">NpgsqlTransaction</see> - /// object representing the new transaction.</returns> - /// <remarks> - /// Currently there's no support for nested transactions. - /// </remarks> - public new NpgsqlTransaction BeginTransaction() - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "BeginTransaction"); - return this.BeginTransaction(IsolationLevel.ReadCommitted); - } - - /// <summary> - /// Begins a database transaction with the specified isolation level. - /// </summary> - /// <param name="level">The <see cref="System.Data.IsolationLevel">isolation level</see> under which the transaction should run.</param> - /// <returns>A <see cref="Npgsql.NpgsqlTransaction">NpgsqlTransaction</see> - /// object representing the new transaction.</returns> - /// <remarks> - /// Currently the IsolationLevel ReadCommitted and Serializable are supported by the PostgreSQL backend. - /// There's no support for nested transactions. - /// </remarks> - public new NpgsqlTransaction BeginTransaction(IsolationLevel level) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "BeginTransaction", level); - - CheckConnectionOpen(); - - if (connector.Transaction != null) - { - throw new InvalidOperationException(resman.GetString("Exception_NoNestedTransactions")); - } - - return new NpgsqlTransaction(this, level); - } - - /// <summary> - /// Opens a database connection with the property settings specified by the - /// <see cref="Npgsql.NpgsqlConnection.ConnectionString">ConnectionString</see>. - /// </summary> - public override void Open() - { - CheckConnectionClosed(); - - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Open"); - - // Check if there is any missing argument. - if (!settings.ContainsKey(Keywords.Host)) - { - throw new ArgumentException(resman.GetString("Exception_MissingConnStrArg"), - NpgsqlConnectionStringBuilder.GetKeyName(Keywords.Host)); - } - if (!settings.ContainsKey(Keywords.UserName) && !settings.ContainsKey(Keywords.IntegratedSecurity)) - { - throw new ArgumentException(resman.GetString("Exception_MissingConnStrArg"), - NpgsqlConnectionStringBuilder.GetKeyName(Keywords.UserName)); - } - - // 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(); - } - - if (Enlist) - { - promotable.Enlist(Transaction.Current); - } - } - - /// <summary> - /// This method changes the current database by disconnecting from the actual - /// database and connecting to the specified. - /// </summary> - /// <param name="dbName">The name of the database to use in place of the current database.</param> - public override void ChangeDatabase(String dbName) - { - CheckNotDisposed(); - - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ChangeDatabase", dbName); - - if (dbName == null) - { - throw new ArgumentNullException("dbName"); - } - - if (string.IsNullOrEmpty(dbName)) - { - throw new ArgumentOutOfRangeException(String.Format(resman.GetString("Exception_InvalidDbName"), dbName), "dbName"); - } - - String oldDatabaseName = Database; - - Close(); - - settings[Keywords.Database] = dbName; - - Open(); - } - - internal void EmergencyClose() - { - _fakingOpen = true; - } - - /// <summary> - /// Releases the connection to the database. If the connection is pooled, it will be - /// made available for re-use. If it is non-pooled, the actual connection will be shutdown. - /// </summary> - public override void Close() - { - if (!disposed) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Close"); - - promotable.Prepare(); - - if (connector != null) - { - connector.Notification -= NotificationDelegate; - connector.Notice -= NoticeDelegate; - - if (SyncNotification) - { - connector.RemoveNotificationThread(); - } - - NpgsqlConnectorPool.ConnectorPoolMgr.ReleaseConnector(this, connector); - - - connector = null; - } - } - } - - /// <summary> - /// Creates and returns a <see cref="System.Data.Common.DbCommand">DbCommand</see> - /// object associated with the <see cref="System.Data.Common.DbConnection">IDbConnection</see>. - /// </summary> - /// <returns>A <see cref="System.Data.Common.DbCommand">DbCommand</see> object.</returns> - protected override DbCommand CreateDbCommand() - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CreateDbCommand"); - return CreateCommand(); - } - - /// <summary> - /// Creates and returns a <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see> - /// object associated with the <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see>. - /// </summary> - /// <returns>A <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see> object.</returns> - public new NpgsqlCommand CreateCommand() - { - CheckNotDisposed(); - - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CreateCommand"); - return new NpgsqlCommand("", this); - } - - /// <summary> - /// Releases all resources used by the - /// <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see>. - /// </summary> - /// <param name="disposing"><b>true</b> when called from Dispose(); - /// <b>false</b> when being called from the finalizer.</param> - protected override void Dispose(bool disposing) - { - if (!disposed) - { - if (disposing) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Dispose"); - Close(); - } - else - { - if (FullState != ConnectionState.Closed) - { - NpgsqlEventLog.LogMsg(resman, "Log_ConnectionLeaking", LogLevel.Debug); - NpgsqlConnectorPool.ConnectorPoolMgr.FixPoolCountBecauseOfConnectionDisposeFalse(this); - } - } - - base.Dispose(disposing); - disposed = true; - } - } - - /// <summary> - /// Create a new connection based on this one. - /// </summary> - /// <returns>A new NpgsqlConnection object.</returns> - Object ICloneable.Clone() - { - return Clone(); - } - - /// <summary> - /// Create a new connection based on this one. - /// </summary> - /// <returns>A new NpgsqlConnection object.</returns> - public NpgsqlConnection Clone() - { - CheckNotDisposed(); - - NpgsqlConnection C = new NpgsqlConnection(ConnectionString); - - C.Notice += this.Notice; - - if (connector != null) - { - C.Open(); - } - - return C; - } - - // - // Internal methods and properties - // - internal void OnNotice(object O, NpgsqlNoticeEventArgs E) - { - if (Notice != null) - { - Notice(this, E); - } - } - - internal void OnNotification(object O, NpgsqlNotificationEventArgs E) - { - if (Notification != null) - { - Notification(this, E); - } - } - - /// <summary> - /// The connector object connected to the backend. - /// </summary> - internal NpgsqlConnector Connector - { - get { return connector; } - } - - - /// <summary> - /// Gets the NpgsqlConnectionStringBuilder containing the parsed connection string values. - /// </summary> - internal NpgsqlConnectionStringBuilder ConnectionStringValues - { - get { return settings; } - } - - /// <summary> - /// User name. - /// </summary> - internal String UserName - { - get { return settings.UserName; } - } - - public bool UseExtendedTypes - { - get - { - bool ext = settings.UseExtendedTypes; - return ext; - } - } - - /// <summary> - /// Password. - /// </summary> - internal String Password - { - get { return settings.Password; } - } - - /// <summary> - /// Determine if connection pooling will be used for this connection. - /// </summary> - internal Boolean Pooling - { - get { return (settings.Pooling && (settings.MaxPoolSize > 0)); } - } - - internal Int32 MinPoolSize - { - get { return settings.MinPoolSize; } - } - - internal Int32 MaxPoolSize - { - get { return settings.MaxPoolSize; } - } - - internal Int32 Timeout - { - get { return settings.Timeout; } - } - - internal Boolean Enlist - { - get { return settings.Enlist; } - } - - - // - // Event handlers - // - - /// <summary> - /// Default SSL CertificateSelectionCallback implementation. - /// </summary> - internal X509Certificate DefaultCertificateSelectionCallback(X509CertificateCollection clientCertificates, - X509Certificate serverCertificate, string targetHost, - X509CertificateCollection serverRequestedCertificates) - { - if (CertificateSelectionCallback != null) - { - return CertificateSelectionCallback(clientCertificates, serverCertificate, targetHost, serverRequestedCertificates); - } - else - { - return null; - } - } - - /// <summary> - /// Default SSL CertificateValidationCallback implementation. - /// </summary> - internal bool DefaultCertificateValidationCallback(X509Certificate certificate, int[] certificateErrors) - { - if (CertificateValidationCallback != null) - { - return CertificateValidationCallback(certificate, certificateErrors); - } - else - { - return true; - } - } - - /// <summary> - /// Default SSL PrivateKeySelectionCallback implementation. - /// </summary> - internal AsymmetricAlgorithm DefaultPrivateKeySelectionCallback(X509Certificate certificate, string targetHost) - { - if (PrivateKeySelectionCallback != null) - { - return PrivateKeySelectionCallback(certificate, targetHost); - } - else - { - return null; - } - } - - - // - // Private methods and properties - // - - - /// <summary> - /// Write each key/value pair in the connection string to the log. - /// </summary> - private void LogConnectionString() - { - foreach (string key in settings.Keys) - { - NpgsqlEventLog.LogMsg(resman, "Log_ConnectionStringValues", LogLevel.Debug, key, settings[key]); - } - } - - private void CheckConnectionOpen() - { - if (disposed) - { - throw new ObjectDisposedException(CLASSNAME); - } - - if (_fakingOpen) - { - if (connector != null) - { - try - { - Close(); - } - catch - { - } - } - Open(); - _fakingOpen = false; - } - - if (connector == null) - { - throw new InvalidOperationException(resman.GetString("Exception_ConnNotOpen")); - } - } - - private void CheckConnectionClosed() - { - if (disposed) - { - throw new ObjectDisposedException(CLASSNAME); - } - - if (connector != null) - { - throw new InvalidOperationException(resman.GetString("Exception_ConnOpen")); - } - } - - private void CheckNotDisposed() - { - if (disposed) - { - throw new ObjectDisposedException(CLASSNAME); - } - } - - - /// <summary> - /// Returns the supported collections - /// </summary> - public override DataTable GetSchema() - { - return NpgsqlSchema.GetMetaDataCollections(); - } - - /// <summary> - /// Returns the schema collection specified by the collection name. - /// </summary> - /// <param name="collectionName">The collection name.</param> - /// <returns>The collection specified.</returns> - public override DataTable GetSchema(string collectionName) - { - return GetSchema(collectionName, null); - } - - /// <summary> - /// Returns the schema collection specified by the collection name filtered by the restrictions. - /// </summary> - /// <param name="collectionName">The collection name.</param> - /// <param name="restrictions"> - /// The restriction values to filter the results. A description of the restrictions is contained - /// in the Restrictions collection. - /// </param> - /// <returns>The collection specified.</returns> - public override DataTable GetSchema(string collectionName, string[] restrictions) - { - switch (collectionName) - { - case "MetaDataCollections": - return NpgsqlSchema.GetMetaDataCollections(); - case "Restrictions": - return NpgsqlSchema.GetRestrictions(); - case "DataSourceInformation": - return NpgsqlSchema.GetDataSourceInformation(); - case "DataTypes": - case "ReservedWords": - throw new NotSupportedException(); - // custom collections for npgsql - case "Databases": - return new NpgsqlSchema(new NpgsqlConnection(ConnectionString)).GetDatabases(restrictions); - case "Tables": - return new NpgsqlSchema(new NpgsqlConnection(ConnectionString)).GetTables(restrictions); - case "Columns": - return new NpgsqlSchema(new NpgsqlConnection(ConnectionString)).GetColumns(restrictions); - case "Views": - return new NpgsqlSchema(new NpgsqlConnection(ConnectionString)).GetViews(restrictions); - case "Users": - return new NpgsqlSchema(new NpgsqlConnection(ConnectionString)).GetUsers(restrictions); - default: - throw new NotSupportedException(); - } - } - - public void ClearPool() - { - NpgsqlConnectorPool.ConnectorPoolMgr.ClearPool(this); - } - - public static void ClearAllPools() - { - NpgsqlConnectorPool.ConnectorPoolMgr.ClearAllPools(); - } - - public override void EnlistTransaction(Transaction transaction) - { - promotable.Enlist(transaction); - } - -#if NET35 - protected override DbProviderFactory DbProviderFactory + return connection_string.ToBool(ConnectionStringKeys.SyncNotification, ConnectionStringDefaults.SyncNotification); + } + } + + /// <summary> + /// Gets the current state of the connection. + /// </summary> + /// <value>A bitwise combination of the <see cref="System.Data.ConnectionState">ConnectionState</see> values. The default is <b>Closed</b>.</value> + [Browsable(false)] + public ConnectionState State { + get + { + CheckNotDisposed(); + + if (connector != null) + { + return connector.State; + } + else + { + return ConnectionState.Closed; + } + } + } + + /// <summary> + /// Version of the PostgreSQL backend. + /// This can only be called when there is an active connection. + /// </summary> + [Browsable(false)] + public ServerVersion ServerVersion { + get + { + CheckConnectionOpen(); + return connector.ServerVersion; + } + } + + /// <summary> + /// Protocol version in use. + /// This can only be called when there is an active connection. + /// </summary> + [Browsable(false)] + public ProtocolVersion BackendProtocolVersion { + get + { + CheckConnectionOpen(); + return connector.BackendProtocolVersion; + } + } + + /// <summary> + /// Begins a database transaction. + /// </summary> + /// <returns>An <see cref="System.Data.IDbTransaction">IDbTransaction</see> + /// object representing the new transaction.</returns> + /// <remarks> + /// Currently there's no support for nested transactions. + /// </remarks> + IDbTransaction IDbConnection.BeginTransaction() + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IDbConnection.BeginTransaction"); + + return BeginTransaction(); + } + + /// <summary> + /// Begins a database transaction with the specified isolation level. + /// </summary> + /// <param name="level">The <see cref="System.Data.IsolationLevel">isolation level</see> under which the transaction should run.</param> + /// <returns>An <see cref="System.Data.IDbTransaction">IDbTransaction</see> + /// object representing the new transaction.</returns> + /// <remarks> + /// Currently the IsolationLevel ReadCommitted and Serializable are supported by the PostgreSQL backend. + /// There's no support for nested transactions. + /// </remarks> + IDbTransaction IDbConnection.BeginTransaction(IsolationLevel level) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IDbConnection.BeginTransaction", level); + + return BeginTransaction(level); + } + + /// <summary> + /// Begins a database transaction. + /// </summary> + /// <returns>A <see cref="Npgsql.NpgsqlTransaction">NpgsqlTransaction</see> + /// object representing the new transaction.</returns> + /// <remarks> + /// Currently there's no support for nested transactions. + /// </remarks> + public NpgsqlTransaction BeginTransaction() + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "BeginTransaction"); + return this.BeginTransaction(IsolationLevel.ReadCommitted); + } + + /// <summary> + /// Begins a database transaction with the specified isolation level. + /// </summary> + /// <param name="level">The <see cref="System.Data.IsolationLevel">isolation level</see> under which the transaction should run.</param> + /// <returns>A <see cref="Npgsql.NpgsqlTransaction">NpgsqlTransaction</see> + /// object representing the new transaction.</returns> + /// <remarks> + /// Currently the IsolationLevel ReadCommitted and Serializable are supported by the PostgreSQL backend. + /// There's no support for nested transactions. + /// </remarks> + public NpgsqlTransaction BeginTransaction(IsolationLevel level) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "BeginTransaction", level); + + CheckConnectionOpen(); + + if (connector.Transaction != null) + { + throw new InvalidOperationException(resman.GetString("Exception_NoNestedTransactions")); + } + + return new NpgsqlTransaction(this, level); + } + + /// <summary> + /// Opens a database connection with the property settings specified by the + /// <see cref="Npgsql.NpgsqlConnection.ConnectionString">ConnectionString</see>. + /// </summary> + public void Open() + { + CheckConnectionClosed(); + + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Open"); + + // Check if there is any missing argument. + if (! connection_string.Contains(ConnectionStringKeys.Host)) + throw new ArgumentException(resman.GetString("Exception_MissingConnStrArg"), ConnectionStringKeys.Host); + if (! connection_string.Contains(ConnectionStringKeys.UserName)) + throw new ArgumentException(resman.GetString("Exception_MissingConnStrArg"), ConnectionStringKeys.UserName); + + // 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> + /// This method changes the current database by disconnecting from the actual + /// database and connecting to the specified. + /// </summary> + /// <param name="dbName">The name of the database to use in place of the current database.</param> + public void ChangeDatabase(String dbName) + { + CheckNotDisposed(); + + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ChangeDatabase", dbName); + + if (dbName == null) + throw new ArgumentNullException("dbName"); + + if (dbName == String.Empty) + throw new ArgumentOutOfRangeException(String.Format(resman.GetString("Exception_InvalidDbName"), dbName), "dbName"); + + String oldDatabaseName = Database; + + Close(); + + connection_string[ConnectionStringKeys.Database] = dbName; + + Open(); + } + + /// <summary> + /// Releases the connection to the database. If the connection is pooled, it will be + /// made available for re-use. If it is non-pooled, the actual connection will be shutdown. + /// </summary> + public void Close() + { + if (!disposed) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Close"); + + if (connector != null) + { + + connector.Notification -= NotificationDelegate; + connector.Notice -= NoticeDelegate; + + if (SyncNotification) + connector.RemoveNotificationThread(); + + NpgsqlConnectorPool.ConnectorPoolMgr.ReleaseConnector(this, connector); + + + + connector = null; + } + } + } + + /// <summary> + /// Creates and returns a <see cref="System.Data.IDbCommand">IDbCommand</see> + /// object associated with the <see cref="System.Data.IDbConnection">IDbConnection</see>. + /// </summary> + /// <returns>A <see cref="System.Data.IDbCommand">IDbCommand</see> object.</returns> + IDbCommand IDbConnection.CreateCommand() + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IDbConnection.CreateCommand"); + return (NpgsqlCommand) CreateCommand(); + } + + /// <summary> + /// Creates and returns a <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see> + /// object associated with the <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see>. + /// </summary> + /// <returns>A <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see> object.</returns> + public NpgsqlCommand CreateCommand() + { + CheckNotDisposed(); + + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CreateCommand"); + return new NpgsqlCommand("", this); + } + + /// <summary> + /// Releases all resources used by the + /// <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see>. + /// </summary> + /// <param name="disposing"><b>true</b> when called from Dispose(); + /// <b>false</b> when being called from the finalizer.</param> + protected override void Dispose(bool disposing) + { + + if (!disposed) + { + if (disposing) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Dispose"); + Close(); + } + else + { + if (State != ConnectionState.Closed) + { + NpgsqlEventLog.LogMsg(resman, "Log_ConnectionLeaking", LogLevel.Debug); + NpgsqlConnectorPool.ConnectorPoolMgr.FixPoolCountBecauseOfConnectionDisposeFalse(this); + } + } + + base.Dispose (disposing); + disposed = true; + + } + } + + /// <summary> + /// Create a new connection based on this one. + /// </summary> + /// <returns>A new NpgsqlConnection object.</returns> + Object ICloneable.Clone() + { + return Clone(); + } + + /// <summary> + /// Create a new connection based on this one. + /// </summary> + /// <returns>A new NpgsqlConnection object.</returns> + public NpgsqlConnection Clone() + { + CheckNotDisposed(); + + NpgsqlConnection C = new NpgsqlConnection(ConnectionString); + + C.Notice += this.Notice; + + if (connector != null) + { + C.Open(); + } + + return C; + } + + // + // Internal methods and properties + // + internal void OnNotice(object O, NpgsqlNoticeEventArgs E) + { + if (Notice != null) + { + Notice(this, E); + } + } + + internal void OnNotification(object O, NpgsqlNotificationEventArgs E) + { + if (Notification != null) + { + Notification(this, E); + } + } + + /// <summary> + /// The connector object connected to the backend. + /// </summary> + internal NpgsqlConnector Connector { get { - return NpgsqlFactory.Instance; + return connector; } } -#endif - } + + /// <summary> + /// Gets the NpgsqlConnectionString containing the parsed connection string values. + /// </summary> + internal NpgsqlConnectionString ConnectionStringValues { + get + { + return connection_string; + } + } + + /// <summary> + /// User name. + /// </summary> + internal String UserName { + get + { + return connection_string.ToString(ConnectionStringKeys.UserName); + } + } + + /// <summary> + /// Password. + /// </summary> + internal String Password { + get + { + return connection_string.ToString(ConnectionStringKeys.Password); + } + } + + /// <summary> + /// Determine if connection pooling will be used for this connection. + /// </summary> + internal Boolean Pooling { + get + { + return ( + connection_string.ToBool(ConnectionStringKeys.Pooling, ConnectionStringDefaults.Pooling) && + connection_string.ToInt32(ConnectionStringKeys.MaxPoolSize, ConnectionStringDefaults.MaxPoolSize) > 0 + ); + } + } + + internal Int32 MinPoolSize { + get + { + return connection_string.ToInt32(ConnectionStringKeys.MinPoolSize, 0, MaxPoolSize, ConnectionStringDefaults.MinPoolSize); + } + } + + internal Int32 MaxPoolSize { + get + { + return connection_string.ToInt32(ConnectionStringKeys.MaxPoolSize, 0, 1024, ConnectionStringDefaults.MaxPoolSize); + } + } + + internal Int32 Timeout { + get + { + return connection_string.ToInt32(ConnectionStringKeys.Timeout, 0, 1024, ConnectionStringDefaults.Timeout); + } + } + + + + // + // Event handlers + // + + /// <summary> + /// Default SSL CertificateSelectionCallback implementation. + /// </summary> + internal X509Certificate DefaultCertificateSelectionCallback( + X509CertificateCollection clientCertificates, + X509Certificate serverCertificate, + string targetHost, + X509CertificateCollection serverRequestedCertificates) + { + if (CertificateSelectionCallback != null) + { + return CertificateSelectionCallback(clientCertificates, serverCertificate, targetHost, serverRequestedCertificates); + } + else + { + return null; + } + } + + /// <summary> + /// Default SSL CertificateValidationCallback implementation. + /// </summary> + internal bool DefaultCertificateValidationCallback( + X509Certificate certificate, + int[] certificateErrors) + { + if (CertificateValidationCallback != null) + { + return CertificateValidationCallback(certificate, certificateErrors); + } + else + { + return true; + } + } + + /// <summary> + /// Default SSL PrivateKeySelectionCallback implementation. + /// </summary> + internal AsymmetricAlgorithm DefaultPrivateKeySelectionCallback( + X509Certificate certificate, + string targetHost) + { + if (PrivateKeySelectionCallback != null) + { + return PrivateKeySelectionCallback(certificate, targetHost); + } + else + { + return null; + } + } + + + + // + // Private methods and properties + // + + + /// <summary> + /// Write each key/value pair in the connection string to the log. + /// </summary> + private void LogConnectionString() + { + foreach (DictionaryEntry DE in connection_string) + { + NpgsqlEventLog.LogMsg(resman, "Log_ConnectionStringValues", LogLevel.Debug, DE.Key, DE.Value); + } + } + + private void CheckConnectionOpen() + { + if (disposed) + { + throw new ObjectDisposedException(CLASSNAME); + } + + if (connector == null) + { + throw new InvalidOperationException(resman.GetString("Exception_ConnNotOpen")); + } + } + + private void CheckConnectionClosed() + { + if (disposed) + { + throw new ObjectDisposedException(CLASSNAME); + } + + if (connector != null) + { + throw new InvalidOperationException(resman.GetString("Exception_ConnOpen")); + } + } + + private void CheckNotDisposed() + { + if (disposed) + { + throw new ObjectDisposedException(CLASSNAME); + } + } + + + /// <summary> + /// Returns the supported collections + /// <summary> + public DataTable GetSchema() + { + return NpgsqlSchema.GetMetaDataCollections(); + } + + /// <summary> + /// Returns the schema collection specified by the collection name. + /// </summary> + /// <param name="collectionName">The collection name.</param> + /// <returns>The collection specified.</returns> + public DataTable GetSchema(string collectionName) + { + return GetSchema(collectionName, null); + } + + /// <summary> + /// Returns the schema collection specified by the collection name filtered by the restrictions. + /// </summary> + /// <param name="collectionName">The collection name.</param> + /// <param name="restrictions"> + /// The restriction values to filter the results. A description of the restrictions is contained + /// in the Restrictions collection. + /// </param> + /// <returns>The collection specified.</returns> + public DataTable GetSchema(string collectionName, string[] restrictions) + { + switch(collectionName) + { + case "MetaDataCollections": + return NpgsqlSchema.GetMetaDataCollections(); + case "Restrictions": + return NpgsqlSchema.GetRestrictions(); + case "DataSourceInformation": + case "DataTypes": + case "ReservedWords": + throw new NotSupportedException(); + // custom collections for npgsql + case "Databases": + return new NpgsqlSchema(new NpgsqlConnection(ConnectionString)).GetDatabases(restrictions); + case "Tables": + return new NpgsqlSchema(new NpgsqlConnection(ConnectionString)).GetTables(restrictions); + case "Columns": + return new NpgsqlSchema(new NpgsqlConnection(ConnectionString)).GetColumns(restrictions); + case "Views": + return new NpgsqlSchema(new NpgsqlConnection(ConnectionString)).GetViews(restrictions); + case "Users": + return new NpgsqlSchema(new NpgsqlConnection(ConnectionString)).GetUsers(restrictions); + default: + throw new NotSupportedException(); + } + } + + public void ClearPool() + { + NpgsqlConnectorPool.ConnectorPoolMgr.ClearPool(this); + } + + public void ClearAllPools() + { + NpgsqlConnectorPool.ConnectorPoolMgr.ClearAllPools(); + } + + } + + + } diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlConnector.cs b/mcs/class/Npgsql/Npgsql/NpgsqlConnector.cs index 81003118b83..fd6c391b0aa 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlConnector.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlConnector.cs @@ -1,41 +1,42 @@ -// Copyright (C) 2002 The Npgsql Development Team -// npgsql-general@gborg.postgresql.org -// http://gborg.postgresql.org/project/npgsql/projdisplay.php +// Copyright (C) 2002 The Npgsql Development Team +// npgsql-general@gborg.postgresql.org +// http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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. // -// Connector.cs +// 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 +// +// Connector.cs // ------------------------------------------------------------------ -// Project -// Npgsql -// Status -// 0.00.0000 - 06/17/2002 - ulrich sprick - created -// - 06/??/2004 - Glen Parker<glenebob@nwlink.com> rewritten +// Project +// Npgsql +// Status +// 0.00.0000 - 06/17/2002 - ulrich sprick - created +// - 06/??/2004 - Glen Parker<glenebob@nwlink.com> rewritten using System; -using System.Collections.Generic; -using System.Data; +using System.Collections; using System.IO; -using System.Net.Sockets; +using System.Text; +using System.Data; +using System.Security; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Threading; +using System.Net.Sockets; + using Mono.Security.Protocol.Tls; + using NpgsqlTypes; namespace Npgsql @@ -49,113 +50,91 @@ namespace Npgsql internal class NpgsqlConnector { // Immutable. - private readonly NpgsqlConnectionStringBuilder settings; + internal NpgsqlConnectionString ConnectionString; /// <summary> /// Occurs on NoticeResponses from the PostgreSQL backend. /// </summary> - internal event NoticeEventHandler Notice; + internal event NoticeEventHandler Notice; /// <summary> /// Occurs on NotificationResponses from the PostgreSQL backend. /// </summary> - internal event NotificationEventHandler Notification; + internal event NotificationEventHandler Notification; /// <summary> /// Mono.Security.Protocol.Tls.CertificateSelectionCallback delegate. /// </summary> - internal event CertificateSelectionCallback CertificateSelectionCallback; + internal event CertificateSelectionCallback CertificateSelectionCallback; /// <summary> /// Mono.Security.Protocol.Tls.CertificateValidationCallback delegate. /// </summary> - internal event CertificateValidationCallback CertificateValidationCallback; + internal event CertificateValidationCallback CertificateValidationCallback; /// <summary> /// Mono.Security.Protocol.Tls.PrivateKeySelectionCallback delegate. /// </summary> - internal event PrivateKeySelectionCallback PrivateKeySelectionCallback; + internal event PrivateKeySelectionCallback PrivateKeySelectionCallback; - private ConnectionState _connection_state; + private ConnectionState _connection_state; // The physical network connection to the backend. - private Stream _stream; - - private Socket _socket; + private Stream _stream; + private Socket _socket; + // Mediator which will hold data generated from backend. - private readonly NpgsqlMediator _mediator; + private NpgsqlMediator _mediator; - private ProtocolVersion _backendProtocolVersion; - private Version _serverVersion; + private ProtocolVersion _backendProtocolVersion; + private ServerVersion _serverVersion; // Values for possible CancelRequest messages. - private NpgsqlBackEndKeyData _backend_keydata; + private NpgsqlBackEndKeyData _backend_keydata; // Flag for transaction status. // private Boolean _inTransaction = false; - private NpgsqlTransaction _transaction = null; - - private Boolean _supportsPrepare = false; - - private Boolean _supportsSavepoint = false; + private NpgsqlTransaction _transaction = null; - private NpgsqlBackendTypeMapping _oidToNameMapping = null; + private Boolean _supportsPrepare = false; - private Boolean _isInitialized; + private NpgsqlBackendTypeMapping _oidToNameMapping = null; - private readonly Boolean _pooled; - private readonly Boolean _shared; + private Encoding _encoding; - private NpgsqlState _state; + private Boolean _isInitialized; + private Boolean _pooled; + private Boolean _shared; - private Int32 _planIndex; - private Int32 _portalIndex; + private NpgsqlState _state; - private const String _planNamePrefix = "npgsqlplan"; - private const String _portalNamePrefix = "npgsqlportal"; + private Int32 _planIndex; + private Int32 _portalIndex; - private Thread _notificationThread; + private const String _planNamePrefix = "npgsqlplan"; + private const String _portalNamePrefix = "npgsqlportal"; + + private Thread _notificationThread; + // The AutoResetEvent to synchronize processing threads. - internal AutoResetEvent _notificationAutoResetEvent; - + internal AutoResetEvent _notificationAutoResetEvent; + // Counter of notification thread start/stop requests in order to - internal Int16 _notificationThreadStopCount; - - private Exception _notificationException; + internal Int16 _notificationThreadStopCount; - internal ForwardsOnlyDataReader CurrentReader; - - // Some kinds of messages only get one response, and do not - // expect a ready_for_query response. - private bool _requireReadyForQuery = true; - - private readonly Dictionary<string, NpgsqlParameterStatus> _serverParameters = - new Dictionary<string, NpgsqlParameterStatus>(StringComparer.InvariantCultureIgnoreCase); - -#if WINDOWS && UNMANAGED - - private SSPIHandler _sspi; - - internal SSPIHandler SSPI - { - get { return _sspi; } - set { _sspi = value; } - } - -#endif /// <summary> /// Constructor. /// </summary> /// <param name="Shared">Controls whether the connector can be shared.</param> - public NpgsqlConnector(NpgsqlConnectionStringBuilder ConnectionString, bool Pooled, bool Shared) + public NpgsqlConnector(NpgsqlConnectionString ConnectionString, bool Pooled, bool Shared) { - this.settings = ConnectionString; + this.ConnectionString = ConnectionString; _connection_state = ConnectionState.Closed; _pooled = Pooled; _shared = Shared; @@ -167,169 +146,142 @@ namespace Npgsql _portalIndex = 0; _notificationThreadStopCount = 1; _notificationAutoResetEvent = new AutoResetEvent(true); - } - //Finalizer should never be used, but if some incident has left to a connector being abandoned (most likely - //case being a user not cleaning up a connection properly) then this way we can at least reduce the damage. - ~NpgsqlConnector() - { - Close(); } internal String Host { - get { return settings.Host; } + get + { + return ConnectionString.ToString(ConnectionStringKeys.Host); + } } internal Int32 Port { - get { return settings.Port; } + get + { + return ConnectionString.ToInt32(ConnectionStringKeys.Port, ConnectionStringDefaults.Port); + } } internal String Database { - get { return settings.ContainsKey(Keywords.Database) ? settings.Database : settings.UserName; } + get + { + return ConnectionString.ToString(ConnectionStringKeys.Database, UserName); + } } internal String UserName { - get { return settings.UserName; } + get + { + return ConnectionString.ToString(ConnectionStringKeys.UserName); + } } internal String Password { - get { return settings.Password; } + get + { + return ConnectionString.ToString(ConnectionStringKeys.Password); + } } internal Boolean SSL { - get { return settings.SSL; } + get + { + return ConnectionString.ToBool(ConnectionStringKeys.SSL); + } } - + internal SslMode SslMode { - get { return settings.SslMode; } + get + { + return ConnectionString.ToSslMode(ConnectionStringKeys.SslMode); + } } - + internal Int32 ConnectionTimeout { - get { return settings.Timeout; } + get + { + return ConnectionString.ToInt32(ConnectionStringKeys.Timeout, ConnectionStringDefaults.Timeout); + } } internal Int32 CommandTimeout { - get { return settings.CommandTimeout; } - } - - internal Boolean Enlist - { - get { return settings.Enlist; } - } - - public bool UseExtendedTypes - { - get { return settings.UseExtendedTypes; } - } - - internal Boolean IntegratedSecurity - { - get { return settings.IntegratedSecurity; } + get + { + return ConnectionString.ToInt32(ConnectionStringKeys.CommandTimeout, ConnectionStringDefaults.CommandTimeout); + } } + /// <summary> /// Gets the current state of the connection. /// </summary> - internal ConnectionState State - { + internal ConnectionState State { get { - if (_connection_state != ConnectionState.Closed && CurrentReader != null && !CurrentReader._cleanedUp) - { - return ConnectionState.Open | ConnectionState.Fetching; - } return _connection_state; } } - /// <summary> - /// Return Connection String. - /// </summary> - internal string ConnectionString - { - get { return settings.ConnectionString; } - } // State - internal void Query(NpgsqlCommand queryCommand) - { - CurrentState.Query(this, queryCommand); - } - - internal IEnumerable<IServerResponseObject> QueryEnum(NpgsqlCommand queryCommand) + internal void Query (NpgsqlCommand queryCommand) { - if (CurrentReader != null) - { - if (!CurrentReader._cleanedUp) - { - throw new InvalidOperationException( - "There is already an open DataReader associated with this Command which must be closed first."); - } - CurrentReader.Close(); - } - return CurrentState.QueryEnum(this, queryCommand); + CurrentState.Query(this, queryCommand ); } - internal void Authenticate(string password) + internal void Authenticate (string password) { - CurrentState.Authenticate(this, password); + CurrentState.Authenticate(this, password ); } - internal void Parse(NpgsqlParse parse) + internal void Parse (NpgsqlParse parse) { CurrentState.Parse(this, parse); } - internal void Flush() + internal void Flush () { CurrentState.Flush(this); } - internal void TestConnector() + internal void Sync () { - CurrentState.TestConnector(this); + CurrentState.Sync(this); } - internal NpgsqlRowDescription Sync() - { - return CurrentState.Sync(this); - } - - internal void Bind(NpgsqlBind bind) + internal void Bind (NpgsqlBind bind) { CurrentState.Bind(this, bind); } - - internal void Describe(NpgsqlDescribe describe) + + internal void Describe (NpgsqlDescribe describe) { CurrentState.Describe(this, describe); } - internal void Execute(NpgsqlExecute execute) + internal void Execute (NpgsqlExecute execute) { CurrentState.Execute(this, execute); } - internal IEnumerable<IServerResponseObject> ExecuteEnum(NpgsqlExecute execute) - { - return CurrentState.ExecuteEnum(this, execute); - } /// <summary> /// This method checks if the connector is still ok. /// We try to send a simple query text, select 1 as ConnectionTest; /// </summary> + internal Boolean IsValid() { try @@ -340,7 +292,7 @@ namespace Npgsql // Clear mediator. Mediator.ResetResponses(); - this.RequireReadyForQuery = true; + Mediator.ResetExpectations(); } @@ -351,21 +303,25 @@ namespace Npgsql return true; } + + + /// <summary> /// This method is responsible for releasing all resources associated with this Connector. /// </summary> + internal void ReleaseResources() { - if (_connection_state != ConnectionState.Closed) - { - ReleasePlansPortals(); - ReleaseRegisteredListen(); - } + ReleasePlansPortals(); + ReleaseRegisteredListen(); + + } internal void ReleaseRegisteredListen() { Query(new NpgsqlCommand("unlisten *", this)); + } /// <summary> @@ -377,56 +333,91 @@ namespace Npgsql if (_planIndex > 0) { - for (i = 1; i <= _planIndex; i++) - { - try - { - Query(new NpgsqlCommand(String.Format("deallocate \"{0}\";", _planNamePrefix + i), this)); - } - - // Ignore any error which may occur when releasing portals as this portal name may not be valid anymore. i.e.: the portal name was used on a prepared query which had errors. - catch(Exception) {} - } + for(i = 1; i <= _planIndex; i++) + Query(new NpgsqlCommand(String.Format("deallocate \"{0}\";", _planNamePrefix + i), this)); } _portalIndex = 0; _planIndex = 0; + + + } + + + + + + /// <summary> + /// Check for mediator errors (sent by backend) and throw the appropriate + /// exception if errors found. This needs to be called after every interaction + /// with the backend. + /// </summary> + internal void CheckErrors() + { + if (_mediator.Errors.Count > 0) + { + throw new NpgsqlException(_mediator.Errors); + } } - internal void FireNotice(NpgsqlError e) + /// <summary> + /// Check for notices and fire the appropiate events. + /// This needs to be called after every interaction + /// with the backend. + /// </summary> + internal void CheckNotices() { if (Notice != null) { - try + foreach (NpgsqlError E in _mediator.Notices) { - Notice(this, new NpgsqlNoticeEventArgs(e)); + Notice(this, new NpgsqlNoticeEventArgs(E)); } - catch - { - } //Eat exceptions from user code. } } - internal void FireNotification(NpgsqlNotificationEventArgs e) + /// <summary> + /// Check for notifications and fire the appropiate events. + /// This needs to be called after every interaction + /// with the backend. + /// </summary> + internal void CheckNotifications() { if (Notification != null) { - try - { - Notification(this, e); + + foreach (NpgsqlNotificationEventArgs E in _mediator.Notifications) + { + // Wrap our notification thread call while running on user land code. + // This prevents our thread of possibly dying there if there is no exception handling. + try + { + Notification(this, E); + } + catch(Exception){} + } } - catch - { - } //Eat exceptions from user code. - } + + } + + /// <summary> + /// Check for errors AND notifications in one call. + /// </summary> + internal void CheckErrorsAndNotifications() + { + CheckNotices(); + CheckNotifications(); + CheckErrors(); } /// <summary> /// Default SSL CertificateSelectionCallback implementation. /// </summary> - internal X509Certificate DefaultCertificateSelectionCallback(X509CertificateCollection clientCertificates, - X509Certificate serverCertificate, string targetHost, - X509CertificateCollection serverRequestedCertificates) + internal X509Certificate DefaultCertificateSelectionCallback( + X509CertificateCollection clientCertificates, + X509Certificate serverCertificate, + string targetHost, + X509CertificateCollection serverRequestedCertificates) { if (CertificateSelectionCallback != null) { @@ -441,7 +432,9 @@ namespace Npgsql /// <summary> /// Default SSL CertificateValidationCallback implementation. /// </summary> - internal bool DefaultCertificateValidationCallback(X509Certificate certificate, int[] certificateErrors) + internal bool DefaultCertificateValidationCallback( + X509Certificate certificate, + int[] certificateErrors) { if (CertificateValidationCallback != null) { @@ -456,7 +449,9 @@ namespace Npgsql /// <summary> /// Default SSL PrivateKeySelectionCallback implementation. /// </summary> - internal AsymmetricAlgorithm DefaultPrivateKeySelectionCallback(X509Certificate certificate, string targetHost) + internal AsymmetricAlgorithm DefaultPrivateKeySelectionCallback( + X509Certificate certificate, + string targetHost) { if (PrivateKeySelectionCallback != null) { @@ -471,10 +466,28 @@ namespace Npgsql /// <summary> /// Version of backend server this connector is connected to. /// </summary> - internal Version ServerVersion + internal ServerVersion ServerVersion + { + get + { + return _serverVersion; + } + set + { + _serverVersion = value; + } + } + + internal Encoding Encoding { - get { return _serverVersion; } - set { _serverVersion = value; } + get + { + return _encoding; + } + set + { + _encoding = value; + } } /// <summary> @@ -482,26 +495,43 @@ namespace Npgsql /// </summary> internal ProtocolVersion BackendProtocolVersion { - get { return _backendProtocolVersion; } - set { _backendProtocolVersion = value; } + get + { + return _backendProtocolVersion; + } + set + { + _backendProtocolVersion = value; + } } /// <summary> /// The physical connection stream to the backend. /// </summary> - internal Stream Stream - { - get { return _stream; } - set { _stream = value; } + internal Stream Stream { + get + { + return _stream; + } + set + { + _stream = value; + } } - + /// <summary> /// The physical connection socket to the backend. /// </summary> - internal Socket Socket - { - get { return _socket; } - set { _socket = value; } + + internal Socket Socket { + get + { + return _socket; + } + set + { + _socket = value; + } } /// <summary> @@ -509,69 +539,94 @@ namespace Npgsql /// </summary> internal Boolean IsInitialized { - get { return _isInitialized; } - set { _isInitialized = value; } + get + { + return _isInitialized; + } + set + { + _isInitialized = value; + } } - internal NpgsqlState CurrentState - { - get { return _state; } - set { _state = value; } + internal NpgsqlState CurrentState { + get + { + return _state; + } + set + { + _state = value; + } } internal bool Pooled { - get { return _pooled; } + get + { + return _pooled; + } } internal bool Shared { - get { return _shared; } + get + { + return _shared; + } } - internal NpgsqlBackEndKeyData BackEndKeyData - { - get { return _backend_keydata; } - set { _backend_keydata = value; } + internal NpgsqlBackEndKeyData BackEndKeyData { + get + { + return _backend_keydata; + } } - internal NpgsqlBackendTypeMapping OidToNameMapping - { - get { return _oidToNameMapping; } + internal NpgsqlBackendTypeMapping OidToNameMapping { + get + { + return _oidToNameMapping; + } } /// <summary> /// The connection mediator. /// </summary> - internal NpgsqlMediator Mediator - { - get { return _mediator; } + internal NpgsqlMediator Mediator { + get + { + return _mediator; + } } /// <summary> /// Report if the connection is in a transaction. /// </summary> - internal NpgsqlTransaction Transaction - { - get { return _transaction; } - set { _transaction = value; } + internal NpgsqlTransaction Transaction { + get + { + return _transaction; + } + set + { + _transaction = value; + } } /// <summary> /// Report whether the current connection can support prepare functionality. /// </summary> - internal Boolean SupportsPrepare - { - get { return _supportsPrepare; } - set { _supportsPrepare = value; } - } - - internal Boolean SupportsSavepoint - { - get { return _supportsSavepoint; } - set { _supportsSavepoint = value; } - + internal Boolean SupportsPrepare { + get + { + return _supportsPrepare; + } + set + { + _supportsPrepare = value; + } } /// <summary> @@ -579,16 +634,15 @@ namespace Npgsql /// SupportsPrepare means the server can use prepared query plans (7.3+) /// </summary> // FIXME - should be private - internal void ProcessServerVersion() + internal void ProcessServerVersion () { - this._supportsPrepare = (ServerVersion >= new Version(7, 3, 0)); - this._supportsSavepoint = (ServerVersion >= new Version(8, 0, 0)); + this._supportsPrepare = (ServerVersion >= new ServerVersion(7, 3, 0)); } - /*/// <value>Counts the numbers of Connections that share + /// <value>Counts the numbers of Connections that share /// this Connector. Used in Release() to decide wether this /// connector is to be moved to the PooledConnectors list.</value> - // internal int mShareCount;*/ + // internal int mShareCount; /// <summary> /// Opens the physical connection to the server. @@ -597,104 +651,96 @@ namespace Npgsql /// Method of the connection pool manager.</remarks> internal void Open() { - ServerVersion = null; + ProtocolVersion PV; + // If Connection.ConnectionString specifies a protocol version, we will // not try to fall back to version 2 on failure. + if (ConnectionString.Contains(ConnectionStringKeys.Protocol)) + { + PV = ConnectionString.ToProtocolVersion(ConnectionStringKeys.Protocol); + } + else + { + PV = ProtocolVersion.Unknown; + } - _backendProtocolVersion = (settings.Protocol == ProtocolVersion.Unknown) - ? ProtocolVersion.Version3 - : settings.Protocol; + _backendProtocolVersion = (PV == ProtocolVersion.Unknown) ? ProtocolVersion.Version3 : PV; // Reset state to initialize new connector in pool. + Encoding = Encoding.Default; CurrentState = NpgsqlClosedState.Instance; // Get a raw connection, possibly SSL... CurrentState.Open(this); - try - { - // Establish protocol communication and handle authentication... - CurrentState.Startup(this); - } - catch (NpgsqlException ne) + // Establish protocol communication and handle authentication... + CurrentState.Startup(this); + + // Check for protocol not supported. If we have been told what protocol to use, + // we will not try this step. + if (_mediator.Errors.Count > 0 && PV == ProtocolVersion.Unknown) { - // Check for protocol not supported. If we have been told what protocol to use, - // we will not try this step. - if (settings.Protocol != ProtocolVersion.Unknown) - { - throw; - } // If we attempted protocol version 3, it may be possible to drop back to version 2. - if (BackendProtocolVersion != ProtocolVersion.Version3) + if (BackendProtocolVersion == ProtocolVersion.Version3) { - throw; - } - NpgsqlError Error0 = (NpgsqlError) ne.Errors[0]; + NpgsqlError Error0 = (NpgsqlError)_mediator.Errors[0]; - // If NpgsqlError..ctor() encounters a version 2 error, - // it will set its own protocol version to version 2. That way, we can tell - // easily if the error was a FATAL: protocol error. - if (Error0.BackendProtocolVersion != ProtocolVersion.Version2) - { - throw; + // If NpgsqlError.ReadFromStream_Ver_3() encounters a version 2 error, + // it will set its own protocol version to version 2. That way, we can tell + // easily if the error was a FATAL: protocol error. + if (Error0.BackendProtocolVersion == ProtocolVersion.Version2) + { + // Try using the 2.0 protocol. + _mediator.ResetResponses(); + BackendProtocolVersion = ProtocolVersion.Version2; + CurrentState = NpgsqlClosedState.Instance; + + // Get a raw connection, possibly SSL... + CurrentState.Open(this); + // Establish protocol communication and handle authentication... + CurrentState.Startup(this); + } } - // Try using the 2.0 protocol. - _mediator.ResetResponses(); - BackendProtocolVersion = ProtocolVersion.Version2; - CurrentState = NpgsqlClosedState.Instance; - - // Get a raw connection, possibly SSL... - CurrentState.Open(this); - // Establish protocol communication and handle authentication... - CurrentState.Startup(this); } + // Check for errors and do the Right Thing. + // FIXME - CheckErrors needs to be moved to Connector + CheckErrors(); + + _backend_keydata = _mediator.BackendKeyData; + // Change the state of connection to open and ready. _connection_state = ConnectionState.Open; CurrentState = NpgsqlReadyState.Instance; + String ServerVersionString = String.Empty; + + // First try to determine backend server version using the newest method. + if (((NpgsqlParameterStatus)_mediator.Parameters["__npgsql_server_version"]) != null) + ServerVersionString = ((NpgsqlParameterStatus)_mediator.Parameters["__npgsql_server_version"]).ParameterValue; + + // Fall back to the old way, SELECT VERSION(). // This should not happen for protocol version 3+. - if (ServerVersion == null) + if (ServerVersionString.Length == 0) { - NpgsqlCommand command = new NpgsqlCommand("set DATESTYLE TO ISO;select version();", this); - ServerVersion = new Version(PGUtil.ExtractServerVersion((string) command.ExecuteScalar())); + NpgsqlCommand command = new NpgsqlCommand("select version();set DATESTYLE TO ISO;", this); + ServerVersionString = PGUtil.ExtractServerVersion( (String)command.ExecuteScalar() ); } + // Cook version string so we can use it for enabling/disabling things based on + // backend version. + ServerVersion = PGUtil.ParseServerVersion(ServerVersionString); + // Adjust client encoding. - NpgsqlParameterStatus clientEncodingParam = null; - if( - !ServerParameters.TryGetValue("client_encoding", out clientEncodingParam) || - (!string.Equals(clientEncodingParam.ParameterValue, "UTF8", StringComparison.OrdinalIgnoreCase) && !string.Equals(clientEncodingParam.ParameterValue, "UNICODE", StringComparison.OrdinalIgnoreCase)) - ) - new NpgsqlCommand("SET CLIENT_ENCODING TO UTF8", this).ExecuteBlind(); + //NpgsqlCommand commandEncoding1 = new NpgsqlCommand("show client_encoding", _connector); + //String clientEncoding1 = (String)commandEncoding1.ExecuteScalar(); - if (!string.IsNullOrEmpty(settings.SearchPath)) + if (ConnectionString.ToString(ConnectionStringKeys.Encoding, ConnectionStringDefaults.Encoding).ToUpper() == "UNICODE") { - /*NpgsqlParameter p = new NpgsqlParameter("p", DbType.String); - p.Value = settings.SearchPath; - NpgsqlCommand commandSearchPath = new NpgsqlCommand("SET SEARCH_PATH TO :p,public", this); - commandSearchPath.Parameters.Add(p); - commandSearchPath.ExecuteNonQuery();*/ - - /*NpgsqlParameter p = new NpgsqlParameter("p", DbType.String); - p.Value = settings.SearchPath; - NpgsqlCommand commandSearchPath = new NpgsqlCommand("SET SEARCH_PATH TO :p,public", this); - commandSearchPath.Parameters.Add(p); - commandSearchPath.ExecuteNonQuery();*/ - - // TODO: Add proper message when finding a semicolon in search_path. - // This semicolon could lead to a sql injection security hole as someone could write in connection string: - // searchpath=public;delete from table; and it would be executed. - - if (settings.SearchPath.Contains(";")) - { - throw new InvalidOperationException(); - } - - // This is using string concatenation because set search_path doesn't allow type casting. ::text - NpgsqlCommand commandSearchPath = new NpgsqlCommand("SET SEARCH_PATH=" + settings.SearchPath, this); - commandSearchPath.ExecuteBlind(); + Encoding = Encoding.UTF8; + NpgsqlCommand commandEncoding = new NpgsqlCommand("SET CLIENT_ENCODING TO UNICODE", this); + commandEncoding.ExecuteNonQuery(); } // Make a shallow copy of the type mapping that the connector will own. @@ -718,40 +764,36 @@ namespace Npgsql { try { - if (_connection_state != ConnectionState.Closed) - { - _connection_state = ConnectionState.Closed; - this.CurrentState.Close(this); - _serverParameters.Clear(); - ServerVersion = null; - } - } - catch - { + this.CurrentState.Close(this); } + catch {} } - + internal void CancelRequest() { - NpgsqlConnector CancelConnector = new NpgsqlConnector(settings, false, false); - + + 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() + internal String NextPortalName() { - return _portalNamePrefix + Interlocked.Increment(ref _portalIndex); + + return _portalNamePrefix + System.Threading.Interlocked.Increment(ref _portalIndex); } @@ -760,166 +802,114 @@ namespace Npgsql ///</summary> internal String NextPlanName() { - return _planNamePrefix + Interlocked.Increment(ref _planIndex); + 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 = 1; + _notificationThreadStopCount = 1; + } - + internal void AddNotificationThread() { + _notificationThreadStopCount = 0; _notificationAutoResetEvent.Set(); - + NpgsqlContextHolder contextHolder = new NpgsqlContextHolder(this, CurrentState); - + _notificationThread = new Thread(new ThreadStart(contextHolder.ProcessServerMessages)); - + _notificationThread.Start(); + + + } - - //Use with using(){} to perform the sentry pattern - //on stopping and starting notification thread - //(The sentry pattern is a generalisation of RAII where we - //have a pair of actions - one "undoing" the previous - //and we want to execute the first and second around other code, - //then we treat it much like resource mangement in RAII. - //try{}finally{} also does execute-around, but sentry classes - //have some extra flexibility (e.g. they can be "owned" by - //another object and then cleaned up when that object is - //cleaned up), and can act as the sole gate-way - //to the code in question, guaranteeing that using code can't be written - //so that the "undoing" is forgotten. - internal class NotificationThreadBlock : IDisposable - { - private NpgsqlConnector _connector; - - public NotificationThreadBlock(NpgsqlConnector connector) - { - (_connector = connector).StopNotificationThread(); - } - - public void Dispose() - { - if (_connector != null) - { - _connector.ResumeNotificationThread(); - } - _connector = null; - } - } - - internal NotificationThreadBlock BlockNotificationThread() - { - return new NotificationThreadBlock(this); - } - - private void StopNotificationThread() + + internal void StopNotificationThread() { - // first check to see if an exception has - // been thrown by the notification thread. - if (_notificationException != null) - throw _notificationException; - + _notificationThreadStopCount++; - + if (_notificationThreadStopCount == 1) // If this call was the first to increment. { + _notificationAutoResetEvent.WaitOne(); + } } - - private void ResumeNotificationThread() + + internal void ResumeNotificationThread() { _notificationThreadStopCount--; if (_notificationThreadStopCount == 0) { // Release the synchronization handle. - + _notificationAutoResetEvent.Set(); } + } - + internal Boolean IsNotificationThreadRunning { - get { return _notificationThreadStopCount <= 0; } + get + { + return _notificationThreadStopCount <= 0; + + } } - - + + internal class NpgsqlContextHolder { - private readonly NpgsqlConnector connector; - private readonly NpgsqlState state; - + + private NpgsqlConnector connector; + private NpgsqlState state; + internal NpgsqlContextHolder(NpgsqlConnector connector, NpgsqlState state) { this.connector = connector; this.state = state; + } - + internal void ProcessServerMessages() { - try + + while(true) { - while (true) + this.connector._notificationAutoResetEvent.WaitOne(); + + if (this.connector.Socket.Poll(1000, SelectMode.SelectRead)) { - Thread.Sleep(0); - //To give runtime chance to release correctly the lock. See http://pgfoundry.org/forum/message.php?msg_id=1002650 for more information. - this.connector._notificationAutoResetEvent.WaitOne(); - - if (this.connector.Socket.Poll(100, SelectMode.SelectRead)) - { - // reset any responses just before getting new ones - this.connector.Mediator.ResetResponses(); - this.state.ProcessBackendResponses(this.connector); - } - - this.connector._notificationAutoResetEvent.Set(); + // reset any responses just before getting new ones + this.connector.Mediator.ResetResponses(); + this.state.ProcessBackendResponses(this.connector); + this.connector.CheckErrorsAndNotifications(); } - } - catch (IOException ex) - { - this.connector._notificationException = ex; + this.connector._notificationAutoResetEvent.Set(); } - + + + } - + } - public bool RequireReadyForQuery - { - get { return _requireReadyForQuery; } - set { _requireReadyForQuery = value; } - } - public void AddParameterStatus(NpgsqlParameterStatus ps) - { - if (_serverParameters.ContainsKey(ps.Parameter)) - { - _serverParameters[ps.Parameter] = ps; - } - else - { - _serverParameters.Add(ps.Parameter, ps); - } - } - public IDictionary<string, NpgsqlParameterStatus> ServerParameters - { - get { return new ReadOnlyDictionary<string, NpgsqlParameterStatus>(_serverParameters); } - } } } diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlConnectorPool.cs b/mcs/class/Npgsql/Npgsql/NpgsqlConnectorPool.cs index 2bc87f24316..4690c8e654f 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlConnectorPool.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlConnectorPool.cs @@ -2,22 +2,19 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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 // // ConnectorPool.cs // ------------------------------------------------------------------ @@ -27,602 +24,534 @@ // System.Queue. using System; -using System.Collections.Generic; -using System.Data; +using System.Collections; using System.Threading; using System.Timers; -using Timer = System.Timers.Timer; namespace Npgsql { - /// <summary> - /// This class manages all connector objects, pooled AND non-pooled. - /// </summary> - internal class NpgsqlConnectorPool - { - /// <summary> - /// A queue with an extra Int32 for keeping track of busy connections. - /// </summary> - private class ConnectorQueue : Queue<NpgsqlConnector> - { - /// <summary> - /// The number of pooled Connectors that belong to this queue but - /// are currently in use. - /// </summary> - public Int32 UseCount = 0; - - public Int32 ConnectionLifeTime; - public Int32 InactiveTime = 0; - public Int32 MinPoolSize; - } - - /// <value>Unique static instance of the connector pool - /// mamager.</value> - internal static NpgsqlConnectorPool ConnectorPoolMgr = new NpgsqlConnectorPool(); - - public NpgsqlConnectorPool() - { - PooledConnectors = new Dictionary<string, ConnectorQueue>(); - Timer = new Timer(1000); - Timer.AutoReset = true; - Timer.Elapsed += new ElapsedEventHandler(TimerElapsedHandler); - Timer.Start(); - } - - - ~NpgsqlConnectorPool() - { - Timer.Stop(); - } - - private void TimerElapsedHandler(object sender, ElapsedEventArgs e) - { - NpgsqlConnector Connector; - lock (this) - { - foreach (ConnectorQueue Queue in PooledConnectors.Values) - { - if (Queue.Count > 0) - { - if (Queue.Count + Queue.UseCount > Queue.MinPoolSize) - { - if (Queue.InactiveTime >= Queue.ConnectionLifeTime) - { - Int32 diff = Queue.Count + Queue.UseCount - Queue.MinPoolSize; - Int32 toBeClosed = (diff + 1) / 2; - toBeClosed = Math.Min(toBeClosed, Queue.Count); - - if (diff < 2) - { - diff = 2; - } - Queue.InactiveTime -= Queue.ConnectionLifeTime / (int)(Math.Log(diff) / Math.Log(2)); - for (Int32 i = 0; i < toBeClosed; ++i) - { - Connector = Queue.Dequeue(); - Connector.Close(); - } - } - else - { - Queue.InactiveTime++; - } - } - else - { - Queue.InactiveTime = 0; - } - } - else - { - Queue.InactiveTime = 0; - } - } - } - } - - - /// <value>Map of index to unused pooled connectors, avaliable to the - /// next RequestConnector() call.</value> - /// <remarks>This hashmap will be indexed by connection string. - /// This key will hold a list of queues of pooled connectors available to be used.</remarks> - private readonly Dictionary<string, ConnectorQueue> PooledConnectors; - - /*/// <value>Map of shared connectors, avaliable to the + /// <summary> + /// This class manages all connector objects, pooled AND non-pooled. + /// </summary> + internal class NpgsqlConnectorPool + { + /// <summary> + /// A queue with an extra Int32 for keeping track of busy connections. + /// </summary> + private class ConnectorQueue : System.Collections.Queue + { + /// <summary> + /// The number of pooled Connectors that belong to this queue but + /// are currently in use. + /// </summary> + public Int32 UseCount = 0; + public Int32 ConnectionLifeTime; + public Int32 InactiveTime = 0; + public Int32 MinPoolSize; + + } + + /// <value>Unique static instance of the connector pool + /// mamager.</value> + internal static NpgsqlConnectorPool ConnectorPoolMgr = new NpgsqlConnectorPool(); + + public NpgsqlConnectorPool() + { + PooledConnectors = new Hashtable(); + Timer = new System.Timers.Timer(1000); + Timer.AutoReset = true; + Timer.Elapsed += new ElapsedEventHandler(TimerElapsedHandler); + Timer.Start(); + + } + + + ~NpgsqlConnectorPool() + { + Timer.Stop(); + } + + private void TimerElapsedHandler(object sender, ElapsedEventArgs e) + { + NpgsqlConnector Connector; + lock (this) + { + foreach (ConnectorQueue Queue in PooledConnectors.Values) + { + if (Queue.Count > 0) + { + if (Queue.Count + Queue.UseCount > Queue.MinPoolSize) + { + if (Queue.InactiveTime >= Queue.ConnectionLifeTime) + { + Int32 diff = Queue.Count + Queue.UseCount - Queue.MinPoolSize; + Int32 toBeClosed = (diff + 1) / 2; + if (diff < 2) + diff = 2; + Queue.InactiveTime -= Queue.ConnectionLifeTime / (int)(Math.Log(diff) / Math.Log(2)); + for (Int32 i = 0; i < toBeClosed; ++i) + { + Connector = (NpgsqlConnector)Queue.Dequeue(); + Connector.Close(); + } + } + else + { + Queue.InactiveTime++; + } + } + else + { + Queue.InactiveTime = 0; + } + } + else + { + Queue.InactiveTime = 0; + } + } + } + } + + + + /// <value>Map of index to unused pooled connectors, avaliable to the + /// next RequestConnector() call.</value> + /// <remarks>This hashmap will be indexed by connection string. + /// This key will hold a list of queues of pooled connectors available to be used.</remarks> + private Hashtable PooledConnectors; + + /// <value>Map of shared connectors, avaliable to the /// next RequestConnector() call.</value> /// <remarks>This hashmap will be indexed by connection string. /// This key will hold a list of shared connectors available to be used.</remarks> // To be implemented - //private Dictionary<?, ?> SharedConnectors;*/ - - - /// <value>Timer for tracking unused connections in pools.</value> - // I used System.Timers.Timer because of bad experience with System.Threading.Timer - // on Windows - it's going mad sometimes and don't respect interval was set. - private readonly Timer Timer; - - /// <summary> - /// Searches the shared and pooled connector lists for a - /// matching connector object or creates a new one. - /// </summary> - /// <param name="Connection">The NpgsqlConnection that is requesting - /// the connector. Its ConnectionString will be used to search the - /// pool for available connectors.</param> - /// <returns>A connector object.</returns> - public NpgsqlConnector RequestConnector(NpgsqlConnection Connection) - { - NpgsqlConnector Connector; - - if (Connection.Pooling) - { - Connector = RequestPooledConnector(Connection); - } - else - { - Connector = GetNonPooledConnector(Connection); - } - - return Connector; - } - - /// <summary> - /// Find a pooled connector. Handle locking and timeout here. - /// </summary> - private NpgsqlConnector RequestPooledConnector(NpgsqlConnection Connection) - { - NpgsqlConnector Connector; - Int32 timeoutMilliseconds = Connection.Timeout * 1000; - - lock (this) - { - Connector = RequestPooledConnectorInternal(Connection); - } - - while (Connector == null && timeoutMilliseconds > 0) - { - Int32 ST = timeoutMilliseconds > 1000 ? 1000 : timeoutMilliseconds; - - Thread.Sleep(ST); - timeoutMilliseconds -= ST; - - lock (this) - { - Connector = RequestPooledConnectorInternal(Connection); - } - } - - if (Connector == null) - { - if (Connection.Timeout > 0) - { - throw new Exception("Timeout while getting a connection from pool."); - } - else - { - throw new Exception("Connection pool exceeds maximum size."); - } - } - - return Connector; - } - - /// <summary> - /// Find a pooled connector. Handle shared/non-shared here. - /// </summary> - private NpgsqlConnector RequestPooledConnectorInternal(NpgsqlConnection Connection) - { - NpgsqlConnector Connector = null; - Boolean Shared = false; - - // If sharing were implemented, I suppose Shared would be set based - // on some property on the Connection. - - if (Shared) - { - // Connection sharing? What's that? - throw new NotImplementedException("Internal: Shared pooling not implemented"); - - } - Connector = GetPooledConnector(Connection); - - - return Connector; - } - - private delegate void CleanUpConnectorDel(NpgsqlConnection Connection, NpgsqlConnector Connector); - - private void CleanUpConnectorMethod(NpgsqlConnection Connection, NpgsqlConnector Connector) - { - try - { - Connector.CurrentReader.Close(); - Connector.CurrentReader = null; - ReleaseConnector(Connection, Connector); - } - catch - { - } - } - - private void CleanUpConnector(NpgsqlConnection Connection, NpgsqlConnector Connector) - { - new CleanUpConnectorDel(CleanUpConnectorMethod).BeginInvoke(Connection, Connector, null, null); - } - - /// <summary> - /// Releases a connector, possibly back to the pool for future use. - /// </summary> - /// <remarks> - /// Pooled connectors will be put back into the pool if there is room. - /// Shared connectors should just have their use count decremented - /// since they always stay in the shared pool. - /// </remarks> - /// <param name="Connector">The connector to release.</param> - public void ReleaseConnector(NpgsqlConnection Connection, NpgsqlConnector Connector) - { - if (Connector.CurrentReader != null) - { - CleanUpConnector(Connection, Connector); - } - else if (Connector.Pooled) - { - ReleasePooledConnector(Connection, Connector); - } - else - { - UngetNonPooledConnector(Connection, Connector); - } - } - - /// <summary> - /// Release a pooled connector. Handle locking here. - /// </summary> - private void ReleasePooledConnector(NpgsqlConnection Connection, NpgsqlConnector Connector) - { - lock (this) - { - ReleasePooledConnectorInternal(Connection, Connector); - } - } - - /// <summary> - /// Release a pooled connector. Handle shared/non-shared here. - /// </summary> - private void ReleasePooledConnectorInternal(NpgsqlConnection Connection, NpgsqlConnector Connector) - { - if (!Connector.Shared) - { - UngetPooledConnector(Connection, Connector); - } - else - { - // Connection sharing? What's that? - throw new NotImplementedException("Internal: Shared pooling not implemented"); - } - } - - /// <summary> - /// Create a connector without any pooling functionality. - /// </summary> - private static NpgsqlConnector GetNonPooledConnector(NpgsqlConnection Connection) - { - NpgsqlConnector Connector; - - Connector = CreateConnector(Connection); - - Connector.CertificateSelectionCallback += Connection.CertificateSelectionCallbackDelegate; - Connector.CertificateValidationCallback += Connection.CertificateValidationCallbackDelegate; - Connector.PrivateKeySelectionCallback += Connection.PrivateKeySelectionCallbackDelegate; - - Connector.Open(); - - return Connector; - } - - /// <summary> - /// Find an available pooled connector in the non-shared pool, or create - /// a new one if none found. - /// </summary> - private NpgsqlConnector GetPooledConnector(NpgsqlConnection Connection) - { - ConnectorQueue Queue; - NpgsqlConnector Connector = null; - - // Try to find a queue. - if (!PooledConnectors.TryGetValue(Connection.ConnectionString, out Queue)) - { - Queue = new ConnectorQueue(); - Queue.ConnectionLifeTime = Connection.ConnectionLifeTime; - Queue.MinPoolSize = Connection.MinPoolSize; - PooledConnectors[Connection.ConnectionString] = Queue; - } - - // Fix queue use count. Use count may be dropped below zero if Queue was cleared and there were connections open. - if (Queue.UseCount < 0) - { - Queue.UseCount = 0; - } - - - if (Queue.Count > 0) - { - // Found a queue with connectors. Grab the top one. - - // Check if the connector is still valid. - - Connector = Queue.Dequeue(); - /*try - { - Connector.TestConnector(); - Connector.RequireReadyForQuery = true; - } - catch //This connector is broken! - { - try - { - Connector.Close(); - } - catch - { - try - { - Connector.Stream.Close(); - } - catch - { - } - } - return GetPooledConnector(Connection); //Try again - }*/ - - if (!Connector.IsValid()) - { - try - { - Connector.Close(); - } - catch - { - try - { - Connector.Stream.Close(); - } - catch - { - } - } - return GetPooledConnector(Connection); //Try again - - } - Queue.UseCount++; - } - else if (Queue.Count + Queue.UseCount < Connection.MaxPoolSize) - { - Connector = CreateConnector(Connection); - - Connector.CertificateSelectionCallback += Connection.CertificateSelectionCallbackDelegate; - Connector.CertificateValidationCallback += Connection.CertificateValidationCallbackDelegate; - Connector.PrivateKeySelectionCallback += Connection.PrivateKeySelectionCallbackDelegate; - - try - { - Connector.Open(); - } - catch - { - try - { - Connector.Close(); - } - catch - { - } - - throw; - } - - - Queue.UseCount++; - } - - // Meet the MinPoolSize requirement if needed. - if (Connection.MinPoolSize > 0) - { - while (Queue.Count + Queue.UseCount < Connection.MinPoolSize) - { - NpgsqlConnector Spare = CreateConnector(Connection); - - Spare.CertificateSelectionCallback += Connection.CertificateSelectionCallbackDelegate; - Spare.CertificateValidationCallback += Connection.CertificateValidationCallbackDelegate; - Spare.PrivateKeySelectionCallback += Connection.PrivateKeySelectionCallbackDelegate; - - Spare.Open(); - - Spare.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate; - Spare.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate; - Spare.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate; - - Queue.Enqueue(Spare); - } - } - - return Connector; - } - - /* - /// <summary> - /// Find an available shared connector in the shared pool, or create - /// a new one if none found. - /// </summary> - private NpgsqlConnector GetSharedConnector(NpgsqlConnection Connection) - { - // To be implemented - - return null; - } - */ - - private static NpgsqlConnector CreateConnector(NpgsqlConnection Connection) - { - return new NpgsqlConnector(Connection.ConnectionStringValues.Clone(), Connection.Pooling, false); - } - - - /// <summary> - /// This method is only called when NpgsqlConnection.Dispose(false) is called which means a - /// finalization. This also means, an NpgsqlConnection was leak. We clear pool count so that - /// client doesn't end running out of connections from pool. When the connection is finalized, its underlying - /// socket is closed. - /// </summary> - public void FixPoolCountBecauseOfConnectionDisposeFalse(NpgsqlConnection Connection) - { - ConnectorQueue Queue; - - // Prevent multithread access to connection pool count. - lock (this) - { - // Try to find a queue. - if (PooledConnectors.TryGetValue(Connection.ConnectionString, out Queue) && Queue != null) - { - Queue.UseCount--; - } - } - } - - /// <summary> - /// Close the connector. - /// </summary> - /// <param name="Connection"></param> - /// <param name="Connector">Connector to release</param> - private static void UngetNonPooledConnector(NpgsqlConnection Connection, NpgsqlConnector Connector) - { - Connector.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate; - Connector.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate; - Connector.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate; - - if (Connector.Transaction != null) - { - Connector.Transaction.Cancel(); - } - - Connector.Close(); - } - - /// <summary> - /// Put a pooled connector into the pool queue. - /// </summary> - /// <param name="Connector">Connector to pool</param> - private void UngetPooledConnector(NpgsqlConnection Connection, NpgsqlConnector Connector) - { - ConnectorQueue Queue; - - // Find the queue. - if (!PooledConnectors.TryGetValue(Connection.ConnectionString, out Queue) || Queue == null) - { - return; // Queue may be emptied by connection problems. See ClearPool below. - } - - Connector.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate; - Connector.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate; - Connector.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate; - - Queue.UseCount--; - - if (!Connector.IsInitialized) - { - if (Connector.Transaction != null) - { - Connector.Transaction.Cancel(); - } - - Connector.Close(); - } - else - { - if (Connector.Transaction != null) - { - try - { - Connector.Transaction.Rollback(); - } - catch - { - Connector.Close(); - } - } - } - - if (Connector.State == ConnectionState.Open && - (Thread.CurrentThread.ThreadState & (ThreadState.Aborted | ThreadState.AbortRequested)) == 0) - { - // Release all resources associated with this connector. - Connector.ReleaseResources(); - - Queue.Enqueue(Connector); - } - } - - /* - /// <summary> - /// Stop sharing a shared connector. - /// </summary> - /// <param name="Connector">Connector to unshare</param> - private void UngetSharedConnector(NpgsqlConnection Connection, NpgsqlConnector Connector) - { - // To be implemented - } - */ - - private static void ClearQueue(ConnectorQueue Queue) - { - if (Queue == null) - { - return; - } - - while (Queue.Count > 0) - { - NpgsqlConnector connector = Queue.Dequeue(); - - try - { - connector.Close(); - } - catch - { - // Maybe we should log something here to say we got an exception while closing connector? - } - } - } - - - internal void ClearPool(NpgsqlConnection Connection) - { - // Prevent multithread access to connection pool count. - lock (this) - { - ConnectorQueue queue; - // Try to find a queue. - if (PooledConnectors.TryGetValue(Connection.ConnectionString, out queue)) + //private Hashtable SharedConnectors; + + + /// <value>Timer for tracking unused connections in pools.</value> + // I used System.Timers.Timer because of bad experience with System.Threading.Timer + // on Windows - it's going mad sometimes and don't respect interval was set. + private System.Timers.Timer Timer; + + /// <summary> + /// Searches the shared and pooled connector lists for a + /// matching connector object or creates a new one. + /// </summary> + /// <param name="Connection">The NpgsqlConnection that is requesting + /// the connector. Its ConnectionString will be used to search the + /// pool for available connectors.</param> + /// <returns>A connector object.</returns> + public NpgsqlConnector RequestConnector (NpgsqlConnection Connection) + { + NpgsqlConnector Connector; + + if (Connection.Pooling) + { + Connector = RequestPooledConnector(Connection); + } + else + { + Connector = GetNonPooledConnector(Connection); + } + + return Connector; + } + + /// <summary> + /// Find a pooled connector. Handle locking and timeout here. + /// </summary> + private NpgsqlConnector RequestPooledConnector (NpgsqlConnection Connection) + { + NpgsqlConnector Connector; + Int32 timeoutMilliseconds = Connection.Timeout * 1000; + + lock(this) + { + Connector = RequestPooledConnectorInternal(Connection); + } + + while (Connector == null && timeoutMilliseconds > 0) + { + Int32 ST = timeoutMilliseconds > 1000 ? 1000 : timeoutMilliseconds; + + Thread.Sleep(ST); + timeoutMilliseconds -= ST; + + lock(this) { - ClearQueue(queue); + Connector = RequestPooledConnectorInternal(Connection); + } + } - PooledConnectors.Remove(Connection.ConnectionString); + if (Connector == null) + { + if (Connection.Timeout > 0) + { + throw new Exception("Timeout while getting a connection from pool."); } - } - } - - - internal void ClearAllPools() - { - lock (this) - { - foreach (ConnectorQueue Queue in PooledConnectors.Values) - { - ClearQueue(Queue); - } - PooledConnectors.Clear(); - } - } - } + else + { + throw new Exception("Connection pool exceeds maximum size."); + } + } + + return Connector; + } + + /// <summary> + /// Find a pooled connector. Handle shared/non-shared here. + /// </summary> + private NpgsqlConnector RequestPooledConnectorInternal (NpgsqlConnection Connection) + { + NpgsqlConnector Connector = null; + Boolean Shared = false; + + // If sharing were implemented, I suppose Shared would be set based + // on some property on the Connection. + + if (! Shared) + { + Connector = GetPooledConnector(Connection); + } + else + { + // Connection sharing? What's that? + throw new NotImplementedException("Internal: Shared pooling not implemented"); + } + + return Connector; + } + + /// <summary> + /// Releases a connector, possibly back to the pool for future use. + /// </summary> + /// <remarks> + /// Pooled connectors will be put back into the pool if there is room. + /// Shared connectors should just have their use count decremented + /// since they always stay in the shared pool. + /// </remarks> + /// <param name="Connector">The connector to release.</param> + /// <param name="ForceClose">Force the connector to close, even if it is pooled.</param> + public void ReleaseConnector (NpgsqlConnection Connection, NpgsqlConnector Connector) + { + if (Connector.Pooled) + { + ReleasePooledConnector(Connection, Connector); + } + else + { + UngetNonPooledConnector(Connection, Connector); + } + } + + /// <summary> + /// Release a pooled connector. Handle locking here. + /// </summary> + private void ReleasePooledConnector (NpgsqlConnection Connection, NpgsqlConnector Connector) + { + lock(this) + { + ReleasePooledConnectorInternal(Connection, Connector); + } + } + + /// <summary> + /// Release a pooled connector. Handle shared/non-shared here. + /// </summary> + private void ReleasePooledConnectorInternal (NpgsqlConnection Connection, NpgsqlConnector Connector) + { + if (! Connector.Shared) + { + UngetPooledConnector(Connection, Connector); + } + else + { + // Connection sharing? What's that? + throw new NotImplementedException("Internal: Shared pooling not implemented"); + } + } + + /// <summary> + /// Create a connector without any pooling functionality. + /// </summary> + private NpgsqlConnector GetNonPooledConnector(NpgsqlConnection Connection) + { + NpgsqlConnector Connector; + + Connector = CreateConnector(Connection); + + Connector.CertificateSelectionCallback += Connection.CertificateSelectionCallbackDelegate; + Connector.CertificateValidationCallback += Connection.CertificateValidationCallbackDelegate; + Connector.PrivateKeySelectionCallback += Connection.PrivateKeySelectionCallbackDelegate; + + Connector.Open(); + + return Connector; + } + + /// <summary> + /// Find an available pooled connector in the non-shared pool, or create + /// a new one if none found. + /// </summary> + private NpgsqlConnector GetPooledConnector(NpgsqlConnection Connection) + { + ConnectorQueue Queue; + NpgsqlConnector Connector = null; + + // Try to find a queue. + Queue = (ConnectorQueue)PooledConnectors[Connection.ConnectionString.ToString()]; + + if (Queue == null) + { + Queue = new ConnectorQueue(); + Queue.ConnectionLifeTime = Connection.ConnectionLifeTime; + Queue.MinPoolSize = Connection.MinPoolSize; + PooledConnectors[Connection.ConnectionString.ToString()] = Queue; + } + + // Fix queue use count. Use count may be dropped below zero if Queue was cleared and there were connections open. + if (Queue.UseCount < 0) + Queue.UseCount = 0; + + + if (Queue.Count > 0) + { + // Found a queue with connectors. Grab the top one. + + // Check if the connector is still valid. + + Connector = (NpgsqlConnector)Queue.Dequeue(); + Queue.UseCount++; + + + + } + else if (Queue.Count + Queue.UseCount < Connection.MaxPoolSize) + { + Connector = CreateConnector(Connection); + + Connector.CertificateSelectionCallback += Connection.CertificateSelectionCallbackDelegate; + Connector.CertificateValidationCallback += Connection.CertificateValidationCallbackDelegate; + Connector.PrivateKeySelectionCallback += Connection.PrivateKeySelectionCallbackDelegate; + + try + { + Connector.Open(); + } + catch { + try + { + Connector.Close(); + } + catch {} + + throw; + } + + + Queue.UseCount++; + } + + // Meet the MinPoolSize requirement if needed. + if (Connection.MinPoolSize > 0) + { + while (Queue.Count + Queue.UseCount < Connection.MinPoolSize) + { + NpgsqlConnector Spare = CreateConnector(Connection); + + Spare.CertificateSelectionCallback += Connection.CertificateSelectionCallbackDelegate; + Spare.CertificateValidationCallback += Connection.CertificateValidationCallbackDelegate; + Spare.PrivateKeySelectionCallback += Connection.PrivateKeySelectionCallbackDelegate; + + Spare.Open(); + + Spare.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate; + Spare.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate; + Spare.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate; + + Queue.Enqueue(Spare); + } + } + + return Connector; + } + + /// <summary> + /// Find an available shared connector in the shared pool, or create + /// a new one if none found. + /// </summary> + private NpgsqlConnector GetSharedConnector(NpgsqlConnection Connection) + { + // To be implemented + + return null; + } + + private NpgsqlConnector CreateConnector(NpgsqlConnection Connection) + { + return new NpgsqlConnector( + Connection.ConnectionStringValues.Clone(), + Connection.Pooling, + false + ); + } + + + /// <summary> + /// This method is only called when NpgsqlConnection.Dispose(false) is called which means a + /// finalization. This also means, an NpgsqlConnection was leak. We clear pool count so that + /// client doesn't end running out of connections from pool. When the connection is finalized, its underlying + /// socket is closed. + /// </summary + public void FixPoolCountBecauseOfConnectionDisposeFalse(NpgsqlConnection Connection) + { + ConnectorQueue Queue; + + // Prevent multithread access to connection pool count. + lock(this) + { + // Try to find a queue. + Queue = (ConnectorQueue)PooledConnectors[Connection.ConnectionString.ToString()]; + + if (Queue != null) + Queue.UseCount--; + + } + } + + /// <summary> + /// Close the connector. + /// </summary> + /// <param name="Connector">Connector to release</param> + private void UngetNonPooledConnector(NpgsqlConnection Connection, NpgsqlConnector Connector) + { + Connector.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate; + Connector.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate; + Connector.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate; + + if (Connector.Transaction != null) + { + Connector.Transaction.Cancel(); + } + + Connector.Close(); + } + + /// <summary> + /// Put a pooled connector into the pool queue. + /// </summary> + /// <param name="Connector">Connector to pool</param> + private void UngetPooledConnector(NpgsqlConnection Connection, NpgsqlConnector Connector) + { + ConnectorQueue Queue; + + // Find the queue. + Queue = (ConnectorQueue)PooledConnectors[Connector.ConnectionString.ToString()]; + + if (Queue == null) + return; // Queue may be emptied by connection problems. See ClearPool below. + + Connector.CertificateSelectionCallback -= Connection.CertificateSelectionCallbackDelegate; + Connector.CertificateValidationCallback -= Connection.CertificateValidationCallbackDelegate; + Connector.PrivateKeySelectionCallback -= Connection.PrivateKeySelectionCallbackDelegate; + + Queue.UseCount--; + + if (! Connector.IsInitialized) + { + if (Connector.Transaction != null) + { + Connector.Transaction.Cancel(); + } + + Connector.Close(); + } + else + { + if (Connector.Transaction != null) + { + try + { + Connector.Transaction.Rollback(); + } + catch { + Connector.Close() + ; + } + } + } + + if (Connector.State == System.Data.ConnectionState.Open) + { + // Release all resources associated with this connector. + Connector.ReleaseResources(); + + Queue.Enqueue(Connector); + } + } + + /// <summary> + /// Stop sharing a shared connector. + /// </summary> + /// <param name="Connector">Connector to unshare</param> + private void UngetSharedConnector(NpgsqlConnection Connection, NpgsqlConnector Connector) + { + // To be implemented + } + + private void ClearQueue(ConnectorQueue Queue) + { + if (Queue == null) + return; + + while (Queue.Count > 0) + { + NpgsqlConnector connector = (NpgsqlConnector)Queue.Dequeue(); + + try + { + connector.Close(); + } + catch { + // Maybe we should log something here to say we got an exception while closing connector? + + } + + } + + } + + + internal void ClearPool(NpgsqlConnection Connection) + { + // Prevent multithread access to connection pool count. + lock(this) + { + // Try to find a queue. + ConnectorQueue queue = (ConnectorQueue)PooledConnectors[Connection.ConnectionString.ToString()]; + + ClearQueue(queue); + + PooledConnectors[Connection.ConnectionString.ToString()] = null; + + } + + + } + + internal void ClearAllPools() + { + + lock (this) + { + foreach (ConnectorQueue Queue in PooledConnectors.Values) + ClearQueue(Queue); + + } + + + } + + } } diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlDataAdapter.cs b/mcs/class/Npgsql/Npgsql/NpgsqlDataAdapter.cs index 167642c66b1..d41193edd0c 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlDataAdapter.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlDataAdapter.cs @@ -10,212 +10,267 @@ // http://gborg.postgresql.org/project/npgsql/projdisplay.php // // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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.Data.Common; +using System.Resources; namespace Npgsql { - /// <summary> - /// Represents the method that handles the <see cref="Npgsql.NpgsqlDataAdapter.RowUpdated">RowUpdated</see> events. - /// </summary> - /// <param name="sender">The source of the event.</param> - /// <param name="e">A <see cref="NpgsqlRowUpdatedEventArgs">NpgsqlRowUpdatedEventArgs</see> that contains the event data.</param> - public delegate void NpgsqlRowUpdatedEventHandler(Object sender, NpgsqlRowUpdatedEventArgs e); - - /// <summary> - /// Represents the method that handles the <see cref="Npgsql.NpgsqlDataAdapter.RowUpdating">RowUpdating</see> events. - /// </summary> - /// <param name="sender">The source of the event.</param> - /// <param name="e">A <see cref="NpgsqlRowUpdatingEventArgs">NpgsqlRowUpdatingEventArgs</see> that contains the event data.</param> - public delegate void NpgsqlRowUpdatingEventHandler(Object sender, NpgsqlRowUpdatingEventArgs e); - - - /// <summary> - /// This class represents an adapter from many commands: select, update, insert and delete to fill <see cref="System.Data.DataSet">Datasets.</see> - /// </summary> - public sealed class NpgsqlDataAdapter : DbDataAdapter, IDbDataAdapter - { - private NpgsqlCommand _selectCommand; - private NpgsqlCommand _updateCommand; - private NpgsqlCommand _deleteCommand; - private NpgsqlCommand _insertCommand; - - // Log support - private static readonly String CLASSNAME = "NpgsqlDataAdapter"; - - - public event NpgsqlRowUpdatedEventHandler RowUpdated; - public event NpgsqlRowUpdatingEventHandler RowUpdating; - - public NpgsqlDataAdapter() - { - } - - public NpgsqlDataAdapter(NpgsqlCommand selectCommand) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME); - _selectCommand = selectCommand; - } - - public NpgsqlDataAdapter(String selectCommandText, NpgsqlConnection selectConnection) - : this(new NpgsqlCommand(selectCommandText, selectConnection)) - { - } - - public NpgsqlDataAdapter(String selectCommandText, String selectConnectionString) - : this(selectCommandText, new NpgsqlConnection(selectConnectionString)) - { - } - - - protected override RowUpdatedEventArgs CreateRowUpdatedEvent(DataRow dataRow, IDbCommand command, - StatementType statementType, - DataTableMapping tableMapping) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CreateRowUpdatedEvent"); - return new NpgsqlRowUpdatedEventArgs(dataRow, command, statementType, tableMapping); - } - - protected override RowUpdatingEventArgs CreateRowUpdatingEvent(DataRow dataRow, IDbCommand command, - StatementType statementType, - DataTableMapping tableMapping) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CreateRowUpdatingEvent"); - return new NpgsqlRowUpdatingEventArgs(dataRow, command, statementType, tableMapping); - } - - protected override void OnRowUpdated(RowUpdatedEventArgs value) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "OnRowUpdated"); - //base.OnRowUpdated(value); - if ((RowUpdated != null) && (value is NpgsqlRowUpdatedEventArgs)) - { - RowUpdated(this, (NpgsqlRowUpdatedEventArgs) value); - } - } - - protected override void OnRowUpdating(RowUpdatingEventArgs value) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "OnRowUpdating"); - if ((RowUpdating != null) && (value is NpgsqlRowUpdatingEventArgs)) - { - RowUpdating(this, (NpgsqlRowUpdatingEventArgs) value); - } - } - - ITableMappingCollection IDataAdapter.TableMappings - { - get { return TableMappings; } - } - - IDbCommand IDbDataAdapter.DeleteCommand - { - get - { - NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "IDbDataAdapter.DeleteCommand"); - return DeleteCommand; - } - - set { DeleteCommand = (NpgsqlCommand) value; } - } - - - public new NpgsqlCommand DeleteCommand - { - get { return _deleteCommand; } - - set { _deleteCommand = value; } - } - - IDbCommand IDbDataAdapter.SelectCommand - { - get { return SelectCommand; } - - set { SelectCommand = (NpgsqlCommand) value; } - } - - - public new NpgsqlCommand SelectCommand - { - get { return _selectCommand; } - - set { _selectCommand = value; } - } - - IDbCommand IDbDataAdapter.UpdateCommand - { - get - { - NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "IDbDataAdapter.UpdateCommand"); - return UpdateCommand; - } - - set { UpdateCommand = (NpgsqlCommand) value; } - } - - - public new NpgsqlCommand UpdateCommand - { - get { return _updateCommand; } - - set { _updateCommand = value; } - } - - IDbCommand IDbDataAdapter.InsertCommand - { - get { return InsertCommand; } - - set { InsertCommand = (NpgsqlCommand) value; } - } - - - public new NpgsqlCommand InsertCommand - { - get - { - NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "InsertCommand"); - return _insertCommand; - } - - set { _insertCommand = value; } - } - } + /// <summary> + /// Represents the method that handles the <see cref="Npgsql.NpgsqlDataAdapter.RowUpdated">RowUpdated</see> events. + /// </summary> + /// <param name="sender">The source of the event.</param> + /// <param name="e">A <see cref="Npgsql.NpgsqlRowUpdatedEventArgs">NpgsqlRowUpdatedEventArgs</see> that contains the event data.</param> + public delegate void NpgsqlRowUpdatedEventHandler(Object sender, NpgsqlRowUpdatedEventArgs e); + + /// <summary> + /// Represents the method that handles the <see cref="Npgsql.NpgsqlDataAdapter.RowUpdating">RowUpdating</see> events. + /// </summary> + /// <param name="sender">The source of the event.</param> + /// <param name="e">A <see cref="Npgsql.NpgsqlRowUpdatingEventArgs">NpgsqlRowUpdatingEventArgs</see> that contains the event data.</param> + public delegate void NpgsqlRowUpdatingEventHandler(Object sender, NpgsqlRowUpdatingEventArgs e); + + + /// <summary> + /// This class represents an adapter from many commands: select, update, insert and delete to fill <see cref="System.Data.DataSet">Datasets.</see> + /// </summary> + public sealed class NpgsqlDataAdapter : DbDataAdapter, IDbDataAdapter + { + + private NpgsqlCommand _selectCommand; + private NpgsqlCommand _updateCommand; + private NpgsqlCommand _deleteCommand; + private NpgsqlCommand _insertCommand; + + // Log support + private static readonly String CLASSNAME = "NpgsqlDataAdapter"; + + + public event NpgsqlRowUpdatedEventHandler RowUpdated; + public event NpgsqlRowUpdatingEventHandler RowUpdating; + + public NpgsqlDataAdapter() + {} + + public NpgsqlDataAdapter(NpgsqlCommand selectCommand) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME); + _selectCommand = selectCommand; + } + + public NpgsqlDataAdapter(String selectCommandText, NpgsqlConnection selectConnection) : this(new NpgsqlCommand(selectCommandText, selectConnection)) + {} + + public NpgsqlDataAdapter(String selectCommandText, String selectConnectionString) : this(selectCommandText, new NpgsqlConnection(selectConnectionString)) + {} + + + protected override RowUpdatedEventArgs CreateRowUpdatedEvent( + DataRow dataRow, + IDbCommand command, + StatementType statementType, + DataTableMapping tableMapping + ) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CreateRowUpdatedEvent"); + return new NpgsqlRowUpdatedEventArgs(dataRow, command, statementType, tableMapping); + + + + } + + protected override RowUpdatingEventArgs CreateRowUpdatingEvent( + DataRow dataRow, + IDbCommand command, + StatementType statementType, + DataTableMapping tableMapping + ) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CreateRowUpdatingEvent"); + return new NpgsqlRowUpdatingEventArgs(dataRow, command, statementType, tableMapping); + } + + protected override void OnRowUpdated( + RowUpdatedEventArgs value + ) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "OnRowUpdated"); + //base.OnRowUpdated(value); + if ((RowUpdated != null) && (value is NpgsqlRowUpdatedEventArgs)) + RowUpdated(this, (NpgsqlRowUpdatedEventArgs) value); + + } + + protected override void OnRowUpdating( + RowUpdatingEventArgs value + ) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "OnRowUpdating"); + if ((RowUpdating != null) && (value is NpgsqlRowUpdatingEventArgs)) + RowUpdating(this, (NpgsqlRowUpdatingEventArgs) value); + } + + ITableMappingCollection IDataAdapter.TableMappings + { + get + { + return TableMappings; + } + } + + IDbCommand IDbDataAdapter.DeleteCommand + { + get + { + NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "IDbDataAdapter.DeleteCommand"); + return (NpgsqlCommand) DeleteCommand; + } + + set + { + DeleteCommand = (NpgsqlCommand) value; + } + } + + + public NpgsqlCommand DeleteCommand + { + get + { + return _deleteCommand; + } + + set + { + _deleteCommand = value; + } + } + + IDbCommand IDbDataAdapter.SelectCommand + { + get + { + return (NpgsqlCommand) SelectCommand; + } + + set + { + SelectCommand = (NpgsqlCommand) value; + } + } + + + public NpgsqlCommand SelectCommand + { + get + { + return _selectCommand; + } + + set + { + _selectCommand = value; + } + } + + IDbCommand IDbDataAdapter.UpdateCommand + { + get + { + NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "IDbDataAdapter.UpdateCommand"); + return (NpgsqlCommand) UpdateCommand; + } + + set + { + UpdateCommand = (NpgsqlCommand) value; + } + } + + + public NpgsqlCommand UpdateCommand + { + get + { + return _updateCommand; + } + + set + { + _updateCommand = value; + } + } + + IDbCommand IDbDataAdapter.InsertCommand + { + get + { + return (NpgsqlCommand) InsertCommand; + } + + set + { + InsertCommand = (NpgsqlCommand) value; + } + } + + + public NpgsqlCommand InsertCommand + { + get + { + NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "InsertCommand"); + return _insertCommand; + } + + set + { + _insertCommand = value; + } + } + + + } } + public class NpgsqlRowUpdatingEventArgs : RowUpdatingEventArgs { - public NpgsqlRowUpdatingEventArgs(DataRow dataRow, IDbCommand command, StatementType statementType, - DataTableMapping tableMapping) - : base(dataRow, command, statementType, tableMapping) + public NpgsqlRowUpdatingEventArgs ( + DataRow dataRow, + IDbCommand command, + StatementType statementType, + DataTableMapping tableMapping + ) : base(dataRow, command, statementType, tableMapping) + + {} - { - } } public class NpgsqlRowUpdatedEventArgs : RowUpdatedEventArgs { - public NpgsqlRowUpdatedEventArgs(DataRow dataRow, IDbCommand command, StatementType statementType, - DataTableMapping tableMapping) - : base(dataRow, command, statementType, tableMapping) + public NpgsqlRowUpdatedEventArgs ( + DataRow dataRow, + IDbCommand command, + StatementType statementType, + DataTableMapping tableMapping + ) : base(dataRow, command, statementType, tableMapping) + + {} - { - } -}
\ No newline at end of file +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlDataReader.cs b/mcs/class/Npgsql/Npgsql/NpgsqlDataReader.cs index b15c8b62253..6244c36e493 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlDataReader.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlDataReader.cs @@ -1,3 +1,4 @@ + // Npgsql.NpgsqlDataReader.cs // // Author: @@ -8,686 +9,965 @@ // http://gborg.postgresql.org/project/npgsql/projdisplay.php // // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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.Collections; -using System.Collections.Generic; using System.Data; -using System.Data.Common; +using System.Collections; using System.Text; -using System.Threading; + using NpgsqlTypes; namespace Npgsql { - /// <summary> - /// Provides a means of reading a forward-only stream of rows from a PostgreSQL backend. This class cannot be inherited. - /// </summary> - public abstract class NpgsqlDataReader : DbDataReader - { - //NpgsqlDataReader is abstract because the desired implementation depends upon whether the user - //is using the backwards-compatibility option of preloading the entire reader (configurable from the - //connection string). - //Everything that can be done here is, but where the implementation must be different between the - //two modi operandi, that code will differ between the two implementations, ForwardsOnlyDataReader - //and CachingDataReader. - //Since the concrete classes are internal and returned to the user through an NpgsqlDataReader reference, - //the differences between the two is hidden from the user. Because CachingDataReader is a less efficient - //class supplied only to resolve some backwards-compatibility issues that are possible with some code, all - //internal use uses ForwardsOnlyDataReader directly. - internal NpgsqlConnector _connector; - internal NpgsqlConnection _connection; - internal DataTable _currentResultsetSchema; - internal CommandBehavior _behavior; - internal NpgsqlCommand _command; - - internal NpgsqlDataReader(NpgsqlCommand command, CommandBehavior behavior) - { - _behavior = behavior; - _connection = (_command = command).Connection; - _connector = command.Connector; - } + /// <summary> + /// Provides a means of reading a forward-only stream of rows from a PostgreSQL backend. This class cannot be inherited. + /// </summary> + public sealed class NpgsqlDataReader : IDataReader, IEnumerable + { + private NpgsqlConnection _connection; + private ArrayList _resultsets; + private ArrayList _responses; + private Int32 _rowIndex; + private Int32 _resultsetIndex; + private Int32 _recordsAffected; + 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 = -1; + _recordsAffected = -1; + + // positioned before the first results. + // move to the first results + NextResult(); + + _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."); + } + } - internal bool _isClosed = false; + private void CheckHaveRow() + { + CheckHaveResultSet(); - /// <summary> - /// Is raised whenever Close() is called. - /// </summary> - public event EventHandler ReaderClosed; + 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."); + } + } + + + /// <summary> + /// Releases the resources used by the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see>. + /// </summary> + public void Dispose() + { + Dispose(true); + } + + /// <summary> + /// Releases the resources used by the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see>. + /// </summary> + protected void Dispose (bool disposing) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Dispose"); + if (disposing) + { + this.Close(); + } + } + + /// <summary> + /// Gets a value indicating the depth of nesting for the current row. Always returns zero. + /// </summary> + public Int32 Depth + { + get + { + NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "Depth"); + return 0; + } + } + + /// <summary> + /// Gets a value indicating whether the data reader is closed. + /// </summary> + public Boolean IsClosed + { + get + { + NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "IsClosed"); + return _isClosed; + } + } + + /// <summary> + /// Gets the number of rows changed, inserted, or deleted by execution of the SQL statement. + /// </summary> + public Int32 RecordsAffected + { + get + { + NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "RecordsAffected"); + return _recordsAffected; + } + } - internal abstract long? LastInsertedOID { get; } + /// <summary> + /// Indicates if NpgsqlDatareader has rows to be read. + /// </summary> - private bool TryGetTypeInfo(int fieldIndex, out NpgsqlBackendTypeInfo backendTypeInfo) - { - if (CurrentDescription == null) - { - throw new IndexOutOfRangeException(); //Essentially, all indices are out of range. - } - return (backendTypeInfo = CurrentDescription[fieldIndex].TypeInfo) != null; - } + public Boolean HasRows + { + get + { + return (HaveResultSet() ? _currentResultset.Count > 0 : false); + } - internal abstract void CheckHaveRow(); - internal abstract NpgsqlRowDescription CurrentDescription { get; } + } - /// <summary> - /// Return the data type name of the column at index <param name="Index"></param>. - /// </summary> - public override String GetDataTypeName(Int32 Index) - { - NpgsqlBackendTypeInfo TI; - return TryGetTypeInfo(Index, out TI) ? TI.Name : GetDataTypeOID(Index); - } + /// <summary> + /// Closes the data reader object. + /// </summary> + public void Close() + { + if ((_behavior & CommandBehavior.CloseConnection) == CommandBehavior.CloseConnection) + { + _connection.Close(); + } - /// <summary> - /// Return the data type of the column at index <param name="Index"></param>. - /// </summary> - public override Type GetFieldType(Int32 Index) - { - NpgsqlBackendTypeInfo TI; - return TryGetTypeInfo(Index, out TI) ? TI.Type : typeof (string); //Default type is string. - } + _isClosed = true; + if (this.ReaderClosed != null) + this.ReaderClosed(this, EventArgs.Empty); + } + + /// <summary> + /// Is raised whenever Close() is called. + /// </summary> + public event EventHandler ReaderClosed; + + /// <summary> + /// Advances the data reader to the next result, when multiple result sets were returned by the PostgreSQL backend. + /// </summary> + /// <returns>True if the reader was advanced, otherwise false.</returns> + public Boolean NextResult() + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "NextResult"); + + _currentResultset = null; + while((_resultsetIndex + 1) < _resultsets.Count && !HaveResultSet()) + { + _resultsetIndex++; + _rowIndex = -1; + _currentResultset = (NpgsqlResultSet)_resultsets[_resultsetIndex]; + + if (!HaveResultSet()) + { + String[] _returnStringTokens = ((String)_responses[_resultsetIndex]).Split(null); // whitespace separator. + int responseAffectedRows = 0; + + try + { + responseAffectedRows = Int32.Parse(_returnStringTokens[_returnStringTokens.Length - 1]); + } + catch (FormatException) + { + responseAffectedRows = -1; + } + + if (responseAffectedRows != -1) + { + if (_recordsAffected == -1) + { + _recordsAffected = responseAffectedRows; + } + else + { + _recordsAffected += responseAffectedRows; + } + } + } + } + return HaveResultSet(); + + } + + /// <summary> + /// Advances the data reader to the next row. + /// </summary> + /// <returns>True if the reader was advanced, otherwise false.</returns> + 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; + } - /// <summary> - /// Gets the number of columns in the current row. - /// </summary> - public override Int32 FieldCount - { - get { return CurrentDescription == null ? -1 : CurrentDescription.NumFields; } - } - /// <summary> - /// Return the column name of the column at index <param name="Index"></param>. - /// </summary> - public override String GetName(Int32 Index) - { - if (CurrentDescription == null) - { - throw new IndexOutOfRangeException(); //Essentially, all indices are out of range. - } + } - return CurrentDescription[Index].Name; - } + /// <summary> + /// Returns a System.Data.DataTable that describes the column metadata of the DataReader. + /// </summary> + public DataTable GetSchemaTable() + { + + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetSchemaTable"); - /// <summary> - /// Return the data type OID of the column at index <param name="Index"></param>. - /// </summary> - /// FIXME: Why this method returns String? - public String GetDataTypeOID(Int32 Index) - { - if (CurrentDescription == null) - { - throw new IndexOutOfRangeException(); //Essentially, all indices are out of range. - } + if(_currentResultsetSchema == null) + _currentResultsetSchema = GetResultsetSchema(); - return CurrentDescription[Index].TypeOID.ToString(); - } + return _currentResultsetSchema; + } + /// <summary> + /// Gets the number of columns in the current row. + /// </summary> + public Int32 FieldCount + { + get + { - /// <summary> - /// Gets the value of a column in its native format. - /// </summary> - public override Object this[Int32 i] - { - get { return GetValue(i); } - } + NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "FieldCount"); - /// <summary> - /// Return the column name of the column named <param name="Name"></param>. - /// </summary> - public override Int32 GetOrdinal(String Name) - { - if (CurrentDescription == null) - { - throw new IndexOutOfRangeException(); //Essentially, all indices are out of range. - } - return CurrentDescription.FieldIndex(Name); - } + if (! HaveResultSet()) //Executed a non return rows query. + return -1; + else + return _currentResultset.RowDescription.NumFields; - /// <summary> - /// Gets the value of a column in its native format. - /// </summary> - public override Object this[String name] - { - get - { - Int32 fieldIndex = CurrentDescription.FieldIndex(name); - if (fieldIndex == -1) - { - throw new IndexOutOfRangeException("Field not found"); - } - return GetValue(fieldIndex); - } - } + } - /// <summary> - /// Return the data DbType of the column at index <param name="Index"></param>. - /// </summary> - public DbType GetFieldDbType(Int32 Index) - { - NpgsqlBackendTypeInfo TI; - return TryGetTypeInfo(Index, out TI) ? TI.DbType : DbType.String; - } + } - /// <summary> - /// Return the data NpgsqlDbType of the column at index <param name="Index"></param>. - /// </summary> - public NpgsqlDbType GetFieldNpgsqlDbType(Int32 Index) - { - NpgsqlBackendTypeInfo TI; - return TryGetTypeInfo(Index, out TI) ? TI.NpgsqlDbType : NpgsqlDbType.Text; - } + /// <summary> + /// Return the column name of the column at index <param name="Index"></param>. + /// </summary> + public String GetName(Int32 Index) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetName"); - /// <summary> - /// Get the value of a column as a <see cref="NpgsqlInterval"/>. - /// <remarks>If the differences between <see cref="NpgsqlInterval"/> and <see cref="System.Timespan"/> - /// in handling of days and months is not important to your application, use <see cref="GetTimeSpan()"/> - /// instead.</remarks> - /// </summary> - /// <param name="i">Index of the field to find.</param> - /// <returns><see cref="NpgsqlInterval"/> value of the field.</returns> - public NpgsqlInterval GetInterval(Int32 i) - { - return (NpgsqlInterval) GetValue(i); - } + CheckHaveResultSet(); - public NpgsqlTime GetTime(int i) - { - return (NpgsqlTime) GetValue(i); - } + return _currentResultset.RowDescription[Index].name; + } - public NpgsqlTimeTZ GetTimeTZ(int i) - { - return (NpgsqlTimeTZ) GetValue(i); - } + /// <summary> + /// Return the data type OID of the column at index <param name="Index"></param>. + /// </summary> + /// FIXME: Why this method returns String? + public String GetDataTypeOID(Int32 Index) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDataTypeName"); - public NpgsqlTimeStamp GetTimeStamp(int i) - { - return (NpgsqlTimeStamp) GetValue(i); - } + CheckHaveResultSet(); - public NpgsqlTimeStampTZ GetTimeStampTZ(int i) - { - return (NpgsqlTimeStampTZ) GetValue(i); - } + NpgsqlBackendTypeInfo TI = GetTypeInfo(Index); - public NpgsqlDate GetDate(int i) - { - return (NpgsqlDate) GetValue(i); - } + return _currentResultset.RowDescription[Index].type_oid.ToString(); + } - protected void SendClosedEvent() - { - if (this.ReaderClosed != null) - { - this.ReaderClosed(this, EventArgs.Empty); - } - } + /// <summary> + /// Return the data type name of the column at index <param name="Index"></param>. + /// </summary> + public String GetDataTypeName(Int32 Index) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDataTypeName"); - /// <summary> - /// Gets the value of a column converted to a Guid. - /// </summary> - public override Guid GetGuid(Int32 i) - { - return (Guid) GetValue(i); - } + CheckHaveResultSet(); - /// <summary> - /// Gets the value of a column as Int16. - /// </summary> - public override Int16 GetInt16(Int32 i) - { - return (Int16) GetValue(i); - } + NpgsqlBackendTypeInfo TI = GetTypeInfo(Index); - /// <summary> - /// Gets the value of a column as Int32. - /// </summary> - public override Int32 GetInt32(Int32 i) - { - return (Int32) GetValue(i); - } + if (TI == null) + { + return _currentResultset.RowDescription[Index].type_oid.ToString(); + } + else + { + return TI.Name; + } + } - /// <summary> - /// Gets the value of a column as Int64. - /// </summary> - public override Int64 GetInt64(Int32 i) - { - return (Int64) GetValue(i); - } + /// <summary> + /// Return the data type of the column at index <param name="Index"></param>. + /// </summary> + public Type GetFieldType(Int32 Index) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFieldType"); - /// <summary> - /// Gets the value of a column as Single. - /// </summary> - public override Single GetFloat(Int32 i) - { - return (Single) GetValue(i); - } + CheckHaveResultSet(); - /// <summary> - /// Gets the value of a column as Double. - /// </summary> - public override Double GetDouble(Int32 i) - { - return (Double) GetValue(i); - } + NpgsqlBackendTypeInfo TI = GetTypeInfo(Index); - /// <summary> - /// Gets the value of a column as String. - /// </summary> - public override String GetString(Int32 i) - { - return (String) GetValue(i); - } + if (TI == null) + { + return typeof(String); //Default type is string. + } + else + { + return TI.Type; + } + } - /// <summary> - /// Gets the value of a column as Decimal. - /// </summary> - public override Decimal GetDecimal(Int32 i) - { - return (Decimal) GetValue(i); - } + /// <summary> + /// Return the data DbType of the column at index <param name="Index"></param>. + /// </summary> + public DbType GetFieldDbType(Int32 Index) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFieldType"); - /// <summary> - /// Gets a value indicating the depth of nesting for the current row. Always returns zero. - /// </summary> - public override Int32 Depth - { - get { return 0; } - } + CheckHaveResultSet(); - /// <summary> - /// Gets a value indicating whether the data reader is closed. - /// </summary> - public override Boolean IsClosed - { - get { return _isClosed; } - } + NpgsqlBackendTypeInfo TI = GetTypeInfo(Index); - /// <summary> - /// Copy values from each column in the current row into <param name="Values"></param>. - /// </summary> - /// <returns>The number of column values copied.</returns> - public override Int32 GetValues(Object[] Values) - { - CheckHaveRow(); + if (TI == null) + { + return DbType.String; + } + else + { + //return TI.DBType; + return DbType.String; + } + } - // 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; + /// <summary> + /// Return the data NpgsqlDbType of the column at index <param name="Index"></param>. + /// </summary> + public NpgsqlDbType GetFieldNpgsqlDbType(Int32 Index) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFieldType"); - for (Int32 i = 0; i < maxColumnIndex; i++) - { - Values[i] = GetValue(i); - } + CheckHaveResultSet(); - return maxColumnIndex; - } + NpgsqlBackendTypeInfo TI = GetTypeInfo(Index); - /// <summary> - /// Gets the value of a column as Boolean. - /// </summary> - public override Boolean GetBoolean(Int32 i) - { - // Should this be done using the GetValue directly and not by converting to String - // and parsing from there? - return (Boolean) GetValue(i); - } + if (TI == null) + { + return NpgsqlDbType.Text; + } + else + { + return TI.NpgsqlDbType; - /// <summary> - /// Gets the value of a column as Byte. Not implemented. - /// </summary> - public override Byte GetByte(Int32 i) - { - throw new NotImplementedException(); - } + } + } - /// <summary> - /// Gets the value of a column as Char. - /// </summary> - public override Char GetChar(Int32 i) - { - //This is an interesting one. In the world of databases we've the idea of chars which is 0 to n characters - //where n is stated (and can perhaps be infinite) and various variations upon that (postgres is admirable - //in being relatively consistent and in not generally encouraging limiting n purely for performance reasons, - //but across many different platforms we'll find such things as text, ntext, char, nchar, varchar, nvarchar, - //and so on with some platforms not having them all and many implementaiton differences). - // - //In the world of .NET, and many other languages, we have the idea of characters and of strings - which are - //sequences of characters with differing degress of encapsulation from C just having char* through to .NET - //having full-blown objects - // - //Database char, varchar, text, etc. are all generally mapped to strings. There's a bit of a question as to - //what maps to a .NET char. Interestingly enough, SQLDataReader doesn't support GetChar() and neither do - //a few other providers (Oracle for example). It would seem that IDataReader.GetChar() was defined largely - //to have a complete set of .NET base types. Still, the closets thing in the database world to a char value - //is a char(1) or varchar(1) - that is to say the value of a string of length one, so that's what is used here. - string s = GetString(i); - if (s.Length != 1) - { - throw new InvalidCastException(); - } - return s[0]; - } - /// <summary> - /// Gets the value of a column as DateTime. - /// </summary> - public override DateTime GetDateTime(Int32 i) - { - return (DateTime) GetValue(i); - } + /// <summary> + /// Return the value of the column at index <param name="Index"></param>. + /// </summary> + public Object GetValue(Int32 Index) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetValue"); - /// <summary> - /// Returns a System.Data.DataTable that describes the column metadata of the DataReader. - /// </summary> - public override DataTable GetSchemaTable() - { - return _currentResultsetSchema = _currentResultsetSchema ?? GetResultsetSchema(); - } + if (Index < 0 || Index >= _currentResultset.RowDescription.NumFields) + { + throw new IndexOutOfRangeException("Column index out of range"); + } - private DataTable GetResultsetSchema() - { - DataTable result = null; + CheckHaveRow(); - if (CurrentDescription.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 (_connector.BackendProtocolVersion == ProtocolVersion.Version2) - { - FillSchemaTable_v2(result); - } - else if (_connector.BackendProtocolVersion == ProtocolVersion.Version3) - { - FillSchemaTable_v3(result); - } - } + return ((NpgsqlAsciiRow)_currentResultset[_rowIndex])[Index]; + } - return result; - } + /// <summary> + /// Copy values from each column in the current row into <param name="Values"></param>. + /// </summary> + /// <returns>The number of column values copied.</returns> + public Int32 GetValues(Object[] Values) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetValues"); - private void FillSchemaTable_v2(DataTable schema) - { - List<string> keyList = (_behavior & CommandBehavior.KeyInfo) == CommandBehavior.KeyInfo - ? new List<string>(GetPrimaryKeys(GetTableNameFromQuery())) - : new List<string>(); + CheckHaveRow(); - for (Int16 i = 0; i < CurrentDescription.NumFields; i++) - { - DataRow row = schema.NewRow(); + // 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; - row["ColumnName"] = GetName(i); - row["ColumnOrdinal"] = i + 1; - if (CurrentDescription[i].TypeModifier != -1 && CurrentDescription[i].TypeInfo != null && - (CurrentDescription[i].TypeInfo.Name == "varchar" || CurrentDescription[i].TypeInfo.Name == "bpchar")) - { - row["ColumnSize"] = CurrentDescription[i].TypeModifier - 4; - } - else if (CurrentDescription[i].TypeModifier != -1 && CurrentDescription[i].TypeInfo != null && - (CurrentDescription[i].TypeInfo.Name == "bit" || CurrentDescription[i].TypeInfo.Name == "varbit")) - { - row["ColumnSize"] = CurrentDescription[i].TypeModifier; - } - else - { - row["ColumnSize"] = (int) CurrentDescription[i].TypeSize; - } - if (CurrentDescription[i].TypeModifier != -1 && CurrentDescription[i].TypeInfo != null && - CurrentDescription[i].TypeInfo.Name == "numeric") - { - row["NumericPrecision"] = ((CurrentDescription[i].TypeModifier - 4) >> 16) & ushort.MaxValue; - row["NumericScale"] = (CurrentDescription[i].TypeModifier - 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"] = true; - // without other information, must allow dbnull on the client - if (CurrentDescription[i].TypeInfo != null) - { - row["ProviderType"] = CurrentDescription[i].TypeInfo.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); + for (Int32 i = 0; i < maxColumnIndex; i++) + { + Values[i] = GetValue(i); + } + + return maxColumnIndex; + + } + + /// <summary> + /// Return the column name of the column named <param name="Name"></param>. + /// </summary> + public Int32 GetOrdinal(String Name) + { + CheckHaveResultSet(); + return _currentResultset.RowDescription.FieldIndex(Name); + } + + /// <summary> + /// Gets the value of a column in its native format. + /// </summary> + public Object this [ Int32 i ] + { + get + { + NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, i); + return GetValue(i); + } + } + + /// <summary> + /// Gets the value of a column in its native format. + /// </summary> + 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); + } + } + + /// <summary> + /// Gets the value of a column as Boolean. + /// </summary> + 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); + } + + /// <summary> + /// Gets the value of a column as Byte. Not implemented. + /// </summary> + public Byte GetByte(Int32 i) + { + throw new NotImplementedException(); + } + + /// <summary> + /// Gets raw data from a column. + /// </summary> + 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; + + } + + /// <summary> + /// Gets the value of a column as Char. Not implemented. + /// </summary> + public Char GetChar(Int32 i) + { + throw new NotImplementedException(); + } + + /// <summary> + /// Gets raw data from a column. + /// </summary> + 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); + } + + /// <summary> + /// Gets the value of a column converted to a Guid. Not implemented. + /// </summary> + public Guid GetGuid(Int32 i) + { + throw new NotImplementedException(); + } + + /// <summary> + /// Gets the value of a column as Int16. + /// </summary> + public Int16 GetInt16(Int32 i) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetInt16"); + + return (Int16) GetValue(i); + } + + /// <summary> + /// Gets the value of a column as Int32. + /// </summary> + public Int32 GetInt32(Int32 i) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetInt32"); + + return (Int32) GetValue(i); + } + + /// <summary> + /// Gets the value of a column as Int64. + /// </summary> + public Int64 GetInt64(Int32 i) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetInt64"); + + return (Int64) GetValue(i); + } + + /// <summary> + /// Gets the value of a column as Single. + /// </summary> + public Single GetFloat(Int32 i) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFloat"); + + return (Single) GetValue(i); + } + + /// <summary> + /// Gets the value of a column as Double. + /// </summary> + public Double GetDouble(Int32 i) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDouble"); + + return (Double) GetValue(i); + } + + /// <summary> + /// Gets the value of a column as String. + /// </summary> + public String GetString(Int32 i) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetString"); + + return (String) GetValue(i); + } + + /// <summary> + /// Gets the value of a column as Decimal. + /// </summary> + public Decimal GetDecimal(Int32 i) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDecimal"); + + return (Decimal) GetValue(i); + } + + /// <summary> + /// Gets the value of a column as DateTime. + /// </summary> + public DateTime GetDateTime(Int32 i) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDateTime"); + + return (DateTime) GetValue(i); + } + + /// <summary> + /// Not implemented. + /// </summary> + public IDataReader GetData(Int32 i) + { + throw new NotImplementedException(); + } + + /// <summary> + /// Report whether the value in a column is DBNull. + /// </summary> + 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()); } - } - private void FillSchemaTable_v3(DataTable schema) - { - Dictionary<long, Table> oidTableLookup = new Dictionary<long, Table>(); + 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(); - // needs to be null because there is a difference - // between an empty dictionary and not setting it - // the default values will be different - Dictionary<string, Column> columnLookup = null; + Hashtable columnLookup = null; if ((_behavior & CommandBehavior.KeyInfo) == CommandBehavior.KeyInfo) { - List<int> tableOids = new List<int>(); - for (int i = 0; i != CurrentDescription.NumFields; ++i) + ArrayList tableOids = new ArrayList(); + for(short i=0; i<rd.NumFields; ++i) { - if (CurrentDescription[i].TableOID != 0 && !tableOids.Contains(CurrentDescription[i].TableOID)) - { - tableOids.Add(CurrentDescription[i].TableOID); - } + if (rd[i].table_oid != 0 && !tableOids.Contains(rd[i].table_oid)) + tableOids.Add(rd[i].table_oid); } oidTableLookup = GetTablesFromOids(tableOids); - if (oidTableLookup.Count == 1) + if (oidTableLookup != null && oidTableLookup.Count == 1) { - // only 1, but we can't index into the Dictionary - foreach (int key in oidTableLookup.Keys) + // only 1, but we can't index into the Hashtable + foreach(DictionaryEntry entry in oidTableLookup) { - keyLookup = GetKeys(key); + keyLookup = GetKeys((Int32)entry.Key); } } columnLookup = GetColumns(); } - for (Int16 i = 0; i < CurrentDescription.NumFields; i++) - { - DataRow row = schema.NewRow(); + DataRow row; + for (Int16 i = 0; i < rd.NumFields; i++) + { + row = schema.NewRow(); string baseColumnName = GetBaseColumnName(columnLookup, i); - row["ColumnName"] = GetName(i); - row["ColumnOrdinal"] = i + 1; - if (CurrentDescription[i].TypeModifier != -1 && CurrentDescription[i].TypeInfo != null && - (CurrentDescription[i].TypeInfo.Name == "varchar" || CurrentDescription[i].TypeInfo.Name == "bpchar")) - { - row["ColumnSize"] = CurrentDescription[i].TypeModifier - 4; - } - else if (CurrentDescription[i].TypeModifier != -1 && CurrentDescription[i].TypeInfo != null && - (CurrentDescription[i].TypeInfo.Name == "bit" || CurrentDescription[i].TypeInfo.Name == "varbit")) - { - row["ColumnSize"] = CurrentDescription[i].TypeModifier; - } - else - { - row["ColumnSize"] = (int) CurrentDescription[i].TypeSize; - } - if (CurrentDescription[i].TypeModifier != -1 && CurrentDescription[i].TypeInfo != null && - CurrentDescription[i].TypeInfo.Name == "numeric") - { - row["NumericPrecision"] = ((CurrentDescription[i].TypeModifier - 4) >> 16) & ushort.MaxValue; - row["NumericScale"] = (CurrentDescription[i].TypeModifier - 4) & ushort.MaxValue; - } - else - { - row["NumericPrecision"] = 0; - row["NumericScale"] = 0; - } - row["IsUnique"] = IsUnique(keyLookup, baseColumnName); - row["IsKey"] = IsKey(keyLookup, baseColumnName); - if (CurrentDescription[i].TableOID != 0 && oidTableLookup.ContainsKey(CurrentDescription[i].TableOID)) - { - row["BaseCatalogName"] = oidTableLookup[CurrentDescription[i].TableOID].Catalog; - row["BaseSchemaName"] = oidTableLookup[CurrentDescription[i].TableOID].Schema; - row["BaseTableName"] = oidTableLookup[CurrentDescription[i].TableOID].Name; - } - else - { - row["BaseCatalogName"] = row["BaseSchemaName"] = row["BaseTableName"] = ""; - } - row["BaseColumnName"] = baseColumnName; - row["DataType"] = GetFieldType(i); - row["AllowDBNull"] = IsNullable(columnLookup, i); - if (CurrentDescription[i].TypeInfo != null) - { - row["ProviderType"] = CurrentDescription[i].TypeInfo.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); - } - } + 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"] = 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 static Boolean IsKey(String ColumnName, IEnumerable<string> ListOfKeys) - { - foreach (String s in ListOfKeys) - { - if (s == ColumnName) - { - return true; - } - } + private Boolean IsKey(String ColumnName, ArrayList ListOfKeys) + { + if (ListOfKeys == null || ListOfKeys.Count == 0) + return false; - return false; - } + foreach(String s in ListOfKeys) + { - private IEnumerable<string> GetPrimaryKeys(String tablename) - { - if (string.IsNullOrEmpty(tablename)) - { - yield break; - } + if (s == ColumnName) + return true; + } - 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"; + return false; + } - using (NpgsqlConnection metadataConn = _connection.Clone()) - { - using (NpgsqlCommand c = new NpgsqlCommand(getPKColumns, metadataConn)) - { - c.Parameters.Add(new NpgsqlParameter("tablename", NpgsqlDbType.Text)); - c.Parameters["tablename"].Value = tablename; + private ArrayList GetPrimaryKeys(String tablename) + { + if (tablename == String.Empty) + return null; - using (NpgsqlDataReader dr = c.GetReader(CommandBehavior.SingleResult | CommandBehavior.SequentialAccess)) - { - while (dr.Read()) - { - yield return dr.GetString(0); - } - } - } - } - } + 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; - private static bool IsKey(KeyLookup keyLookup, string fieldName) + NpgsqlDataReader dr = c.ExecuteReader(); + + + while (dr.Read()) + result.Add(dr[0]); + + + metadataConn.Close(); + + return result; + } + + private bool IsKey(KeyLookup keyLookup, string fieldName) { - return keyLookup.primaryKey.Contains(fieldName); + if (keyLookup.primaryKey == null || keyLookup.primaryKey.Count == 0) + return false; + + for (int i=0; i<keyLookup.primaryKey.Count; ++i) + { + if (fieldName == (String)keyLookup.primaryKey[i]) + return true; + } + + return false; } - private static bool IsUnique(KeyLookup keyLookup, string fieldName) + private bool IsUnique(KeyLookup keyLookup, string fieldName) { - return keyLookup.uniqueColumns.Contains(fieldName); + if (keyLookup.uniqueColumns == null || keyLookup.uniqueColumns.Count == 0) + return false; + + for (int i=0; i<keyLookup.uniqueColumns.Count; ++i) + { + if (fieldName == (String)keyLookup.uniqueColumns[i]) + return true; + } + + return false; } - private class KeyLookup + private struct KeyLookup { /// <summary> /// Contains the column names as the keys /// </summary> - public readonly List<string> primaryKey = new List<string>(); - + public ArrayList primaryKey; /// <summary> /// Contains all unique columns /// </summary> - public readonly List<string> uniqueColumns = new List<string>(); + 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"; + + 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.GetReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) + using (NpgsqlDataReader dr = c.ExecuteReader()) { string previousKeyName = null; string possiblyUniqueColumn = null; @@ -699,6 +979,7 @@ namespace Npgsql // 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 @@ -724,895 +1005,176 @@ namespace Npgsql // 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(Dictionary<string, Column> columnLookup, Int32 FieldIndex) - { - if (columnLookup == null || CurrentDescription[FieldIndex].TableOID == 0) - { - return true; - } - string lookupKey = string.Format("{0},{1}", CurrentDescription[FieldIndex].TableOID, CurrentDescription[FieldIndex].ColumnAttributeNumber); - Column col = null; - return columnLookup.TryGetValue(lookupKey, out col) ? !col.NotNull : true; + return lookup; } - private string GetBaseColumnName(Dictionary<string, Column> columnLookup, Int32 FieldIndex) - { - if (columnLookup == null || CurrentDescription[FieldIndex].TableOID == 0) - { - return GetName(FieldIndex); - } + private Boolean IsNullable(Hashtable columnLookup, Int32 FieldIndex) + { + if (columnLookup == null || _currentResultset.RowDescription[FieldIndex].table_oid == 0) + return true; - string lookupKey = string.Format("{0},{1}", CurrentDescription[FieldIndex].TableOID, CurrentDescription[FieldIndex].ColumnAttributeNumber); - Column col = null; - return columnLookup.TryGetValue(lookupKey, out col) ? col.Name : 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 !(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(Dictionary<string, Column> columnLookup, Int32 FieldIndex) + private bool IsAutoIncrement(Hashtable columnLookup, Int32 FieldIndex) { - if (columnLookup == null || CurrentDescription[FieldIndex].TableOID == 0) - { + if (columnLookup == null || _currentResultset.RowDescription[FieldIndex].table_oid == 0) return false; - } - string lookupKey = string.Format("{0},{1}", CurrentDescription[FieldIndex].TableOID, CurrentDescription[FieldIndex].ColumnAttributeNumber); - Column col = null; - return - columnLookup.TryGetValue(lookupKey, out col) - ? col.ColumnDefault is string && col.ColumnDefault.ToString().StartsWith("nextval(") - : true; + 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; } - ///<summary> - /// This methods parses the command text and tries to get the tablename - /// from it. - ///</summary> - private String GetTableNameFromQuery() - { - Int32 fromClauseIndex = _command.CommandText.ToLowerInvariant().IndexOf("from"); + ///<summary> + /// This methods parses the command text and tries to get the tablename + /// from it. + ///</summary> + private String GetTableNameFromQuery() + { + Int32 fromClauseIndex = _command.CommandText.ToLower().IndexOf("from"); - String tableName = _command.CommandText.Substring(fromClauseIndex + 4).Trim(); + String tableName = _command.CommandText.Substring(fromClauseIndex + 4).Trim(); - if (string.IsNullOrEmpty(tableName))// == String.Empty) - { - return String.Empty; - } + if (tableName == String.Empty) + return String.Empty; - /*if (tableName.EndsWith(".")); + /*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 Table - { - public readonly string Catalog; - public readonly string Schema; - public readonly string Name; - public readonly long Id; - - public Table(IDataReader rdr) - { - Catalog = rdr.GetString(0); - Schema = rdr.GetString(1); - Name = rdr.GetString(2); - Id = rdr.GetInt64(3); - } - } - - private Dictionary<long, Table> GetTablesFromOids(List<int> oids) - { - if (oids.Count == 0) - { - return new Dictionary<long, Table>(); //Empty collection is simpler than requiring tests for null; - } - - // the column index is used to find data. - // any changes to the order of the columns needs to be reflected in struct Tables - StringBuilder sb = - new StringBuilder( - "SELECT current_database(), nc.nspname, c.relname, c.oid 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.GetReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult) - ) - { - Dictionary<long, Table> oidLookup = new Dictionary<long, Table>(oids.Count); - int columnCount = reader.FieldCount; - while (reader.Read()) - { - Table t = new Table(reader); - oidLookup.Add(t.Id, t); - } - return oidLookup; - } - } - } - } - - private class Column - { - public readonly string Name; - public readonly bool NotNull; - public readonly long TableId; - public readonly short ColumnNum; - public readonly object ColumnDefault; - - public string Key - { - get { return string.Format("{0},{1}", TableId, ColumnNum); } - } - - public Column(IDataReader rdr) - { - Name = rdr.GetString(0); - NotNull = rdr.GetBoolean(1); - TableId = rdr.GetInt64(2); - ColumnNum = rdr.GetInt16(3); - ColumnDefault = rdr.GetValue(4); - } - } - - private Dictionary<string, Column> 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 < CurrentDescription.NumFields; ++i) - { - if (CurrentDescription[i].TableOID != 0) - { - if (!first) - { - sb.Append(" OR "); - } - sb.AppendFormat("(a.attrelid={0} AND a.attnum={1})", CurrentDescription[i].TableOID, - CurrentDescription[i].ColumnAttributeNumber); - 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.GetReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult) - ) - { - Dictionary<string, Column> columnLookup = new Dictionary<string, Column>(); - while (reader.Read()) - { - Column column = new Column(reader); - columnLookup.Add(column.Key, column); - } - return columnLookup; - } - } - } - } - - public override IEnumerator GetEnumerator() - { - return new DbEnumerator(this); - } - } - - /// <summary> - /// This is the primary implementation of NpgsqlDataReader. It is the one used in normal cases (where the - /// preload-reader option is not set in the connection string to resolve some potential backwards-compatibility - /// issues), the only implementation used internally, and in cases where CachingDataReader is used, it is still - /// used to do the actual "leg-work" of turning a response stream from the server into a datareader-style - /// object - with CachingDataReader then filling it's cache from here. - /// </summary> - internal class ForwardsOnlyDataReader : NpgsqlDataReader, IStreamOwner - { - private readonly IEnumerator<IServerResponseObject> _dataEnumerator; - private NpgsqlRowDescription _currentDescription; - private NpgsqlRow _currentRow = null; - private int? _recordsAffected = null; - private int? _nextRecordsAffected; - private long? _lastInsertOID = null; - private long? _nextInsertOID = null; - internal bool _cleanedUp = false; - private readonly NpgsqlConnector.NotificationThreadBlock _threadBlock; - private readonly bool _synchOnReadError; //maybe this should always be done? - - //Unfortunately we sometimes don't know we're going to be dealing with - //a description until it comes when we look for a row or a message, and - //we may also need test if we may have rows for HasRows before the first call - //to Read(), so we need to be able to cache one of each. - private NpgsqlRowDescription _pendingDescription = null; - private NpgsqlRow _pendingRow = null; - - // Logging related values - private static readonly String CLASSNAME = "ForwardsOnlyDataReader"; - - internal ForwardsOnlyDataReader(IEnumerable<IServerResponseObject> dataEnumeration, CommandBehavior behavior, - NpgsqlCommand command, NpgsqlConnector.NotificationThreadBlock threadBlock, - bool synchOnReadError) - : base(command, behavior) - { - _dataEnumerator = dataEnumeration.GetEnumerator(); - _connector.CurrentReader = this; - _threadBlock = threadBlock; - _synchOnReadError = synchOnReadError; - //DataReaders always start prepared to read from the first Resultset (if any). - NextResult(); - UpdateOutputParameters(); - } - - internal override NpgsqlRowDescription CurrentDescription - { - get { return _currentDescription; } - } - - private void UpdateOutputParameters() - { - if (CurrentDescription != null) - { - NpgsqlRow row = null; - Queue<NpgsqlParameter> pending = new Queue<NpgsqlParameter>(); - List<int> taken = new List<int>(); - foreach (NpgsqlParameter p in _command.Parameters) - { - if (p.Direction == ParameterDirection.InputOutput || p.Direction == ParameterDirection.Output) - { - int idx = CurrentDescription.FieldIndex(p.CleanName); - if (idx == -1) - { - pending.Enqueue(p); - } - else - { - if ((row = row ?? ParameterUpdateRow) == null) - { - return; - } - p.Value = row[idx]; - taken.Add(idx); - } - } - } - for (int i = 0; pending.Count != 0 && i != (row = row ?? ParameterUpdateRow).NumFields; ++i) - { - if (!taken.Contains(i)) - { - pending.Dequeue().Value = row[i]; - } - } - } - } - - // We always receive a ForwardsOnlyRow, but if we are not - // doing SequentialAccess we want the flexibility of CachingRow, - // so here we either return the ForwardsOnlyRow we received, or - // build a CachingRow from it, as appropriate. - private NpgsqlRow BuildRow(ForwardsOnlyRow fo) - { - if (fo == null) - { - return null; - } - else if ((_behavior & CommandBehavior.SequentialAccess) == CommandBehavior.SequentialAccess) - { - return fo; - } - else - { - return new CachingRow(fo); - } - } - - private NpgsqlRow ParameterUpdateRow - { - get - { - NpgsqlRow ret = CurrentRow ?? _pendingRow ?? GetNextRow(false); - if (ret is ForwardsOnlyRow) - { - ret = _pendingRow = new CachingRow((ForwardsOnlyRow) ret); - } - return ret; - } - } - - /// <summary> - /// Iterate through the objects returned through from the server. - /// If it's a CompletedResponse the rowsaffected count is updated appropriately, - /// and we iterate again, otherwise we return it (perhaps updating our cache of pending - /// rows if appropriate). - /// </summary> - /// <returns>The next <see cref="IServerResponseObject"/> we will deal with.</returns> - private IServerResponseObject GetNextResponseObject() - { - try - { - CurrentRow = null; - if (_pendingRow != null) - { - _pendingRow.Dispose(); - } - _pendingRow = null; - while (_dataEnumerator.MoveNext()) - { - IServerResponseObject respNext = _dataEnumerator.Current; - if (respNext is CompletedResponse) - { - CompletedResponse cr = respNext as CompletedResponse; - if (cr.RowsAffected.HasValue) - { - _nextRecordsAffected = (_nextRecordsAffected ?? 0) + cr.RowsAffected.Value; - } - _nextInsertOID = cr.LastInsertedOID ?? _nextInsertOID; - } - else if (respNext is ForwardsOnlyRow) - { - return _pendingRow = BuildRow((ForwardsOnlyRow) respNext); - } - else - { - return respNext; - } - } - CleanUp(true); - return null; - } - catch - { - CleanUp(true); - if (_synchOnReadError) //Should we always do this? - { - // As per documentation: - // "[...] When an error is detected while processing any extended-query message, - // the backend issues ErrorResponse, then reads and discards messages until a - // Sync is reached, then issues ReadyForQuery and returns to normal message processing.[...]" - // So, send a sync command if we get any problems. - - _connector.Sync(); - } - throw; - } - } - - /// <summary> - /// Advances the data reader to the next result, when multiple result sets were returned by the PostgreSQL backend. - /// </summary> - /// <returns>True if the reader was advanced, otherwise false.</returns> - private NpgsqlRowDescription GetNextRowDescription() - { - if ((_behavior & CommandBehavior.SingleResult) != 0 && CurrentDescription != null) - { - CleanUp(false); - return null; - } - NpgsqlRowDescription rd = _pendingDescription; - while (rd == null) - { - object objNext = GetNextResponseObject(); - if (objNext == null) - { - break; - } - if (objNext is NpgsqlRow) - { - (objNext as NpgsqlRow).Dispose(); - } - - rd = objNext as NpgsqlRowDescription; - } - - _pendingDescription = null; - _recordsAffected = _nextRecordsAffected; - _nextRecordsAffected = null; - _lastInsertOID = _nextInsertOID; - _nextInsertOID = null; - return rd; - } - - private NpgsqlRow CurrentRow - { - get { return _currentRow; } - set - { - if (_currentRow != null) - { - _currentRow.Dispose(); - } - _currentRow = value; - } - } - - private NpgsqlRow GetNextRow(bool clearPending) - { - if (_pendingDescription != null) - { - return null; - } - if (((_behavior & CommandBehavior.SingleRow) != 0 && CurrentRow != null && _pendingDescription == null) || - ((_behavior & CommandBehavior.SchemaOnly) != 0)) - { - if (!clearPending) - { - return null; - } - //We should only have one row, and we've already had it. Move to end - //of recordset. - CurrentRow = null; - for (object skip = GetNextResponseObject(); - skip != null && (_pendingDescription = skip as NpgsqlRowDescription) == null; - skip = GetNextResponseObject()) - { - if (skip is NpgsqlRow) - { - (skip as NpgsqlRow).Dispose(); - } - } - - return null; - } - if (_pendingRow != null) - { - NpgsqlRow ret = _pendingRow; - if (clearPending) - { - _pendingRow = null; - } - return ret; - } - CurrentRow = null; - object objNext = GetNextResponseObject(); - if (clearPending) - { - _pendingRow = null; - } - if (objNext is NpgsqlRowDescription) - { - _pendingDescription = objNext as NpgsqlRowDescription; - return null; - } - return objNext as NpgsqlRow; - } + foreach (Char c in tableName.Substring (0, tableName.Length - 1)) + if (!Char.IsLetterOrDigit (c) && c != '_' && c != '.') + return String.Empty; - internal override void CheckHaveRow() - { - if (CurrentRow == null) - { - throw new InvalidOperationException("Invalid attempt to read when no data is present."); - } - } - /// <summary> - /// Releases the resources used by the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see>. - /// </summary> - protected override void Dispose(bool disposing) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Dispose"); - base.Dispose(disposing); - } + return tableName; + } - /// <summary> - /// Gets the number of rows changed, inserted, or deleted by execution of the SQL statement. - /// </summary> - public override Int32 RecordsAffected - { - get - { - NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "RecordsAffected"); - return _recordsAffected ?? -1; - } - } + 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; + } - internal override long? LastInsertedOID - { - get { return _lastInsertOID; } - } + private Hashtable GetTablesFromOids(ArrayList oids) + { + if (oids.Count == 0) + return null; - /// <summary> - /// Indicates if NpgsqlDatareader has rows to be read. - /// </summary> - public override Boolean HasRows - { - get { return GetNextRow(false) != null; } - } + StringBuilder sb = new StringBuilder(); - private void CleanUp(bool finishedMessages) - { - lock (this) - { - if (_cleanedUp) - { - return; - } - _cleanedUp = true; - } - if (!finishedMessages) - { - do - { - if ((Thread.CurrentThread.ThreadState & (ThreadState.Aborted | ThreadState.AbortRequested)) != 0) - { - _connection.EmergencyClose(); - return; - } - } - while (GetNextResponseObject() != null); - } - _connector.CurrentReader = null; - _threadBlock.Dispose(); - } - - /// <summary> - /// Closes the data reader object. - /// </summary> - public override void Close() - { - CleanUp(false); - if ((_behavior & CommandBehavior.CloseConnection) == CommandBehavior.CloseConnection) - { - _connection.Close(); - } - _isClosed = true; - SendClosedEvent(); - } - - /// <summary> - /// Advances the data reader to the next result, when multiple result sets were returned by the PostgreSQL backend. - /// </summary> - /// <returns>True if the reader was advanced, otherwise false.</returns> - public override Boolean NextResult() - { - try - { - CurrentRow = null; - _currentResultsetSchema = null; - return (_currentDescription = GetNextRowDescription()) != null; - } - catch (System.IO.IOException ex) + // 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) { - throw _command.ClearPoolAndCreateException(ex); + if (!first) + sb.Append(','); + sb.Append(oid); + first = false; } - } + sb.Append(')'); - /// <summary> - /// Advances the data reader to the next row. - /// </summary> - /// <returns>True if the reader was advanced, otherwise false.</returns> - public override Boolean Read() - { - try + using (NpgsqlConnection connection = _connection.Clone()) + using (NpgsqlCommand command = new NpgsqlCommand(sb.ToString(), connection)) + using (NpgsqlDataReader reader = command.ExecuteReader()) { - //CurrentRow = null; - return (CurrentRow = GetNextRow(true)) != null; + 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; } - catch (System.IO.IOException ex) + } + + 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) { - throw _command.ClearPoolAndCreateException(ex); + 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; + } } - } - - /// <summary> - /// Return the value of the column at index <param name="Index"></param>. - /// </summary> - public override Object GetValue(Int32 Index) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetValue"); - - if (Index < 0 || Index >= CurrentDescription.NumFields) - { - throw new IndexOutOfRangeException("Column index out of range"); - } - - CheckHaveRow(); - - object ret = CurrentRow[Index]; - if (ret is Exception) - { - throw (Exception) ret; - } - return ret; - } - - /// <summary> - /// Gets raw data from a column. - /// </summary> - public override Int64 GetBytes(Int32 i, Int64 fieldOffset, Byte[] buffer, Int32 bufferoffset, Int32 length) - { - return CurrentRow.GetBytes(i, fieldOffset, buffer, bufferoffset, length); - } - - - /// <summary> - /// Gets raw data from a column. - /// </summary> - public override Int64 GetChars(Int32 i, Int64 fieldoffset, Char[] buffer, Int32 bufferoffset, Int32 length) - { - return CurrentRow.GetChars(i, fieldoffset, buffer, bufferoffset, length); - } - - - /// <summary> - /// Report whether the value in a column is DBNull. - /// </summary> - public override Boolean IsDBNull(Int32 i) - { - CheckHaveRow(); - - return CurrentRow.IsDBNull(i); - } - } - - /// <summary> - /// <para>Provides an implementation of NpgsqlDataReader in which all data is pre-loaded into memory. - /// This operates by first creating a ForwardsOnlyDataReader as usual, and then loading all of it's - /// Rows into memory. There is a general principle that when there is a trade-off between a class design that - /// is more efficient and/or scalable on the one hand and one that is less efficient but has more functionality - /// (in this case the internal-only functionality of caching results) that one can build the less efficent class - /// from the most efficient without significant extra loss in efficiency, but not the other way around. The relationship - /// between ForwardsOnlyDataReader and CachingDataReader is an example of this).</para> - /// <para>Since the interface presented to the user is still forwards-only, queues are used to - /// store this information, so that dequeueing as we go we give the garbage collector the best opportunity - /// possible to reclaim any memory that is no longer in use.</para> - /// <para>ForwardsOnlyDataReader being used to actually - /// obtain the information from the server means that the "leg-work" is still only done (and need only be - /// maintained) in one place.</para> - /// <para>This class exists to allow for certain potential backwards-compatibility issues to be resolved - /// with little effort on the part of affected users. It is considerably less efficient than ForwardsOnlyDataReader - /// and hence never used internally.</para> - /// </summary> - internal class CachingDataReader : NpgsqlDataReader - { - private class DataRow : List<object> - { - } - - private class ResultSet : Queue<DataRow> - { - private readonly int _recordsAffected; - private readonly long? _lastInsertedOID; - private readonly NpgsqlRowDescription _description; - - public ResultSet(NpgsqlRowDescription description, int recordsAffected, long? lastInsertedOID) - { - _description = description; - _recordsAffected = recordsAffected; - _lastInsertedOID = lastInsertedOID; - } - - public NpgsqlRowDescription Description - { - get { return _description; } - } - - public int RecordsAffected - { - get { return _recordsAffected; } - } - - public long? LastInsertedOID - { - get { return _lastInsertedOID; } - } - } - - private readonly Queue<ResultSet> _results = new Queue<ResultSet>(); - private ResultSet _currentResult; - private DataRow _currentRow; - private int _lastRecordsAffected; - - public CachingDataReader(ForwardsOnlyDataReader reader, CommandBehavior behavior) - : base(reader._command, behavior) - { - do - { - ResultSet rs = new ResultSet(reader.CurrentDescription, reader.RecordsAffected, reader.LastInsertedOID); - while (reader.Read()) - { - DataRow dr = new DataRow(); - for (int i = 0; i != reader.FieldCount; ++i) - { - dr.Add(reader.GetValue(i)); - } - rs.Enqueue(dr); - } - _results.Enqueue(rs); - } - while (reader.NextResult()); - reader.Dispose(); - NextResult(); - UpdateOutputParameters(); - } - - private void UpdateOutputParameters() - { - if (CurrentDescription != null && _currentResult != null && _currentResult.Count != 0) - { - DataRow row = _currentResult.Peek(); - Queue<NpgsqlParameter> pending = new Queue<NpgsqlParameter>(); - List<int> taken = new List<int>(); - foreach (NpgsqlParameter p in _command.Parameters) - { - if (p.Direction == ParameterDirection.InputOutput || p.Direction == ParameterDirection.Output) - { - int idx = CurrentDescription.FieldIndex(p.CleanName); - if (idx == -1) - { - pending.Enqueue(p); - } - else - { - p.Value = row[idx]; - taken.Add(idx); - } - } - } - for (int i = 0; pending.Count != 0 && i != CurrentDescription.NumFields; ++i) - { - if (!taken.Contains(i)) - { - pending.Dequeue().Value = row[i]; - } - } - } - } - - internal override long? LastInsertedOID - { - get { return _currentResult.LastInsertedOID; } - } - - internal override NpgsqlRowDescription CurrentDescription - { - get { return _currentResult.Description; } - } - - public override bool HasRows - { - get - { - if (_currentRow != null || _currentResult.Count != 0) - { - return true; - } - foreach (ResultSet rs in _results) - { - if (rs.Count != 0) - { - return true; - } - } - return false; - } - } - - public override int RecordsAffected - { - get { return _lastRecordsAffected; } - } - - internal override void CheckHaveRow() - { - if (_currentRow == null) - { - throw new InvalidOperationException("Invalid attempt to read when no data is present."); - } - } - - public override void Close() - { - if ((_behavior & CommandBehavior.CloseConnection) == CommandBehavior.CloseConnection) - { - _connection.Close(); - } - _isClosed = true; - SendClosedEvent(); - } - - public override long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length) - { - byte[] source = (byte[]) this[i]; - if (buffer == null) - { - return source.Length - fieldOffset; - } - long finalLength = Math.Max(0, Math.Min(length, source.Length - fieldOffset)); - Array.Copy(source, fieldOffset, buffer, bufferoffset, finalLength); - return finalLength; - } - - public override long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length) - { - string source = (string) this[i]; - if (buffer == null) - { - return source.Length - fieldoffset; - } - long finalLength = Math.Max(0, Math.Min(length, source.Length - fieldoffset)); - Array.Copy(source.ToCharArray(), fieldoffset, buffer, bufferoffset, finalLength); - return finalLength; - } - - public override Object GetValue(Int32 ordinal) - { - CheckHaveRow(); - if (ordinal < 0 || ordinal >= CurrentDescription.NumFields) - { - throw new IndexOutOfRangeException(); - } - return _currentRow[ordinal]; - } + sb.Append(')'); - public override bool IsDBNull(int ordinal) - { - CheckHaveRow(); - return GetValue(ordinal) == DBNull.Value; - } + // if the loop ended without setting first to false, then there will be no results from the query + if (first) + return null; - public override bool NextResult() - { - if (_results.Count == 0) - { - _currentResult = null; - return false; - } - _lastRecordsAffected = (_currentResult = _results.Dequeue()).RecordsAffected; - return true; - } - - public override bool Read() - { - if (_currentResult.Count == 0) - { - _currentRow = null; - return false; - } - _currentRow = _currentResult.Dequeue(); - return true; - } - } -}
\ No newline at end of file + 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); + } + } +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlDescribe.cs b/mcs/class/Npgsql/Npgsql/NpgsqlDescribe.cs index 68dc168846e..9642b1b285e 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlDescribe.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlDescribe.cs @@ -9,52 +9,61 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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 Parse message sent to PostgreSQL - /// server. - /// </summary> - /// - internal sealed class NpgsqlDescribe : ClientMessage - { - private readonly Char _whatToDescribe; - private readonly String _portalName; - - public NpgsqlDescribe(Char whatToDescribe, String portalName) - { - _whatToDescribe = whatToDescribe; - _portalName = portalName; - } - - public override void WriteToStream(Stream outputStream) - { - outputStream.WriteByte((byte) FrontEndMessageCode.Describe); - - PGUtil.WriteInt32(outputStream, 4 + 1 + UTF8Encoding.GetByteCount(_portalName) + 1); - - outputStream.WriteByte((Byte) _whatToDescribe); - PGUtil.WriteString(_portalName, outputStream); - } - } -}
\ No newline at end of file + + /// <summary> + /// This class represents the Parse message sent to PostgreSQL + /// server. + /// </summary> + /// + internal sealed class NpgsqlDescribe + { + // Logging related values + private static readonly String CLASSNAME = "NpgsqlDescribe"; + + private Char _whatToDescribe; + private String _portalName; + + public NpgsqlDescribe(Char whatToDescribe, String portalName) + { + _whatToDescribe = whatToDescribe; + _portalName = portalName; + + } + + public void WriteToStream(Stream outputStream, Encoding encoding) + { + outputStream.WriteByte((Byte)'D'); + + PGUtil.WriteInt32(outputStream, 4 + + 1 + + encoding.GetByteCount(_portalName) + 1); + + outputStream.WriteByte((Byte)_whatToDescribe); + PGUtil.WriteString(_portalName, outputStream, encoding); + + + } + + } +} + diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlError.cs b/mcs/class/Npgsql/Npgsql/NpgsqlError.cs index 3b4c426345f..d9149c2ece3 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlError.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlError.cs @@ -9,22 +9,19 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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; @@ -32,281 +29,332 @@ using System.Text; namespace Npgsql { - /// <summary> - /// EventArgs class to send Notice parameters, which are just NpgsqlError's in a lighter context. - /// </summary> - public class NpgsqlNoticeEventArgs : EventArgs - { - /// <summary> - /// Notice information. - /// </summary> - public NpgsqlError Notice = null; - - internal NpgsqlNoticeEventArgs(NpgsqlError eNotice) - { - Notice = eNotice; - } - } - - /// <summary> - /// This class represents the ErrorResponse and NoticeResponse - /// message sent from PostgreSQL server. - /// </summary> - [Serializable] - public sealed class NpgsqlError - { - private readonly ProtocolVersion protocol_version; - private readonly String _severity = String.Empty; - private readonly String _code = String.Empty; - private readonly String _message = String.Empty; - private readonly String _detail = String.Empty; - private readonly String _hint = String.Empty; - private readonly String _position = String.Empty; - private readonly String _internalPosition = String.Empty; - private readonly String _internalQuery = String.Empty; - private readonly String _where = String.Empty; - private readonly String _file = String.Empty; - private readonly String _line = String.Empty; - private readonly String _routine = String.Empty; - private String _errorSql = String.Empty; - - /// <summary> - /// Severity code. All versions. - /// </summary> - public String Severity - { - get { return _severity; } - } - - /// <summary> - /// Error code. PostgreSQL 7.4 and up. - /// </summary> - public String Code - { - get { return _code; } - } - - /// <summary> - /// Terse error message. All versions. - /// </summary> - public String Message - { - get { return _message; } - } - - /// <summary> - /// Detailed error message. PostgreSQL 7.4 and up. - /// </summary> - public String Detail - { - get { return _detail; } - } - - /// <summary> - /// Suggestion to help resolve the error. PostgreSQL 7.4 and up. - /// </summary> - public String Hint - { - get { return _hint; } - } - - /// <summary> - /// Position (one based) within the query string where the error was encounterd. PostgreSQL 7.4 and up. - /// </summary> - public String Position - { - get { return _position; } - } - - /// <summary> - /// Position (one based) within the query string where the error was encounterd. This position refers to an internal command executed for example inside a PL/pgSQL function. PostgreSQL 7.4 and up. - /// </summary> - public String InternalPosition - { - get { return _internalPosition; } - } - - /// <summary> - /// Internal query string where the error was encounterd. This position refers to an internal command executed for example inside a PL/pgSQL function. PostgreSQL 7.4 and up. - /// </summary> - public String InternalQuery - { - get { return _internalQuery; } - } - /// <summary> - /// Trace back information. PostgreSQL 7.4 and up. - /// </summary> - public String Where - { - get { return _where; } - } - - /// <summary> - /// Source file (in backend) reporting the error. PostgreSQL 7.4 and up. - /// </summary> - public String File - { - get { return _file; } - } - - /// <summary> - /// Source file line number (in backend) reporting the error. PostgreSQL 7.4 and up. - /// </summary> - public String Line - { - get { return _line; } - } - - /// <summary> - /// Source routine (in backend) reporting the error. PostgreSQL 7.4 and up. - /// </summary> - public String Routine - { - get { return _routine; } - } - - /// <summary> - /// String containing the sql sent which produced this error. - /// </summary> - public String ErrorSql - { - set { _errorSql = value; } - get { return _errorSql; } - } - - /// <summary> - /// Return a string representation of this error object. - /// </summary> - public override String ToString() - { - StringBuilder B = new StringBuilder(); - - if (Severity.Length > 0) - { - B.AppendFormat("{0}: ", Severity); - } - if (Code.Length > 0) - { - B.AppendFormat("{0}: ", Code); - } - B.AppendFormat("{0}", Message); - // CHECKME - possibly multi-line, that is yucky - // if (Hint.Length > 0) { - // B.AppendFormat(" ({0})", Hint); - // } - - return B.ToString(); - } - - internal NpgsqlError(ProtocolVersion protocolVersion, Stream stream) - { - switch (protocol_version = protocolVersion) - { - case ProtocolVersion.Version2: - string[] parts = PGUtil.ReadString(stream).Split(new char[] {':'}, 2); - if (parts.Length == 2) - { - _severity = parts[0].Trim(); - _message = parts[1].Trim(); - } - else - { - _severity = string.Empty; - _message = parts[0].Trim(); - } - break; - case ProtocolVersion.Version3: - // Check the messageLength value. If it is 1178686529, this would be the - // "FATA" string, which would mean a protocol 2.0 error string. - if (PGUtil.ReadInt32(stream) == 1178686529) - { - string[] v2Parts = ("FATA" + PGUtil.ReadString(stream)).Split(new char[] {':'}, 2); - if (v2Parts.Length == 2) - { - _severity = v2Parts[0].Trim(); - _message = v2Parts[1].Trim(); - } - else - { - _severity = string.Empty; - _message = v2Parts[0].Trim(); - } - protocol_version = ProtocolVersion.Version2; - } - else - { - for (char field = (char) stream.ReadByte(); field != 0; field = (char) stream.ReadByte()) - { - switch (field) - { - case 'S': - _severity = PGUtil.ReadString(stream); - ; - break; - case 'C': - _code = PGUtil.ReadString(stream); - ; - break; - case 'M': - _message = PGUtil.ReadString(stream); - ; - break; - case 'D': - _detail = PGUtil.ReadString(stream); - ; - break; - case 'H': - _hint = PGUtil.ReadString(stream); - ; - break; - case 'P': - _position = PGUtil.ReadString(stream); - ; - break; - case 'p': - _internalPosition = PGUtil.ReadString(stream); - ; - break; - case 'q': - _internalQuery = PGUtil.ReadString(stream); - ; - break; - case 'W': - _where = PGUtil.ReadString(stream); - ; - break; - case 'F': - _file = PGUtil.ReadString(stream); - ; - break; - case 'L': - _line = PGUtil.ReadString(stream); - ; - break; - case 'R': - _routine = PGUtil.ReadString(stream); - ; - break; - - } - } - } - break; - } - } - - internal NpgsqlError(ProtocolVersion protocolVersion, String errorMessage) - { - protocol_version = protocolVersion; - _message = errorMessage; - } - - /// <summary> - /// Backend protocol version in use. - /// </summary> - internal ProtocolVersion BackendProtocolVersion - { - get { return protocol_version; } - } - } + /// <summary> + /// EventArgs class to send Notice parameters, which are just NpgsqlError's in a lighter context. + /// </summary> + public class NpgsqlNoticeEventArgs : EventArgs + { + /// <summary> + /// Notice information. + /// </summary> + public NpgsqlError Notice = null; + + internal NpgsqlNoticeEventArgs(NpgsqlError eNotice) + { + Notice = eNotice; + } + } + + /// <summary> + /// This class represents the ErrorResponse and NoticeResponse + /// message sent from PostgreSQL server. + /// </summary> + [Serializable] + public sealed class NpgsqlError + { + // Logging related values + private static readonly String CLASSNAME = "NpgsqlError"; + + private ProtocolVersion protocol_version; + private String _severity = String.Empty; + private String _code = String.Empty; + private String _message = String.Empty; + private String _detail = String.Empty; + private String _hint = String.Empty; + private String _position = String.Empty; + private String _where = String.Empty; + private String _file = String.Empty; + private String _line = String.Empty; + private String _routine = String.Empty; + private String _errorSql = String.Empty; + + /// <summary> + /// Severity code. All versions. + /// </summary> + public String Severity + { + get + { + return _severity; + } + } + + /// <summary> + /// Error code. PostgreSQL 7.4 and up. + /// </summary> + public String Code + { + get + { + return _code; + } + } + + /// <summary> + /// Terse error message. All versions. + /// </summary> + public String Message + { + get + { + return _message; + } + } + + /// <summary> + /// Detailed error message. PostgreSQL 7.4 and up. + /// </summary> + public String Detail + { + get + { + return _detail; + } + } + + /// <summary> + /// Suggestion to help resolve the error. PostgreSQL 7.4 and up. + /// </summary> + public String Hint + { + get + { + return _hint; + } + } + + /// <summary> + /// Position (one based) within the query string where the error was encounterd. PostgreSQL 7.4 and up. + /// </summary> + public String Position + { + get + { + return _position; + } + } + + /// <summary> + /// Trace back information. PostgreSQL 7.4 and up. + /// </summary> + public String Where + { + get + { + return _where; + } + } + + /// <summary> + /// Source file (in backend) reporting the error. PostgreSQL 7.4 and up. + /// </summary> + public String File + { + get + { + return _file; + } + } + + /// <summary> + /// Source file line number (in backend) reporting the error. PostgreSQL 7.4 and up. + /// </summary> + public String Line + { + get + { + return _line; + } + } + + /// <summary> + /// Source routine (in backend) reporting the error. PostgreSQL 7.4 and up. + /// </summary> + public String Routine + { + get + { + return _routine; + } + } + + /// <summary> + /// String containing the sql sent which produced this error. + /// </summary> + public String ErrorSql + { + set + { + _errorSql = value; + } + get + { + return _errorSql; + } + } + /// <summary> + /// Return a string representation of this error object. + /// </summary> + public override String ToString() + { + StringBuilder B = new StringBuilder(); + + if (Severity.Length > 0) + { + B.AppendFormat("{0}: ", Severity); + } + if (Code.Length > 0) + { + B.AppendFormat("{0}: ", Code); + } + B.AppendFormat("{0}", Message); + // CHECKME - possibly multi-line, that is yucky + // if (Hint.Length > 0) { + // B.AppendFormat(" ({0})", Hint); + // } + + return B.ToString(); + } + + internal NpgsqlError(ProtocolVersion protocolVersion) + { + protocol_version = protocolVersion; + } + + internal NpgsqlError(ProtocolVersion protocolVersion, String errorMessage) + { + protocol_version = protocolVersion; + _message = errorMessage; + } + + /// <summary> + /// Backend protocol version in use. + /// </summary> + internal ProtocolVersion BackendProtocolVersion + { + get + { + return protocol_version; + } + } + + internal void ReadFromStream(Stream inputStream, Encoding encoding) + { + switch (protocol_version) { + case ProtocolVersion.Version2 : + ReadFromStream_Ver_2(inputStream, encoding); + break; + + case ProtocolVersion.Version3 : + ReadFromStream_Ver_3(inputStream, encoding); + break; + + } + } + + private void ReadFromStream_Ver_2(Stream inputStream, Encoding encoding) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_2"); + + String Raw; + String[] Parts; + + Raw = PGUtil.ReadString(inputStream, encoding); + + Parts = Raw.Split(new char[] {':'}, 2); + + if (Parts.Length == 2) + { + _severity = Parts[0].Trim(); + _message = Parts[1].Trim(); + } + else + { + _message = Parts[0].Trim(); + } + } + + private void ReadFromStream_Ver_3(Stream inputStream, Encoding encoding) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_3"); + + Int32 messageLength = PGUtil.ReadInt32(inputStream, new Byte[4]); + + // [TODO] Would this be the right way to do? + // Check the messageLength value. If it is 1178686529, this would be the + // "FATA" string, which would mean a protocol 2.0 error string. + if (messageLength == 1178686529) + { + String Raw; + String[] Parts; + + Raw = "FATA" + PGUtil.ReadString(inputStream, encoding); + + Parts = Raw.Split(new char[] {':'}, 2); + + if (Parts.Length == 2) + { + _severity = Parts[0].Trim(); + _message = Parts[1].Trim(); + } + else + { + _message = Parts[0].Trim(); + } + + protocol_version = ProtocolVersion.Version2; + + return; + } + + Char field; + String fieldValue; + + field = (Char) inputStream.ReadByte(); + + // Now start to read fields. + while (field != 0) + { + fieldValue = PGUtil.ReadString(inputStream, encoding); + + switch (field) + { + case 'S': + _severity = fieldValue; + break; + case 'C': + _code = fieldValue; + break; + case 'M': + _message = fieldValue; + break; + case 'D': + _detail = fieldValue; + break; + case 'H': + _hint = fieldValue; + break; + case 'P': + _position = fieldValue; + break; + case 'W': + _where = fieldValue; + break; + case 'F': + _file = fieldValue; + break; + case 'L': + _line = fieldValue; + break; + case 'R': + _routine = fieldValue; + break; + + } + + field = (Char) inputStream.ReadByte(); + + } + } + } } diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlEventLog.cs b/mcs/class/Npgsql/Npgsql/NpgsqlEventLog.cs index 74356e2ba60..e50d070cae6 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlEventLog.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlEventLog.cs @@ -10,381 +10,338 @@ // http://gborg.postgresql.org/project/npgsql/projdisplay.php // // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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.Diagnostics; using System.IO; +using System.Text; +using System.Diagnostics; +using System; using System.Resources; namespace Npgsql { - /// <summary> - /// The level of verbosity of the NpgsqlEventLog - /// </summary> - public enum LogLevel - { - /// <summary> - /// Don't log at all - /// </summary> - None = 0, - /// <summary> - /// Only log the most common issues - /// </summary> - Normal = 1, - /// <summary> - /// Log everything - /// </summary> - Debug = 2 - } + + /// <summary> + /// The level of verbosity of the NpgsqlEventLog + /// </summary> + public enum LogLevel { + /// <summary> + /// Don't log at all + /// </summary> + None = 0, + /// <summary> + /// Only log the most common issues + /// </summary> + Normal = 1, + /// <summary> + /// Log everything + /// </summary> + Debug = 2 + } - /// <summary> - /// This class handles all the Npgsql event and debug logging - /// </summary> - public class NpgsqlEventLog - { - // Logging related values - private static readonly String CLASSNAME = "NpgsqlEventLog"; - private static String logfile; - private static LogLevel level; - private static Boolean echomessages; + /// <summary> + /// This class handles all the Npgsql event and debug logging + /// </summary> + public class NpgsqlEventLog + { + // Logging related values + private static readonly String CLASSNAME = "NpgsqlEventLog"; + private static String logfile; + private static LogLevel level; + private static Boolean echomessages; - private static readonly ResourceManager LogResMan; + private static ResourceManager LogResMan; - private NpgsqlEventLog() - { - } + private NpgsqlEventLog() + {} + // static constructor + static NpgsqlEventLog() + { + LogResMan = new ResourceManager(typeof(NpgsqlEventLog)); + } - // static constructor - static NpgsqlEventLog() - { - LogResMan = new ResourceManager(typeof(NpgsqlEventLog)); - } + ///<summary> + /// Sets/Returns the level of information to log to the logfile. + /// </summary> + /// <value>The current <see cref="Npgsql.LogLevel">LogLevel</see></value> + public static LogLevel Level { + get + { + LogPropertyGet(LogLevel.Debug, CLASSNAME, "LogLevel"); + return level; + } + set + { + LogPropertySet(LogLevel.Debug, CLASSNAME, "LogLevel", value); + level = value; + } + } - ///<summary> - /// Sets/Returns the level of information to log to the logfile. - /// </summary> - /// <value>The current <see cref="Npgsql.LogLevel">LogLevel</see></value> - public static LogLevel Level - { - get - { - LogPropertyGet(LogLevel.Debug, CLASSNAME, "LogLevel"); - return level; - } - set - { - LogPropertySet(LogLevel.Debug, CLASSNAME, "LogLevel", value); - level = value; - } - } + ///<summary> + /// Sets/Returns the filename to use for logging. + /// </summary> + /// <value>The filename of the current Log file.</value> + public static String LogName { + get + { + LogPropertyGet(LogLevel.Debug, CLASSNAME, "LogName"); + return logfile; + } + set + { + LogPropertySet(LogLevel.Normal, CLASSNAME, "LogName", value); + logfile = value; + } + } - ///<summary> - /// Sets/Returns the filename to use for logging. - /// </summary> - /// <value>The filename of the current Log file.</value> - public static String LogName - { - get - { - LogPropertyGet(LogLevel.Debug, CLASSNAME, "LogName"); - return logfile; - } - set - { - LogPropertySet(LogLevel.Normal, CLASSNAME, "LogName", value); - logfile = value; - } - } + ///<summary> + /// Sets/Returns whether Log messages should be echoed to the console + /// </summary> + /// <value><b>true</b> if Log messages are echoed to the console, otherwise <b>false</b></value> + public static Boolean EchoMessages { + get + { + LogPropertyGet(LogLevel.Debug, CLASSNAME, "EchoMessages"); + return echomessages; + } + set + { + LogPropertySet(LogLevel.Normal, CLASSNAME, "EchoMessages", value); + echomessages = value; + } + } - ///<summary> - /// Sets/Returns whether Log messages should be echoed to the console - /// </summary> - /// <value><b>true</b> if Log messages are echoed to the console, otherwise <b>false</b></value> - public static Boolean EchoMessages - { - get - { - LogPropertyGet(LogLevel.Debug, CLASSNAME, "EchoMessages"); - return echomessages; - } - set - { - LogPropertySet(LogLevel.Normal, CLASSNAME, "EchoMessages", value); - echomessages = value; - } - } + // Event/Debug Logging + // This method should be private and only used by the internal methods that support localization. + /// <summary> + /// Writes a string to the Npgsql event log if msglevel is bigger then <see cref="Npgsql.NpgsqlEventLog.Level">NpgsqlEventLog.Level</see> + /// </summary> + /// <remarks> + /// This method is obsolete and should no longer be used. + /// It is likely to be removed in future versions of Npgsql + /// </remarks> + /// <param name="message">The message to write to the event log</param> + /// <param name="msglevel">The minimum <see cref="Npgsql.LogLevel">LogLevel</see> for which this message should be logged.</param> + private static void LogMsg(String message, LogLevel msglevel) + { + if (msglevel > level) + return; - // Event/Debug Logging - // This method should be private and only used by the internal methods that support localization. - /// <summary> - /// Writes a string to the Npgsql event log if msglevel is bigger then <see cref="Npgsql.NpgsqlEventLog.Level">NpgsqlEventLog.Level</see> - /// </summary> - /// <remarks> - /// This method is obsolete and should no longer be used. - /// It is likely to be removed in future versions of Npgsql - /// </remarks> - /// <param name="message">The message to write to the event log</param> - /// <param name="msglevel">The minimum <see cref="Npgsql.LogLevel">LogLevel</see> for which this message should be logged.</param> - private static void LogMsg(String message, LogLevel msglevel) - { - if (msglevel > level) - { - return; - } + Process proc = Process.GetCurrentProcess(); - Process proc = Process.GetCurrentProcess(); + if (echomessages) + { + Console.WriteLine(message); + } - if (echomessages) - { - Console.WriteLine(message); - } + if (logfile != null) + { + if (logfile != "") + { + lock(logfile) + { - if (!string.IsNullOrEmpty(logfile)) - { - lock (logfile) - { - StreamWriter writer = new StreamWriter(logfile, true); + StreamWriter writer = new StreamWriter(logfile, true); - // The format of the logfile is - // [Date] [Time] [PID] [Level] [Message] - writer.WriteLine("{0}\t{1}\t{2}\t{3}", DateTime.Now, proc.Id, msglevel, message); - writer.Close(); - } - } + // The format of the logfile is + // [Date] [Time] [PID] [Level] [Message] + writer.WriteLine(System.DateTime.Now + "\t" + proc.Id + "\t" + msglevel + "\t" + message); + writer.Close(); + } + } + } + } - } + /// <summary> + /// Writes a string to the Npgsql event log if msglevel is bigger then <see cref="Npgsql.NpgsqlEventLog.Level">NpgsqlEventLog.Level</see> + /// </summary> + /// <param name="resman">The <see cref="System.Resources.ResourceManager">ResourceManager</see> to get the localized resources</param> + /// <param name="ResourceString">The name of the resource that should be fetched by the <see cref="System.Resources.ResourceManager">ResourceManager</see></param> + /// <param name="msglevel">The minimum <see cref="Npgsql.LogLevel">LogLevel</see> for which this message should be logged.</param> + /// <param name="Parameters">The additional parameters that shall be included into the log-message (must be compatible with the string in the resource):</param> + internal static void LogMsg(ResourceManager resman, string ResourceString, LogLevel msglevel, params Object[] Parameters) + { + if (msglevel > level) + return; - /// <summary> - /// Writes a string to the Npgsql event log if msglevel is bigger then <see cref="Npgsql.NpgsqlEventLog.Level">NpgsqlEventLog.Level</see> - /// </summary> - /// <param name="resman">The <see cref="System.Resources.ResourceManager">ResourceManager</see> to get the localized resources</param> - /// <param name="ResourceString">The name of the resource that should be fetched by the <see cref="System.Resources.ResourceManager">ResourceManager</see></param> - /// <param name="msglevel">The minimum <see cref="Npgsql.LogLevel">LogLevel</see> for which this message should be logged.</param> - /// <param name="Parameters">The additional parameters that shall be included into the log-message (must be compatible with the string in the resource):</param> - internal static void LogMsg(ResourceManager resman, string ResourceString, LogLevel msglevel, - params Object[] Parameters) - { - if (msglevel > level) - { - return; - } + string message = resman.GetString(ResourceString); - string message = resman.GetString(ResourceString); + if (message == null) { + message = String.Format("Unable to find resource string {0} for class {1}", ResourceString, resman.BaseName); + } else if (Parameters.Length > 0) { + message = String.Format(message, Parameters); + } - if (message == null) - { - message = String.Format("Unable to find resource string {0} for class {1}", ResourceString, resman.BaseName); - } - else if (Parameters.Length > 0) - { - message = String.Format(message, Parameters); - } + LogMsg(message, msglevel); + } - LogMsg(message, msglevel); - } + /// <summary> + /// Writes the default log-message for the action of calling the Get-part of an Indexer to the log file. + /// </summary> + /// <param name="msglevel">The minimum <see cref="Npgsql.LogLevel">LogLevel</see> for which this message should be logged.</param> + /// <param name="ClassName">The name of the class that contains the Indexer</param> + /// <param name="IndexerParam">The parameter given to the Indexer</param> + internal static void LogIndexerGet(LogLevel msglevel, string ClassName, object IndexerParam) + { + if (msglevel > level) + return; + string message = String.Format(LogResMan.GetString("Indexer_Get"), ClassName, IndexerParam); + LogMsg(message, msglevel); + } - /// <summary> - /// Writes the default log-message for the action of calling the Get-part of an Indexer to the log file. - /// </summary> - /// <param name="msglevel">The minimum <see cref="Npgsql.LogLevel">LogLevel</see> for which this message should be logged.</param> - /// <param name="ClassName">The name of the class that contains the Indexer</param> - /// <param name="IndexerParam">The parameter given to the Indexer</param> - internal static void LogIndexerGet(LogLevel msglevel, string ClassName, object IndexerParam) - { - if (msglevel > level) - { - return; - } - string message = String.Format(LogResMan.GetString("Indexer_Get"), ClassName, IndexerParam); - LogMsg(message, msglevel); - } + /// <summary> + /// Writes the default log-message for the action of calling the Set-part of an Indexer to the logfile. + /// </summary> + /// <param name="msglevel">The minimum <see cref="Npgsql.LogLevel">LogLevel</see> for which this message should be logged.</param> + /// <param name="ClassName">The name of the class that contains the Indexer</param> + /// <param name="IndexerParam">The parameter given to the Indexer</param> + /// <param name="value">The value the Indexer is set to</param> + internal static void LogIndexerSet(LogLevel msglevel, string ClassName, object IndexerParam, object value) + { + if (msglevel > level) + return; + string message = String.Format(LogResMan.GetString("Indexer_Set"), ClassName, IndexerParam, value); + LogMsg(message, msglevel); + } - /// <summary> - /// Writes the default log-message for the action of calling the Set-part of an Indexer to the logfile. - /// </summary> - /// <param name="msglevel">The minimum <see cref="Npgsql.LogLevel">LogLevel</see> for which this message should be logged.</param> - /// <param name="ClassName">The name of the class that contains the Indexer</param> - /// <param name="IndexerParam">The parameter given to the Indexer</param> - /// <param name="value">The value the Indexer is set to</param> - internal static void LogIndexerSet(LogLevel msglevel, string ClassName, object IndexerParam, object value) - { - if (msglevel > level) - { - return; - } - string message = String.Format(LogResMan.GetString("Indexer_Set"), ClassName, IndexerParam, value); - LogMsg(message, msglevel); - } + /// <summary> + /// Writes the default log-message for the action of calling the Get-part of a Property to the logfile. + /// </summary> + /// <param name="msglevel">The minimum <see cref="Npgsql.LogLevel">LogLevel</see> for which this message should be logged.</param> + /// <param name="ClassName">The name of the class that contains the Property</param> + /// <param name="PropertyName">The name of the Property</param> + internal static void LogPropertyGet(LogLevel msglevel, string ClassName, string PropertyName) + { + if (msglevel > level) + return; + string message = String.Format(LogResMan.GetString("Property_Get"), ClassName, PropertyName); + LogMsg(message, msglevel); + } - /// <summary> - /// Writes the default log-message for the action of calling the Get-part of a Property to the logfile. - /// </summary> - /// <param name="msglevel">The minimum <see cref="Npgsql.LogLevel">LogLevel</see> for which this message should be logged.</param> - /// <param name="ClassName">The name of the class that contains the Property</param> - /// <param name="PropertyName">The name of the Property</param> - internal static void LogPropertyGet(LogLevel msglevel, string ClassName, string PropertyName) - { - if (msglevel > level) - { - return; - } - string message = String.Format(LogResMan.GetString("Property_Get"), ClassName, PropertyName); - LogMsg(message, msglevel); - } + /// <summary> + /// Writes the default log-message for the action of calling the Set-part of a Property to the logfile. + /// </summary> + /// <param name="msglevel">The minimum <see cref="Npgsql.LogLevel">LogLevel</see> for which this message should be logged.</param> + /// <param name="ClassName">The name of the class that contains the Property</param> + /// <param name="PropertyName">The name of the Property</param> + /// <param name="value">The value the Property is set to</param> + internal static void LogPropertySet(LogLevel msglevel, string ClassName, string PropertyName, object value) + { + if (msglevel > level) + return; + string message = String.Format(LogResMan.GetString("Property_Set"), ClassName, PropertyName, value); + LogMsg(message, msglevel); + } - /// <summary> - /// Writes the default log-message for the action of calling the Set-part of a Property to the logfile. - /// </summary> - /// <param name="msglevel">The minimum <see cref="Npgsql.LogLevel">LogLevel</see> for which this message should be logged.</param> - /// <param name="ClassName">The name of the class that contains the Property</param> - /// <param name="PropertyName">The name of the Property</param> - /// <param name="value">The value the Property is set to</param> - internal static void LogPropertySet(LogLevel msglevel, string ClassName, string PropertyName, object value) - { - if (msglevel > level) - { - return; - } - string message = String.Format(LogResMan.GetString("Property_Set"), ClassName, PropertyName, value); - LogMsg(message, msglevel); - } + /// <summary> + /// Writes the default log-message for the action of calling a Method without Arguments to the logfile. + /// </summary> + /// <param name="msglevel">The minimum <see cref="Npgsql.LogLevel">LogLevel</see> for which this message should be logged.</param> + /// <param name="ClassName">The name of the class that contains the Method</param> + /// <param name="MethodName">The name of the Method</param> + internal static void LogMethodEnter(LogLevel msglevel, string ClassName, string MethodName) + { + if (msglevel > level) + return; + string message = String.Format(LogResMan.GetString("Method_0P_Enter"), ClassName, MethodName); + LogMsg(message, msglevel); + } - /// <summary> - /// Writes the default log-message for the action of calling a Method without Arguments to the logfile. - /// </summary> - /// <param name="msglevel">The minimum <see cref="Npgsql.LogLevel">LogLevel</see> for which this message should be logged.</param> - /// <param name="ClassName">The name of the class that contains the Method</param> - /// <param name="MethodName">The name of the Method</param> - internal static void LogMethodEnter(LogLevel msglevel, string ClassName, string MethodName) - { - if (msglevel > level) - { - return; - } - string message = String.Format(LogResMan.GetString("Method_0P_Enter"), ClassName, MethodName); - LogMsg(message, msglevel); - } + /// <summary> + /// Writes the default log-message for the action of calling a Method with one Argument to the logfile. + /// </summary> + /// <param name="msglevel">The minimum <see cref="Npgsql.LogLevel">LogLevel</see> for which this message should be logged.</param> + /// <param name="ClassName">The name of the class that contains the Method</param> + /// <param name="MethodName">The name of the Method</param> + /// <param name="MethodParameter">The value of the Argument of the Method</param> + internal static void LogMethodEnter(LogLevel msglevel, string ClassName, string MethodName, object MethodParameter) + { + if (msglevel > level) + return; + string message = String.Format(LogResMan.GetString("Method_1P_Enter"), ClassName, MethodName, MethodParameter); + LogMsg(message, msglevel); + } - /// <summary> - /// Writes the default log-message for the action of calling a Method with one Argument to the logfile. - /// </summary> - /// <param name="msglevel">The minimum <see cref="Npgsql.LogLevel">LogLevel</see> for which this message should be logged.</param> - /// <param name="ClassName">The name of the class that contains the Method</param> - /// <param name="MethodName">The name of the Method</param> - /// <param name="MethodParameter">The value of the Argument of the Method</param> - internal static void LogMethodEnter(LogLevel msglevel, string ClassName, string MethodName, object MethodParameter) - { - if (msglevel > level) - { - return; - } - string message = String.Format(LogResMan.GetString("Method_1P_Enter"), ClassName, MethodName, MethodParameter); - LogMsg(message, msglevel); - } + /// <summary> + /// Writes the default log-message for the action of calling a Method with two Arguments to the logfile. + /// </summary> + /// <param name="msglevel">The minimum <see cref="Npgsql.LogLevel">LogLevel</see> for which this message should be logged.</param> + /// <param name="ClassName">The name of the class that contains the Method</param> + /// <param name="MethodName">The name of the Method</param> + /// <param name="MethodParameter1">The value of the first Argument of the Method</param> + /// <param name="MethodParameter2">The value of the second Argument of the Method</param> + internal static void LogMethodEnter(LogLevel msglevel, string ClassName, string MethodName, object MethodParameter1, object MethodParameter2) + { + if (msglevel > level) + return; + string message = String.Format(LogResMan.GetString("Method_2P_Enter"), ClassName, MethodName, MethodParameter1, MethodParameter2); + LogMsg(message, msglevel); + } - /// <summary> - /// Writes the default log-message for the action of calling a Method with two Arguments to the logfile. - /// </summary> - /// <param name="msglevel">The minimum <see cref="Npgsql.LogLevel">LogLevel</see> for which this message should be logged.</param> - /// <param name="ClassName">The name of the class that contains the Method</param> - /// <param name="MethodName">The name of the Method</param> - /// <param name="MethodParameter1">The value of the first Argument of the Method</param> - /// <param name="MethodParameter2">The value of the second Argument of the Method</param> - internal static void LogMethodEnter(LogLevel msglevel, string ClassName, string MethodName, object MethodParameter1, - object MethodParameter2) - { - if (msglevel > level) - { - return; - } - string message = - String.Format(LogResMan.GetString("Method_2P_Enter"), ClassName, MethodName, MethodParameter1, MethodParameter2); - LogMsg(message, msglevel); - } + /// <summary> + /// Writes the default log-message for the action of calling a Method with three Arguments to the logfile. + /// </summary> + /// <param name="msglevel">The minimum <see cref="Npgsql.LogLevel">LogLevel</see> for which this message should be logged.</param> + /// <param name="ClassName">The name of the class that contains the Method</param> + /// <param name="MethodName">The name of the Method</param> + /// <param name="MethodParameter1">The value of the first Argument of the Method</param> + /// <param name="MethodParameter2">The value of the second Argument of the Method</param> + /// <param name="MethodParameter3">The value of the third Argument of the Method</param> + internal static void LogMethodEnter(LogLevel msglevel, string ClassName, string MethodName, object MethodParameter1, object MethodParameter2, object MethodParameter3) + { + if (msglevel > level) + return; + string message = String.Format(LogResMan.GetString("Method_3P_Enter"), ClassName, MethodName, MethodParameter1, MethodParameter2, MethodParameter3); + LogMsg(message, msglevel); + } - /// <summary> - /// Writes the default log-message for the action of calling a Method with three Arguments to the logfile. - /// </summary> - /// <param name="msglevel">The minimum <see cref="Npgsql.LogLevel">LogLevel</see> for which this message should be logged.</param> - /// <param name="ClassName">The name of the class that contains the Method</param> - /// <param name="MethodName">The name of the Method</param> - /// <param name="MethodParameter1">The value of the first Argument of the Method</param> - /// <param name="MethodParameter2">The value of the second Argument of the Method</param> - /// <param name="MethodParameter3">The value of the third Argument of the Method</param> - internal static void LogMethodEnter(LogLevel msglevel, string ClassName, string MethodName, object MethodParameter1, - object MethodParameter2, object MethodParameter3) - { - if (msglevel > level) - { - return; - } - string message = - String.Format(LogResMan.GetString("Method_3P_Enter"), ClassName, MethodName, MethodParameter1, MethodParameter2, - MethodParameter3); - LogMsg(message, msglevel); - } + /// <summary> + /// Writes the default log-message for the action of calling a Method with more than three Arguments to the logfile. + /// </summary> + /// <param name="msglevel">The minimum <see cref="Npgsql.LogLevel">LogLevel</see> for which this message should be logged.</param> + /// <param name="ClassName">The name of the class that contains the Method</param> + /// <param name="MethodName">The name of the Method</param> + /// <param name="MethodParameters">A <see cref="System.Object">Object</see>-Array with zero or more Ojects that are Arguments of the Method.</param> + internal static void LogMethodEnter(LogLevel msglevel, string ClassName, string MethodName, params object[] MethodParameters) + { + if (msglevel > level) + return; + string message = String.Empty; + switch (MethodParameters.Length) + { + case 4: + message = String.Format(LogResMan.GetString("Method_4P_Enter"), ClassName, MethodName, + MethodParameters[0], MethodParameters[1], MethodParameters[2], MethodParameters[3]); + break; + case 5: + message = String.Format(LogResMan.GetString("Method_5P_Enter"), ClassName, MethodName, + MethodParameters[0], MethodParameters[1], MethodParameters[2], MethodParameters[3], MethodParameters[4]); + break; + case 6: + message = String.Format(LogResMan.GetString("Method_6P_Enter"), ClassName, MethodName, + MethodParameters[0], MethodParameters[1], MethodParameters[2], MethodParameters[3], MethodParameters[4], MethodParameters[5]); + break; + default: + // should always be true - but who knows ;-) + if (MethodParameters.Length > 6) + message = String.Format(LogResMan.GetString("Method_6P+_Enter"), ClassName, MethodName, MethodParameters[0], MethodParameters[1]); + break; + } + LogMsg(message, msglevel); + } - /// <summary> - /// Writes the default log-message for the action of calling a Method with more than three Arguments to the logfile. - /// </summary> - /// <param name="msglevel">The minimum <see cref="Npgsql.LogLevel">LogLevel</see> for which this message should be logged.</param> - /// <param name="ClassName">The name of the class that contains the Method</param> - /// <param name="MethodName">The name of the Method</param> - /// <param name="MethodParameters">A <see cref="System.Object">Object</see>-Array with zero or more Ojects that are Arguments of the Method.</param> - internal static void LogMethodEnter(LogLevel msglevel, string ClassName, string MethodName, - params object[] MethodParameters) - { - if (msglevel > level) - { - return; - } - string message = String.Empty; - switch (MethodParameters.Length) - { - case 4: - message = - String.Format(LogResMan.GetString("Method_4P_Enter"), ClassName, MethodName, MethodParameters[0], - MethodParameters[1], MethodParameters[2], MethodParameters[3]); - break; - case 5: - message = - String.Format(LogResMan.GetString("Method_5P_Enter"), ClassName, MethodName, MethodParameters[0], - MethodParameters[1], MethodParameters[2], MethodParameters[3], MethodParameters[4]); - break; - case 6: - message = - String.Format(LogResMan.GetString("Method_6P_Enter"), ClassName, MethodName, MethodParameters[0], - MethodParameters[1], MethodParameters[2], MethodParameters[3], MethodParameters[4], - MethodParameters[5]); - break; - default: - // should always be true - but who knows ;-) - if (MethodParameters.Length > 6) - { - message = - String.Format(LogResMan.GetString("Method_6P+_Enter"), ClassName, MethodName, MethodParameters[0], - MethodParameters[1]); - } - break; - } - LogMsg(message, msglevel); - } - } -}
\ No newline at end of file + } +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlException.cs b/mcs/class/Npgsql/Npgsql/NpgsqlException.cs index fbbc5bda61a..4201dbc7669 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlException.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlException.cs @@ -9,231 +9,269 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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.Collections; -using System.Data.Common; -using System.IO; using System.Resources; +using System.IO; +using System.Text; +using System.Collections; using System.Runtime.Serialization; namespace Npgsql { - /// <summary> - /// The exception that is thrown when the PostgreSQL backend reports errors. - /// </summary> - [Serializable] - public sealed class NpgsqlException : DbException - { - private readonly IList errors; - - // Logging related values - //private static readonly String CLASSNAME = "NpgsqlException"; - private static readonly ResourceManager resman = new ResourceManager(typeof (NpgsqlException)); - - // To allow deserialization. - private NpgsqlException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - errors = (IList) info.GetValue("errors", typeof (IList)); - } - - /// <summary> - /// Construct a backend error exception based on a list of one or more - /// backend errors. The basic Exception.Message will be built from the - /// first (usually the only) error in the list. - /// </summary> - internal NpgsqlException(IList errors) - : base(errors[0].ToString()) - { - NpgsqlEventLog.LogMsg(resman, "Log_ExceptionOccured", LogLevel.Normal, Message); - this.errors = new ArrayList(errors); - } - - - 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)); - } - - - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - base.GetObjectData(info, context); - - // Add custom data, in this case the list of errors when serializing. - // Thanks Robert Chartier for info: http://www.15seconds.com/issue/020903.htm - - //use the info object to add the items you want serialized - info.AddValue("errors", errors, typeof (IList)); - } - - /// <summary> - /// Provide access to the entire list of errors provided by the PostgreSQL backend. - /// </summary> - public NpgsqlError this[Int32 Index] - { - get { return (NpgsqlError) errors[Index]; } - } - - - /// <summary> - /// Severity code. All versions. - /// </summary> - public String Severity - { - get { return this[0].Severity; } - } - - /// <summary> - /// Error code. PostgreSQL 7.4 and up. - /// </summary> - public String Code - { - get { return this[0].Code; } - } - - /// <summary> - /// Basic error message. All versions. - /// </summary> - public String BaseMessage - { - get { return this[0].Message; } - } - - /// <summary> - /// Detailed error message. PostgreSQL 7.4 and up. - /// </summary> - public String Detail - { - get { return this[0].Detail; } - } - - /// <summary> - /// Suggestion to help resolve the error. PostgreSQL 7.4 and up. - /// </summary> - public String Hint - { - get { return this[0].Hint; } - } - - /// <summary> - /// Position (one based) within the query string where the error was encounterd. PostgreSQL 7.4 and up. - /// </summary> - public String Position - { - get { return this[0].Position; } - } - - /// <summary> - /// Trace back information. PostgreSQL 7.4 and up. - /// </summary> - public String Where - { - get { return this[0].Where; } - } - - /// <summary> - /// Source file (in backend) reporting the error. PostgreSQL 7.4 and up. - /// </summary> - public String File - { - get { return this[0].File; } - } - - /// <summary> - /// Source file line number (in backend) reporting the error. PostgreSQL 7.4 and up. - /// </summary> - public String Line - { - get { return this[0].Line; } - } - - /// <summary> - /// Source routine (in backend) reporting the error. PostgreSQL 7.4 and up. - /// </summary> - public String Routine - { - get { return this[0].Routine; } - } - - /// <summary> - /// String containing the sql sent which produced this error. - /// </summary> - public String ErrorSql - { - get { return this[0].ErrorSql; } - } - - - /// <summary> - /// Returns the entire list of errors provided by the PostgreSQL backend. - /// </summary> - public IList Errors - { - get { return errors; } - } - - /// <summary> - /// Format a .NET style exception string. - /// Include all errors in the list, including any hints. - /// </summary> - public override String ToString() - { - if (Errors != null) - { - StringWriter S = new StringWriter(); - - S.WriteLine("{0}:", this.GetType().FullName); - - foreach (NpgsqlError PgError in Errors) - { - AppendString(S, "{0}", PgError.Message); - AppendString(S, "Severity: {0}", PgError.Severity); - AppendString(S, "Code: {0}", PgError.Code); - AppendString(S, "Hint: {0}", PgError.Hint); - } - - S.Write(StackTrace); - - return S.ToString(); - } - - return base.ToString(); - } - - /// <summary> - /// Append a line to the given Stream, first checking for zero-length. - /// </summary> - private static void AppendString(StringWriter Stream, string Format, string Str) - { - if (Str.Length > 0) - { - Stream.WriteLine(Format, Str); - } - } - } -}
\ No newline at end of file + /// <summary> + /// The exception that is thrown when the PostgreSQL backend reports errors. + /// </summary> + [Serializable] + public sealed class NpgsqlException : ApplicationException + { + private IList errors; + + // Logging related values + private static readonly String CLASSNAME = "NpgsqlException"; + private static ResourceManager resman = new ResourceManager(typeof(NpgsqlException)); + + // To allow deserialization. + private NpgsqlException(SerializationInfo info, StreamingContext context) : base(info, context) + { + errors = (IList)info.GetValue("errors", typeof(IList)); + } + + /// <summary> + /// Construct a backend error exception based on a list of one or more + /// backend errors. The basic Exception.Message will be built from the + /// first (usually the only) error in the list. + /// </summary> + internal NpgsqlException(IList errors) : base(((NpgsqlError)errors[0]).ToString()) + { + NpgsqlEventLog.LogMsg(resman, "Log_ExceptionOccured", LogLevel.Normal, Message); + this.errors = new ArrayList(errors); + + } + + + 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) + { + base.GetObjectData(info, context); + + // Add custom data, in this case the list of errors when serializing. + // Thanks Robert Chartier for info: http://www.15seconds.com/issue/020903.htm + + //use the info object to add the items you want serialized + info.AddValue("errors", errors, typeof(IList)); + + + } + + /// <summary> + /// Provide access to the entire list of errors provided by the PostgreSQL backend. + /// </summary> + public NpgsqlError this[Int32 Index] + { + get + { + return (NpgsqlError)errors[Index]; + } + } + + + /// <summary> + /// Severity code. All versions. + /// </summary> + public String Severity + { + get + { + return this[0].Severity; + } + } + + /// <summary> + /// Error code. PostgreSQL 7.4 and up. + /// </summary> + public String Code + { + get + { + return this[0].Code; + } + } + + /// <summary> + /// Basic error message. All versions. + /// </summary> + public String BaseMessage + { + get + { + return this[0].Message; + } + } + + /// <summary> + /// Detailed error message. PostgreSQL 7.4 and up. + /// </summary> + public String Detail + { + get + { + return this[0].Detail; + } + } + + /// <summary> + /// Suggestion to help resolve the error. PostgreSQL 7.4 and up. + /// </summary> + public String Hint + { + get + { + return this[0].Hint; + } + } + + /// <summary> + /// Position (one based) within the query string where the error was encounterd. PostgreSQL 7.4 and up. + /// </summary> + public String Position + { + get + { + return this[0].Position; + } + } + + /// <summary> + /// Trace back information. PostgreSQL 7.4 and up. + /// </summary> + public String Where + { + get + { + return this[0].Where; + } + } + + /// <summary> + /// Source file (in backend) reporting the error. PostgreSQL 7.4 and up. + /// </summary> + public String File + { + get + { + return this[0].File; + } + } + + /// <summary> + /// Source file line number (in backend) reporting the error. PostgreSQL 7.4 and up. + /// </summary> + public String Line + { + get + { + return this[0].Line; + } + } + + /// <summary> + /// Source routine (in backend) reporting the error. PostgreSQL 7.4 and up. + /// </summary> + public String Routine + { + get + { + return this[0].Routine; + } + } + + /// <summary> + /// String containing the sql sent which produced this error. + /// </summary> + public String ErrorSql + { + get + { + return this[0].ErrorSql; + } + } + + + /// <summary> + /// Returns the entire list of errors provided by the PostgreSQL backend. + /// </summary> + public IList Errors + { + get + { + return errors; + } + } + + /// <summary> + /// Format a .NET style exception string. + /// Include all errors in the list, including any hints. + /// </summary> + public override String ToString() + { + + if (Errors != null) + { + StringWriter S = new StringWriter(); + + S.WriteLine("{0}:", this.GetType().FullName); + + foreach (NpgsqlError PgError in Errors) + { + AppendString(S, "{0}", PgError.Message); + AppendString(S, "Severity: {0}", PgError.Severity); + AppendString(S, "Code: {0}", PgError.Code); + AppendString(S, "Hint: {0}", PgError.Hint); + } + + S.Write(StackTrace); + + return S.ToString(); + } + + return base.ToString(); + + } + + /// <summary> + /// Append a line to the given Stream, first checking for zero-length. + /// </summary> + private static void AppendString(StringWriter Stream, string Format, string Str) + { + if (Str.Length > 0) + { + Stream.WriteLine(Format, Str); + } + } + + } + +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlExecute.cs b/mcs/class/Npgsql/Npgsql/NpgsqlExecute.cs index d06c3c351a5..8b9f3e7fd2f 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlExecute.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlExecute.cs @@ -8,58 +8,68 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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 Parse message sent to PostgreSQL - /// server. - /// </summary> - /// - internal sealed class NpgsqlExecute : ClientMessage - { - private readonly String _portalName; - private readonly Int32 _maxRows; + /// <summary> + /// This class represents the Parse message sent to PostgreSQL + /// server. + /// </summary> + /// + internal sealed class NpgsqlExecute + { + // Logging related values + private static readonly String CLASSNAME = "NpgsqlExecute"; + + private String _portalName; + private Int32 _maxRows; + + + public NpgsqlExecute(String portalName, Int32 maxRows) + { + _portalName = portalName; + _maxRows = maxRows; + } + + public String PortalName + { + get + { + return _portalName; + } + } + + public void WriteToStream(Stream outputStream, Encoding encoding) + { + outputStream.WriteByte((Byte)'E'); - public NpgsqlExecute(String portalName, Int32 maxRows) - { - _portalName = portalName; - _maxRows = maxRows; - } + PGUtil.WriteInt32(outputStream, 4 + + encoding.GetByteCount(_portalName) + 1 + + 4); - public String PortalName - { - get { return _portalName; } - } + PGUtil.WriteString(_portalName, outputStream, encoding); + PGUtil.WriteInt32(outputStream, _maxRows); - public override void WriteToStream(Stream outputStream) - { - outputStream.WriteByte((byte) FrontEndMessageCode.Execute); + } - PGUtil.WriteInt32(outputStream, 4 + UTF8Encoding.GetByteCount(_portalName) + 1 + 4); + } +} - PGUtil.WriteString(_portalName, outputStream); - PGUtil.WriteInt32(outputStream, _maxRows); - } - } -}
\ No newline at end of file diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlFlush.cs b/mcs/class/Npgsql/Npgsql/NpgsqlFlush.cs index 349842f3da0..4a97a509304 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlFlush.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlFlush.cs @@ -9,42 +9,45 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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 Parse message sent to PostgreSQL - /// server. - /// </summary> - /// - internal sealed class NpgsqlFlush : ClientMessage - { - // Logging related values - //private static readonly String CLASSNAME = "NpgsqlFlush"; - - public override void WriteToStream(Stream outputStream) - { - outputStream.WriteByte((byte) FrontEndMessageCode.Flush); - - PGUtil.WriteInt32(outputStream, 4); - } - } -}
\ No newline at end of file + + /// <summary> + /// This class represents the Parse message sent to PostgreSQL + /// server. + /// </summary> + /// + internal sealed class NpgsqlFlush + { + // Logging related values + private static readonly String CLASSNAME = "NpgsqlFlush"; + + public void WriteToStream(Stream outputStream, Encoding encoding) + { + outputStream.WriteByte((Byte)'H'); + + PGUtil.WriteInt32(outputStream, 4); + + } + + } +} + diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlMediator.cs b/mcs/class/Npgsql/Npgsql/NpgsqlMediator.cs index 6e1881cfc11..7a44c4d2613 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlMediator.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlMediator.cs @@ -9,98 +9,253 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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; +using System.Collections; +using System.Collections.Specialized; namespace Npgsql { - ///<summary> - /// This class is responsible for serving as bridge between the backend - /// protocol handling and the core classes. It is used as the mediator for - /// exchanging data generated/sent from/to backend. - /// </summary> - /// - internal sealed class NpgsqlMediator - { - // Stream for user to exchange COPY data - private Stream _copyStream; - // Size of data chunks read from user stream and written to server in COPY IN - private int _copyBufferSize = 8192; - // Very temporary holder of data received during COPY OUT - private byte[] _receivedCopyData; - - - // - // Responses collected from the backend. - // - private String _sqlSent; - private Int32 _commandTimeout; - - - public NpgsqlMediator() - { - _sqlSent = String.Empty; - _commandTimeout = 20; - } - - public void ResetResponses() - { - _sqlSent = String.Empty; - _commandTimeout = 20; - } - - public String SqlSent - { - set { _sqlSent = value; } - - get { return _sqlSent; } - } - - public Int32 CommandTimeout - { - set { _commandTimeout = value; } - - get { return _commandTimeout; } - } - - public Stream CopyStream - { - get { return _copyStream; } - set { _copyStream = value; } - } - - public int CopyBufferSize - { - get { return _copyBufferSize; } - set { _copyBufferSize = value; } - } - - public byte[] ReceivedCopyData - { - get - { - byte[] result = _receivedCopyData; - _receivedCopyData = null; - return result; - } - set { _receivedCopyData = value; } - } - } -}
\ No newline at end of file + ///<summary> + /// This class is responsible for serving as bridge between the backend + /// protocol handling and the core classes. It is used as the mediator for + /// exchanging data generated/sent from/to backend. + /// </summary> + /// + internal sealed class NpgsqlMediator + { + // + // Expectations that depend on context. + // Non-default values must be set before collecting responses. + // + // Some kinds of messages only get one response, and do not + // expect a ready_for_query response. + private bool _require_ready_for_query; + + // + // Responses collected from the backend. + // + private ArrayList _errors; + private ArrayList _notices; + private ArrayList _resultSets; + private ArrayList _responses; + private ArrayList _notifications; + private ListDictionary _parameters; + private NpgsqlBackEndKeyData _backend_key_data; + private NpgsqlRowDescription _rd; + private ArrayList _rows; + private String _sqlSent; + private Int32 _commandTimeout; + + + public NpgsqlMediator() + { + _require_ready_for_query = true; + + _errors = new ArrayList(); + _notices = new ArrayList(); + _resultSets = new ArrayList(); + _responses = new ArrayList(); + _notifications = new ArrayList(); + _parameters = new ListDictionary(CaseInsensitiveComparer.Default); + _backend_key_data = null; + _sqlSent = String.Empty; + _commandTimeout = 20; + } + + public void ResetExpectations() + { + _require_ready_for_query = true; + } + + public void ResetResponses() + { + _errors.Clear(); + _notices.Clear(); + _resultSets.Clear(); + _responses.Clear(); + _notifications.Clear(); + _parameters.Clear(); + _backend_key_data = null; + _sqlSent = String.Empty; + _commandTimeout = 20; + } + + + + + public Boolean RequireReadyForQuery + { + get + { + return _require_ready_for_query; + } + set + { + _require_ready_for_query = value; + } + } + + + + public NpgsqlRowDescription LastRowDescription + { + get + { + return _rd; + } + } + + public ArrayList ResultSets + { + get + { + return _resultSets; + } + } + + public ArrayList CompletedResponses + { + get + { + return _responses; + } + } + + public ArrayList Errors + { + get + { + return _errors; + } + } + + public ArrayList Notices + { + get + { + return _notices; + } + } + + public ArrayList Notifications + { + get + { + return _notifications; + } + } + + public ListDictionary Parameters + { + get + { + return _parameters; + } + } + + public NpgsqlBackEndKeyData BackendKeyData + { + get + { + return _backend_key_data; + } + } + + public String SqlSent + { + set + { + _sqlSent = value; + } + + get + { + return _sqlSent; + } + } + + public Int32 CommandTimeout + { + set + { + _commandTimeout = value; + } + + get + { + return _commandTimeout; + } + + } + + public void AddNotification(NpgsqlNotificationEventArgs data) + { + _notifications.Add(data); + } + + public void AddCompletedResponse(String response) + { + if (_rd != null) + { + // Finished receiving the resultset. Add it to the buffer. + _resultSets.Add(new NpgsqlResultSet(_rd, _rows)); + + // Add a placeholder response. + _responses.Add(null); + + // Discard the RowDescription. + _rd = null; + } + else + { + // Add a placeholder resultset. + _resultSets.Add(null); + // It was just a non query string. Just add the response. + _responses.Add(response); + } + + } + + public void AddRowDescription(NpgsqlRowDescription rowDescription) + { + _rd = rowDescription; + _rows = new ArrayList(); + } + + public void AddAsciiRow(NpgsqlAsciiRow asciiRow) + { + _rows.Add(asciiRow); + } + + public void AddBinaryRow(NpgsqlBinaryRow binaryRow) + { + _rows.Add(binaryRow); + } + + + public void SetBackendKeydata(NpgsqlBackEndKeyData keydata) + { + _backend_key_data = keydata; + } + + public void AddParameterStatus(String Key, NpgsqlParameterStatus PS) + { + _parameters[Key] = PS; + } + } +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlMessageTypes.cs b/mcs/class/Npgsql/Npgsql/NpgsqlMessageTypes.cs index fd031ce2cf4..b223053f8f8 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlMessageTypes.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlMessageTypes.cs @@ -8,40 +8,136 @@ // http://gborg.postgresql.org/project/npgsql/projdisplay.php // // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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; namespace Npgsql { - public enum FrontEndMessageCode :byte - { - StartupPacket = (byte) ' ', - Termination = (byte) 'X', - CopyFail = (byte) 'f', - CopyData = (byte) 'd', - CopyDone = (byte) 'c', - Flush = (byte) 'H', - Query = (byte) 'Q', - Parse = (byte) 'P', - Bind = (byte) 'B', - Execute = (byte) 'E', - Describe = (byte) 'D', - Close = (byte) 'C', - Sync = (byte) 'S' - } -}
\ No newline at end of file + /// <summary> + /// Class NpgsqlMessageTypes_Ver_2. + /// Defines PG frontend/backend protocol message types and parameters used in protocol version 2. + /// </summary> + internal sealed class NpgsqlMessageTypes_Ver_2 + { + private NpgsqlMessageTypes_Ver_2() + {} + + public const Char StartupPacket = ' '; + public const Char Terminate = 'X'; + + public const Char AsciiRow = 'D'; + public const Char BinaryRow = 'B'; + + public const Char AuthenticationRequest = 'R'; + // specific Authentication request types + public const Int32 AuthenticationOk = 0; + public const Int32 AuthenticationKerberosV4 = 1; + public const Int32 AuthenticationKerberosV5 = 2; + public const Int32 AuthenticationClearTextPassword = 3; + public const Int32 AuthenticationCryptPassword = 4; + public const Int32 AuthenticationMD5Password = 5; + public const Int32 AuthenticationSCMCredential = 6; + + public const Char BackendKeyData = 'K'; + public const Char CancelRequest = 'F'; + public const Char CompletedResponse = 'C'; + public const Char CopyDataRows = ' '; + public const Char CopyInResponse = 'G'; + public const Char CopyOutResponse = 'H'; + public const Char CursorResponse = 'P'; + public const Char EmptyQueryResponse = 'I'; + public const Char ErrorResponse = 'E'; + public const Char FunctionCall = 'F'; + + public const Char FunctionResultResponse = 'V'; + // specific function result responses + public const Char FunctionResultNonEmptyResponse = 'G'; + public const Char FunctionResultVoidResponse = '0'; + + public const Char NoticeResponse = 'N'; + public const Char NotificationResponse = 'A'; + public const Char PasswordPacket = ' '; + public const Char Query = 'Q'; + public const Char ReadyForQuery = 'Z'; + public const Char RowDescription = 'T'; + public const Char SSLRequest = ' '; + } + + + /// <summary> + /// Class NpgsqlMessageTypes_Ver_3. + /// Defines PG frontend/backend protocol message types and parameters used in protocol version 3. + /// </summary> + internal sealed class NpgsqlMessageTypes_Ver_3 + { + private NpgsqlMessageTypes_Ver_3() + {} + + public const Char StartupPacket = ' '; + public const Char Termination = 'X'; + + public const Char DataRow = 'D'; + + public const Char AuthenticationRequest = 'R'; + // specific Authentication request types + public const Int32 AuthenticationOk = 0; + public const Int32 AuthenticationKerberosV4 = 1; + public const Int32 AuthenticationKerberosV5 = 2; + public const Int32 AuthenticationClearTextPassword = 3; + public const Int32 AuthenticationCryptPassword = 4; + public const Int32 AuthenticationMD5Password = 5; + public const Int32 AuthenticationSCMCredential = 6; + + public const Char BackendKeyData = 'K'; + public const Char CancelRequest = 'F'; + public const Char CompletedResponse = 'C'; + public const Char CopyDataRows = ' '; + public const Char CopyInResponse = 'G'; + public const Char CopyOutResponse = 'H'; + public const Char EmptyQueryResponse = 'I'; + public const Char ErrorResponse = 'E'; + public const Char FunctionCall = 'F'; + public const Char FunctionCallResponse = 'V'; + + public const Char NoticeResponse = 'N'; + public const Char NotificationResponse = 'A'; + public const Char ParameterStatus = 'S'; + public const Char PasswordPacket = ' '; + public const Char Query = 'Q'; + public const Char ReadyForQuery = 'Z'; + public const Char RowDescription = 'T'; + public const Char SSLRequest = ' '; + + // extended query frontend messages + public const Char Parse = 'P'; + public const Char Bind = 'B'; + public const Char Execute = 'E'; + public const Char Describe = 'D'; + public const Char Close = 'C'; + public const Char Flush = 'H'; + public const Char Sync = 'S'; + + // extended query backend messages + public const Char ParseComplete = '1'; + public const Char BindComplete = '2'; + public const Char PortalSuspended = 's'; + public const Char ParameterDescription = 't'; + public const Char NoData = 'n'; + public const Char CloseComplete = '3'; + + } +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlNotificationEventArgs.cs b/mcs/class/Npgsql/Npgsql/NpgsqlNotificationEventArgs.cs index d6453c7308a..f0e90534a08 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlNotificationEventArgs.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlNotificationEventArgs.cs @@ -8,54 +8,44 @@ // http://gborg.postgresql.org/project/npgsql/projdisplay.php // // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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; namespace Npgsql { - /// <summary> - /// EventArgs class to send Notification parameters. - /// </summary> - public class NpgsqlNotificationEventArgs : EventArgs - { - /// <summary> - /// Process ID of the PostgreSQL backend that sent this notification. - /// </summary> - public readonly int PID; - - /// <summary> - /// Condition that triggered that notification. - /// </summary> - public readonly string Condition; + /// <summary> + /// EventArgs class to send Notification parameters. + /// </summary> + public class NpgsqlNotificationEventArgs : EventArgs + { + /// <summary> + /// Process ID of the PostgreSQL backend that sent this notification. + /// </summary> + public Int32 PID = 0; - /// <summary> - /// Additional Information From Notifiying Process (for future use, currently postgres always sets this to an empty string) - /// </summary> - public readonly string AdditionalInformation; + /// <summary> + /// Condition that triggered that notification. + /// </summary> + public String Condition = null; - internal NpgsqlNotificationEventArgs(Stream stream, bool readAdditional) - { - PID = PGUtil.ReadInt32(stream); - Condition = PGUtil.ReadString(stream); - AdditionalInformation = readAdditional ? PGUtil.ReadString(stream) : string.Empty; - } - } -}
\ No newline at end of file + internal NpgsqlNotificationEventArgs(Int32 nPID, String nCondition) + { + PID = nPID; + Condition = nCondition; + } + } +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlParameter.cs b/mcs/class/Npgsql/Npgsql/NpgsqlParameter.cs index 94debbd8321..af0271f03d3 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlParameter.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlParameter.cs @@ -3,91 +3,87 @@ // Npgsql.NpgsqlParameter.cs // // Author: -// Francisco Jr. (fxjrlists@yahoo.com.br) +// 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 +// Copyright (C) 2002 The Npgsql Development Team +// npgsql-general@gborg.postgresql.org +// http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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.ComponentModel; using System.Data; -using System.Data.Common; -using System.Resources; +using System.ComponentModel; using NpgsqlTypes; #if WITHDESIGN using Npgsql.Design; #endif + namespace Npgsql { ///<summary> /// This class represents a parameter to a command that will be sent to server ///</summary> -#if WITHDESIGN + #if WITHDESIGN [TypeConverter(typeof(NpgsqlParameterConverter))] -#endif - - public sealed class NpgsqlParameter : DbParameter, ICloneable + #endif + + public sealed class NpgsqlParameter : MarshalByRefObject, IDbDataParameter, IDataParameter, ICloneable { + // Logging related values private static readonly String CLASSNAME = "NpgsqlParameter"; // Fields to implement IDbDataParameter interface. - private byte precision = 0; - private byte scale = 0; - private Int32 size = 0; + private byte precision = 0; + private byte scale = 0; + private Int32 size = 0; // Fields to implement IDataParameter - //private NpgsqlDbType npgsqldb_type = NpgsqlDbType.Text; + //private NpgsqlDbType npgsqldb_type = NpgsqlDbType.Text; //private DbType db_type = DbType.String; - private NpgsqlNativeTypeInfo type_info; - private ParameterDirection direction = ParameterDirection.Input; - private Boolean is_nullable = false; - private String m_Name = String.Empty; - private String source_column = String.Empty; - private DataRowVersion source_version = DataRowVersion.Current; - private Object value = DBNull.Value; - private Boolean sourceColumnNullMapping; - private readonly ResourceManager resman; - - private Boolean useCast = false; - + private NpgsqlNativeTypeInfo type_info; + private ParameterDirection direction = ParameterDirection.Input; + private Boolean is_nullable = false; + private String name = String.Empty; + private String source_column = String.Empty; + private DataRowVersion source_version = DataRowVersion.Current; + private Object value = DBNull.Value; + private System.Resources.ResourceManager resman; + + /// <summary> + /// Initializes a new instance of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> class. /// </summary> public NpgsqlParameter() { - resman = new ResourceManager(this.GetType()); + resman = new System.Resources.ResourceManager(this.GetType()); NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME); //type_info = NpgsqlTypesHelper.GetNativeTypeInfo(typeof(String)); } /// <summary> /// Initializes a new instance of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> - /// class with the parameter m_Name and a value of the new <b>NpgsqlParameter</b>. + /// class with the parameter name and a value of the new <b>NpgsqlParameter</b>. /// </summary> - /// <param m_Name="parameterName">The m_Name of the parameter to map.</param> - /// <param m_Name="value">An <see cref="System.Object">Object</see> that is the value of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see>.</param> + /// <param name="parameterName">The name of the parameter to map.</param> + /// <param name="value">An <see cref="System.Object">Object</see> that is the value of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see>.</param> /// <remarks> /// <p>When you specify an <see cref="System.Object">Object</see> /// in the value parameter, the <see cref="System.Data.DbType">DbType</see> is @@ -98,115 +94,132 @@ namespace Npgsql /// </remarks> public NpgsqlParameter(String parameterName, object value) { - resman = new ResourceManager(this.GetType()); + resman = new System.Resources.ResourceManager(this.GetType()); NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME, parameterName, value); this.ParameterName = parameterName; this.value = value; - if ((this.value == null) || (this.value == DBNull.Value)) + if ((this.value == null) || (this.value == DBNull.Value) ) { // don't really know what to do - leave default and do further exploration // Default type for null values is String. this.value = DBNull.Value; type_info = NpgsqlTypesHelper.GetNativeTypeInfo(typeof(String)); + return; } - else if (!NpgsqlTypesHelper.TryGetNativeTypeInfo(value.GetType(), out type_info)) + else { - throw new InvalidCastException(String.Format(resman.GetString("Exception_ImpossibleToCast"), value.GetType())); + type_info = NpgsqlTypesHelper.GetNativeTypeInfo(value.GetType()); + if (type_info == null) + { + throw new InvalidCastException(String.Format(resman.GetString("Exception_ImpossibleToCast"), value.GetType())); + } + + + } } - + + /// <summary> + /// Internal constructor to handle parameter creation from CommandBuilder passing a NpgsqlNativeTypeInfo directly. + /// </summary> + internal NpgsqlParameter(String parameterName, NpgsqlNativeTypeInfo type_info) + { + resman = new System.Resources.ResourceManager(this.GetType()); + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME, parameterName, value, type_info); + + this.ParameterName = parameterName; + this.value = DBNull.Value; + + this.type_info = (type_info == null) ? NpgsqlTypesHelper.GetNativeTypeInfo(typeof(String)) : type_info; + } + /// <summary> /// Initializes a new instance of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> - /// class with the parameter m_Name and the data type. + /// class with the parameter name and the data type. /// </summary> - /// <param m_Name="parameterName">The m_Name of the parameter to map.</param> - /// <param m_Name="parameterType">One of the <see cref="System.Data.DbType">DbType</see> values.</param> - public NpgsqlParameter(String parameterName, NpgsqlDbType parameterType) - : this(parameterName, parameterType, 0, String.Empty) - { - } + /// <param name="parameterName">The name of the parameter to map.</param> + /// <param name="parameterType">One of the <see cref="System.Data.DbType">DbType</see> values.</param> + public NpgsqlParameter(String parameterName, NpgsqlDbType parameterType) : this(parameterName, parameterType, 0, String.Empty) + {} - public NpgsqlParameter(String parameterName, DbType parameterType) - : this(parameterName, NpgsqlTypesHelper.GetNativeTypeInfo(parameterType).NpgsqlDbType, 0, String.Empty) - { - } + public NpgsqlParameter(String parameterName, DbType parameterType) : this(parameterName, NpgsqlTypesHelper.GetNativeTypeInfo(parameterType).NpgsqlDbType, 0, String.Empty) + {} /// <summary> /// Initializes a new instance of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> - /// class with the parameter m_Name, the <see cref="System.Data.DbType">DbType</see>, and the size. + /// class with the parameter name, the <see cref="System.Data.DbType">DbType</see>, and the size. /// </summary> - /// <param m_Name="parameterName">The m_Name of the parameter to map.</param> - /// <param m_Name="parameterType">One of the <see cref="System.Data.DbType">DbType</see> values.</param> - /// <param m_Name="size">The length of the parameter.</param> - public NpgsqlParameter(String parameterName, NpgsqlDbType parameterType, Int32 size) - : this(parameterName, parameterType, size, String.Empty) - { - } + /// <param name="parameterName">The name of the parameter to map.</param> + /// <param name="parameterType">One of the <see cref="System.Data.DbType">DbType</see> values.</param> + /// <param name="size">The length of the parameter.</param> + public NpgsqlParameter(String parameterName, NpgsqlDbType parameterType, Int32 size) : this(parameterName, parameterType, size, String.Empty) + {} - public NpgsqlParameter(String parameterName, DbType parameterType, Int32 size) - : this(parameterName, NpgsqlTypesHelper.GetNativeTypeInfo(parameterType).NpgsqlDbType, size, String.Empty) - { - } + public NpgsqlParameter(String parameterName, DbType parameterType, Int32 size) : this(parameterName, NpgsqlTypesHelper.GetNativeTypeInfo(parameterType).NpgsqlDbType, size, String.Empty) + {} /// <summary> /// Initializes a new instance of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> - /// class with the parameter m_Name, the <see cref="System.Data.DbType">DbType</see>, the size, - /// and the source column m_Name. + /// class with the parameter name, the <see cref="System.Data.DbType">DbType</see>, the size, + /// and the source column name. /// </summary> - /// <param m_Name="parameterName">The m_Name of the parameter to map.</param> - /// <param m_Name="parameterType">One of the <see cref="System.Data.DbType">DbType</see> values.</param> - /// <param m_Name="size">The length of the parameter.</param> - /// <param m_Name="sourceColumn">The m_Name of the source column.</param> + /// <param name="parameterName">The name of the parameter to map.</param> + /// <param name="parameterType">One of the <see cref="System.Data.DbType">DbType</see> values.</param> + /// <param name="size">The length of the parameter.</param> + /// <param name="sourceColumn">The name of the source column.</param> public NpgsqlParameter(String parameterName, NpgsqlDbType parameterType, Int32 size, String sourceColumn) { - resman = new ResourceManager(this.GetType()); + + resman = new System.Resources.ResourceManager(this.GetType()); NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME, parameterName, parameterType, size, source_column); this.ParameterName = parameterName; - - NpgsqlDbType = parameterType; //Allow the setter to catch any exceptions. + + type_info = NpgsqlTypesHelper.GetNativeTypeInfo(parameterType); + if (type_info == null) + throw new InvalidCastException(String.Format(resman.GetString("Exception_ImpossibleToCast"), parameterType)); this.size = size; source_column = sourceColumn; - } - public NpgsqlParameter(String parameterName, DbType parameterType, Int32 size, String sourceColumn) - : this(parameterName, NpgsqlTypesHelper.GetNativeTypeInfo(parameterType).NpgsqlDbType, size, sourceColumn) - { + } + public NpgsqlParameter(String parameterName, DbType parameterType, Int32 size, String sourceColumn) : this(parameterName, NpgsqlTypesHelper.GetNativeTypeInfo(parameterType).NpgsqlDbType, size, sourceColumn) + {} + + /// <summary> /// Initializes a new instance of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> - /// class with the parameter m_Name, the <see cref="System.Data.DbType">DbType</see>, the size, - /// the source column m_Name, a <see cref="System.Data.ParameterDirection">ParameterDirection</see>, + /// class with the parameter name, the <see cref="System.Data.DbType">DbType</see>, the size, + /// the source column name, a <see cref="System.Data.ParameterDirection">ParameterDirection</see>, /// the precision of the parameter, the scale of the parameter, a /// <see cref="System.Data.DataRowVersion">DataRowVersion</see> to use, and the /// value of the parameter. /// </summary> - /// <param m_Name="parameterName">The m_Name of the parameter to map.</param> - /// <param m_Name="parameterType">One of the <see cref="System.Data.DbType">DbType</see> values.</param> - /// <param m_Name="size">The length of the parameter.</param> - /// <param m_Name="sourceColumn">The m_Name of the source column.</param> - /// <param m_Name="direction">One of the <see cref="System.Data.ParameterDirection">ParameterDirection</see> values.</param> - /// <param m_Name="isNullable"><b>true</b> if the value of the field can be null, otherwise <b>false</b>.</param> - /// <param m_Name="precision">The total number of digits to the left and right of the decimal point to which + /// <param name="parameterName">The name of the parameter to map.</param> + /// <param name="parameterType">One of the <see cref="System.Data.DbType">DbType</see> values.</param> + /// <param name="size">The length of the parameter.</param> + /// <param name="sourceColumn">The name of the source column.</param> + /// <param name="direction">One of the <see cref="System.Data.ParameterDirection">ParameterDirection</see> values.</param> + /// <param name="isNullable"><b>true</b> if the value of the field can be null, otherwise <b>false</b>.</param> + /// <param name="precision">The total number of digits to the left and right of the decimal point to which /// <see cref="Npgsql.NpgsqlParameter.Value">Value</see> is resolved.</param> - /// <param m_Name="scale">The total number of decimal places to which + /// <param name="scale">The total number of decimal places to which /// <see cref="Npgsql.NpgsqlParameter.Value">Value</see> is resolved.</param> - /// <param m_Name="sourceVersion">One of the <see cref="System.Data.DataRowVersion">DataRowVersion</see> values.</param> - /// <param m_Name="value">An <see cref="System.Object">Object</see> that is the value + /// <param name="sourceVersion">One of the <see cref="System.Data.DataRowVersion">DataRowVersion</see> values.</param> + /// <param name="value">An <see cref="System.Object">Object</see> that is the value /// of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see>.</param> - public NpgsqlParameter(String parameterName, NpgsqlDbType parameterType, Int32 size, String sourceColumn, - ParameterDirection direction, bool isNullable, byte precision, byte scale, - DataRowVersion sourceVersion, object value) + public NpgsqlParameter (String parameterName, NpgsqlDbType parameterType, Int32 size, String sourceColumn, ParameterDirection direction, bool isNullable, byte precision, byte scale, DataRowVersion sourceVersion, object value) { - resman = new ResourceManager(this.GetType()); + + resman = new System.Resources.ResourceManager(this.GetType()); this.ParameterName = parameterName; this.Size = size; @@ -225,19 +238,16 @@ namespace Npgsql } else { - NpgsqlDbType = parameterType; //allow the setter to catch exceptions if necessary. + type_info = NpgsqlTypesHelper.GetNativeTypeInfo(parameterType); + if (type_info == null) + throw new InvalidCastException(String.Format(resman.GetString("Exception_ImpossibleToCast"), parameterType)); } - } - public NpgsqlParameter(String parameterName, DbType parameterType, Int32 size, String sourceColumn, - ParameterDirection direction, bool isNullable, byte precision, byte scale, - DataRowVersion sourceVersion, object value) - : this( - parameterName, NpgsqlTypesHelper.GetNativeTypeInfo(parameterType).NpgsqlDbType, size, sourceColumn, direction, - isNullable, precision, scale, sourceVersion, value) - { } + public NpgsqlParameter (String parameterName, DbType parameterType, Int32 size, String sourceColumn, ParameterDirection direction, bool isNullable, byte precision, byte scale, DataRowVersion sourceVersion, object value) : this(parameterName, NpgsqlTypesHelper.GetNativeTypeInfo(parameterType).NpgsqlDbType, size, sourceColumn, direction, isNullable, precision, scale, sourceVersion, value) + {} + // Implementation of IDbDataParameter /// <summary> /// Gets or sets the maximum number of digits used to represent the @@ -262,23 +272,6 @@ namespace Npgsql precision = value; } } - - - public Boolean UseCast - { - get - { - //return useCast; //&& (value != DBNull.Value); - // This check for Datetime.minvalue and maxvalue is needed in order to - // workaround a problem when comparing date values with infinity. - // This is a known issue with postgresql and it is reported here: - // http://archives.postgresql.org/pgsql-general/2008-10/msg00535.php - // Josh's solution to add cast is documented here: - // http://pgfoundry.org/forum/message.php?msg_id=1004118 - - return useCast || DateTime.MinValue.Equals(value) || DateTime.MinValue.Equals(value); - } - } /// <summary> /// Gets or sets the number of decimal places to which @@ -308,7 +301,7 @@ namespace Npgsql /// <value>The maximum size, in bytes, of the data within the column. /// The default value is inferred from the parameter value.</value> [Category("Data"), DefaultValue(0)] - public override Int32 Size + public Int32 Size { get { @@ -328,24 +321,22 @@ namespace Npgsql /// </summary> /// <value>One of the <see cref="System.Data.DbType">DbType</see> values. The default is <b>String</b>.</value> [Category("Data"), RefreshProperties(RefreshProperties.All), DefaultValue(DbType.String)] - public override DbType DbType + public DbType DbType { get { NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "DbType"); return TypeInfo.DbType; - } // [TODO] Validate data type. + } + + // [TODO] Validate data type. set { - NpgsqlEventLog.LogPropertySet(LogLevel.Normal, CLASSNAME, "DbType", value); - - useCast = value != DbType.Object; - - if (!NpgsqlTypesHelper.TryGetNativeTypeInfo(value, out type_info)) - { + type_info = NpgsqlTypesHelper.GetNativeTypeInfo(value); + if (type_info == null) throw new InvalidCastException(String.Format(resman.GetString("Exception_ImpossibleToCast"), value)); - } + } } @@ -358,34 +349,30 @@ namespace Npgsql { get { - NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "NpgsqlDbType"); + NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "DbType"); return TypeInfo.NpgsqlDbType; - } // [TODO] Validate data type. + } + + // [TODO] Validate data type. set { - NpgsqlEventLog.LogPropertySet(LogLevel.Normal, CLASSNAME, "NpgsqlDbType", value); - useCast = true; - if (value == NpgsqlDbType.Array) - { - throw new ArgumentOutOfRangeException(resman.GetString("Exception_ParameterTypeIsOnlyArray")); - } - if (!NpgsqlTypesHelper.TryGetNativeTypeInfo(value, out type_info)) - { + NpgsqlEventLog.LogPropertySet(LogLevel.Normal, CLASSNAME, "DbType", value); + type_info = NpgsqlTypesHelper.GetNativeTypeInfo(value); + if (type_info == null) throw new InvalidCastException(String.Format(resman.GetString("Exception_ImpossibleToCast"), value)); - } + } } + internal NpgsqlNativeTypeInfo TypeInfo { get { - if (type_info == null) - { - type_info = NpgsqlTypesHelper.GetNativeTypeInfo(typeof(String)); - } + if (type_info == null) + type_info = NpgsqlTypesHelper.GetNativeTypeInfo(typeof(String)); return type_info; } } @@ -397,7 +384,7 @@ namespace Npgsql /// <value>One of the <see cref="System.Data.ParameterDirection">ParameterDirection</see> /// values. The default is <b>Input</b>.</value> [Category("Data"), DefaultValue(ParameterDirection.Input)] - public override ParameterDirection Direction + public ParameterDirection Direction { get { @@ -416,12 +403,12 @@ namespace Npgsql /// Gets or sets a value indicating whether the parameter accepts null values. /// </summary> /// <value><b>true</b> if null values are accepted; otherwise, <b>false</b>. The default is <b>false</b>.</value> - -#if WITHDESIGN + + #if WITHDESIGN [EditorBrowsable(EditorBrowsableState.Advanced), Browsable(false), DefaultValue(false), DesignOnly(true)] -#endif - - public override Boolean IsNullable + #endif + + public Boolean IsNullable { get { @@ -437,60 +424,40 @@ namespace Npgsql } /// <summary> - /// Gets or sets the m_Name of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see>. + /// Gets or sets the name of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see>. /// </summary> - /// <value>The m_Name of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see>. + /// <value>The name of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see>. /// The default is an empty string.</value> [DefaultValue("")] - public override String ParameterName + public String ParameterName { get { NpgsqlEventLog.LogPropertyGet(LogLevel.Normal, CLASSNAME, "ParameterName"); - return m_Name; + return name; } set { - m_Name = value; + name = value; if (value == null) - { - m_Name = String.Empty; - } - // no longer prefix with : so that the m_Name returned is the m_Name set - - m_Name = m_Name.Trim(); - - NpgsqlEventLog.LogPropertySet(LogLevel.Normal, CLASSNAME, "ParameterName", m_Name); - } - } - - /// <summary> - /// The m_Name scrubbed of any optional marker - /// </summary> - internal string CleanName - { - get - { - string name = ParameterName; - if (name[0] == ':' || name[0] == '@') - { - return name.Length > 1 ? name.Substring(1) : string.Empty; - } - return name; + name = String.Empty; + if ( (name.Equals(String.Empty)) || ((name[0] != ':') && (name[0] != '@')) ) + name = ':' + name; + NpgsqlEventLog.LogPropertySet(LogLevel.Normal, CLASSNAME, "ParameterName", value); } } /// <summary> - /// Gets or sets the m_Name of the source column that is mapped to the + /// Gets or sets the name of the source column that is mapped to the /// <see cref="System.Data.DataSet">DataSet</see> and used for loading or /// returning the <see cref="Npgsql.NpgsqlParameter.Value">Value</see>. /// </summary> - /// <value>The m_Name of the source column that is mapped to the + /// <value>The name of the source column that is mapped to the /// <see cref="System.Data.DataSet">DataSet</see>. The default is an empty string.</value> [Category("Data"), DefaultValue("")] - public override String SourceColumn + public String SourceColumn { get { @@ -512,7 +479,7 @@ namespace Npgsql /// <value>One of the <see cref="System.Data.DataRowVersion">DataRowVersion</see> values. /// The default is <b>Current</b>.</value> [Category("Data"), DefaultValue(DataRowVersion.Current)] - public override DataRowVersion SourceVersion + public DataRowVersion SourceVersion { get { @@ -533,69 +500,53 @@ namespace Npgsql /// <value>An <see cref="System.Object">Object</see> that is the value of the parameter. /// The default value is null.</value> [TypeConverter(typeof(StringConverter)), Category("Data")] - public override Object Value + public Object Value { get { NpgsqlEventLog.LogPropertyGet(LogLevel.Normal, CLASSNAME, "Value"); return value; - } // [TODO] Check and validate data type. + } + + // [TODO] Check and validate data type. set { NpgsqlEventLog.LogPropertySet(LogLevel.Normal, CLASSNAME, "Value", value); this.value = value; - if ((this.value == null) || (this.value == DBNull.Value)) + if ((this.value == null) || (this.value == DBNull.Value) ) { // don't really know what to do - leave default and do further exploration // Default type for null values is String. this.value = DBNull.Value; if (type_info == null) - { type_info = NpgsqlTypesHelper.GetNativeTypeInfo(typeof(String)); - } + } - else if (type_info == null && !NpgsqlTypesHelper.TryGetNativeTypeInfo(value.GetType(), out type_info)) + else { - throw new InvalidCastException(String.Format(resman.GetString("Exception_ImpossibleToCast"), value.GetType())); + if (type_info == null) + { + type_info = NpgsqlTypesHelper.GetNativeTypeInfo(value.GetType()); + if (type_info == null) + throw new InvalidCastException(String.Format(resman.GetString("Exception_ImpossibleToCast"), value.GetType())); + + } + } } } - public override void ResetDbType() - { - type_info = NpgsqlTypesHelper.GetNativeTypeInfo(typeof(String)); - } - - public override bool SourceColumnNullMapping - { - get { return sourceColumnNullMapping; } - set { sourceColumnNullMapping = value; } - } - /// <summary> /// Creates a new <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> that /// is a copy of the current instance. /// </summary> /// <returns>A new <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> that is a copy of this instance.</returns> - object ICloneable.Clone() + object System.ICloneable.Clone() { - // use fields instead of properties - // to avoid auto-initializing something like type_info - NpgsqlParameter clone = new NpgsqlParameter(); - clone.precision = precision; - clone.scale = scale; - clone.size = size; - clone.type_info = type_info; - clone.direction = direction; - clone.is_nullable = is_nullable; - clone.m_Name = m_Name; - clone.source_column = source_column; - clone.source_version = source_version; - clone.value = value; - clone.sourceColumnNullMapping = sourceColumnNullMapping; - - return clone; + return new NpgsqlParameter(this.ParameterName, this.NpgsqlDbType, this.Size, this.SourceColumn, this.Direction, this.IsNullable, this.Precision, this.Scale, this.SourceVersion, this.Value); } + + } } diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlParameter.resx b/mcs/class/Npgsql/Npgsql/NpgsqlParameter.resx index f53797bed38..568029414d9 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlParameter.resx +++ b/mcs/class/Npgsql/Npgsql/NpgsqlParameter.resx @@ -100,7 +100,4 @@ <data name="Exception_ImpossibleToCast"> <value>Can't cast {0} into any valid DbType.</value> </data> - <data name="Exception_ParameterTypeIsOnlyArray"> - <value>Cannot set NpgsqlDbType to just Array, Binary-Or with the element type (e.g. Array of Box is NpgsqlDbType.Array | NpgsqlDbType.Box).</value> - </data> -</root> +</root>
\ No newline at end of file diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlParameterCollection.cs b/mcs/class/Npgsql/Npgsql/NpgsqlParameterCollection.cs index c48c55851ec..03140387ebf 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlParameterCollection.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlParameterCollection.cs @@ -12,562 +12,466 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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.Reflection; +using System.Data; using System.Collections; -using System.Collections.Generic; using System.ComponentModel; -using System.Data.Common; -using System.Resources; using NpgsqlTypes; #if WITHDESIGN - +using Npgsql.Design; #endif namespace Npgsql { - /// <summary> - /// Represents a collection of parameters relevant to a <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see> - /// as well as their respective mappings to columns in a <see cref="System.Data.DataSet">DataSet</see>. - /// This class cannot be inherited. - /// </summary> - -#if WITHDESIGN + /// <summary> + /// Represents a collection of parameters relevant to a <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see> + /// as well as their respective mappings to columns in a <see cref="System.Data.DataSet">DataSet</see>. + /// This class cannot be inherited. + /// </summary> + + #if WITHDESIGN [ListBindable(false)] [Editor(typeof(NpgsqlParametersEditor), typeof(System.Drawing.Design.UITypeEditor))] -#endif - - public sealed class NpgsqlParameterCollection : DbParameterCollection, IList<NpgsqlParameter> - { - private readonly List<NpgsqlParameter> InternalList = new List<NpgsqlParameter>(); - - // Logging related value - private static readonly String CLASSNAME = "NpgsqlParameterCollection"; - - // Our resource manager - private readonly ResourceManager resman; - - /// <summary> - /// Initializes a new instance of the NpgsqlParameterCollection class. - /// </summary> - internal NpgsqlParameterCollection() - { - this.resman = new ResourceManager(this.GetType()); - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME); - } - - #region NpgsqlParameterCollection Member - - /// <summary> - /// Gets the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> with the specified name. - /// </summary> - /// <param name="parameterName">The name of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> to retrieve.</param> - /// <value>The <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> with the specified name, or a null reference if the parameter is not found.</value> - -#if WITHDESIGN + #endif + + public sealed class NpgsqlParameterCollection : MarshalByRefObject, IDataParameterCollection + { + private ArrayList InternalList = new ArrayList(); + + // Logging related value + private static readonly String CLASSNAME = "NpgsqlParameterCollection"; + + // Our resource manager + private System.Resources.ResourceManager resman; + + /// <summary> + /// Initializes a new instance of the NpgsqlParameterCollection class. + /// </summary> + internal NpgsqlParameterCollection() + { + this.resman = new System.Resources.ResourceManager(this.GetType()); + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME); + } + +#region NpgsqlParameterCollection Member + + /// <summary> + /// Gets the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> with the specified name. + /// </summary> + /// <param name="parameterName">The name of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> to retrieve.</param> + /// <value>The <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> with the specified name, or a null reference if the parameter is not found.</value> + + #if WITHDESIGN [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] -#endif - - public new NpgsqlParameter this[string parameterName] - { - get - { - NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, parameterName); - return this.InternalList[IndexOf(parameterName)]; - } - set - { - NpgsqlEventLog.LogIndexerSet(LogLevel.Debug, CLASSNAME, parameterName, value); - this.InternalList[IndexOf(parameterName)] = value; - } - } - - /// <summary> - /// Gets the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> at the specified index. - /// </summary> - /// <param name="index">The zero-based index of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> to retrieve.</param> - /// <value>The <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> at the specified index.</value> - -#if WITHDESIGN + #endif + + public NpgsqlParameter this[string parameterName] { + get + { + NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, parameterName); + return (NpgsqlParameter)this.InternalList[IndexOf(parameterName)]; + } + set + { + NpgsqlEventLog.LogIndexerSet(LogLevel.Debug, CLASSNAME, parameterName, value); + this.InternalList[IndexOf(parameterName)] = value; + } + } + + /// <summary> + /// Gets the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> at the specified index. + /// </summary> + /// <param name="index">The zero-based index of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> to retrieve.</param> + /// <value>The <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> at the specified index.</value> + + #if WITHDESIGN [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] -#endif - - public new NpgsqlParameter this[int index] - { - get - { - NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, index); - return this.InternalList[index]; - } - set - { - NpgsqlEventLog.LogIndexerSet(LogLevel.Debug, CLASSNAME, index, value); - this.InternalList[index] = value; - } - } - - /// <summary> - /// Adds the specified <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object to the <see cref="Npgsql.NpgsqlParameterCollection">NpgsqlParameterCollection</see>. - /// </summary> - /// <param name="value">The <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> to add to the collection.</param> - /// <returns>The index of the new <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object.</returns> - public NpgsqlParameter Add(NpgsqlParameter value) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Add", value); - - // Do not allow parameters without name. - - this.InternalList.Add(value); - - // Check if there is a name. If not, add a name based in the index of parameter. - if (value.ParameterName.Trim() == String.Empty || (value.ParameterName.Length == 1 && value.ParameterName[0] == ':')) - { - value.ParameterName = ":" + "Parameter" + (IndexOf(value) + 1); - } - - - return value; - } - - /// <summary> - /// Adds a <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> to the <see cref="Npgsql.NpgsqlParameterCollection">NpgsqlParameterCollection</see> given the specified parameter name and value. - /// </summary> - /// <param name="parameterName">The name of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see>.</param> - /// <param name="value">The Value of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> to add to the collection.</param> - /// <returns>The index of the new <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object.</returns> - /// <remarks> - /// Use caution when using this overload of the - /// <b>Add</b> method to specify integer parameter values. - /// Because this overload takes a <i>value</i> of type Object, - /// you must convert the integral value to an <b>Object</b> - /// type when the value is zero, as the following C# example demonstrates. - /// <code>parameters.Add(":pname", Convert.ToInt32(0));</code> - /// If you do not perform this conversion, the compiler will assume you - /// are attempting to call the NpgsqlParameterCollection.Add(string, DbType) overload. - /// </remarks> - public NpgsqlParameter Add(string parameterName, object value) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Add", parameterName, value); - return this.Add(new NpgsqlParameter(parameterName, value)); - } - - /// <summary> - /// Adds a <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> to the <see cref="Npgsql.NpgsqlParameterCollection">NpgsqlParameterCollection</see> given the parameter name and the data type. - /// </summary> - /// <param name="parameterName">The name of the parameter.</param> - /// <param name="parameterType">One of the DbType values.</param> - /// <returns>The index of the new <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object.</returns> - public NpgsqlParameter Add(string parameterName, NpgsqlDbType parameterType) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Add", parameterName, parameterType); - return this.Add(new NpgsqlParameter(parameterName, parameterType)); - } - - /// <summary> - /// Adds a <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> to the <see cref="Npgsql.NpgsqlParameterCollection">NpgsqlParameterCollection</see> with the parameter name, the data type, and the column length. - /// </summary> - /// <param name="parameterName">The name of the parameter.</param> - /// <param name="parameterType">One of the DbType values.</param> - /// <param name="size">The length of the column.</param> - /// <returns>The index of the new <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object.</returns> - public NpgsqlParameter Add(string parameterName, NpgsqlDbType parameterType, int size) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Add", parameterName, parameterType, size); - return this.Add(new NpgsqlParameter(parameterName, parameterType, size)); - } - - /// <summary> - /// Adds a <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> to the <see cref="Npgsql.NpgsqlParameterCollection">NpgsqlParameterCollection</see> with the parameter name, the data type, the column length, and the source column name. - /// </summary> - /// <param name="parameterName">The name of the parameter.</param> - /// <param name="parameterType">One of the DbType values.</param> - /// <param name="size">The length of the column.</param> - /// <param name="sourceColumn">The name of the source column.</param> - /// <returns>The index of the new <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object.</returns> - public NpgsqlParameter Add(string parameterName, NpgsqlDbType parameterType, int size, string sourceColumn) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Add", parameterName, parameterType, size, sourceColumn); - return this.Add(new NpgsqlParameter(parameterName, parameterType, size, sourceColumn)); - } - - #endregion - - #region IDataParameterCollection Member - - /// <summary> - /// Removes the specified <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> from the collection using the parameter name. - /// </summary> - /// <param name="parameterName">The name of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object to retrieve.</param> - public override void RemoveAt(string parameterName) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "RemoveAt", parameterName); - this.InternalList.RemoveAt(IndexOf(parameterName)); - } - - /// <summary> - /// Gets a value indicating whether a <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> with the specified parameter name exists in the collection. - /// </summary> - /// <param name="parameterName">The name of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object to find.</param> - /// <returns><b>true</b> if the collection contains the parameter; otherwise, <b>false</b>.</returns> - public override bool Contains(string parameterName) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Contains", parameterName); - return (IndexOf(parameterName) != -1); - } - - /// <summary> - /// Gets the location of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> in the collection with a specific parameter name. - /// </summary> - /// <param name="parameterName">The name of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object to find.</param> - /// <returns>The zero-based location of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> in the collection.</returns> - public override int IndexOf(string parameterName) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IndexOf", parameterName); - - // Iterate values to see what is the index of parameter. - int index = 0; - int bestChoose = -1; - if ((parameterName[0] == ':') || (parameterName[0] == '@')) - { - parameterName = parameterName.Remove(0, 1); - } - - - foreach (NpgsqlParameter parameter in this) - { - // allow for optional use of ':' and '@' in the ParameterName property - string cleanName = parameter.CleanName; - if(cleanName == parameterName) - { - return index; - } - if(string.Compare(parameterName, cleanName, StringComparison.InvariantCultureIgnoreCase) == 0) - { - bestChoose = index; - } - index++; - } - return bestChoose; - } - - #endregion - - #region IList Member - - public override bool IsReadOnly - { - get - { - NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "IsReadOnly"); - return false; - } - } - - /// <summary> - /// Removes the specified <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> from the collection using a specific index. - /// </summary> - /// <param name="index">The zero-based index of the parameter.</param> - public override void RemoveAt(int index) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "RemoveAt", index); - this.InternalList.RemoveAt(index); - } - - /// <summary> - /// Inserts a <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> into the collection at the specified index. - /// </summary> - /// <param name="index">The zero-based index where the parameter is to be inserted within the collection.</param> - /// <param name="value">The <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> to add to the collection.</param> - public override void Insert(int index, object value) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Insert", index, value); - CheckType(value); - this.InternalList.Insert(index, (NpgsqlParameter) value); - } - - /// <summary> - /// Removes the specified <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> from the collection. - /// </summary> - /// <param name="value">The <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> to remove from the collection.</param> - public override void Remove(object value) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Remove", value); - CheckType(value); - this.InternalList.Remove((NpgsqlParameter) value); - } - - /// <summary> - /// Gets a value indicating whether a <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> exists in the collection. - /// </summary> - /// <param name="value">The value of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object to find.</param> - /// <returns>true if the collection contains the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object; otherwise, false.</returns> - public override bool Contains(object value) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Contains", value); - if (!(value is NpgsqlParameter)) - { - return false; - } - return this.InternalList.Contains((NpgsqlParameter) value); - } - - - /// <summary> - /// Gets a value indicating whether a <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> with the specified parameter name exists in the collection. - /// </summary> - /// <param name="parameterName">The name of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object to find.</param> - /// <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> - /// <returns><b>true</b> if the collection contains the parameter and param will contain the parameter; otherwise, <b>false</b>.</returns> - public bool TryGetValue(string parameterName, out NpgsqlParameter parameter) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "TryGetValue", parameterName); - - int index = IndexOf(parameterName); - - if (index != -1) - { - parameter = this[index]; - - return true; - } - - else - { - parameter = null; - - return false; - } - } - - - /// <summary> - /// Removes all items from the collection. - /// </summary> - public override void Clear() - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Clear"); - this.InternalList.Clear(); - } - - /// <summary> - /// Gets the location of a <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> in the collection. - /// </summary> - /// <param name="value">The value of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object to find.</param> - /// <returns>The zero-based index of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object in the collection.</returns> - public override int IndexOf(object value) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IndexOf", value); - CheckType(value); - return this.InternalList.IndexOf((NpgsqlParameter) value); - } - - /// <summary> - /// Adds the specified <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object to the <see cref="Npgsql.NpgsqlParameterCollection">NpgsqlParameterCollection</see>. - /// </summary> - /// <param name="value">The <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> to add to the collection.</param> - /// <returns>The zero-based index of the new <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object.</returns> - public override int Add(object value) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Add", value); - CheckType(value); - this.Add((NpgsqlParameter) value); - return IndexOf(value); - } - - public override bool IsFixedSize - { - get - { - NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "IsFixedSize"); - return false; - } - } - - #endregion - - #region ICollection Member - - public override bool IsSynchronized - { - get - { - NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "IsSynchronized"); - return (InternalList as ICollection).IsSynchronized; - } - } - - /// <summary> - /// Gets the number of <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> objects in the collection. - /// </summary> - /// <value>The number of <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> objects in the collection.</value> - -#if WITHDESIGN + #endif + + public NpgsqlParameter this[int index] { + get + { + NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, index); + return (NpgsqlParameter)this.InternalList[index]; + } + set + { + NpgsqlEventLog.LogIndexerSet(LogLevel.Debug, CLASSNAME, index, value); + this.InternalList[index] = value; + } + } + + /// <summary> + /// Adds the specified <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object to the <see cref="Npgsql.NpgsqlParameterCollection">NpgsqlParameterCollection</see>. + /// </summary> + /// <param name="value">The <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> to add to the collection.</param> + /// <returns>The index of the new <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object.</returns> + public NpgsqlParameter Add(NpgsqlParameter value) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Add", value); + + // Do not allow parameters without name. + + this.InternalList.Add(value); + + // Check if there is a name. If not, add a name based in the index of parameter. + if (value.ParameterName.Trim() == String.Empty || + (value.ParameterName.Length == 1 && value.ParameterName[0] == ':')) + value.ParameterName = ":" + "Parameter" + (IndexOf(value) + 1); + + + return value; + } + + /// <summary> + /// Adds a <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> to the <see cref="Npgsql.NpgsqlParameterCollection">NpgsqlParameterCollection</see> given the specified parameter name and value. + /// </summary> + /// <param name="parameterName">The name of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see>.</param> + /// <param name="value">The Value of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> to add to the collection.</param> + /// <returns>The index of the new <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object.</returns> + /// <remarks> + /// Use caution when using this overload of the + /// <b>Add</b> method to specify integer parameter values. + /// Because this overload takes a <i>value</i> of type Object, + /// you must convert the integral value to an <b>Object</b> + /// type when the value is zero, as the following C# example demonstrates. + /// <code>parameters.Add(":pname", Convert.ToInt32(0));</code> + /// If you do not perform this conversion, the compiler will assume you + /// are attempting to call the NpgsqlParameterCollection.Add(string, DbType) overload. + /// </remarks> + public NpgsqlParameter Add(string parameterName, object value) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Add", parameterName, value); + return this.Add(new NpgsqlParameter(parameterName, value)); + } + + /// <summary> + /// Adds a <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> to the <see cref="Npgsql.NpgsqlParameterCollection">NpgsqlParameterCollection</see> given the parameter name and the data type. + /// </summary> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="parameterType">One of the DbType values.</param> + /// <returns>The index of the new <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object.</returns> + public NpgsqlParameter Add(string parameterName, NpgsqlDbType parameterType) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Add", parameterName, parameterType); + return this.Add(new NpgsqlParameter(parameterName, parameterType)); + } + + /// <summary> + /// Adds a <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> to the <see cref="Npgsql.NpgsqlParameterCollection">NpgsqlParameterCollection</see> with the parameter name, the data type, and the column length. + /// </summary> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="parameterType">One of the DbType values.</param> + /// <param name="size">The length of the column.</param> + /// <returns>The index of the new <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object.</returns> + public NpgsqlParameter Add(string parameterName, NpgsqlDbType parameterType, int size) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Add", parameterName, parameterType, size); + return this.Add(new NpgsqlParameter(parameterName, parameterType, size)); + } + + /// <summary> + /// Adds a <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> to the <see cref="Npgsql.NpgsqlParameterCollection">NpgsqlParameterCollection</see> with the parameter name, the data type, the column length, and the source column name. + /// </summary> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="parameterType">One of the DbType values.</param> + /// <param name="size">The length of the column.</param> + /// <param name="sourceColumn">The name of the source column.</param> + /// <returns>The index of the new <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object.</returns> + public NpgsqlParameter Add(string parameterName, NpgsqlDbType parameterType, int size, string sourceColumn) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Add", parameterName, parameterType, size, sourceColumn); + return this.Add(new NpgsqlParameter(parameterName, parameterType, size, sourceColumn)); + } + +#endregion + +#region IDataParameterCollection Member + + object System.Data.IDataParameterCollection.this[string parameterName] { + get + { + NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, parameterName); + return this.InternalList[IndexOf(parameterName)]; + } + set + { + NpgsqlEventLog.LogIndexerSet(LogLevel.Debug, CLASSNAME, parameterName, value); + CheckType(value); + this.InternalList[IndexOf(parameterName)] = value; + } + } + + /// <summary> + /// Removes the specified <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> from the collection using the parameter name. + /// </summary> + /// <param name="parameterName">The name of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object to retrieve.</param> + public void RemoveAt(string parameterName) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "RemoveAt", parameterName); + this.InternalList.RemoveAt(IndexOf(parameterName)); + } + + /// <summary> + /// Gets a value indicating whether a <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> with the specified parameter name exists in the collection. + /// </summary> + /// <param name="parameterName">The name of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object to find.</param> + /// <returns><b>true</b> if the collection contains the parameter; otherwise, <b>false</b>.</returns> + public bool Contains(string parameterName) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Contains", parameterName); + return (IndexOf(parameterName) != -1); + } + + /// <summary> + /// Gets the location of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> in the collection with a specific parameter name. + /// </summary> + /// <param name="parameterName">The name of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object to find.</param> + /// <returns>The zero-based location of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> in the collection.</returns> + public int IndexOf(string parameterName) + { + 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.Remove(0, 1);
+
+ foreach (NpgsqlParameter parameter in this)
+ {
+ if (parameter.ParameterName.Remove(0, 1) == parameterName)
+ return index;
+ index++; + } + return -1; + } + +#endregion + +#region IList Member + + bool IList.IsReadOnly { + get + { + NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "IsReadOnly"); + return this.InternalList.IsReadOnly; + } + } + + object System.Collections.IList.this[int index] { + get + { + NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, index); + return (NpgsqlParameter)this.InternalList[index]; + } + set + { + NpgsqlEventLog.LogIndexerSet(LogLevel.Debug, CLASSNAME, index, value); + CheckType(value); + this.InternalList[index] = value; + } + } + + /// <summary> + /// Removes the specified <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> from the collection using a specific index. + /// </summary> + /// <param name="index">The zero-based index of the parameter.</param> + public void RemoveAt(int index) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "RemoveAt", index); + this.InternalList.RemoveAt(index); + } + + /// <summary> + /// Inserts a <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> into the collection at the specified index. + /// </summary> + /// <param name="index">The zero-based index where the parameter is to be inserted within the collection.</param> + /// <param name="value">The <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> to add to the collection.</param> + public void Insert(int index, object value) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Insert", index, value); + CheckType(value); + this.InternalList.Insert(index, value); + } + + /// <summary> + /// Removes the specified <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> from the collection. + /// </summary> + /// <param name="value">The <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> to remove from the collection.</param> + public void Remove(object value) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Remove", value); + CheckType(value); + this.InternalList.Remove(value); + } + + /// <summary> + /// Gets a value indicating whether a <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> exists in the collection. + /// </summary> + /// <param name="value">The value of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object to find.</param> + /// <returns>true if the collection contains the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object; otherwise, false.</returns> + public bool Contains(object value) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Contains", value); + CheckType(value); + return this.InternalList.Contains(value); + }
+
+ /// <summary>
+ /// Gets a value indicating whether a <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> with the specified parameter name exists in the collection.
+ /// </summary>
+ /// <param name="parameterName">The name of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object to find.</param>
+ /// <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>
+ /// <returns><b>true</b> if the collection contains the parameter and param will contain the parameter; otherwise, <b>false</b>.</returns>
+ public bool TryGetValue(string parameterName, out NpgsqlParameter parameter)
+ {
+ NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "TryGetValue", parameterName);
+ int index = IndexOf(parameterName);
+ if (index != -1)
+ {
+ parameter = this[index];
+ return true;
+ }
+ else
+ {
+ parameter = null;
+ return false;
+ }
+ }
+ + /// <summary> + /// Removes all items from the collection. + /// </summary> + public void Clear() + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Clear"); + this.InternalList.Clear(); + } + + /// <summary> + /// Gets the location of a <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> in the collection. + /// </summary> + /// <param name="value">The value of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object to find.</param> + /// <returns>The zero-based index of the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object in the collection.</returns> + public int IndexOf(object value) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IndexOf", value); + CheckType(value); + return this.InternalList.IndexOf(value); + } + + /// <summary> + /// Adds the specified <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object to the <see cref="Npgsql.NpgsqlParameterCollection">NpgsqlParameterCollection</see>. + /// </summary> + /// <param name="value">The <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> to add to the collection.</param> + /// <returns>The zero-based index of the new <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> object.</returns> + public int Add(object value) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Add", value); + CheckType(value); + this.Add((NpgsqlParameter)value); + return IndexOf(value); + } + + bool IList.IsFixedSize { + get + { + NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "IsFixedSize"); + return this.InternalList.IsFixedSize; + } + } + +#endregion + +#region ICollection Member + + bool ICollection.IsSynchronized { + get + { + NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "IsSynchronized"); + return this.InternalList.IsSynchronized; + } + } + + /// <summary> + /// Gets the number of <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> objects in the collection. + /// </summary> + /// <value>The number of <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> objects in the collection.</value> + + #if WITHDESIGN [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] -#endif - - public override int Count - { - get - { - NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "Count"); - return this.InternalList.Count; - } - } - - /// <summary> - /// Copies <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> objects from the <see cref="Npgsql.NpgsqlParameterCollection">NpgsqlParameterCollection</see> to the specified array. - /// </summary> - /// <param name="array">An <see cref="System.Array">Array</see> to which to copy the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> objects in the collection.</param> - /// <param name="index">The starting index of the array.</param> - public override void CopyTo(Array array, int index) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CopyTo", array, index); - (InternalList as ICollection).CopyTo(array, index); - IRaiseItemChangedEvents x = InternalList as IRaiseItemChangedEvents; - } - - public override object SyncRoot - { - get - { - NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "SyncRoot"); - return (InternalList as ICollection).SyncRoot; - } - } - - #endregion - - #region IEnumerable Member - - /// <summary> - /// Returns an enumerator that can iterate through the collection. - /// </summary> - /// <returns>An <see cref="System.Collections.IEnumerator">IEnumerator</see> that can be used to iterate through the collection.</returns> - public override IEnumerator GetEnumerator() - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetEnumerator"); - return this.InternalList.GetEnumerator(); - } - - #endregion - - public override void AddRange(Array values) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "AddRange", values); - foreach (NpgsqlParameter parameter in values) - { - Add(parameter); - } - } - - protected override DbParameter GetParameter(string parameterName) - { - return this[parameterName]; - } - - protected override DbParameter GetParameter(int index) - { - return this[index]; - } - - protected override void SetParameter(string parameterName, DbParameter value) - { - this[parameterName] = (NpgsqlParameter) value; - } - - protected override void SetParameter(int index, DbParameter value) - { - this[index] = (NpgsqlParameter) value; - } - - /// <summary> - /// In methods taking an object as argument this method is used to verify - /// that the argument has the type <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> - /// </summary> - /// <param name="Object">The object to verify</param> - private void CheckType(object Object) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CheckType", Object); - if (!(Object is NpgsqlParameter)) - { - throw new InvalidCastException( - String.Format(this.resman.GetString("Exception_WrongType"), Object.GetType())); - } - } - -/* - /// <summary> - /// In methods taking an array as argument this method is used to verify - /// that the argument has the type <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see>[] - /// </summary> - /// <param name="array">The array to verify</param> - private void CheckType(Array array) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CheckType", array); - if (array.GetType() != typeof (NpgsqlParameter[])) - { - throw new InvalidCastException( - String.Format(this.resman.GetString("Exception_WrongType"), array.GetType().ToString())); - } - } -*/ - - NpgsqlParameter IList<NpgsqlParameter>.this[int index] - { - get { return InternalList[index]; } - set { InternalList[index] = value; } - } - - public int IndexOf(NpgsqlParameter item) - { - return InternalList.IndexOf(item); - } - - public void Insert(int index, NpgsqlParameter item) - { - InternalList.Insert(index, item); - } - - public bool Contains(NpgsqlParameter item) - { - return InternalList.Contains(item); - } - - public bool Remove(NpgsqlParameter item) - { - return Remove(item); - } - - IEnumerator<NpgsqlParameter> IEnumerable<NpgsqlParameter>.GetEnumerator() - { - return InternalList.GetEnumerator(); - } - - public void CopyTo(NpgsqlParameter[] array, int arrayIndex) - { - InternalList.CopyTo(array, arrayIndex); - } - - void ICollection<NpgsqlParameter>.Add(NpgsqlParameter item) - { - Add(item); - } - } + #endif + + public int Count { + get + { + NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "Count"); + return this.InternalList.Count; + } + } + + /// <summary> + /// Copies <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> objects from the <see cref="Npgsql.NpgsqlParameterCollection">NpgsqlParameterCollection</see> to the specified array. + /// </summary> + /// <param name="array">An <see cref="System.Array">Array</see> to which to copy the <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> objects in the collection.</param> + /// <param name="index">The starting index of the array.</param> + public void CopyTo(Array array, int index) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CopyTo", array, index); + this.InternalList.CopyTo(array, index); + } + + object ICollection.SyncRoot { + get + { + NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "SyncRoot"); + return this.InternalList.SyncRoot; + } + } + +#endregion + +#region IEnumerable Member + + /// <summary> + /// Returns an enumerator that can iterate through the collection. + /// </summary> + /// <returns>An <see cref="System.Collections.IEnumerator">IEnumerator</see> that can be used to iterate through the collection.</returns> + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetEnumerator"); + return this.InternalList.GetEnumerator(); + } + +#endregion + + /// <summary> + /// In methods taking an object as argument this method is used to verify + /// that the argument has the type <see cref="Npgsql.NpgsqlParameter">NpgsqlParameter</see> + /// </summary> + /// <param name="Object">The object to verify</param> + private void CheckType(object Object) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CheckType", Object); + if(Object.GetType() != typeof(NpgsqlParameter)) + throw new InvalidCastException(String.Format(this.resman.GetString("Exception_WrongType"), Object.GetType().ToString())); + } + + } } diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlParameterStatus.cs b/mcs/class/Npgsql/Npgsql/NpgsqlParameterStatus.cs index 56fa029cd19..6df8f7ef285 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlParameterStatus.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlParameterStatus.cs @@ -8,43 +8,76 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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.Collections; using System.IO; +using System.Text; +using System.Net; +using NpgsqlTypes; + namespace Npgsql { - /// <summary> - /// This class represents the ParameterStatus message sent from PostgreSQL - /// server. - /// </summary> - /// - internal sealed class NpgsqlParameterStatus - { - public readonly string Parameter; - public readonly string ParameterValue; - - public NpgsqlParameterStatus(Stream stream) - { - //Read message length - PGUtil.EatStreamBytes(stream, 4); - Parameter = PGUtil.ReadString(stream); - ParameterValue = PGUtil.ReadString(stream); - } - } -}
\ No newline at end of file + + /// <summary> + /// This class represents the ParameterStatus message sent from PostgreSQL + /// server. + /// </summary> + /// + internal sealed class NpgsqlParameterStatus + { + + private String _parameter; + private String _parameterValue; + + + public void ReadFromStream(Stream inputStream, Encoding encoding) + { + + //Read message length + Byte[] inputBuffer = new Byte[4]; + PGUtil.CheckedStreamRead(inputStream, inputBuffer, 0, 4 ); + + Int32 messageLength = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(inputBuffer, 0)); + + _parameter = PGUtil.ReadString(inputStream, encoding); + _parameterValue = PGUtil.ReadString(inputStream, encoding); + + + } + + public String Parameter + { + get + { + return _parameter; + } + } + + public String ParameterValue + { + get + { + return _parameterValue; + } + } + + + } + + +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlParse.cs b/mcs/class/Npgsql/Npgsql/NpgsqlParse.cs index 059d12e94dc..129a266a068 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlParse.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlParse.cs @@ -9,74 +9,79 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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.Collections; using System.IO; +using System.Text; +using System.Net; +using NpgsqlTypes; + namespace Npgsql { - /// <summary> - /// This class represents the Parse message sent to PostgreSQL - /// server. - /// </summary> - /// - internal sealed class NpgsqlParse : ClientMessage - { - // Logging related values - //private static readonly String CLASSNAME = "NpgsqlParse"; - - private readonly String _prepareName; - private readonly String _queryString; - private readonly Int32[] _parameterIDs; - - - public NpgsqlParse(String prepareName, String queryString, Int32[] parameterIDs) - { - _prepareName = prepareName; - _queryString = queryString; - _parameterIDs = parameterIDs; - } - - public override void WriteToStream(Stream outputStream) - { - outputStream.WriteByte((byte) FrontEndMessageCode.Parse); - - // message length = - // Int32 self - // name of prepared statement + 1 null string terminator + - // query string + 1 null string terminator - // + Int16 - // + Int32 * number of parameters. - Int32 messageLength = 4 + UTF8Encoding.GetByteCount(_prepareName) + 1 + UTF8Encoding.GetByteCount(_queryString) + 1 + - 2 + (_parameterIDs.Length*4); - //Int32 messageLength = 4 + _prepareName.Length + 1 + _queryString.Length + 1 + 2 + (_parameterIDs.Length * 4); - - PGUtil.WriteInt32(outputStream, messageLength); - PGUtil.WriteString(_prepareName, outputStream); - PGUtil.WriteString(_queryString, outputStream); - PGUtil.WriteInt16(outputStream, (Int16) _parameterIDs.Length); - - - for (Int32 i = 0; i < _parameterIDs.Length; i++) - { - PGUtil.WriteInt32(outputStream, _parameterIDs[i]); - } - } - } -}
\ No newline at end of file + + /// <summary> + /// This class represents the Parse message sent to PostgreSQL + /// server. + /// </summary> + /// + internal sealed class NpgsqlParse + { + // Logging related values + private static readonly String CLASSNAME = "NpgsqlParse"; + + private String _prepareName; + private String _queryString; + private Int32[] _parameterIDs; + + + public NpgsqlParse(String prepareName, String queryString, Int32[] parameterIDs) + { + _prepareName = prepareName; + _queryString = queryString; + _parameterIDs = parameterIDs; + + } + + public void WriteToStream(Stream outputStream, Encoding encoding) + { + outputStream.WriteByte((Byte)'P'); + + // message length = + // Int32 self + // name of prepared statement + 1 null string terminator + + // query string + 1 null string terminator + // + Int16 + // + Int32 * number of parameters. + Int32 messageLength = 4 + encoding.GetByteCount(_prepareName) + 1 + encoding.GetByteCount(_queryString) + 1 + 2 + (_parameterIDs.Length * 4); + //Int32 messageLength = 4 + _prepareName.Length + 1 + _queryString.Length + 1 + 2 + (_parameterIDs.Length * 4); + + PGUtil.WriteInt32(outputStream, messageLength); + PGUtil.WriteString(_prepareName, outputStream, encoding); + PGUtil.WriteString(_queryString, outputStream, encoding); + PGUtil.WriteInt16(outputStream, (Int16)_parameterIDs.Length); + + + for(Int32 i = 0; i < _parameterIDs.Length; i++) + PGUtil.WriteInt32(outputStream, _parameterIDs[i]); + + + + + } + } +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlPasswordPacket.cs b/mcs/class/Npgsql/Npgsql/NpgsqlPasswordPacket.cs index 2eb14dc4c0a..5b9f498bead 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlPasswordPacket.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlPasswordPacket.cs @@ -10,76 +10,75 @@ // http://gborg.postgresql.org/project/npgsql/projdisplay.php // // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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; +using System.Net; namespace Npgsql { - /// <summary> - /// This class represents a PasswordPacket message sent to backend - /// PostgreSQL. - /// </summary> - internal sealed class NpgsqlPasswordPacket : ClientMessage - { - // Logging related values - private static readonly String CLASSNAME = "NpgsqlPasswordPacket"; - - private readonly String password; - private readonly ProtocolVersion protocolVersion; - - - public NpgsqlPasswordPacket(String password, ProtocolVersion protocolVersion) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME); - - this.password = password; - this.protocolVersion = protocolVersion; - } - - public override void WriteToStream(Stream outputStream) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteToStream"); - - switch (protocolVersion) - { - case ProtocolVersion.Version2: - // Write the size of the packet. - // 4 + (passwordlength + 1) -> Int32 + NULL terminated string. - // output_stream.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(4 + (password.Length + 1))), 0, 4); - PGUtil.WriteInt32(outputStream, 4 + UTF8Encoding.GetByteCount(password) + 1); - - // Write String. - PGUtil.WriteString(password, outputStream); - - break; - - case ProtocolVersion.Version3: - outputStream.WriteByte((Byte) 'p'); - PGUtil.WriteInt32(outputStream, 4 + UTF8Encoding.GetByteCount(password) + 1); - - // Write String. - PGUtil.WriteString(password, outputStream); - - break; - } - } - } -}
\ No newline at end of file + /// <summary> + /// This class represents a PasswordPacket message sent to backend + /// PostgreSQL. + /// </summary> + internal sealed class NpgsqlPasswordPacket + { + // Logging related values + private static readonly String CLASSNAME = "NpgsqlPasswordPacket"; + + private String password; + private ProtocolVersion protocolVersion; + + + public NpgsqlPasswordPacket(String password, ProtocolVersion protocolVersion) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME); + + this.password = password; + this.protocolVersion = protocolVersion; + } + + public void WriteToStream(Stream outputStream, Encoding encoding) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteToStream"); + + switch (protocolVersion) { + case ProtocolVersion.Version2 : + // Write the size of the packet. + // 4 + (passwordlength + 1) -> Int32 + NULL terminated string. + // output_stream.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(4 + (password.Length + 1))), 0, 4); + PGUtil.WriteInt32(outputStream, 4 + encoding.GetByteCount(password) + 1); + + // Write String. + PGUtil.WriteString(password, outputStream, encoding); + + break; + + case ProtocolVersion.Version3 : + outputStream.WriteByte((Byte)'p'); + PGUtil.WriteInt32(outputStream, 4 + encoding.GetByteCount(password) + 1); + + // Write String. + PGUtil.WriteString(password, outputStream, encoding); + + break; + + } + } + } +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlQuery.cs b/mcs/class/Npgsql/Npgsql/NpgsqlQuery.cs index b74abfa0197..a8818ea89b0 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlQuery.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlQuery.cs @@ -7,67 +7,68 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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; +using System.Net.Sockets; namespace Npgsql { - /// <summary> - /// Summary description for NpgsqlQuery - /// </summary> - internal sealed class NpgsqlQuery : ClientMessage - { - private readonly NpgsqlCommand _command; - private readonly ProtocolVersion _protocolVersion; - - public NpgsqlQuery(NpgsqlCommand command, ProtocolVersion protocolVersion) - { - _command = command; - _protocolVersion = protocolVersion; - } + /// <summary> + /// Summary description for NpgsqlQuery + /// </summary> + internal sealed class NpgsqlQuery + { + private NpgsqlCommand _command; + private ProtocolVersion _protocolVersion; - public override void WriteToStream(Stream outputStream) - { - //NpgsqlEventLog.LogMsg( this.ToString() + _commandText, LogLevel.Debug ); + public NpgsqlQuery(NpgsqlCommand command, ProtocolVersion protocolVersion) + { + _command = command; + _protocolVersion = protocolVersion; + } + public void WriteToStream( Stream outputStream, Encoding encoding ) + { + //NpgsqlEventLog.LogMsg( this.ToString() + _commandText, LogLevel.Debug ); - String commandText = _command.GetCommandText(); - // Tell to mediator what command is being sent. + String commandText = _command.GetCommandText(); + + // Tell to mediator what command is being sent. + + _command.Connector.Mediator.SqlSent = commandText; + + // Send the query to server. + // Write the byte 'Q' to identify a query message. + outputStream.WriteByte((Byte)'Q'); - _command.Connector.Mediator.SqlSent = commandText; + if (_protocolVersion == ProtocolVersion.Version3) + { + // Write message length. Int32 + string length + null terminator. + PGUtil.WriteInt32(outputStream, 4 + encoding.GetByteCount(commandText) + 1); + } - // Send the query to server. - // Write the byte 'Q' to identify a query message. - outputStream.WriteByte((byte) FrontEndMessageCode.Query); + // Write the query. In this case it is the CommandText text. + // It is a string terminated by a C NULL character. + PGUtil.WriteString(commandText, outputStream, encoding); + } - if (_protocolVersion == ProtocolVersion.Version3) - { - // Write message length. Int32 + string length + null terminator. - PGUtil.WriteInt32(outputStream, 4 + UTF8Encoding.GetByteCount(commandText) + 1); - } - // Write the query. In this case it is the CommandText text. - // It is a string terminated by a C NULL character. - PGUtil.WriteString(commandText, outputStream); - } - } -}
\ No newline at end of file + } +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlReadyState.cs b/mcs/class/Npgsql/Npgsql/NpgsqlReadyState.cs index 1dec93216ac..0676480c57a 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlReadyState.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlReadyState.cs @@ -7,151 +7,151 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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.Collections.Generic; using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Resources; namespace Npgsql { - internal sealed class NpgsqlReadyState : NpgsqlState - { - public static readonly NpgsqlReadyState Instance = new NpgsqlReadyState(); - - - // Flush and Sync messages. It doesn't need to be created every time it is called. - private static readonly NpgsqlFlush _flushMessage = new NpgsqlFlush(); - - private static readonly NpgsqlSync _syncMessage = new NpgsqlSync(); - - private readonly String CLASSNAME = "NpgsqlReadyState"; - - private NpgsqlReadyState() - : base() - { - } - - public override IEnumerable<IServerResponseObject> QueryEnum(NpgsqlConnector context, NpgsqlCommand command) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "QueryEnum"); - - - //String commandText = command.GetCommandText(); - //NpgsqlEventLog.LogMsg(resman, "Log_QuerySent", LogLevel.Debug, commandText); - - // Send the query request to backend. - - NpgsqlQuery query = new NpgsqlQuery(command, context.BackendProtocolVersion); - - query.WriteToStream(context.Stream); - context.Stream.Flush(); - - return ProcessBackendResponsesEnum(context); - } - - public override void Parse(NpgsqlConnector context, NpgsqlParse parse) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Parse"); - - Stream stream = context.Stream; - parse.WriteToStream(stream); - //stream.Flush(); - } - - - public override IEnumerable<IServerResponseObject> SyncEnum(NpgsqlConnector context) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Sync"); - _syncMessage.WriteToStream(context.Stream); - context.Stream.Flush(); - return ProcessBackendResponsesEnum(context); - } - - public override void Flush(NpgsqlConnector context) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Flush"); - _flushMessage.WriteToStream(context.Stream); - context.Stream.Flush(); - ProcessBackendResponses(context); - } - - public override void Bind(NpgsqlConnector context, NpgsqlBind bind) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Bind"); - - Stream stream = context.Stream; - - bind.WriteToStream(stream); - //stream.Flush(); - } - - public override void Describe(NpgsqlConnector context, NpgsqlDescribe describe) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Describe"); - describe.WriteToStream(context.Stream); - //context.Stream.Flush(); - } - - public override void Execute(NpgsqlConnector context, NpgsqlExecute execute) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Execute"); - NpgsqlDescribe describe = new NpgsqlDescribe('P', execute.PortalName); - Stream stream = context.Stream; - describe.WriteToStream(stream); - execute.WriteToStream(stream); - //stream.Flush(); - Sync(context); - } - - public override IEnumerable<IServerResponseObject> ExecuteEnum(NpgsqlConnector context, NpgsqlExecute execute) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Execute"); - NpgsqlDescribe describe = new NpgsqlDescribe('P', execute.PortalName); - Stream stream = context.Stream; - describe.WriteToStream(stream); - execute.WriteToStream(stream); - //stream.Flush(); - return SyncEnum(context); - } - - public override void Close(NpgsqlConnector context) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Close"); - Stream stream = context.Stream; - stream.WriteByte((byte) FrontEndMessageCode.Termination); - if (context.BackendProtocolVersion >= ProtocolVersion.Version3) - { - PGUtil.WriteInt32(stream, 4); - } - stream.Flush(); - - try - { - stream.Close(); - } - catch - { - } - - context.Stream = null; - ChangeState(context, NpgsqlClosedState.Instance); - } - } -}
\ No newline at end of file + + + internal sealed class NpgsqlReadyState : NpgsqlState + { + private static NpgsqlReadyState _instance = new NpgsqlReadyState(); + + + // Flush and Sync messages. It doesn't need to be created every time it is called. + private static readonly NpgsqlFlush _flushMessage = new NpgsqlFlush(); + + private static readonly NpgsqlSync _syncMessage = new NpgsqlSync(); + + private readonly String CLASSNAME = "NpgsqlReadyState"; + + private NpgsqlReadyState() : base() + { } + + public static NpgsqlReadyState Instance + { + get + { + return _instance; + } + } + + + + public override void Query( NpgsqlConnector context, NpgsqlCommand command ) + { + + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Query"); + + + + //String commandText = command.GetCommandText(); + //NpgsqlEventLog.LogMsg(resman, "Log_QuerySent", LogLevel.Debug, commandText); + + // Send the query request to backend. + + NpgsqlQuery query = new NpgsqlQuery(command, context.BackendProtocolVersion); + + 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"); + + Stream stream = context.Stream; + parse.WriteToStream(stream, context.Encoding); + //stream.Flush(); + } + + + public override void Sync(NpgsqlConnector context) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Sync"); + _syncMessage.WriteToStream(context.Stream, context.Encoding); + context.Stream.Flush(); + ProcessBackendResponses(context); + } + + public override void Flush(NpgsqlConnector context) + { + 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"); + + Stream stream = context.Stream; + + bind.WriteToStream(stream, context.Encoding); + //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); + Stream stream = context.Stream; + describe.WriteToStream(stream, context.Encoding); + execute.WriteToStream(stream, context.Encoding); + //stream.Flush(); + Sync(context); + } + + public override void Close( NpgsqlConnector context ) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Close"); + Stream stream = context.Stream; + stream.WriteByte((Byte)'X'); + if (context.BackendProtocolVersion >= ProtocolVersion.Version3) + PGUtil.WriteInt32(stream, 4); + stream.Flush(); + + try + { + stream.Close(); + } + catch {} + + context.Stream = null; + ChangeState( context, NpgsqlClosedState.Instance ) + ; + } + } +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlRow.cs b/mcs/class/Npgsql/Npgsql/NpgsqlRow.cs index 79c2da8dc4b..848d682066e 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlRow.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlRow.cs @@ -9,473 +9,70 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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.Collections.Generic; +using System.Collections; using System.IO; -using System.Reflection; -using System.Resources; using System.Text; +using System.Net; using NpgsqlTypes; + namespace Npgsql { - /// <summary> - /// This is the abstract base class for NpgsqlAsciiRow and NpgsqlBinaryRow. - /// </summary> - internal abstract class NpgsqlRow : IStreamOwner - { - protected static readonly ResourceManager resman = new ResourceManager(MethodBase.GetCurrentMethod().DeclaringType); - public abstract object this[int index] { get; } - public abstract int NumFields { get; } - public abstract bool IsDBNull(int index); - public abstract void Dispose(); - public abstract long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length); - public abstract long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length); - } - - internal sealed class CachingRow : NpgsqlRow - { - private readonly List<object> _data = new List<object>(); - private readonly ForwardsOnlyRow _inner; - - public CachingRow(ForwardsOnlyRow fo) - { - _inner = fo; - } - - public override object this[Int32 index] - { - get - { - if ((index < 0) || (index >= NumFields)) - { - throw new IndexOutOfRangeException("this[] index value"); - } - while (_data.Count <= index) - { - _data.Add(_inner[_data.Count]); - } - return _data[index]; - } - } - - public override int NumFields - { - get { return _inner.NumFields; } - } - - public override bool IsDBNull(int index) - { - return this[index] == DBNull.Value; - } - - public override long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length) - { - byte[] source = (byte[]) this[i]; - if (buffer == null) - { - return source.Length - fieldOffset; - } - long finalLength = Math.Max(0, Math.Min(length, source.Length - fieldOffset)); - Array.Copy(source, fieldOffset, buffer, bufferoffset, finalLength); - return finalLength; - } - - public override long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length) - { - string source = (string) this[i]; - if (buffer == null) - { - return source.Length - fieldoffset; - } - long finalLength = Math.Max(0, Math.Min(length, source.Length - fieldoffset)); - Array.Copy(source.ToCharArray(), fieldoffset, buffer, bufferoffset, finalLength); - return finalLength; - } - - public override void Dispose() - { - _inner.Dispose(); - } - } - - internal sealed class ForwardsOnlyRow : NpgsqlRow - { - private int _lastIndex = -1; - private readonly RowReader _reader; - - public ForwardsOnlyRow(RowReader reader) - { - _reader = reader; - } - - private void SetIndex(int index, bool allowCurrent) - { - if (index < 0 || index >= NumFields) - { - throw new IndexOutOfRangeException(); - } - if (allowCurrent && _reader.CurrentlyStreaming ? index < _lastIndex : index <= _lastIndex) - { - throw new InvalidOperationException( - string.Format(resman.GetString("Row_Sequential_Field_Error"), index, _lastIndex + 1)); - } - _reader.Skip(index - _lastIndex - 1); - _lastIndex = index; - } - - public override object this[int index] - { - get - { - SetIndex(index, false); - return _reader.GetNext(); - } - } - - public override long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length) - { - if (buffer == null) - { - throw new NotSupportedException(); - } - if (!_reader.CanGetByteStream(i)) - { - throw new InvalidCastException(); - } - SetIndex(i, true); - _reader.SkipBytesTo(fieldOffset); - return _reader.Read(buffer, bufferoffset, length); - } - - public override long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length) - { - if (buffer == null) - { - throw new NotSupportedException(); - } - if (!_reader.CanGetCharStream(i)) - { - throw new InvalidCastException(); - } - SetIndex(i, true); - _reader.SkipCharsTo(fieldoffset); - return _reader.Read(buffer, bufferoffset, length); - } - - public override int NumFields - { - get { return _reader.NumFields; } - } - - public override bool IsDBNull(int index) - { - if (_lastIndex > -1) - { - SetIndex(index - 1, true); - } - return _reader.IsNextDBNull; - } - - public override void Dispose() - { - _reader.Dispose(); - } - } - - /// <summary> - /// Reads a row, field by field, allowing a DataRow to be built appropriately. - /// </summary> - internal abstract class RowReader : IStreamOwner - { - /// <summary> - /// Reads part of a field, as needed (for <see cref="System.Data.IDataRecord.GetChars()"/> - /// and <see cref="System.Data.IDataRecord.GetBytes()"/> - /// </summary> - protected abstract class Streamer : IStreamOwner - { - protected readonly Stream _stream; - protected int _remainingBytes; - private int _alreadyRead = 0; - - protected Streamer(Stream stream, int remainingBytes) - { - _stream = stream; - _remainingBytes = remainingBytes; - } - - public int AlreadyRead - { - get { return _alreadyRead; } - protected set { _alreadyRead = value; } - } - - public void Dispose() - { - PGUtil.EatStreamBytes(_stream, _remainingBytes); - } - } - - /// <summary> - /// Adds further functionality to stream that is dependant upon the type of data read. - /// </summary> - protected abstract class Streamer<T> : Streamer - { - protected Streamer(Stream stream, int remainingBytes) - : base(stream, remainingBytes) - { - } - - public abstract int DoRead(T[] output, int outputIdx, int length); - public abstract int DoSkip(int length); - - public int Read(T[] output, int outputIdx, int length) - { - int ret = DoRead(output, outputIdx, length); - AlreadyRead += ret; - return ret; - } - - private void Skip(int length) - { - AlreadyRead += DoSkip(length); - } - - public void SkipTo(long position) - { - if (position < AlreadyRead) - { - throw new InvalidOperationException(); - } - Skip((int) position - AlreadyRead); - } - } - - /// <summary> - /// Completes the implementation of Streamer for char data. - /// </summary> - protected sealed class CharStreamer : Streamer<char> - { - public CharStreamer(Stream stream, int remainingBytes) - : base(stream, remainingBytes) - { - } - - public override int DoRead(char[] output, int outputIdx, int length) - { - return PGUtil.ReadChars(_stream, output, length, ref _remainingBytes, outputIdx); - } - - public override int DoSkip(int length) - { - return PGUtil.SkipChars(_stream, length, ref _remainingBytes); - } - } - - /// <summary> - /// Completes the implementation of Streamer for byte data. - /// </summary> - protected sealed class ByteStreamer : Streamer<byte> - { - public ByteStreamer(Stream stream, int remainingBytes) - : base(stream, remainingBytes) - { - } - - public override int DoRead(byte[] output, int outputIdx, int length) - { - return PGUtil.ReadEscapedBytes(_stream, output, length, ref _remainingBytes, outputIdx); - } - - public override int DoSkip(int length) - { - return PGUtil.SkipEscapedBytes(_stream, length, ref _remainingBytes); - } - } - - protected static readonly Encoding UTF8Encoding = Encoding.UTF8; - private readonly NpgsqlRowDescription _rowDesc; - private readonly Stream _stream; - private Streamer _streamer; - private int _currentField = -1; - - public RowReader(NpgsqlRowDescription rowDesc, Stream stream) - { - _rowDesc = rowDesc; - _stream = stream; - } - - protected Streamer CurrentStreamer - { - get { return _streamer; } - set - { - if (_streamer != null) - { - _streamer.Dispose(); - } - _streamer = value; - } - } - - public bool CurrentlyStreaming - { - get { return _streamer != null; } - } - - public bool CanGetByteStream(int index) - { -//TODO: Add support for byte[] being read as a stream of bytes. - return _rowDesc[index].TypeInfo.NpgsqlDbType == NpgsqlDbType.Bytea; - } - - public bool CanGetCharStream(int index) - { -//TODO: Add support for arrays of string types? - return _rowDesc[index].TypeInfo.Type.Equals(typeof (string)); - } - - protected Streamer<byte> CurrentByteStreamer - { - get - { - if (CurrentStreamer == null) - { - if (!CanGetByteStream(_currentField + 1)) - { - throw new InvalidCastException(); - } - ++_currentField; - return (CurrentStreamer = new ByteStreamer(Stream, GetNextFieldCount())) as ByteStreamer; - } - else if (!(CurrentStreamer is Streamer<byte>)) - { - throw new InvalidOperationException(); - } - else - { - return CurrentStreamer as ByteStreamer; - } - } - } - - protected Streamer<char> CurrentCharStreamer - { - get - { - if (CurrentStreamer == null) - { - if (!CanGetCharStream(_currentField + 1)) - { - throw new InvalidCastException(); - } - ++_currentField; - return (CurrentStreamer = new CharStreamer(Stream, GetNextFieldCount())) as CharStreamer; - } - else if (!(CurrentStreamer is Streamer<char>)) - { - throw new InvalidOperationException(); - } - else - { - return CurrentStreamer as CharStreamer; - } - } - } - - protected Stream Stream - { - get { return _stream; } - } - - protected NpgsqlRowDescription.FieldData FieldData - { - get { return _rowDesc[_currentField]; } - } - - public int NumFields - { - get { return _rowDesc.NumFields; } - } - - protected int CurrentField - { - get { return _currentField; } - } - - protected abstract object ReadNext(); - - public object GetNext() - { - if (++_currentField == _rowDesc.NumFields) - { - throw new IndexOutOfRangeException(); - } - return ReadNext(); - } - - public abstract bool IsNextDBNull { get; } - protected abstract void SkipOne(); - - public void Skip(int count) - { - if (count > 0) - { - if (_currentField + count >= _rowDesc.NumFields) - { - throw new IndexOutOfRangeException(); - } - while (count-- > 0) - { - ++_currentField; - SkipOne(); - } - } - } - - protected abstract int GetNextFieldCount(); - - public int Read(byte[] output, int outputIdx, int length) - { - return CurrentByteStreamer.Read(output, outputIdx, length); - } - - public void SkipBytesTo(long position) - { - CurrentByteStreamer.SkipTo(position); - } - - public int Read(char[] output, int outputIdx, int length) - { - return CurrentCharStreamer.Read(output, outputIdx, length); - } - - public void SkipCharsTo(long position) - { - CurrentCharStreamer.SkipTo(position); - } - public void Dispose() - { - CurrentStreamer = null; - Skip(_rowDesc.NumFields - _currentField - 1); - } - } -}
\ No newline at end of file + /// <summary> + /// This is the abstract base class for NpgsqlAsciiRow and NpgsqlBinaryRow. + /// </summary> + internal abstract class NpgsqlRow + { + // Logging related values + private static readonly String CLASSNAME = "NpgsqlRow"; + + protected ArrayList data; + protected NpgsqlRowDescription row_desc; + protected ProtocolVersion protocol_version; + + public NpgsqlRow(NpgsqlRowDescription rowDesc, ProtocolVersion protocolVersion) + { + data = new ArrayList(); + row_desc = rowDesc; + protocol_version = protocolVersion; + } + + public virtual void ReadFromStream(Stream inputStream, Encoding encoding) + { + throw new NotImplementedException("Abstract"); + } + + /// <summary> + /// Provide access to the fields in this row. + /// </summary> + public virtual Object this[Int32 index] + { + get + { + NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, index); + if ((index < 0) || (index >= row_desc.NumFields)) { + throw new IndexOutOfRangeException("this[] index value"); + } + + return data[index]; + } + } + } + +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlRowDescription.cs b/mcs/class/Npgsql/Npgsql/NpgsqlRowDescription.cs index 32104f38bcb..0aa3c2fc63c 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlRowDescription.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlRowDescription.cs @@ -9,207 +9,230 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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.Collections.Generic; +using System.Collections; using System.IO; +using System.Text; +using System.Net; +using System.Globalization; + using NpgsqlTypes; namespace Npgsql { - /// <summary> - /// This class represents a RowDescription message sent from - /// the PostgreSQL. - /// </summary> - /// - internal abstract class NpgsqlRowDescription : IServerResponseObject - { - /// <summary> - /// This struct represents the internal data of the RowDescription message. - /// </summary> - public abstract class FieldData - { - private string _name; // Protocol 2/3 - private int _typeOID; // Protocol 2/3 - private short _typeSize; // Protocol 2/3 - private int _typeModifier; // Protocol 2/3 - private int _tableOID; // Protocol 3 - private short _columnAttributeNumber; // Protocol 3 - private FormatCode _formatCode; // Protocol 3. 0 text, 1 binary - private NpgsqlBackendTypeInfo _typeInfo; // everything we know about this field type - - public string Name - { - get { return _name; } - protected set { _name = value; } - } - - public int TypeOID - { - get { return _typeOID; } - protected set { _typeOID = value; } - } - - public short TypeSize - { - get { return _typeSize; } - protected set { _typeSize = value; } - } - - public int TypeModifier - { - get { return _typeModifier; } - protected set { _typeModifier = value; } - } - - public int TableOID - { - get { return _tableOID; } - protected set { _tableOID = value; } - } - - public short ColumnAttributeNumber - { - get { return _columnAttributeNumber; } - protected set { _columnAttributeNumber = value; } - } - - public FormatCode FormatCode - { - get { return _formatCode; } - protected set { _formatCode = value; } - } - - public NpgsqlBackendTypeInfo TypeInfo - { - get { return _typeInfo; } - protected set { _typeInfo = value; } - } - } - - private readonly FieldData[] fields_data; - private readonly Dictionary<string, int> field_name_index_table; - private readonly Dictionary<string, int> caseInsensitiveNameIndexTable; - - protected NpgsqlRowDescription(Stream stream, NpgsqlBackendTypeMapping type_mapping) - { - int num = ReadNumFields(stream); - fields_data = new FieldData[num]; - field_name_index_table = new Dictionary<string, int>(num, StringComparer.InvariantCulture); - caseInsensitiveNameIndexTable = new Dictionary<string, int>(num, StringComparer.InvariantCultureIgnoreCase); - for (int i = 0; i != num; ++i) - { - FieldData fd = BuildFieldData(stream, type_mapping); - fields_data[i] = fd; - if (!field_name_index_table.ContainsKey(fd.Name)) - { - field_name_index_table.Add(fd.Name, i); - if (!caseInsensitiveNameIndexTable.ContainsKey(fd.Name)) - { - caseInsensitiveNameIndexTable.Add(fd.Name, i); - } - } - } - } - - protected abstract FieldData BuildFieldData(Stream stream, NpgsqlBackendTypeMapping typeMapping); - protected abstract int ReadNumFields(Stream stream); - - public FieldData this[int index] - { - get { return fields_data[index]; } - } - - public int NumFields - { - get { return (Int16) fields_data.Length; } - } - - public int FieldIndex(String fieldName) - { - int ret = -1; - return - field_name_index_table.TryGetValue(fieldName, out ret) - ? ret - : (caseInsensitiveNameIndexTable.TryGetValue(fieldName, out ret) ? ret : -1); - } - } - - internal sealed class NpgsqlRowDescriptionV2 : NpgsqlRowDescription - { - public NpgsqlRowDescriptionV2(Stream stream, NpgsqlBackendTypeMapping typeMapping) - : base(stream, typeMapping) - { - } - - private sealed class FieldDataV2 : FieldData - { - public FieldDataV2(Stream stream, NpgsqlBackendTypeMapping typeMapping) - { - Name = PGUtil.ReadString(stream); - TypeInfo = typeMapping[TypeOID = PGUtil.ReadInt32(stream)]; - TypeSize = PGUtil.ReadInt16(stream); - TypeModifier = PGUtil.ReadInt32(stream); - } - } - - protected override FieldData BuildFieldData(Stream stream, NpgsqlBackendTypeMapping type_mapping) - { - return new FieldDataV2(stream, type_mapping); - } - - protected override int ReadNumFields(Stream stream) - { - return PGUtil.ReadInt16(stream); - } - } - - internal sealed class NpgsqlRowDescriptionV3 : NpgsqlRowDescription - { - private sealed class FieldDataV3 : FieldData - { - public FieldDataV3(Stream stream, NpgsqlBackendTypeMapping typeMapping) - { - Name = PGUtil.ReadString(stream); - TableOID = PGUtil.ReadInt32(stream); - ColumnAttributeNumber = PGUtil.ReadInt16(stream); - TypeInfo = typeMapping[TypeOID = PGUtil.ReadInt32(stream)]; - TypeSize = PGUtil.ReadInt16(stream); - TypeModifier = PGUtil.ReadInt32(stream); - FormatCode = (FormatCode) PGUtil.ReadInt16(stream); - } - } - - public NpgsqlRowDescriptionV3(Stream stream, NpgsqlBackendTypeMapping typeMapping) - : base(stream, typeMapping) - { - } - - protected override FieldData BuildFieldData(Stream stream, NpgsqlBackendTypeMapping typeMapping) - { - return new FieldDataV3(stream, typeMapping); - } - - protected override int ReadNumFields(Stream stream) - { - PGUtil.EatStreamBytes(stream, 4); - return PGUtil.ReadInt16(stream); - } - } -}
\ No newline at end of file + + + /// <summary> + /// This struct represents the internal data of the RowDescription message. + /// </summary> + /// + // [FIXME] Is this name OK? Does it represent well the struct intent? + // Should it be a struct or a class? + internal struct NpgsqlRowDescriptionFieldData + { + public String name; // Protocol 2/3 + public Int32 table_oid; // Protocol 3 + public Int16 column_attribute_number; // Protocol 3 + public Int32 type_oid; // Protocol 2/3 + public Int16 type_size; // Protocol 2/3 + public Int32 type_modifier; // Protocol 2/3 + public FormatCode format_code; // Protocol 3. 0 text, 1 binary + public NpgsqlBackendTypeInfo type_info; // everything we know about this field type + } + + /// <summary> + /// This class represents a RowDescription message sent from + /// the PostgreSQL. + /// </summary> + /// + internal sealed class NpgsqlRowDescription + { + // Logging related values + private static readonly String CLASSNAME = "NpgsqlRowDescription"; + + + private NpgsqlRowDescriptionFieldData[] fields_data; + private string[] fields_index; + private Hashtable field_name_index_table; + + private ProtocolVersion protocol_version; + + public NpgsqlRowDescription(ProtocolVersion protocolVersion) + { + protocol_version = protocolVersion; + } + + public void ReadFromStream(Stream input_stream, Encoding encoding, NpgsqlBackendTypeMapping type_mapping) + { + switch (protocol_version) + { + case ProtocolVersion.Version2 : + ReadFromStream_Ver_2(input_stream, encoding, type_mapping); + break; + + case ProtocolVersion.Version3 : + ReadFromStream_Ver_3(input_stream, encoding, type_mapping); + break; + + } + } + + private void ReadFromStream_Ver_2(Stream input_stream, Encoding encoding, NpgsqlBackendTypeMapping type_mapping) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_2"); + + Byte[] input_buffer = new Byte[10]; // Max read will be 4 + 2 + 4 + + // Read the number of fields. + input_stream.Read(input_buffer, 0, 2); + Int16 num_fields = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(input_buffer, 0)); + + + // Temporary FieldData object to get data from stream and put in array. + NpgsqlRowDescriptionFieldData fd; + + fields_data = new NpgsqlRowDescriptionFieldData[num_fields]; + fields_index = new string[num_fields]; + + field_name_index_table = new Hashtable(num_fields); + + + // Now, iterate through each field getting its data. + for (Int16 i = 0; i < num_fields; i++) + { + fd = new NpgsqlRowDescriptionFieldData(); + + // Set field name. + fd.name = PGUtil.ReadString(input_stream, encoding); + + // Read type_oid(Int32), type_size(Int16), type_modifier(Int32) + input_stream.Read(input_buffer, 0, 4 + 2 + 4); + + fd.type_oid = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(input_buffer, 0)); + fd.type_info = type_mapping[fd.type_oid]; + fd.type_size = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(input_buffer, 4)); + fd.type_modifier = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(input_buffer, 6)); + + // Add field data to array. + fields_data[i] = fd; + + fields_index[i] = fd.name; + + if (!field_name_index_table.ContainsKey(fd.name)) + field_name_index_table.Add(fd.name, i); + } + } + + private void ReadFromStream_Ver_3(Stream input_stream, Encoding encoding, NpgsqlBackendTypeMapping type_mapping) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadFromStream_Ver_3"); + + Byte[] input_buffer = new Byte[4]; // Max read will be 4 + 2 + 4 + 2 + 4 + 2 + + // Read the length of message. + // [TODO] Any use for now? + PGUtil.ReadInt32(input_stream, input_buffer); + Int16 num_fields = PGUtil.ReadInt16(input_stream, input_buffer); + + // Temporary FieldData object to get data from stream and put in array. + NpgsqlRowDescriptionFieldData fd; + + fields_data = new NpgsqlRowDescriptionFieldData[num_fields]; + fields_index = new string[num_fields]; + field_name_index_table = new Hashtable(num_fields); + + for (Int16 i = 0; i < num_fields; i++) + { + fd = new NpgsqlRowDescriptionFieldData(); + + fd.name = PGUtil.ReadString(input_stream, encoding); + fd.table_oid = PGUtil.ReadInt32(input_stream, input_buffer); + fd.column_attribute_number = PGUtil.ReadInt16(input_stream, input_buffer); + fd.type_oid = PGUtil.ReadInt32(input_stream, input_buffer); + fd.type_info = type_mapping[fd.type_oid]; + fd.type_size = PGUtil.ReadInt16(input_stream, input_buffer); + fd.type_modifier = PGUtil.ReadInt32(input_stream, input_buffer); + fd.format_code = (FormatCode)PGUtil.ReadInt16(input_stream, input_buffer); + + fields_data[i] = fd; + fields_index[i] = fd.name; + + if (!field_name_index_table.ContainsKey(fd.name)) + field_name_index_table.Add(fd.name, i); + } + } + + public NpgsqlRowDescriptionFieldData this[Int32 index] + { + get + { + return fields_data[index]; + } + } + + public Int16 NumFields + { + get + { + return (Int16)fields_data.Length; + } + } + + public Int16 FieldIndex(String fieldName) + { + + + // First try to find with hashtable, case sensitive. + + Object result1 = field_name_index_table[fieldName]; + + if (result1 != null) + return (Int16)result1; + + + result1 = field_name_index_table[fieldName.ToLower(CultureInfo.InvariantCulture)]; + + if (result1 != null) + return (Int16)result1; + + // Then the index with IndexOf (case-sensitive) + + + Int16 result = (Int16)Array.IndexOf(fields_index, fieldName, 0, fields_index.Length); + + if (result != -1) + { + return result; + } + else + { + + foreach(string name in fields_index) + { + ++result; + if (string.Compare(name, fieldName, true, CultureInfo.InvariantCulture) == 0) + return result; + } + } + + return -1; + + + } + + } +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlSchema.cs b/mcs/class/Npgsql/Npgsql/NpgsqlSchema.cs index d37cd151849..354d83672f5 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlSchema.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlSchema.cs @@ -7,27 +7,23 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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.IO; -using System.Reflection; using System.Text; namespace Npgsql @@ -36,275 +32,255 @@ namespace Npgsql /// Provides the underlying mechanism for reading schema information. /// </summary> internal sealed class NpgsqlSchema - { - private readonly NpgsqlConnection _connection; - - /// <summary> - /// Creates an NpgsqlSchema that can read schema information from the database. - /// </summary> - /// <param name="connection">An open database connection for reading metadata.</param> - internal NpgsqlSchema(NpgsqlConnection connection) - { - _connection = connection; - } - - /// <summary> - /// Returns the MetaDataCollections that lists all possible collections. - /// </summary> - /// <returns>The MetaDataCollections</returns> - internal static DataTable GetMetaDataCollections() - { - DataTable metaDataCollections = new DataTable("MetaDataCollections"); - - metaDataCollections.Columns.AddRange( - new DataColumn[] - { - new DataColumn("CollectionName"), new DataColumn("NumberOfRestrictions", typeof (int)), - new DataColumn("NumberOfIdentifierParts", typeof (int)) - }); - - // Add(object[] { CollectionName, NumberOfRestrictions, NumberOfIdentifierParts }) - metaDataCollections.Rows.Add(new object[] {"MetaDataCollections", 0, 0}); - metaDataCollections.Rows.Add(new object[] {"Restrictions", 0, 0}); - metaDataCollections.Rows.Add(new object[] {"Databases", 1, 1}); - metaDataCollections.Rows.Add(new object[] {"Tables", 4, 3}); - metaDataCollections.Rows.Add(new object[] {"Columns", 4, 4}); - metaDataCollections.Rows.Add(new object[] {"Views", 3, 3}); - metaDataCollections.Rows.Add(new object[] {"Users", 1, 1}); - - return metaDataCollections; - } - - /// <summary> - /// Returns the Restrictions that contains the meaning and position of the values in the restrictions array. - /// </summary> - /// <returns>The Restrictions</returns> - internal static DataTable GetRestrictions() - { - DataTable restrictions = new DataTable("Restrictions"); - - restrictions.Columns.AddRange( - new DataColumn[] - { - new DataColumn("CollectionName"), new DataColumn("RestrictionName"), new DataColumn("RestrictionDefault"), - new DataColumn("RestrictionNumber", typeof (int)) - }); - - restrictions.Rows.Add(new object[] {"Databases", "Name", "Name", 1}); - restrictions.Rows.Add(new object[] {"Tables", "Catalog", "table_catalog", 1}); - restrictions.Rows.Add(new object[] {"Tables", "Schema", "table_schema", 2}); - restrictions.Rows.Add(new object[] {"Tables", "Table", "table_name", 3}); - restrictions.Rows.Add(new object[] {"Tables", "TableType", "table_type", 4}); - restrictions.Rows.Add(new object[] {"Columns", "Catalog", "table_catalog", 1}); - restrictions.Rows.Add(new object[] {"Columns", "Schema", "table_schema", 2}); - restrictions.Rows.Add(new object[] {"Columns", "Table", "table_name", 3}); - restrictions.Rows.Add(new object[] {"Columns", "Column", "column_name", 4}); - restrictions.Rows.Add(new object[] {"Views", "Catalog", "table_catalog", 1}); - restrictions.Rows.Add(new object[] {"Views", "Schema", "table_schema", 2}); - restrictions.Rows.Add(new object[] {"Views", "Table", "table_name", 3}); - - return restrictions; - } - - private NpgsqlCommand BuildCommand(StringBuilder query, string[] restrictions, params string[] names) - { - NpgsqlCommand command = new NpgsqlCommand(); - - if (restrictions != null && names != null) - { - bool addWhere = true; - for (int i = 0; i < restrictions.Length && i < names.Length; ++i) - { - if (restrictions[i] != null && restrictions[i].Length != 0) - { - if (addWhere) - { - query.Append(" WHERE "); - addWhere = false; - } - else - { - query.Append(" AND "); - } - query.AppendFormat("{0} = :{0}", names[i]); - - command.Parameters.Add(new NpgsqlParameter(names[i], restrictions[i])); - } - } - } - command.CommandText = query.ToString(); - command.Connection = _connection; - - return command; - } - - /// <summary> - /// Returns the Databases that contains a list of all accessable databases. - /// </summary> - /// <param name="restrictions">The restrictions to filter the collection.</param> - /// <returns>The Databases</returns> - internal DataTable GetDatabases(string[] restrictions) - { - DataTable databases = new DataTable("Databases"); - - databases.Columns.AddRange( - new DataColumn[] {new DataColumn("database_name"), new DataColumn("owner"), new DataColumn("encoding")}); - - StringBuilder getDatabases = new StringBuilder(); - - getDatabases.Append( - "SELECT d.datname AS database_name, u.usename AS owner, pg_catalog.pg_encoding_to_char(d.encoding) AS encoding FROM pg_catalog.pg_database d LEFT JOIN pg_catalog.pg_user u ON d.datdba = u.usesysid"); - - using (NpgsqlCommand command = BuildCommand(getDatabases, restrictions, "datname")) - { - using (NpgsqlDataAdapter adapter = new NpgsqlDataAdapter(command)) - { - adapter.Fill(databases); - } - } - - return databases; - } - - /// <summary> - /// Returns the Tables that contains table and view names and the database and schema they come from. - /// </summary> - /// <param name="restrictions">The restrictions to filter the collection.</param> - /// <returns>The Tables</returns> - internal DataTable GetTables(string[] restrictions) - { - DataTable tables = new DataTable("Tables"); - - tables.Columns.AddRange( - new DataColumn[] - { - new DataColumn("table_catalog"), new DataColumn("table_schema"), new DataColumn("table_name"), - new DataColumn("table_type") - }); - - StringBuilder getTables = new StringBuilder(); - - getTables.Append("SELECT table_catalog, table_schema, table_name, table_type FROM information_schema.tables"); - - using ( - NpgsqlCommand command = - BuildCommand(getTables, restrictions, "table_catalog", "table_schema", "table_name", "table_type")) - { - using (NpgsqlDataAdapter adapter = new NpgsqlDataAdapter(command)) - { - adapter.Fill(tables); - } - } - - return tables; - } - - /// <summary> - /// Returns the Columns that contains information about columns in tables. - /// </summary> - /// <param name="restrictions">The restrictions to filter the collection.</param> - /// <returns>The Columns.</returns> - internal DataTable GetColumns(string[] restrictions) - { - DataTable columns = new DataTable("Columns"); - - columns.Columns.AddRange( - new DataColumn[] - { - new DataColumn("table_catalog"), new DataColumn("table_schema"), new DataColumn("table_name"), - new DataColumn("column_name"), new DataColumn("ordinal_position", typeof (int)), new DataColumn("column_default"), - new DataColumn("is_nullable"), new DataColumn("data_type"), - new DataColumn("character_maximum_length", typeof (int)), new DataColumn("character_octet_length", typeof (int)), - new DataColumn("numeric_precision", typeof (int)), new DataColumn("numeric_precision_radix", typeof (int)), - new DataColumn("numeric_scale", typeof (int)), new DataColumn("datetime_precision", typeof (int)), - new DataColumn("character_set_catalog"), new DataColumn("character_set_schema"), - new DataColumn("character_set_name"), new DataColumn("collation_catalog") - }); - - StringBuilder getColumns = new StringBuilder(); - - getColumns.Append( - "SELECT table_catalog, table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, udt_name AS data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_precision_radix, numeric_scale, datetime_precision, character_set_catalog, character_set_schema, character_set_name, collation_catalog FROM information_schema.columns"); - - using ( - NpgsqlCommand command = - BuildCommand(getColumns, restrictions, "table_catalog", "table_schema", "table_name", "column_name")) - { - using (NpgsqlDataAdapter adapter = new NpgsqlDataAdapter(command)) - { - adapter.Fill(columns); - } - } - - return columns; - } - - /// <summary> - /// Returns the Views that contains view names and the database and schema they come from. - /// </summary> - /// <param name="restrictions">The restrictions to filter the collection.</param> - /// <returns>The Views</returns> - internal DataTable GetViews(string[] restrictions) - { - DataTable views = new DataTable("Views"); - - views.Columns.AddRange( - new DataColumn[] - { - new DataColumn("table_catalog"), new DataColumn("table_schema"), new DataColumn("table_name"), - new DataColumn("check_option"), new DataColumn("is_updatable") - }); - - StringBuilder getViews = new StringBuilder(); - - getViews.Append( - "SELECT table_catalog, table_schema, table_name, check_option, is_updatable FROM information_schema.views"); - - using (NpgsqlCommand command = BuildCommand(getViews, restrictions, "table_catalog", "table_schema", "table_name")) - { - using (NpgsqlDataAdapter adapter = new NpgsqlDataAdapter(command)) - { - adapter.Fill(views); - } - } - - return views; - } - - /// <summary> - /// Returns the Users containing user names and the sysid of those users. - /// </summary> - /// <param name="restrictions">The restrictions to filter the collection.</param> - /// <returns>The Users.</returns> - internal DataTable GetUsers(string[] restrictions) - { - DataTable users = new DataTable("Users"); - - users.Columns.AddRange(new DataColumn[] {new DataColumn("user_name"), new DataColumn("user_sysid", typeof (int))}); - - StringBuilder getUsers = new StringBuilder(); - - getUsers.Append("SELECT usename as user_name, usesysid as user_sysid FROM pg_catalog.pg_user"); - - using (NpgsqlCommand command = BuildCommand(getUsers, restrictions, "usename")) - { - using (NpgsqlDataAdapter adapter = new NpgsqlDataAdapter(command)) - { - adapter.Fill(users); - } - } - - return users; - } - - internal static DataTable GetDataSourceInformation() - { - DataSet ds = new DataSet(); - using (Stream xmlStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Npgsql.NpgsqlMetaData.xml")) - { - ds.ReadXml(xmlStream); - } - return ds.Tables["DataSourceInformation"].Copy(); - } + { + private NpgsqlConnection _connection; + + /// <summary> + /// Creates an NpgsqlSchema that can read schema information from the database. + /// </summary> + /// <param name="connection">An open database connection for reading metadata.</param> + internal NpgsqlSchema(NpgsqlConnection connection) + { + _connection = connection; + } + + /// <summary> + /// Returns the MetaDataCollections that lists all possible collections. + /// </summary> + /// <returns>The MetaDataCollections</returns> + internal static DataTable GetMetaDataCollections() + { + DataTable metaDataCollections = new DataTable("MetaDataCollections"); + + metaDataCollections.Columns.AddRange(new DataColumn[] { + new DataColumn("CollectionName"), + new DataColumn("NumberOfRestrictions", typeof(int)), + new DataColumn("NumberOfIdentifierParts", typeof(int)) }); + + // Add(object[] { CollectionName, NumberOfRestrictions, NumberOfIdentifierParts }) + metaDataCollections.Rows.Add(new object[]{"MetaDataCollections",0,0}); + metaDataCollections.Rows.Add(new object[]{"Restrictions",0,0}); + metaDataCollections.Rows.Add(new object[]{"Databases",1,1}); + metaDataCollections.Rows.Add(new object[]{"Tables",4,3}); + metaDataCollections.Rows.Add(new object[]{"Columns",4,4}); + metaDataCollections.Rows.Add(new object[]{"Views",3,3}); + metaDataCollections.Rows.Add(new object[]{"Users",1,1}); + + return metaDataCollections; + } + + /// <summary> + /// Returns the Restrictions that contains the meaning and position of the values in the restrictions array. + /// </summary> + /// <returns>The Restrictions</returns> + internal static DataTable GetRestrictions() + { + DataTable restrictions = new DataTable("Restrictions"); + + restrictions.Columns.AddRange(new DataColumn[] { + new DataColumn("CollectionName"), + new DataColumn("RestrictionName"), + new DataColumn("RestrictionDefault"), + new DataColumn("RestrictionNumber", typeof(int)) }); + + restrictions.Rows.Add(new object[]{"Databases","Name","Name",1}); + restrictions.Rows.Add(new object[]{"Tables","Catalog","table_catalog",1}); + restrictions.Rows.Add(new object[]{"Tables","Schema","table_schema",2}); + restrictions.Rows.Add(new object[]{"Tables","Table","table_name",3}); + restrictions.Rows.Add(new object[]{"Tables","TableType","table_type",4}); + restrictions.Rows.Add(new object[]{"Columns","Catalog","table_catalog",1}); + restrictions.Rows.Add(new object[]{"Columns","Schema","table_schema",2}); + restrictions.Rows.Add(new object[]{"Columns","Table","table_name",3}); + restrictions.Rows.Add(new object[]{"Columns","Column","column_name",4}); + restrictions.Rows.Add(new object[]{"Views","Catalog","table_catalog",1}); + restrictions.Rows.Add(new object[]{"Views","Schema","table_schema",2}); + restrictions.Rows.Add(new object[]{"Views","Table","table_name",3}); + + return restrictions; + } + + private NpgsqlCommand BuildCommand(StringBuilder query, string[] restrictions, params string[] names) + { + NpgsqlCommand command = new NpgsqlCommand(); + + if (restrictions != null && names != null) + { + bool addWhere = true; + for(int i=0; i<restrictions.Length && i<names.Length; ++i) + { + if (restrictions[i] != null && restrictions[i].Length != 0) + { + if (addWhere) + { + query.Append(" WHERE "); + addWhere = false; + } + else + { + query.Append(" AND "); + } + query.AppendFormat("{0} = :{0}", names[i]); + + command.Parameters.Add(new NpgsqlParameter(names[i], restrictions[i])); + } + } + } + command.CommandText = query.ToString(); + command.Connection = _connection; + + return command; + } + + /// <summary> + /// Returns the Databases that contains a list of all accessable databases. + /// </summary> + /// <param name="restrictions">The restrictions to filter the collection.</param> + /// <returns>The Databases</returns> + internal DataTable GetDatabases(string[] restrictions) + { + DataTable databases = new DataTable("Databases"); + + databases.Columns.AddRange(new DataColumn[] { + new DataColumn("database_name"), + new DataColumn("owner"), + new DataColumn("encoding") }); + + StringBuilder getDatabases = new StringBuilder(); + + getDatabases.Append("SELECT d.datname AS database_name, u.usename AS owner, pg_catalog.pg_encoding_to_char(d.encoding) AS encoding FROM pg_catalog.pg_database d LEFT JOIN pg_catalog.pg_user u ON d.datdba = u.usesysid"); + + using (NpgsqlCommand command = BuildCommand(getDatabases, restrictions, "datname")) + using (NpgsqlDataAdapter adapter = new NpgsqlDataAdapter(command)) + { + adapter.Fill(databases); + } + + return databases; + } + + /// <summary> + /// Returns the Tables that contains table and view names and the database and schema they come from. + /// </summary> + /// <param name="restrictions">The restrictions to filter the collection.</param> + /// <returns>The Tables</returns> + internal DataTable GetTables(string[] restrictions) + { + DataTable tables = new DataTable("Tables"); + + tables.Columns.AddRange(new DataColumn[] { + new DataColumn("table_catalog"), + new DataColumn("table_schema"), + new DataColumn("table_name"), + new DataColumn("table_type") }); + + StringBuilder getTables = new StringBuilder(); + + getTables.Append("SELECT table_catalog, table_schema, table_name, table_type FROM information_schema.tables"); + + using (NpgsqlCommand command = BuildCommand(getTables, restrictions, "table_catalog", "table_schema", "table_name", "table_type")) + using (NpgsqlDataAdapter adapter = new NpgsqlDataAdapter(command)) + { + adapter.Fill(tables); + } + + return tables; + } + + /// <summary> + /// Returns the Columns that contains information about columns in tables. + /// </summary> + /// <param name="restrictions">The restrictions to filter the collection.</param> + /// <returns>The Columns.</returns> + internal DataTable GetColumns(string[] restrictions) + { + DataTable columns = new DataTable("Columns"); + + columns.Columns.AddRange(new DataColumn[] { + new DataColumn("table_catalog"), + new DataColumn("table_schema"), + new DataColumn("table_name"), + new DataColumn("column_name"), + new DataColumn("ordinal_position", typeof(int)), + new DataColumn("column_default"), + new DataColumn("is_nullable"), + new DataColumn("data_type"), + new DataColumn("character_maximum_length", typeof(int)), + new DataColumn("character_octet_length", typeof(int)), + new DataColumn("numeric_precision", typeof(int)), + new DataColumn("numeric_precision_radix", typeof(int)), + new DataColumn("numeric_scale", typeof(int)), + new DataColumn("datetime_precision", typeof(int)), + new DataColumn("character_set_catalog"), + new DataColumn("character_set_schema"), + new DataColumn("character_set_name"), + new DataColumn("collation_catalog") }); + + StringBuilder getColumns = new StringBuilder(); + + getColumns.Append("SELECT table_catalog, table_schema, table_name, column_name, ordinal_position, column_default, is_nullable, udt_name AS data_type, character_maximum_length, character_octet_length, numeric_precision, numeric_precision_radix, numeric_scale, datetime_precision, character_set_catalog, character_set_schema, character_set_name, collation_catalog FROM information_schema.columns"); + + using (NpgsqlCommand command = BuildCommand(getColumns, restrictions, "table_catalog", "table_schema", "table_name", "column_name")) + using (NpgsqlDataAdapter adapter = new NpgsqlDataAdapter(command)) + { + adapter.Fill(columns); + } + + return columns; + } + + /// <summary> + /// Returns the Views that contains view names and the database and schema they come from. + /// </summary> + /// <param name="restrictions">The restrictions to filter the collection.</param> + /// <returns>The Views</returns> + internal DataTable GetViews(string[] restrictions) + { + DataTable views = new DataTable("Views"); + + views.Columns.AddRange(new DataColumn[] { + new DataColumn("table_catalog"), + new DataColumn("table_schema"), + new DataColumn("table_name"), + new DataColumn("check_option"), + new DataColumn("is_updatable") }); + + StringBuilder getViews = new StringBuilder(); + + getViews.Append("SELECT table_catalog, table_schema, table_name, check_option, is_updatable FROM information_schema.views"); + + using (NpgsqlCommand command = BuildCommand(getViews, restrictions, "table_catalog", "table_schema", "table_name")) + using (NpgsqlDataAdapter adapter = new NpgsqlDataAdapter(command)) + { + adapter.Fill(views); + } + + return views; + } + + /// <summary> + /// Returns the Users containing user names and the sysid of those users. + /// </summary> + /// <param name="restrictions">The restrictions to filter the collection.</param> + /// <returns>The Users.</returns> + internal DataTable GetUsers(string[] restrictions) + { + DataTable users = new DataTable("Users"); + + users.Columns.AddRange(new DataColumn[] { + new DataColumn("user_name"), + new DataColumn("user_sysid", typeof(int)) }); + + StringBuilder getUsers = new StringBuilder(); + + getUsers.Append("SELECT usename as user_name, usesysid as user_sysid FROM pg_catalog.pg_user"); + + using (NpgsqlCommand command = BuildCommand(getUsers, restrictions, "usename")) + using (NpgsqlDataAdapter adapter = new NpgsqlDataAdapter(command)) + { + adapter.Fill(users); + } + + return users; + } } -}
\ No newline at end of file +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlStartupPacket.cs b/mcs/class/Npgsql/Npgsql/NpgsqlStartupPacket.cs index 223e17e89c4..47ef00f35a2 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlStartupPacket.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlStartupPacket.cs @@ -10,143 +10,146 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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; using System.Net; namespace Npgsql { - /// <summary> - /// This class represents a StartupPacket message of PostgreSQL - /// protocol. - /// </summary> - /// - internal sealed class NpgsqlStartupPacket : ClientMessage - { - // Logging related values - private static readonly String CLASSNAME = "NpgsqlStartupPacket"; - // Private fields. - private readonly Int32 packet_size; - private readonly ProtocolVersion protocol_version; - private readonly String database_name; - private readonly String user_name; - private readonly String arguments; - private readonly String unused; - private readonly String optional_tty; + /// <summary> + /// This class represents a StartupPacket message of PostgreSQL + /// protocol. + /// </summary> + /// + internal sealed class NpgsqlStartupPacket + { + // Logging related values + private static readonly String CLASSNAME = "NpgsqlStartupPacket"; + + // Private fields. + private Int32 packet_size; + private ProtocolVersion protocol_version; + private String database_name; + private String user_name; + private String arguments; + private String unused; + private String optional_tty; + + public NpgsqlStartupPacket(Int32 packet_size, + ProtocolVersion protocol_version, + String database_name, + String user_name, + String arguments, + String unused, + String optional_tty) + { + + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME); + // Just copy the values. + + // [FIXME] Validate params? We are the only clients, so, hopefully, we + // know what to send. - public NpgsqlStartupPacket(Int32 packet_size, ProtocolVersion protocol_version, String database_name, String user_name, - String arguments, String unused, String optional_tty) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME); - // Just copy the values. + this.packet_size = packet_size; + this.protocol_version = protocol_version; - // [FIXME] Validate params? We are the only clients, so, hopefully, we - // know what to send. + this.database_name = database_name; + this.user_name = user_name; + this.arguments = arguments; + this.unused = unused; + this.optional_tty = optional_tty; - this.packet_size = packet_size; - this.protocol_version = protocol_version; + } - this.database_name = database_name; - this.user_name = user_name; - this.arguments = arguments; - this.unused = unused; - this.optional_tty = optional_tty; - } + public void WriteToStream(Stream output_stream, Encoding encoding) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteToStream"); - public override void WriteToStream(Stream output_stream) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteToStream"); + switch (protocol_version) { + case ProtocolVersion.Version2 : + WriteToStream_Ver_2(output_stream, encoding); + break; - switch (protocol_version) - { - case ProtocolVersion.Version2: - WriteToStream_Ver_2(output_stream); - break; + case ProtocolVersion.Version3 : + WriteToStream_Ver_3(output_stream, encoding); + break; - case ProtocolVersion.Version3: - WriteToStream_Ver_3(output_stream); - break; - } - } + } + } - private void WriteToStream_Ver_2(Stream output_stream) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteToStream_Ver_2"); + private void WriteToStream_Ver_2(Stream output_stream, Encoding encoding) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteToStream_Ver_2"); - // Packet length = 296 - output_stream.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(this.packet_size)), 0, 4); + // Packet length = 296 + output_stream.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(this.packet_size)), 0, 4); - output_stream.Write( - BitConverter.GetBytes(IPAddress.HostToNetworkOrder(PGUtil.ConvertProtocolVersion(this.protocol_version))), 0, 4); + output_stream.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(PGUtil.ConvertProtocolVersion(this.protocol_version))), 0, 4); - // Database name. - PGUtil.WriteLimString(this.database_name, 64, output_stream); + // Database name. + PGUtil.WriteLimString(this.database_name, 64, output_stream, encoding); - // User name. - PGUtil.WriteLimString(this.user_name, 32, output_stream); + // User name. + PGUtil.WriteLimString(this.user_name, 32, output_stream, encoding); - // Arguments. - PGUtil.WriteLimString(this.arguments, 64, output_stream); + // Arguments. + PGUtil.WriteLimString(this.arguments, 64, output_stream, encoding); - // Unused. - PGUtil.WriteLimString(this.unused, 64, output_stream); + // Unused. + PGUtil.WriteLimString(this.unused, 64, output_stream, encoding); - // Optional tty. - PGUtil.WriteLimString(this.optional_tty, 64, output_stream); - output_stream.Flush(); - } + // Optional tty. + PGUtil.WriteLimString(this.optional_tty, 64, output_stream, encoding); + output_stream.Flush(); + } - private void WriteToStream_Ver_3(Stream output_stream) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteToStream_Ver_3"); + private void WriteToStream_Ver_3(Stream output_stream, Encoding encoding) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteToStream_Ver_3"); - PGUtil.WriteInt32(output_stream, - 4 + 4 + 5 + (UTF8Encoding.GetByteCount(user_name) + 1) + 9 + - (UTF8Encoding.GetByteCount(database_name) + 1) + 10 + 4 + 1); + PGUtil.WriteInt32(output_stream, 4 + 4 + 5 + (encoding.GetByteCount(user_name) + 1) + 9 + (encoding.GetByteCount(database_name) + 1) + 10 + 4 + 1); - PGUtil.WriteInt32(output_stream, PGUtil.ConvertProtocolVersion(this.protocol_version)); + PGUtil.WriteInt32(output_stream, Npgsql.PGUtil.ConvertProtocolVersion(this.protocol_version)); - // User name. - PGUtil.WriteString("user", output_stream); + // User name. + PGUtil.WriteString("user", output_stream, encoding); - // User name. - PGUtil.WriteString(user_name, output_stream); + // User name. + PGUtil.WriteString(user_name, output_stream, encoding); - // Database name. - PGUtil.WriteString("database", output_stream); + // Database name. + PGUtil.WriteString("database", output_stream, encoding); - // Database name. - PGUtil.WriteString(database_name, output_stream); + // Database name. + PGUtil.WriteString(database_name, output_stream, encoding); - // DateStyle. - PGUtil.WriteString("DateStyle", output_stream); + // DateStyle. + PGUtil.WriteString("DateStyle", output_stream, encoding); - // DateStyle. - PGUtil.WriteString("ISO", output_stream); + // DateStyle. + PGUtil.WriteString("ISO", output_stream, encoding); - output_stream.WriteByte(0); - output_stream.Flush(); - } - } -}
\ No newline at end of file + output_stream.WriteByte(0); + output_stream.Flush(); + } + } +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlStartupState.cs b/mcs/class/Npgsql/Npgsql/NpgsqlStartupState.cs index e44737cdad0..dc3793bebba 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlStartupState.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlStartupState.cs @@ -7,47 +7,58 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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.Net; +using System.Net.Sockets; namespace Npgsql { - internal sealed class NpgsqlStartupState : NpgsqlState - { - public static readonly NpgsqlStartupState Instance = new NpgsqlStartupState(); - - private readonly String CLASSNAME = "NpgsqlStartupState"; - - private NpgsqlStartupState() - : base() - { - } - - public override void Authenticate(NpgsqlConnector context, string password) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Authenticate"); - NpgsqlPasswordPacket pwpck = new NpgsqlPasswordPacket(password, context.BackendProtocolVersion); - BufferedStream stream = new BufferedStream(context.Stream); - pwpck.WriteToStream(stream); - stream.Flush(); - } - } -}
\ No newline at end of file + + + internal sealed class NpgsqlStartupState : NpgsqlState + { + private static NpgsqlStartupState _instance = null; + + private readonly String CLASSNAME = "NpgsqlStartupState"; + + private NpgsqlStartupState() : base() + { } + + public static NpgsqlStartupState Instance + { + get + { + if ( _instance == null ) + { + _instance = new NpgsqlStartupState(); + } + return _instance; + } + } + public override void Authenticate( NpgsqlConnector context, string password) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Authenticate"); + NpgsqlPasswordPacket pwpck = new NpgsqlPasswordPacket(password, context.BackendProtocolVersion); + BufferedStream stream = new BufferedStream(context.Stream); + pwpck.WriteToStream(stream, context.Encoding); + stream.Flush(); + + } + } +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlState.cs b/mcs/class/Npgsql/Npgsql/NpgsqlState.cs index 9e46b57f678..e8dcad05163 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlState.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlState.cs @@ -3,39 +3,36 @@ // Npgsql.NpgsqlState.cs // // Author: -// Dave Joyner <d4ljoyn@yahoo.com> +// Dave Joyner <d4ljoyn@yahoo.com> // -// Copyright (C) 2002 The Npgsql Development Team -// npgsql-general@gborg.postgresql.org -// http://gborg.postgresql.org/project/npgsql/projdisplay.php +// Copyright (C) 2002 The Npgsql Development Team +// npgsql-general@gborg.postgresql.org +// http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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.Collections.Generic; using System.Data; using System.IO; +using System.Net; using System.Net.Sockets; -using System.Resources; +using System.Collections; using System.Text; -using System.Net; -using System.Threading; +using System.Resources; + namespace Npgsql { @@ -43,11 +40,11 @@ namespace Npgsql /// implementation. /// </summary> /// + internal abstract class NpgsqlState { - protected static readonly Encoding ENCODING_UTF8 = Encoding.UTF8; private readonly String CLASSNAME = "NpgsqlState"; - protected static ResourceManager resman = new ResourceManager(typeof (NpgsqlState)); + protected static ResourceManager resman = new ResourceManager(typeof(NpgsqlState)); internal NpgsqlState() { @@ -57,247 +54,79 @@ namespace Npgsql { throw new InvalidOperationException("Internal Error! " + this); } - public virtual void Startup(NpgsqlConnector context) { throw new InvalidOperationException("Internal Error! " + this); } - public virtual void Authenticate(NpgsqlConnector context, string password) { throw new InvalidOperationException("Internal Error! " + this); } - - public virtual IEnumerable<IServerResponseObject> QueryEnum(NpgsqlConnector context, NpgsqlCommand command) + public virtual void Query(NpgsqlConnector context, NpgsqlCommand command) { throw new InvalidOperationException("Internal Error! " + this); } - - public void Query(NpgsqlConnector context, NpgsqlCommand command) + public virtual void Ready( NpgsqlConnector context ) { - IterateThroughAllResponses(QueryEnum(context, command)); + throw new InvalidOperationException("Internal Error! " + this); } - public virtual void FunctionCall(NpgsqlConnector context, NpgsqlCommand command) { throw new InvalidOperationException("Internal Error! " + this); } - public virtual void Parse(NpgsqlConnector context, NpgsqlParse parse) { throw new InvalidOperationException("Internal Error! " + this); } - public virtual void Flush(NpgsqlConnector context) { throw new InvalidOperationException("Internal Error! " + this); } - - public virtual IEnumerable<IServerResponseObject> SyncEnum(NpgsqlConnector context) + public virtual void Sync(NpgsqlConnector context) { throw new InvalidOperationException("Internal Error! " + this); } - - public void TestNotify(NpgsqlConnector context) - { - //ZA Hnotifytest CNOTIFY Z - //Qlisten notifytest;notify notifytest; - Stream stm = context.Stream; - string uuidString = "uuid" + Guid.NewGuid().ToString("N"); - PGUtil.WriteString("Qlisten " + uuidString + ";notify " + uuidString + ";", stm); - Queue<byte> buffer = new Queue<byte>(); - byte[] convertBuffer = new byte[36]; - for (;;) - { - int newByte = stm.ReadByte(); - if (newByte == -1) - { - throw new EndOfStreamException(); - } - buffer.Enqueue((byte) newByte); - if (buffer.Count > 35) - { - buffer.CopyTo(convertBuffer, 0); - if (ENCODING_UTF8.GetString(convertBuffer) == uuidString) - { - for (;;) - { - switch (stm.ReadByte()) - { - case -1: - throw new EndOfStreamException(); - case 'Z': - context.Query(new NpgsqlCommand("UNLISTEN *", context)); - return; - } - } - } - else - { - buffer.Dequeue(); - } - } - } - } - - public void TestConnector(NpgsqlConnector context) - { - switch (context.BackendProtocolVersion) - { - case ProtocolVersion.Version2: - TestNotify(context); - break; - case ProtocolVersion.Version3: - EmptySync(context); - break; - default: - throw new NotSupportedException(); - } - } - - public void EmptySync(NpgsqlConnector context) - { - Stream stm = context.Stream; - new NpgsqlSync().WriteToStream(stm); - stm.Flush(); - Queue<int> buffer = new Queue<int>(); - //byte[] compareBuffer = new byte[6]; - int[] messageSought = new int[] {'Z', 0, 0, 0, 5}; - int newByte; - for (;;) - { - switch (newByte = stm.ReadByte()) - { - case -1: - throw new EndOfStreamException(); - case 'E': - case 'I': - case 'T': - if (buffer.Count > 4) - { - bool match = true; - int i = 0; - foreach (byte cmp in buffer) - { - if (cmp != messageSought[i++]) - { - match = false; - break; - } - } - if (match) - { - return; - } - } - break; - default: - buffer.Enqueue(newByte); - if (buffer.Count > 5) - { - buffer.Dequeue(); - } - break; - } - } - } - - public NpgsqlRowDescription Sync(NpgsqlConnector context) - { - NpgsqlRowDescription lastDescription = null; - foreach (IServerResponseObject obj in SyncEnum(context)) - { - if (obj is NpgsqlRowDescription) - { - lastDescription = obj as NpgsqlRowDescription; - } - else - { - if (obj is IDisposable) - { - (obj as IDisposable).Dispose(); - } - } - } - return lastDescription; - } - public virtual void Bind(NpgsqlConnector context, NpgsqlBind bind) { throw new InvalidOperationException("Internal Error! " + this); } - public virtual void Execute(NpgsqlConnector context, NpgsqlExecute execute) { throw new InvalidOperationException("Internal Error! " + this); } - - public virtual IEnumerable<IServerResponseObject> ExecuteEnum(NpgsqlConnector context, NpgsqlExecute execute) - { - 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); } - // COPY methods - - protected virtual void StartCopy(NpgsqlConnector context, NpgsqlCopyFormat copyFormat) - { - throw new InvalidOperationException("Internal Error! " + this); - } - - public virtual byte[] GetCopyData(NpgsqlConnector context) - { - throw new InvalidOperationException("Internal Error! " + this); - } - - public virtual void SendCopyData(NpgsqlConnector context, byte[] buf, int off, int len) - { - throw new InvalidOperationException("Internal Error! " + this); - } - - public virtual void SendCopyDone(NpgsqlConnector context) - { - throw new InvalidOperationException("Internal Error! " + this); - } - - public virtual void SendCopyFail(NpgsqlConnector context, String message) - { - throw new InvalidOperationException("Internal Error! " + this); - } - - public virtual NpgsqlCopyFormat CopyFormat + public virtual void Close( NpgsqlConnector context ) { - get { throw new InvalidOperationException("Internal Error! " + this); } - } - - - public virtual void Close(NpgsqlConnector context) - { - try - { - context.Stream.Close(); - } - catch + if (this != NpgsqlClosedState.Instance) { + try + { + context.Stream.Close(); + } + catch {} + + context.Stream = null; + ChangeState( context, NpgsqlClosedState.Instance ) + ; } - context.Stream = null; - ChangeState(context, NpgsqlClosedState.Instance); } ///<summary> ///This method is used by the states to change the state of the context. /// </summary> - protected static void ChangeState(NpgsqlConnector context, NpgsqlState newState) + protected virtual void ChangeState(NpgsqlConnector context, NpgsqlState newState) { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ChangeState"); context.CurrentState = newState; } @@ -308,735 +137,644 @@ namespace Npgsql /// to handle backend requests. /// </summary> /// - internal void ProcessBackendResponses(NpgsqlConnector context) + internal virtual void ProcessBackendResponses( NpgsqlConnector context ) { - IterateThroughAllResponses(ProcessBackendResponsesEnum(context)); - } - private static void IterateThroughAllResponses(IEnumerable<IServerResponseObject> ienum) - { - foreach (IServerResponseObject obj in ienum) //iterate until finished. + try { - if (obj is IDisposable) + + // Process commandTimeout behavior. + + if ((context.Mediator.CommandTimeout > 0) && (!context.Socket.Poll(1000000 * context.Mediator.CommandTimeout, SelectMode.SelectRead))) { - (obj as IDisposable).Dispose(); + // If timeout occurs when establishing the session with server then + // throw an exception instead of trying to cancel query. This helps to prevent loop as CancelRequest will also try to stablish a connection and sends commands. + if ((this is NpgsqlStartupState || this is NpgsqlConnectedState)) + throw new NpgsqlException(resman.GetString("Exception_ConnectionTimeout")); + else + context.CancelRequest(); + } - } - } + + + + switch (context.BackendProtocolVersion) + { + case ProtocolVersion.Version2 : + ProcessBackendResponses_Ver_2(context); + break; - private class ContextResetter : IDisposable - { - private readonly NpgsqlConnector _connector; + case ProtocolVersion.Version3 : + ProcessBackendResponses_Ver_3(context); + break; - public ContextResetter(NpgsqlConnector connector) - { - _connector = connector; + } } - - public void Dispose() + finally { - _connector.RequireReadyForQuery = true; + // reset expectations right after getting new responses + context.Mediator.ResetExpectations(); } } - ///<summary> - /// This method is responsible to handle all protocol messages sent from the backend. - /// It holds all the logic to do it. - /// To exchange data, it uses a Mediator object from which it reads/writes information - /// to handle backend requests. - /// </summary> - /// - internal IEnumerable<IServerResponseObject> ProcessBackendResponsesEnum(NpgsqlConnector context) + protected virtual void ProcessBackendResponses_Ver_2( NpgsqlConnector context ) { - try - { - // Process commandTimeout behavior. + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses"); + + Stream stream = context.Stream; + NpgsqlMediator mediator = context.Mediator; + + // Often used buffer + Byte[] inputBuffer = new Byte[ 4 ]; - if ((context.Mediator.CommandTimeout > 0) && - (!context.Socket.Poll(1000000*context.Mediator.CommandTimeout, SelectMode.SelectRead))) + Boolean readyForQuery = false; + + byte[] asciiRowBytes = new byte[300]; + char[] asciiRowChars = new char[300]; + + while (!readyForQuery) { - // If timeout occurs when establishing the session with server then - // throw an exception instead of trying to cancel query. This helps to prevent loop as CancelRequest will also try to stablish a connection and sends commands. - if (!((this is NpgsqlStartupState || this is NpgsqlConnectedState))) + // Check the first Byte of response. + switch ( stream.ReadByte() ) { - try + case NpgsqlMessageTypes_Ver_2.ErrorResponse : + { - context.CancelRequest(); + NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion); + error.ReadFromStream(stream, context.Encoding); + error.ErrorSql = mediator.SqlSent; + + mediator.Errors.Add(error); + + NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message); } - catch + + // Return imediately if it is in the startup state or connected state as + // there is no more messages to consume. + // Possible error in the NpgsqlStartupState: + // Invalid password. + // Possible error in the NpgsqlConnectedState: + // No pg_hba.conf configured. + + if (! mediator.RequireReadyForQuery) { + return; } - //We should have gotten an error from CancelRequest(). Whether we did or not, what we - //really have is a timeout exception, and that will be less confusing to the user than - //"operation cancelled by user" or similar, so whatever the case, that is what we'll throw. - // Changed message again to report about the two possible timeouts: connection or command as the establishment timeout only was confusing users when the timeout was a command timeout. - } - throw new NpgsqlException(resman.GetString("Exception_ConnectionOrCommandTimeout")); - } - switch (context.BackendProtocolVersion) - { - case ProtocolVersion.Version2: - return ProcessBackendResponses_Ver_2(context); - case ProtocolVersion.Version3: - return ProcessBackendResponses_Ver_3(context); - default: - throw new NpgsqlException(resman.GetString("Exception_UnknownProtocol")); - } - - } - - catch(ThreadAbortException) - { - try - { - context.CancelRequest(); - context.Close(); - } - catch {} - - throw; - } - - } - protected IEnumerable<IServerResponseObject> ProcessBackendResponses_Ver_2(NpgsqlConnector context) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses"); + break; - using (new ContextResetter(context)) - { - Stream stream = context.Stream; - NpgsqlMediator mediator = context.Mediator; - NpgsqlRowDescription lastRowDescription = null; - List<NpgsqlError> errors = new List<NpgsqlError>(); - for (;;) - { - // Check the first Byte of response. - switch ((BackEndMessageCode) stream.ReadByte()) + case NpgsqlMessageTypes_Ver_2.AuthenticationRequest : + + NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest"); + { - case BackEndMessageCode.ErrorResponse: + Int32 authType = PGUtil.ReadInt32(stream, inputBuffer); - { - NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion, stream); - error.ErrorSql = mediator.SqlSent; + if ( authType == NpgsqlMessageTypes_Ver_2.AuthenticationOk ) + { + NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug); - errors.Add(error); + break; + } - NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message); - } + if ( authType == NpgsqlMessageTypes_Ver_2.AuthenticationClearTextPassword ) + { + NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug); - // Return imediately if it is in the startup state or connected state as - // there is no more messages to consume. - // Possible error in the NpgsqlStartupState: - // Invalid password. - // Possible error in the NpgsqlConnectedState: - // No pg_hba.conf configured. + // Send the PasswordPacket. - if (!context.RequireReadyForQuery) - { - throw new NpgsqlException(errors); - } + ChangeState( context, NpgsqlStartupState.Instance ); + context.Authenticate(context.Password); break; + } - case BackEndMessageCode.AuthenticationRequest: - NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest"); - AuthenticationRequestType authType = (AuthenticationRequestType) PGUtil.ReadInt32(stream); - switch (authType) - { - case AuthenticationRequestType.AuthenticationOk: - NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug); - break; - case AuthenticationRequestType.AuthenticationClearTextPassword: - NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug); - // Send the PasswordPacket. - ChangeState(context, NpgsqlStartupState.Instance); - context.Authenticate(context.Password); - break; - case AuthenticationRequestType.AuthenticationMD5Password: - NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug); - // Now do the "MD5-Thing" - // for this the Password has to be: - // 1. md5-hashed with the username as salt - // 2. md5-hashed again with the salt we get from the backend + if ( authType == NpgsqlMessageTypes_Ver_2.AuthenticationMD5Password ) + { + NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug); + // Now do the "MD5-Thing" + // for this the Password has to be: + // 1. md5-hashed with the username as salt + // 2. md5-hashed again with the salt we get from the backend - MD5 md5 = MD5.Create(); + MD5 md5 = MD5.Create(); - // 1. - byte[] passwd = ENCODING_UTF8.GetBytes(context.Password); - byte[] saltUserName = ENCODING_UTF8.GetBytes(context.UserName); + // 1. + byte[] passwd = context.Encoding.GetBytes(context.Password); + byte[] saltUserName = context.Encoding.GetBytes(context.UserName); - byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length]; + byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length]; - passwd.CopyTo(crypt_buf, 0); - saltUserName.CopyTo(crypt_buf, passwd.Length); + passwd.CopyTo(crypt_buf, 0); + saltUserName.CopyTo(crypt_buf, passwd.Length); - StringBuilder sb = new StringBuilder(); - byte[] hashResult = md5.ComputeHash(crypt_buf); - foreach (byte b in hashResult) - { - sb.Append(b.ToString("x2")); - } + StringBuilder sb = new StringBuilder (); + byte[] hashResult = md5.ComputeHash(crypt_buf); + foreach (byte b in hashResult) + sb.Append (b.ToString ("x2")); - String prehash = sb.ToString(); - byte[] prehashbytes = ENCODING_UTF8.GetBytes(prehash); + String prehash = sb.ToString(); + byte[] prehashbytes = context.Encoding.GetBytes(prehash); - byte[] saltServer = new byte[4]; - stream.Read(saltServer, 0, 4); - // Send the PasswordPacket. - ChangeState(context, NpgsqlStartupState.Instance); - // 2. + byte[] saltServer = new byte[4]; + stream.Read(saltServer, 0, 4); + // Send the PasswordPacket. + ChangeState( context, NpgsqlStartupState.Instance ); - crypt_buf = new byte[prehashbytes.Length + saltServer.Length]; - prehashbytes.CopyTo(crypt_buf, 0); - saltServer.CopyTo(crypt_buf, prehashbytes.Length); - sb = new StringBuilder("md5"); // This is needed as the backend expects md5 result starts with "md5" - hashResult = md5.ComputeHash(crypt_buf); - foreach (byte b in hashResult) - { - sb.Append(b.ToString("x2")); - } + // 2. - context.Authenticate(sb.ToString()); + crypt_buf = new byte[prehashbytes.Length + saltServer.Length]; + prehashbytes.CopyTo(crypt_buf, 0); + saltServer.CopyTo(crypt_buf, prehashbytes.Length); - break; - default: - // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now. - errors.Add( - new NpgsqlError(context.BackendProtocolVersion, - String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType))); - throw new NpgsqlException(errors); - } - break; - case BackEndMessageCode.RowDescription: - yield return lastRowDescription = new NpgsqlRowDescriptionV2(stream, context.OidToNameMapping); - ; - break; - case BackEndMessageCode.DataRow: - yield return new ForwardsOnlyRow(new StringRowReaderV2(lastRowDescription, stream)); - break; - case BackEndMessageCode.BinaryRow: - throw new NotSupportedException(); - case BackEndMessageCode.ReadyForQuery: - ChangeState(context, NpgsqlReadyState.Instance); - if (errors.Count != 0) - { - throw new NpgsqlException(errors); - } - yield break; - case BackEndMessageCode.BackendKeyData: - context.BackEndKeyData = new NpgsqlBackEndKeyData(context.BackendProtocolVersion, stream); - break; - case BackEndMessageCode.NoticeResponse: - context.FireNotice(new NpgsqlError(context.BackendProtocolVersion, stream)); - break; - case BackEndMessageCode.CompletedResponse: - yield return new CompletedResponse(stream); - break; + sb = new StringBuilder ("md5"); // This is needed as the backend expects md5 result starts with "md5" + hashResult = md5.ComputeHash(crypt_buf); + foreach (byte b in hashResult) + sb.Append (b.ToString ("x2")); - case BackEndMessageCode.CursorResponse: - // This is the cursor response message. - // It is followed by a C NULL terminated string with the name of - // the cursor in a FETCH case or 'blank' otherwise. - // In this case it should be always 'blank'. - // [FIXME] Get another name for this function. - NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CursorResponse"); + context.Authenticate(sb.ToString ()); - String cursorName = PGUtil.ReadString(stream); - // Continue waiting for ReadyForQuery message. break; + } - case BackEndMessageCode.EmptyQueryResponse: - NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse"); - PGUtil.ReadString(stream); - break; + // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now. - case BackEndMessageCode.NotificationResponse: - context.FireNotification(new NpgsqlNotificationEventArgs(stream, false)); - if (context.IsNotificationThreadRunning) - { - yield break; - } - break; - case BackEndMessageCode.IO_ERROR: - // Connection broken. Mono returns -1 instead of throw an exception as ms.net does. - throw new IOException(); - - default: - // This could mean a number of things - // We've gotten out of sync with the backend? - // We need to implement this type? - // Backend has gone insane? - throw new DataException("Backend sent unrecognized response type"); + mediator.Errors.Add(new NpgsqlError(context.BackendProtocolVersion, String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType))); + } + + return; + + case NpgsqlMessageTypes_Ver_2.RowDescription: + // This is the RowDescription message. + NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "RowDescription"); + + { + NpgsqlRowDescription rd = new NpgsqlRowDescription(context.BackendProtocolVersion); + rd.ReadFromStream(stream, context.Encoding, context.OidToNameMapping); + + // Initialize the array list which will contain the data from this rowdescription. + mediator.AddRowDescription(rd); + } + + // Now wait for the AsciiRow messages. + break; + + case NpgsqlMessageTypes_Ver_2.AsciiRow: + // This is the AsciiRow message. + NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AsciiRow"); + + { + NpgsqlAsciiRow asciiRow = new NpgsqlAsciiRow(context.Mediator.LastRowDescription, context.BackendProtocolVersion, asciiRowBytes, asciiRowChars); + asciiRow.ReadFromStream(stream, context.Encoding); + + // Add this row to the rows array. + mediator.AddAsciiRow(asciiRow); } + + // Now wait for CompletedResponse message. + break; + + case NpgsqlMessageTypes_Ver_2.BinaryRow: + NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BinaryRow"); + + { + NpgsqlBinaryRow binaryRow = new NpgsqlBinaryRow(context.Mediator.LastRowDescription); + binaryRow.ReadFromStream(stream, context.Encoding); + + mediator.AddBinaryRow(binaryRow); + } + + break; + + case NpgsqlMessageTypes_Ver_2.ReadyForQuery : + + NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ReadyForQuery"); + readyForQuery = true; + ChangeState( context, NpgsqlReadyState.Instance ); + break; + + case NpgsqlMessageTypes_Ver_2.BackendKeyData : + + NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BackendKeyData"); + // BackendKeyData message. + NpgsqlBackEndKeyData backend_keydata = new NpgsqlBackEndKeyData(context.BackendProtocolVersion); + backend_keydata.ReadFromStream(stream); + mediator.SetBackendKeydata(backend_keydata); + + + // Wait for ReadForQuery message + break; + ; + + case NpgsqlMessageTypes_Ver_2.NoticeResponse : + + { + NpgsqlError notice = new NpgsqlError(context.BackendProtocolVersion); + notice.ReadFromStream(stream, context.Encoding); + + mediator.Notices.Add(notice); + + NpgsqlEventLog.LogMsg(resman, "Log_NoticeResponse", LogLevel.Debug, notice.Message); + } + + // Wait for ReadForQuery message + break; + + case NpgsqlMessageTypes_Ver_2.CompletedResponse : + // This is the CompletedResponse message. + // Get the string returned. + + + String result = PGUtil.ReadString(stream, context.Encoding); + + NpgsqlEventLog.LogMsg(resman, "Log_CompletedResponse", LogLevel.Debug, result); + // Add result from the processing. + + mediator.AddCompletedResponse(result); + + // Now wait for ReadyForQuery message. + break; + + case NpgsqlMessageTypes_Ver_2.CursorResponse : + // This is the cursor response message. + // It is followed by a C NULL terminated string with the name of + // the cursor in a FETCH case or 'blank' otherwise. + // In this case it should be always 'blank'. + // [FIXME] Get another name for this function. + NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CursorResponse"); + + String cursorName = PGUtil.ReadString(stream, context.Encoding); + // Continue waiting for ReadyForQuery message. + break; + + case NpgsqlMessageTypes_Ver_2.EmptyQueryResponse : + NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse"); + PGUtil.ReadString(stream, context.Encoding); + break; + + case NpgsqlMessageTypes_Ver_2.NotificationResponse : + + NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "NotificationResponse"); + + 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; + + case -1: + // Connection broken. Mono returns -1 instead of throw an exception as ms.net does. + throw new IOException(); + + default : + // This could mean a number of things + // We've gotten out of sync with the backend? + // We need to implement this type? + // Backend has gone insane? + // FIXME + // what exception should we really throw here? + throw new NotSupportedException("Backend sent unrecognized response type"); + } } } - private enum BackEndMessageCode + protected virtual void ProcessBackendResponses_Ver_3( NpgsqlConnector context ) { - IO_ERROR = -1, // Connection broken. Mono returns -1 instead of throwing an exception as ms.net does. - - CopyData = 'd', - CopyDone = 'c', - DataRow = 'D', - BinaryRow = 'B', //Version 2 only - - BackendKeyData = 'K', - CancelRequest = 'F', - CompletedResponse = 'C', - CopyDataRows = ' ', - CopyInResponse = 'G', - CopyOutResponse = 'H', - CursorResponse = 'P', //Version 2 only - EmptyQueryResponse = 'I', - ErrorResponse = 'E', - FunctionCall = 'F', - FunctionCallResponse = 'V', - - AuthenticationRequest = 'R', - - NoticeResponse = 'N', - NotificationResponse = 'A', - ParameterStatus = 'S', - PasswordPacket = ' ', - ReadyForQuery = 'Z', - RowDescription = 'T', - SSLRequest = ' ', - - // extended query backend messages - ParseComplete = '1', - BindComplete = '2', - PortalSuspended = 's', - ParameterDescription = 't', - NoData = 'n', - CloseComplete = '3' - } + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses"); - private enum AuthenticationRequestType - { - AuthenticationOk = 0, - AuthenticationKerberosV4 = 1, - AuthenticationKerberosV5 = 2, - AuthenticationClearTextPassword = 3, - AuthenticationCryptPassword = 4, - AuthenticationMD5Password = 5, - AuthenticationSCMCredential = 6, - AuthenticationGSS = 7, - AuthenticationGSSContinue = 8, - AuthenticationSSPI = 9 - } + Stream stream = context.Stream; + NpgsqlMediator mediator = context.Mediator; - protected IEnumerable<IServerResponseObject> ProcessBackendResponses_Ver_3(NpgsqlConnector context) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses"); + // Often used buffers + Byte[] inputBuffer = new Byte[ 4 ]; + String Str; + + Boolean readyForQuery = false; + + byte[] asciiRowBytes = new byte[300]; + char[] asciiRowChars = new char[300]; - using (new ContextResetter(context)) + while (!readyForQuery) { - Stream stream = context.Stream; - NpgsqlMediator mediator = context.Mediator; + // Check the first Byte of response. + Int32 message = stream.ReadByte(); + switch ( message ) + { + case NpgsqlMessageTypes_Ver_3.ErrorResponse : - NpgsqlRowDescription lastRowDescription = null; + { + NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion); + error.ReadFromStream(stream, context.Encoding); + error.ErrorSql = mediator.SqlSent; - List<NpgsqlError> errors = new List<NpgsqlError>(); + mediator.Errors.Add(error); - for (;;) - { - // Check the first Byte of response. - BackEndMessageCode message = (BackEndMessageCode) stream.ReadByte(); - switch (message) + NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message); + } + + // Return imediately if it is in the startup state or connected state as + // there is no more messages to consume. + // Possible error in the NpgsqlStartupState: + // Invalid password. + // Possible error in the NpgsqlConnectedState: + // No pg_hba.conf configured. + + if (! mediator.RequireReadyForQuery) { - case BackEndMessageCode.ErrorResponse: + return; + } - NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion, stream); - error.ErrorSql = mediator.SqlSent; + break; - errors.Add(error); - NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message); + case NpgsqlMessageTypes_Ver_3.AuthenticationRequest : - // Return imediately if it is in the startup state or connected state as - // there is no more messages to consume. - // Possible error in the NpgsqlStartupState: - // Invalid password. - // Possible error in the NpgsqlConnectedState: - // No pg_hba.conf configured. + NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest"); - if (!context.RequireReadyForQuery) - { - throw new NpgsqlException(errors); - } + // Eat length + PGUtil.ReadInt32(stream, inputBuffer); - break; - case BackEndMessageCode.AuthenticationRequest: + { + Int32 authType = PGUtil.ReadInt32(stream, inputBuffer); - NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest"); + if ( authType == NpgsqlMessageTypes_Ver_3.AuthenticationOk ) + { + NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug); - // Get the length in case we're getting AuthenticationGSSContinue - int authDataLength = PGUtil.ReadInt32(stream) - 8; + break; + } - AuthenticationRequestType authType = (AuthenticationRequestType) PGUtil.ReadInt32(stream); - switch (authType) - { - case AuthenticationRequestType.AuthenticationOk: - NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug); - break; - case AuthenticationRequestType.AuthenticationClearTextPassword: - NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug); + if ( authType == NpgsqlMessageTypes_Ver_3.AuthenticationClearTextPassword ) + { + NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug); - // Send the PasswordPacket. + // Send the PasswordPacket. - ChangeState(context, NpgsqlStartupState.Instance); - context.Authenticate(context.Password); + ChangeState( context, NpgsqlStartupState.Instance ); + context.Authenticate(context.Password); - break; - case AuthenticationRequestType.AuthenticationMD5Password: - NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug); - // Now do the "MD5-Thing" - // for this the Password has to be: - // 1. md5-hashed with the username as salt - // 2. md5-hashed again with the salt we get from the backend + break; + } - MD5 md5 = MD5.Create(); + if ( authType == NpgsqlMessageTypes_Ver_3.AuthenticationMD5Password ) + { + NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug); + // Now do the "MD5-Thing" + // for this the Password has to be: + // 1. md5-hashed with the username as salt + // 2. md5-hashed again with the salt we get from the backend - // 1. - byte[] passwd = ENCODING_UTF8.GetBytes(context.Password); - byte[] saltUserName = ENCODING_UTF8.GetBytes(context.UserName); + MD5 md5 = MD5.Create(); - byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length]; - passwd.CopyTo(crypt_buf, 0); - saltUserName.CopyTo(crypt_buf, passwd.Length); + // 1. + byte[] passwd = context.Encoding.GetBytes(context.Password); + byte[] saltUserName = context.Encoding.GetBytes(context.UserName); + byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length]; - StringBuilder sb = new StringBuilder(); - byte[] hashResult = md5.ComputeHash(crypt_buf); - foreach (byte b in hashResult) - { - sb.Append(b.ToString("x2")); - } + passwd.CopyTo(crypt_buf, 0); + saltUserName.CopyTo(crypt_buf, passwd.Length); - String prehash = sb.ToString(); - byte[] prehashbytes = ENCODING_UTF8.GetBytes(prehash); - crypt_buf = new byte[prehashbytes.Length + 4]; + StringBuilder sb = new StringBuilder (); + byte[] hashResult = md5.ComputeHash(crypt_buf); + foreach (byte b in hashResult) + sb.Append (b.ToString ("x2")); - stream.Read(crypt_buf, prehashbytes.Length, 4); - // Send the PasswordPacket. - ChangeState(context, NpgsqlStartupState.Instance); + String prehash = sb.ToString(); + byte[] prehashbytes = context.Encoding.GetBytes(prehash); - // 2. - prehashbytes.CopyTo(crypt_buf, 0); - sb = new StringBuilder("md5"); // This is needed as the backend expects md5 result starts with "md5" - hashResult = md5.ComputeHash(crypt_buf); - foreach (byte b in hashResult) - { - sb.Append(b.ToString("x2")); - } - context.Authenticate(sb.ToString()); + stream.Read(inputBuffer, 0, 4); + // Send the PasswordPacket. + ChangeState( context, NpgsqlStartupState.Instance ); - break; -#if WINDOWS && UNMANAGED - case AuthenticationRequestType.AuthenticationSSPI: - { - if (context.IntegratedSecurity) - { - // For SSPI we have to get the IP-Address (hostname doesn't work) - string ipAddressString = ((IPEndPoint)context.Socket.RemoteEndPoint).Address.ToString(); - context.SSPI = new SSPIHandler(ipAddressString, "POSTGRES"); - ChangeState(context, NpgsqlStartupState.Instance); - context.Authenticate(context.SSPI.Continue(null)); - break; - } - else - { - // TODO: correct exception - throw new Exception(); - } - } + // 2. + crypt_buf = new byte[prehashbytes.Length + 4]; + prehashbytes.CopyTo(crypt_buf, 0); + inputBuffer.CopyTo(crypt_buf, prehashbytes.Length); - case AuthenticationRequestType.AuthenticationGSSContinue: - { - byte[] authData = new byte[authDataLength]; - PGUtil.CheckedStreamRead(stream, authData, 0, authDataLength); - context.Authenticate(context.SSPI.Continue(authData)); - break; - } + sb = new StringBuilder ("md5"); // This is needed as the backend expects md5 result starts with "md5" + hashResult = md5.ComputeHash(crypt_buf); + foreach (byte b in hashResult) + sb.Append (b.ToString ("x2")); -#endif + context.Authenticate(sb.ToString ()); - default: - // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now. - errors.Add( - new NpgsqlError(context.BackendProtocolVersion, - String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType))); - throw new NpgsqlException(errors); - } - break; - case BackEndMessageCode.RowDescription: - yield return lastRowDescription = new NpgsqlRowDescriptionV3(stream, context.OidToNameMapping); break; - case BackEndMessageCode.ParameterDescription: + } - // Do nothing,for instance, just read... - int lenght = PGUtil.ReadInt32(stream); - int nb_param = PGUtil.ReadInt16(stream); - for (int i = 0; i < nb_param; i++) - { - int typeoid = PGUtil.ReadInt32(stream); - } + // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now. + mediator.Errors.Add(new NpgsqlError(context.BackendProtocolVersion, String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType))); + } - break; + return; - case BackEndMessageCode.DataRow: - yield return new ForwardsOnlyRow(new StringRowReaderV3(lastRowDescription, stream)); - break; + case NpgsqlMessageTypes_Ver_3.RowDescription: + // This is the RowDescription message. + NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "RowDescription"); + { + NpgsqlRowDescription rd = new NpgsqlRowDescription(context.BackendProtocolVersion); + rd.ReadFromStream(stream, context.Encoding, context.OidToNameMapping); - case BackEndMessageCode.ReadyForQuery: + mediator.AddRowDescription(rd); + } - NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ReadyForQuery"); + // 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; - // Possible status bytes returned: - // I = Idle (no transaction active). - // T = In transaction, ready for more. - // E = Error in transaction, queries will fail until transaction aborted. - // Just eat the status byte, we have no use for it at this time. - PGUtil.ReadInt32(stream); - stream.ReadByte(); + case NpgsqlMessageTypes_Ver_3.DataRow: + // This is the AsciiRow message. + NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "DataRow"); + { + NpgsqlAsciiRow asciiRow = new NpgsqlAsciiRow(context.Mediator.LastRowDescription, context.BackendProtocolVersion, asciiRowBytes, asciiRowChars); + asciiRow.ReadFromStream(stream, context.Encoding); - ChangeState(context, NpgsqlReadyState.Instance); + // Add this row to the rows array. + mediator.AddAsciiRow(asciiRow); + } - if (errors.Count != 0) - { - throw new NpgsqlException(errors); - } + // Now wait for CompletedResponse message. + break; - yield break; + case NpgsqlMessageTypes_Ver_3.ReadyForQuery : - case BackEndMessageCode.BackendKeyData: + NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ReadyForQuery"); - NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BackendKeyData"); - // BackendKeyData message. - NpgsqlBackEndKeyData backend_keydata = new NpgsqlBackEndKeyData(context.BackendProtocolVersion, stream); - context.BackEndKeyData = backend_keydata; + // Possible status bytes returned: + // I = Idle (no transaction active). + // T = In transaction, ready for more. + // E = Error in transaction, queries will fail until transaction aborted. + // Just eat the status byte, we have no use for it at this time. + PGUtil.ReadInt32(stream, inputBuffer); + PGUtil.ReadString(stream, context.Encoding, 1); + readyForQuery = true; + ChangeState( context, NpgsqlReadyState.Instance ); - // Wait for ReadForQuery message - break; + break; - case BackEndMessageCode.NoticeResponse: - // Notices and errors are identical except that we - // just throw notices away completely ignored. - context.FireNotice(new NpgsqlError(context.BackendProtocolVersion, stream)); - break; + case NpgsqlMessageTypes_Ver_3.BackendKeyData : - case BackEndMessageCode.CompletedResponse: - PGUtil.ReadInt32(stream); - yield return new CompletedResponse(stream); - break; - case BackEndMessageCode.ParseComplete: - NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParseComplete"); - // Just read up the message length. - PGUtil.ReadInt32(stream); - yield break; - case BackEndMessageCode.BindComplete: - NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BindComplete"); - // Just read up the message length. - PGUtil.ReadInt32(stream); - yield break; - case BackEndMessageCode.EmptyQueryResponse: - NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse"); - PGUtil.ReadInt32(stream); - break; - case BackEndMessageCode.NotificationResponse: - // Eat the length - PGUtil.ReadInt32(stream); - context.FireNotification(new NpgsqlNotificationEventArgs(stream, true)); - if (context.IsNotificationThreadRunning) - { - yield break; - } - break; - case BackEndMessageCode.ParameterStatus: - NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParameterStatus"); - NpgsqlParameterStatus parameterStatus = new NpgsqlParameterStatus(stream); - - NpgsqlEventLog.LogMsg(resman, "Log_ParameterStatus", LogLevel.Debug, parameterStatus.Parameter, - parameterStatus.ParameterValue); - - context.AddParameterStatus(parameterStatus); - - if (parameterStatus.Parameter == "server_version") - { - // Deal with this here so that if there are - // changes in a future backend version, we can handle it here in the - // protocol handler and leave everybody else put of it. - string versionString = parameterStatus.ParameterValue.Trim(); - for (int idx = 0; idx != versionString.Length; ++idx) - { - char c = parameterStatus.ParameterValue[idx]; - if (!char.IsDigit(c) && c != '.') - { - versionString = versionString.Substring(0, idx); - break; - } - } - context.ServerVersion = new Version(versionString); - } - break; - case BackEndMessageCode.NoData: - // This nodata message may be generated by prepare commands issued with queries which doesn't return rows - // for example insert, update or delete. - // Just eat the message. - NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParameterStatus"); - PGUtil.ReadInt32(stream); - break; + NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BackendKeyData"); + // BackendKeyData message. + NpgsqlBackEndKeyData backend_keydata = new NpgsqlBackEndKeyData(context.BackendProtocolVersion); + backend_keydata.ReadFromStream(stream); + mediator.SetBackendKeydata(backend_keydata); - case BackEndMessageCode.CopyInResponse: - // Enter COPY sub protocol and start pushing data to server - NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CopyInResponse"); - ChangeState(context, NpgsqlCopyInState.Instance); - PGUtil.ReadInt32(stream); // length redundant - context.CurrentState.StartCopy(context, ReadCopyHeader(stream)); - yield break; - // Either StartCopy called us again to finish the operation or control should be passed for user to feed copy data - - case BackEndMessageCode.CopyOutResponse: - // Enter COPY sub protocol and start pulling data from server - NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CopyOutResponse"); - ChangeState(context, NpgsqlCopyOutState.Instance); - PGUtil.ReadInt32(stream); // length redundant - context.CurrentState.StartCopy(context, ReadCopyHeader(stream)); - yield break; - // Either StartCopy called us again to finish the operation or control should be passed for user to feed copy data - - case BackEndMessageCode.CopyData: - NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CopyData"); - Int32 len = PGUtil.ReadInt32(stream) - 4; - byte[] buf = new byte[len]; - stream.Read(buf, 0, len); - context.Mediator.ReceivedCopyData = buf; - yield break; // read data from server one chunk at a time while staying in copy operation mode - - case BackEndMessageCode.CopyDone: - NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CopyDone"); - PGUtil.ReadInt32(stream); // CopyDone can not have content so this is always 4 - // This will be followed by normal CommandComplete + ReadyForQuery so no op needed - break; - case BackEndMessageCode.IO_ERROR: - // Connection broken. Mono returns -1 instead of throwing an exception as ms.net does. - throw new IOException(); - - default: - // This could mean a number of things - // We've gotten out of sync with the backend? - // We need to implement this type? - // Backend has gone insane? - // FIXME - // what exception should we really throw here? - throw new NotSupportedException(String.Format("Backend sent unrecognized response type: {0}", (Char) message)); + // Wait for ReadForQuery message + break; + + case NpgsqlMessageTypes_Ver_3.NoticeResponse : + + // Notices and errors are identical except that we + // just throw notices away completely ignored. + { + NpgsqlError notice = new NpgsqlError(context.BackendProtocolVersion); + notice.ReadFromStream(stream, context.Encoding); + + mediator.Notices.Add(notice); + + NpgsqlEventLog.LogMsg(resman, "Log_NoticeResponse", LogLevel.Debug, notice.Message); } - } - } - } + // Wait for ReadForQuery message + break; - private static NpgsqlCopyFormat ReadCopyHeader(Stream stream) - { - byte copyFormat = (byte) stream.ReadByte(); - Int16 numCopyFields = PGUtil.ReadInt16(stream); - Int16[] copyFieldFormats = new Int16[numCopyFields]; - for (Int16 i = 0; i < numCopyFields; i++) - { - copyFieldFormats[i] = PGUtil.ReadInt16(stream); - } - return new NpgsqlCopyFormat(copyFormat, copyFieldFormats); - } - } + case NpgsqlMessageTypes_Ver_3.CompletedResponse : + // This is the CompletedResponse message. + // Get the string returned. - /// <summary> - /// Represents a completed response message. - /// </summary> - internal class CompletedResponse : IServerResponseObject - { - private readonly int? _rowsAffected; - private readonly long? _lastInsertedOID; + PGUtil.ReadInt32(stream, inputBuffer); + Str = PGUtil.ReadString(stream, context.Encoding); - public CompletedResponse(Stream stream) - { - string[] tokens = PGUtil.ReadString(stream).Split(); - if (tokens.Length > 1) - { - int rowsAffected; - if (int.TryParse(tokens[tokens.Length - 1], out rowsAffected)) - _rowsAffected = rowsAffected; - else - _rowsAffected = null; - - } - _lastInsertedOID = (tokens.Length > 2 && tokens[0].Trim().ToUpperInvariant() == "INSERT") - ? long.Parse(tokens[1]) - : (long?) null; - } + NpgsqlEventLog.LogMsg(resman, "Log_CompletedResponse", LogLevel.Debug, Str); - public long? LastInsertedOID - { - get { return _lastInsertedOID; } - } + // Add result from the processing. + mediator.AddCompletedResponse(Str); - public int? RowsAffected - { - get { return _rowsAffected; } - } - } + break; - /// <summary> - /// For classes representing messages sent from the client to the server. - /// </summary> - internal abstract class ClientMessage - { - protected static readonly Encoding UTF8Encoding = Encoding.UTF8; - public abstract void WriteToStream(Stream outputStream); - } + case NpgsqlMessageTypes_Ver_3.ParseComplete : + NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParseComplete"); + // Just read up the message length. + PGUtil.ReadInt32(stream, inputBuffer); + readyForQuery = true; + break; - /// <summary> - /// Marker interface which identifies a class which represents part of - /// a response from the server. - internal interface IServerResponseObject - { - } + case NpgsqlMessageTypes_Ver_3.BindComplete : + NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BindComplete"); + // Just read up the message length. + PGUtil.ReadInt32(stream, inputBuffer); + readyForQuery = true; + break; - /// <summary> - /// Marker interface which identifies a class which may take possession of a stream for the duration of - /// it's lifetime (possibly temporarily giving that possession to another class for part of that time. - /// - /// It inherits from IDisposable, since any such class must make sure it leaves the stream in a valid state. - /// - /// The most important such class is that compiler-generated from ProcessBackendResponsesEnum. Of course - /// we can't make that inherit from this interface, alas. - /// </summary> - internal interface IStreamOwner : IServerResponseObject, IDisposable - { + case NpgsqlMessageTypes_Ver_3.EmptyQueryResponse : + NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse"); + PGUtil.ReadInt32(stream, inputBuffer); + break; + + case NpgsqlMessageTypes_Ver_3.NotificationResponse : + NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "NotificationResponse"); + + // Eat the length + PGUtil.ReadInt32(stream, inputBuffer); + { + // Process ID sending notification + Int32 PID = PGUtil.ReadInt32(stream, inputBuffer); + // Notification string + String notificationResponse = PGUtil.ReadString( stream, context.Encoding ); + // Additional info, currently not implemented by PG (empty string always), eat it + PGUtil.ReadString( stream, context.Encoding ); + mediator.AddNotification(new NpgsqlNotificationEventArgs(PID, notificationResponse)); + } + + if (context.IsNotificationThreadRunning) + readyForQuery = true; + + // Wait for ReadForQuery message + break; + + case NpgsqlMessageTypes_Ver_3.ParameterStatus : + NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParameterStatus"); + NpgsqlParameterStatus parameterStatus = new NpgsqlParameterStatus(); + parameterStatus.ReadFromStream(stream, context.Encoding); + + NpgsqlEventLog.LogMsg(resman, "Log_ParameterStatus", LogLevel.Debug, parameterStatus.Parameter, parameterStatus.ParameterValue); + + mediator.AddParameterStatus(parameterStatus.Parameter, parameterStatus); + + if (parameterStatus.Parameter == "server_version") + { + // Add this one under our own name so that if the parameter name + // changes in a future backend version, we can handle it here in the + // protocol handler and leave everybody else put of it. + mediator.AddParameterStatus("__npgsql_server_version", parameterStatus); + // context.ServerVersionString = parameterStatus.ParameterValue; + } + + break; + case NpgsqlMessageTypes_Ver_3.NoData : + // This nodata message may be generated by prepare commands issued with queries which doesn't return rows + // for example insert, update or delete. + // Just eat the message. + NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParameterStatus"); + PGUtil.ReadInt32(stream, inputBuffer); + break; + + + case -1: + // Connection broken. Mono returns -1 instead of throw an exception as ms.net does. + throw new IOException(); + + default : + // This could mean a number of things + // We've gotten out of sync with the backend? + // We need to implement this type? + // Backend has gone insane? + // FIXME + // what exception should we really throw here? + throw new NotSupportedException(String.Format("Backend sent unrecognized response type: {0}", (Char)message)); + + } + } + } } -}
\ No newline at end of file +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlState.resx b/mcs/class/Npgsql/Npgsql/NpgsqlState.resx index 71ea189b2b3..d4071715e8f 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlState.resx +++ b/mcs/class/Npgsql/Npgsql/NpgsqlState.resx @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8" ?> +<?xml version="1.0" encoding="utf-8" ?> <root> <!-- Microsoft ResX Schema @@ -140,19 +140,5 @@ <data name="Exception_ConnectionTimeout"> <value>Connection establishment timeout. Increase Timeout value in ConnectionString.</value> </data> - <data name="Exception_ConnectionOrCommandTimeout"> - <value>A timeout has occured. If you were establishing a connection, increase Timeout value in ConnectionString. If you were executing a command, increate the CommandTimeout value in ConnectionString or in your NpgsqlCommand object.</value> - </data> - <data name="Log_ConnectingTo"> - <value>Attempt to connect to '{0}'.</value> - </data> - <data name="Log_FailedConnection"> - <value>Failed to establish a connection to '{0}'.</value> - </data> - - <data name="Exception_FailedConnection"> - <value>Failed to establish a connection to '{0}'.</value> - </data> - </root> diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlSync.cs b/mcs/class/Npgsql/Npgsql/NpgsqlSync.cs index 801f4a966a4..e8c4e0b3af4 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlSync.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlSync.cs @@ -9,42 +9,45 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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 Parse message sent to PostgreSQL - /// server. - /// </summary> - /// - internal sealed class NpgsqlSync : ClientMessage - { - // Logging related values - //private static readonly String CLASSNAME = "NpgsqlSync"; - - public override void WriteToStream(Stream outputStream) - { - outputStream.WriteByte((byte) FrontEndMessageCode.Sync); - - PGUtil.WriteInt32(outputStream, 4); - } - } -}
\ No newline at end of file + + /// <summary> + /// This class represents the Parse message sent to PostgreSQL + /// server. + /// </summary> + /// + internal sealed class NpgsqlSync + { + // Logging related values + private static readonly String CLASSNAME = "NpgsqlSync"; + + public void WriteToStream(Stream outputStream, Encoding encoding) + { + outputStream.WriteByte((Byte)'S'); + + PGUtil.WriteInt32(outputStream, 4); + + } + + } +} + diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlTransaction.cs b/mcs/class/Npgsql/Npgsql/NpgsqlTransaction.cs index 0fd3c5c3497..832c1156ec6 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlTransaction.cs +++ b/mcs/class/Npgsql/Npgsql/NpgsqlTransaction.cs @@ -3,71 +3,65 @@ // Npgsql.NpgsqlTransaction.cs // // Author: -// Francisco Jr. (fxjrlists@yahoo.com.br) +// 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 +// Copyright (C) 2002 The Npgsql Development Team +// npgsql-general@gborg.postgresql.org +// http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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.Data.Common; -using System.Resources; using System.Text; -using System.Threading; +using System.Resources; +using System.Data; + namespace Npgsql { /// <summary> /// Represents a transaction to be made in a PostgreSQL database. This class cannot be inherited. /// </summary> - public sealed class NpgsqlTransaction : DbTransaction + public sealed class NpgsqlTransaction : MarshalByRefObject, IDbTransaction { private static readonly String CLASSNAME = "NpgsqlTransaction"; - private static ResourceManager resman = new ResourceManager(typeof (NpgsqlTransaction)); + private static ResourceManager resman = new ResourceManager(typeof(NpgsqlTransaction)); - private NpgsqlConnection _conn = null; - private readonly IsolationLevel _isolation = IsolationLevel.ReadCommitted; - private bool _disposed = false; + private NpgsqlConnection _conn = null; + private IsolationLevel _isolation = IsolationLevel.ReadCommitted; + private bool _disposed = false; - internal NpgsqlTransaction(NpgsqlConnection conn) - : this(conn, IsolationLevel.ReadCommitted) - { - } + internal NpgsqlTransaction(NpgsqlConnection conn) : this(conn, IsolationLevel.ReadCommitted) + {} internal NpgsqlTransaction(NpgsqlConnection conn, IsolationLevel isolation) { - resman = new ResourceManager(this.GetType()); + resman = new System.Resources.ResourceManager(this.GetType()); NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME); - + _conn = conn; _isolation = isolation; StringBuilder commandText = new StringBuilder("BEGIN; SET TRANSACTION ISOLATION LEVEL "); - if ((isolation == IsolationLevel.RepeatableRead) || (isolation == IsolationLevel.Serializable)) - { + if ( (isolation == IsolationLevel.RepeatableRead) || + (isolation == IsolationLevel.Serializable) + ) commandText.Append("SERIALIZABLE"); - } else { // Set isolation level default to read committed. @@ -78,7 +72,7 @@ namespace Npgsql commandText.Append(";"); NpgsqlCommand command = new NpgsqlCommand(commandText.ToString(), conn.Connector); - command.ExecuteBlind(); + command.ExecuteNonQuery(); _conn.Connector.Transaction = this; } @@ -89,14 +83,21 @@ namespace Npgsql /// </summary> /// <value>The <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see> /// object associated with the transaction.</value> - public new NpgsqlConnection Connection + public NpgsqlConnection Connection { - get { return _conn; } + get + { + return _conn; + } } - protected override DbConnection DbConnection + + IDbConnection IDbTransaction.Connection { - get { return Connection; } + get + { + return Connection; + } } /// <summary> @@ -104,7 +105,7 @@ namespace Npgsql /// </summary> /// <value>The <see cref="System.Data.IsolationLevel">IsolationLevel</see> for this transaction. /// The default is <b>ReadCommitted</b>.</value> - public override IsolationLevel IsolationLevel + public IsolationLevel IsolationLevel { get { @@ -117,36 +118,33 @@ namespace Npgsql } } - protected override void Dispose(bool disposing) + /// <summary> + /// Releases the unmanaged resources used by the + /// <see cref="Npgsql.NpgsqlTransaction">NpgsqlTransaction</see> + /// and optionally releases the managed resources. + /// </summary> + public void Dispose() + { + GC.SuppressFinalize(this); + + this.Dispose(true); + } + + private void Dispose(Boolean disposing) { - if (disposing && this._conn != null) + if(disposing && this._conn != null) { if (_conn.Connector.Transaction != null) - { - if ((Thread.CurrentThread.ThreadState & (ThreadState.Aborted | ThreadState.AbortRequested)) != 0) - { - // can't count on Rollback working if the thread has been aborted - // need to copy since Cancel will set it to null - NpgsqlConnection conn = _conn; - Cancel(); - // must close connection since transaction hasn't been rolled back - conn.Close(); - } - else - { - this.Rollback(); - } - } + this.Rollback(); this._disposed = true; } - base.Dispose(disposing); } /// <summary> /// Commits the database transaction. /// </summary> - public override void Commit() + public void Commit() { CheckDisposed(); @@ -158,7 +156,7 @@ namespace Npgsql NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Commit"); NpgsqlCommand command = new NpgsqlCommand("COMMIT", _conn.Connector); - command.ExecuteBlind(); + command.ExecuteNonQuery(); _conn.Connector.Transaction = null; _conn = null; } @@ -166,7 +164,7 @@ namespace Npgsql /// <summary> /// Rolls back a transaction from a pending state. /// </summary> - public override void Rollback() + public void Rollback() { CheckDisposed(); @@ -178,74 +176,11 @@ namespace Npgsql NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Rollback"); NpgsqlCommand command = new NpgsqlCommand("ROLLBACK", _conn.Connector); - command.ExecuteBlind(); + command.ExecuteNonQuery(); _conn.Connector.Transaction = null; _conn = null; } - - /// <summary> - /// Rolls back a transaction from a pending savepoint state. - /// </summary> - public void Rollback(String savePointName) - { - - CheckDisposed(); - - if (_conn == null) - { - throw new InvalidOperationException(resman.GetString("Exception_NoTransaction")); - } - - if (!_conn.Connector.SupportsSavepoint) - { - throw new InvalidOperationException(resman.GetString("Exception_SavePointNotSupported")); - } - - - - if (savePointName.Contains(";")) - { - throw new InvalidOperationException(resman.GetString("Exception_SavePointWithSemicolon")); - - } - - - NpgsqlCommand command = new NpgsqlCommand("ROLLBACK TO SAVEPOINT " + savePointName, _conn.Connector); - command.ExecuteBlind(); - - } - - /// <summary> - /// Creates a transaction save point. - /// </summary> - - public void Save(String savePointName) - { - - CheckDisposed(); - if (_conn == null) - { - throw new InvalidOperationException(resman.GetString("Exception_NoTransaction")); - } - - if (!_conn.Connector.SupportsSavepoint) - { - throw new InvalidOperationException(resman.GetString("Exception_SavePointNotSupported")); - } - - - if (savePointName.Contains(";")) - { - throw new InvalidOperationException(resman.GetString("Exception_SavePointWithSemicolon")); - - } - - - NpgsqlCommand command = new NpgsqlCommand("SAVEPOINT " + savePointName, _conn.Connector); - command.ExecuteBlind(); - - } /// <summary> /// Cancel the transaction without telling the backend about it. This is /// used to make the transaction go away when closing a connection. @@ -261,18 +196,25 @@ namespace Npgsql } } - internal bool Disposed - { - get { return _disposed; } + internal bool Disposed{ + get + { + return _disposed; + } } internal void CheckDisposed() { if (_disposed) - { throw new ObjectDisposedException(CLASSNAME); - } + } + + ~NpgsqlTransaction() + { + Dispose(false); + } + } -}
\ No newline at end of file +} diff --git a/mcs/class/Npgsql/Npgsql/NpgsqlTransaction.resx b/mcs/class/Npgsql/Npgsql/NpgsqlTransaction.resx index 9096729d4e9..ce394ae88d8 100644 --- a/mcs/class/Npgsql/Npgsql/NpgsqlTransaction.resx +++ b/mcs/class/Npgsql/Npgsql/NpgsqlTransaction.resx @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8" ?> +<?xml version="1.0" encoding="utf-8" ?> <root> <!-- Microsoft ResX Schema @@ -100,10 +100,4 @@ <data name="Exception_NoTransaction"> <value>No transaction in progress.</value> </data> - <data name="Exception_SavePointWithSemicolon"> - <value>Savepoint name cannot have semicolon.</value> - </data> - <data name="Exception_SavePointNotSupported"> - <value>Savepoint is not supported by backend.</value> - </data> </root> diff --git a/mcs/class/Npgsql/Npgsql/PGUtil.cs b/mcs/class/Npgsql/Npgsql/PGUtil.cs index 41d21e1c192..7c6c56739e1 100644 --- a/mcs/class/Npgsql/Npgsql/PGUtil.cs +++ b/mcs/class/Npgsql/Npgsql/PGUtil.cs @@ -9,733 +9,382 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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.Collections; -using System.Collections.Generic; using System.IO; +using System.Text; +using System.Net.Sockets; using System.Net; -using System.Reflection; using System.Resources; -using System.Text; namespace Npgsql { - ///<summary> - /// This class provides many util methods to handle - /// reading and writing of PostgreSQL protocol messages. - /// </summary> - internal static class PGUtil - { - // Logging related values - private static readonly String CLASSNAME = "PGUtil"; - internal static readonly ResourceManager resman = new ResourceManager(MethodBase.GetCurrentMethod().DeclaringType); - //TODO: What should this value be? - //There is an obvious balancing act in setting this value. The larger the value, the fewer times - //we need to use it and the more efficient we are in that regard. The smaller the value, the - //less space is used, and the more efficient we are in that regard. - //Multiples of memory page size are often good, which tends to be 4096 (4kB) and hence 4096 is - //often used for cases like this (e.g. the default size of the buffer in System.IO.BufferedStream). - //This is hard to guess, and even harder to guess if any overhead will be involved making a value - //of 4096 * x - overhead the real ideal with this approach. - //If this buffer were per-instance in a non-static class then 4096 would probably be the one to go for, - //but since this buffer is shared we can perhaps afford to go a bit larger. - // - //Another potentially useful value, since we will be using a network stream which in turn is using - //a TCP/IP stream, is the size of the TCP/IP window. This is even harder to predict than - //memory page size, but is likely to be 1460 * 44 = 64240. It could be lower or higher but experimentation - //shows that on at least the experimentation machine (.NET on WindowsXP, connecting to postgres server on - //localhost) if more than 64240 was available only 64240 would be used in each pass), so for at least - //that machine anything higher than 64240 is a waste. - //64240 being a bit much, settling for 4096 for now. - private const int THRASH_CAN_SIZE = 4096; - //This buffer array is used for reading and ignoring bytes we want to discard. - //It is thread-safe solely because it is never read from! - //Any attempt to read from this array deserves to fail. - //Note that in the cases where we are reading bytes to actually process them, we either - //have a buffer supplied from elsewhere (the calling method) or we create a small buffer - //as needed. This is since the buffer being used means that there that the possible performance - //gain of not creating a new buffer will be cancelled out by whatever buffering will have to be - //done to actually use the data. This is not the case here - we are pre-assigning a buffer for - //this case purely because we don't care what gets put into it. - private static readonly byte[] THRASH_CAN = new byte[THRASH_CAN_SIZE]; - private static readonly Encoding ENCODING_UTF8 = Encoding.UTF8; - - ///<summary> - /// This method takes a ProtocolVersion and returns an integer - /// version number that the Postgres backend will recognize in a - /// startup packet. - /// </summary> - public static Int32 ConvertProtocolVersion(ProtocolVersion Ver) - { - switch (Ver) - { - case ProtocolVersion.Version2: - return (int) ServerVersionCode.ProtocolVersion2; - - case ProtocolVersion.Version3: - return (int) ServerVersionCode.ProtocolVersion3; - } - - throw new ArgumentOutOfRangeException(); - } - - /// <summary> - /// This method takes a version string as returned by SELECT VERSION() and returns - /// a valid version string ("7.2.2" for example). - /// This is only needed when running protocol version 2. - /// This does not do any validity checks. - /// </summary> - public static string ExtractServerVersion(string VersionString) - { - Int32 Start = 0, End = 0; - - // find the first digit and assume this is the start of the version number - for (; Start < VersionString.Length && !char.IsDigit(VersionString[Start]); Start++) - { - ; - } - - End = Start; - - // read until hitting whitespace, which should terminate the version number - for (; End < VersionString.Length && !char.IsWhiteSpace(VersionString[End]); End++) - { - ; - } - - // Deal with this here so that if there are - // changes in a future backend version, we can handle it here in the - // protocol handler and leave everybody else put of it. - - VersionString = VersionString.Substring(Start, End - Start + 1); - - for (int idx = 0; idx != VersionString.Length; ++idx) - { - char c = VersionString[idx]; - if (!char.IsDigit(c) && c != '.') - { - VersionString = VersionString.Substring(0, idx); - break; - } - } - - return VersionString; - } - -/* - /// <summary> - /// Convert the beginning numeric part of the given string to Int32. - /// For example: - /// Strings "12345ABCD" and "12345.54321" would both be converted to int 12345. - /// </summary> - private static Int32 ConvertBeginToInt32(String Raw) - { - Int32 Length = 0; - for (; Length < Raw.Length && Char.IsNumber(Raw[Length]); Length++) - { - ; - } - return Convert.ToInt32(Raw.Substring(0, Length)); - } -*/ - - ///<summary> - /// This method gets a C NULL terminated string from the network stream. - /// It keeps reading a byte in each time until a NULL byte is returned. - /// It returns the resultant string of bytes read. - /// This string is sent from backend. - /// </summary> - public static String ReadString(Stream network_stream) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadString"); - - List<byte> buffer = new List<byte>(); - for (int bRead = network_stream.ReadByte(); bRead != 0; bRead = network_stream.ReadByte()) - { - if (bRead == -1) - { - throw new IOException(); - } - else - { - buffer.Add((byte) bRead); - } - } - - if (NpgsqlEventLog.Level >= LogLevel.Debug) - NpgsqlEventLog.LogMsg(resman, "Log_StringRead", LogLevel.Debug, ENCODING_UTF8.GetString(buffer.ToArray())); - - return ENCODING_UTF8.GetString(buffer.ToArray()); - } - - public static char ReadChar(Stream stream) - { - byte[] buffer = new byte[4]; //No character is more than 4 bytes long. - for (int i = 0; i != 4; ++i) - { - int byteRead = stream.ReadByte(); - if (byteRead == -1) - { - throw new EndOfStreamException(); - } - buffer[i] = (byte) byteRead; - if (ValidUTF8Ending(buffer, 0, i + 1)) //catch multi-byte encodings where we have not yet enough bytes. - { - return ENCODING_UTF8.GetChars(buffer)[0]; - } - } - throw new InvalidDataException(); - } - - public static int ReadChars(Stream stream, char[] output, int maxChars, ref int maxRead, int outputIdx) - { - if (maxChars == 0 || maxRead == 0) - { - return 0; - } - byte[] buffer = new byte[Math.Min(maxRead, maxChars*4)]; - int bytesSoFar = 0; - int charsSoFar = 0; - //A string of x chars length will take at least x bytes and at most - //4x. We set our buffer to 4x in size, but start with only reading x bytes. - //If we don't have the full string at this point, then we now have a new number - //of characters to read, and so we try to read one byte per character remaining to read. - //This hence involves the fewest possible number of passes through CheckedStreamRead - //without the risk of accidentally reading too far into the stream. - do - { - int toRead = Math.Min(maxRead, maxChars - charsSoFar); - CheckedStreamRead(stream, buffer, bytesSoFar, toRead); - maxRead -= toRead; - bytesSoFar += toRead; - } - while (maxRead > 0 && (charsSoFar = PessimisticGetCharCount(buffer, 0, bytesSoFar)) < maxChars); - return ENCODING_UTF8.GetDecoder().GetChars(buffer, 0, bytesSoFar, output, outputIdx, false); - } - - public static int SkipChars(Stream stream, int maxChars, ref int maxRead) - { - //This is the same as ReadChars, but it just discards the characters read. - if (maxChars == 0 || maxRead == 0) - { - return 0; - } - byte[] buffer = new byte[Math.Min(maxRead, ENCODING_UTF8.GetMaxByteCount(maxChars))]; - int bytesSoFar = 0; - int charsSoFar = 0; - do - { - int toRead = Math.Min(maxRead, maxChars - charsSoFar); - CheckedStreamRead(stream, buffer, bytesSoFar, toRead); - maxRead -= toRead; - bytesSoFar += toRead; - } - while (maxRead > 0 && (charsSoFar = PessimisticGetCharCount(buffer, 0, bytesSoFar)) < maxChars); - return charsSoFar; - } - - // Encoding.GetCharCount will count an incomplete character as a character. - // This makes sense if the user wants to assign a char[] buffer, since the incomplete character - // might be represented as U+FFFD or a similar approach may be taken. - // It's not much use though if we know the stream has >= x characters in it and we - // want to read x complete characters - we need to know that the last character is complete. - // Therefore we need to check on this. - // SECURITY CONSIDERATIONS: - // This function assumes we recieve valid UTF-8 data and as such security considerations - // must be noticed. - // In the case of deliberate mal-formed UTF-8 data, this function will not be used - // in it's interpretation. In the worse case it will result in the stream being mis-read - // and the operation malfunctioning, but anyone who is acting as a man-in-the-middle - // on the stream can already do that to us in a variety of interesting ways, so this - // function does not introduce a security issue in this regard. - // Therefore the optimisation of assuming valid UTF-8 data is reasonable and not insecure. - public static bool ValidUTF8Ending(byte[] buffer, int index, int count) - { - if (count <= 0) - { - return false; - } - byte examine = buffer[index + count - 1]; - //simplest case and also the most common with most data- a single-octet character. Handle directly. - if ((examine & 0x80) == 0) - { - return true; - } - //if the last byte isn't a trailing byte we're clearly not at the end. - if ((examine & 0xC0) != 0x80) - { - return false; - } - byte[] masks = new byte[] {0xE0, 0xF0, 0xF8}; - byte[] matches = new byte[] {0xC0, 0xE0, 0xF0}; - for (int i = 0; i + 2 <= count; ++i) - { - examine = buffer[index + count - 2 - i]; - if ((examine & masks[i]) == matches[i]) - { - return true; - } - if ((examine & 0xC0) != 0x80) - { - return false; - } - } - return false; - } - - //This is like Encoding.UTF8.GetCharCount() but it ignores a trailing incomplete - //character. See comments on ValidUTF8Ending() - public static int PessimisticGetCharCount(byte[] buffer, int index, int count) - { - return ENCODING_UTF8.GetCharCount(buffer, index, count) - (ValidUTF8Ending(buffer, index, count) ? 0 : 1); - } - - ///<summary> - /// This method writes a C NULL terminated string to the network stream. - /// It appends a NULL terminator to the end of the String. - /// </summary> - ///<summary> - /// This method writes a C NULL terminated string to the network stream. - /// It appends a NULL terminator to the end of the String. - /// </summary> - public static void WriteString(String the_string, Stream network_stream) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteString"); - - NpgsqlEventLog.LogMsg(resman, "Log_StringWritten", LogLevel.Debug, the_string); - - network_stream.Write(ENCODING_UTF8.GetBytes(the_string + '\x00'), 0, ENCODING_UTF8.GetByteCount(the_string) + 1); - } - - ///<summary> - /// This method writes a C NULL terminated string limited in length to the - /// backend server. - /// It pads the string with null bytes to the size specified. - /// </summary> - public static void WriteLimString(String the_string, Int32 n, Stream network_stream) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteLimString"); - - //Note: We do not know the size in bytes until after we have converted the string. - byte[] bytes = ENCODING_UTF8.GetBytes(the_string); - if (bytes.Length > n) - { - throw new ArgumentOutOfRangeException("the_string", the_string, - string.Format(resman.GetString("LimStringWriteTooLarge"), the_string, n)); - } - - network_stream.Write(bytes, 0, bytes.Length); - - //pad with zeros. - if (bytes.Length < n) - { - bytes = new byte[n - bytes.Length]; - network_stream.Write(bytes, 0, bytes.Length); - } - } - - public static void CheckedStreamRead(Stream stream, Byte[] buffer, Int32 offset, Int32 size) - { - Int32 bytes_from_stream = 0; - Int32 total_bytes_read = 0; - - while (size > 0) - { - bytes_from_stream = stream.Read(buffer, offset + total_bytes_read, size); - total_bytes_read += bytes_from_stream; - size -= bytes_from_stream; - } - } - - public static void EatStreamBytes(Stream stream, int size) - { -//See comment on THRASH_CAN and THRASH_CAN_SIZE. - while (size > 0) - { - size -= stream.Read(THRASH_CAN, 0, size < THRASH_CAN_SIZE ? size : THRASH_CAN_SIZE); - } - } - - public static int ReadEscapedBytes(Stream stream, byte[] output, int maxBytes, ref int maxRead, int outputIdx) - { - maxBytes = maxBytes > output.Length - outputIdx ? output.Length - outputIdx : maxBytes; - int i; - for (i = 0; i != maxBytes && maxRead > 0; ++i) - { - char c = ReadChar(stream); - --maxRead; - if (c == '\\') - { - --maxRead; - switch (c = ReadChar(stream)) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - maxRead -= 2; - output[outputIdx++] = - (byte) - ((int.Parse(c.ToString()) << 6) | (int.Parse(ReadChar(stream).ToString()) << 3) | - int.Parse(ReadChar(stream).ToString())); - break; - default: - output[outputIdx++] = (byte) c; - break; - } - } - else - { - output[outputIdx++] = (byte) c; - } - } - return i; - } - - public static int SkipEscapedBytes(Stream stream, int maxBytes, ref int maxRead) - { - int i; - for (i = 0; i != maxBytes && maxRead > 0; ++i) - { - --maxRead; - if (ReadChar(stream) == '\\') - { - --maxRead; - switch (ReadChar(stream)) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - maxRead -= 2; - EatStreamBytes(stream, 2); //note assumes all representations of '0' through '9' are single-byte. - break; - } - } - } - return i; - } - - /// <summary> - /// Write a 32-bit integer to the given stream in the correct byte order. - /// </summary> - public static void WriteInt32(Stream stream, Int32 value) - { - stream.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(value)), 0, 4); - } - - /// <summary> - /// Read a 32-bit integer from the given stream in the correct byte order. - /// </summary> - public static Int32 ReadInt32(Stream stream) - { - byte[] buffer = new byte[4]; - CheckedStreamRead(stream, buffer, 0, 4); - return IPAddress.NetworkToHostOrder(BitConverter.ToInt32(buffer, 0)); - } - - /// <summary> - /// Write a 16-bit integer to the given stream in the correct byte order. - /// </summary> - public static void WriteInt16(Stream stream, Int16 value) - { - stream.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(value)), 0, 2); - } - - /// <summary> - /// Read a 16-bit integer from the given stream in the correct byte order. - /// </summary> - public static Int16 ReadInt16(Stream stream) - { - byte[] buffer = new byte[2]; - CheckedStreamRead(stream, buffer, 0, 2); - return IPAddress.NetworkToHostOrder(BitConverter.ToInt16(buffer, 0)); - } - - public static int RotateShift(int val, int shift) - { - return (val << shift) | (val >> (sizeof (int) - shift)); - } - } - - /// <summary> - /// Represent the frontend/backend protocol version. - /// </summary> - public enum ProtocolVersion - { - Unknown, - Version2, - Version3 - } - - public enum ServerVersionCode - { - ProtocolVersion2 = 2 << 16, // 131072 - ProtocolVersion3 = 3 << 16 // 196608 - } - - /// <summary> - /// Represent the backend server version. - /// As this class offers no functionality beyond that offered by <see cref="System.Version"/> it has been - /// deprecated in favour of that class. - /// </summary> - /// - [Obsolete("Use System.Version")] - public sealed class ServerVersion : IEquatable<ServerVersion>, IComparable<ServerVersion>, IComparable, ICloneable - { - [Obsolete("Use ServerVersionCode.ProtocolVersion2")] public static readonly Int32 ProtocolVersion2 = 2 << 16; - // 131072 - - [Obsolete("Use ServerVersionCode.ProtocolVersion3")] public static readonly Int32 ProtocolVersion3 = 3 << 16; - // 196608 - - private readonly Version _version; - - private ServerVersion(Version ver) - { - _version = ver; - } - - /// <summary> - /// Server version major number. - /// </summary> - public Int32 Major - { - get { return _version.Major; } - } - - /// <summary> - /// Server version minor number. - /// </summary> - public Int32 Minor - { - get { return _version.Minor; } - } - - /// <summary> - /// Server version patch level number. - /// </summary> - public Int32 Patch - { - get { return _version.Build; } - } - - public static implicit operator Version(ServerVersion sv) - { - return (object) sv == null ? null : sv._version; - } - - public static implicit operator ServerVersion(Version ver) - { - return (object) ver == null ? null : new ServerVersion(ver.Clone() as Version); - } - - public static bool operator ==(ServerVersion One, ServerVersion TheOther) - { - return ((Version) One) == ((Version) TheOther); - } - - public static bool operator !=(ServerVersion One, ServerVersion TheOther) - { - return (Version) One != (Version) TheOther; - } - - public static bool operator >(ServerVersion One, ServerVersion TheOther) - { - return (Version) One > (Version) TheOther; - } - - public static bool operator >=(ServerVersion One, ServerVersion TheOther) - { - return (Version) One >= (Version) TheOther; - } - - public static bool operator <(ServerVersion One, ServerVersion TheOther) - { - return (Version) One < (Version) TheOther; - } - - public static bool operator <=(ServerVersion One, ServerVersion TheOther) - { - return (Version) One <= (Version) TheOther; - } - - public bool Equals(ServerVersion other) - { - return other != null && this == other; - } - - public int CompareTo(ServerVersion other) - { - return _version.CompareTo(other); - } - - public int CompareTo(object obj) - { - return CompareTo(obj as ServerVersion); - } - - public object Clone() - { - return new ServerVersion(_version.Clone() as Version); - } - - public override bool Equals(object O) - { - return Equals(O as ServerVersion); - } - - public override int GetHashCode() - { - //Assume Version has a decent hash code function. - //If this turns out not to be true do not use _Major ^ _Minor ^ _Patch, but make sure the values are spread throughout the 32bit range more to avoid false clashes. - return _version.GetHashCode(); - } - - /// <summary> - /// Returns the string representation of this version in three place dot notation (Major.Minor.Patch). - /// </summary> - public override String ToString() - { - return _version.ToString(); - } - } - - internal enum FormatCode : - short - { - Text = 0, - Binary = 1 - } - - internal class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue> - { - private readonly Dictionary<TKey, TValue> _inner; - - public ReadOnlyDictionary(Dictionary<TKey, TValue> inner) - { - _inner = inner; - } - - private static void StopWrite() - { - throw new NotSupportedException(PGUtil.resman.GetString("Read_Only_Write_Error")); - } - - public TValue this[TKey key] - { - get { return _inner[key]; } - set { StopWrite(); } - } - - public ICollection<TKey> Keys - { - get { return _inner.Keys; } - } - - public ICollection<TValue> Values - { - get { return _inner.Values; } - } - - public int Count - { - get { return _inner.Count; } - } - - public bool IsReadOnly - { - get { return true; } - } - - public bool ContainsKey(TKey key) - { - return _inner.ContainsKey(key); - } - - public void Add(TKey key, TValue value) - { - StopWrite(); - } - - public bool Remove(TKey key) - { - StopWrite(); - return false; - } - - public bool TryGetValue(TKey key, out TValue value) - { - return _inner.TryGetValue(key, out value); - } - - public void Add(KeyValuePair<TKey, TValue> item) - { - StopWrite(); - } - - public void Clear() - { - StopWrite(); - } - - public bool Contains(KeyValuePair<TKey, TValue> item) - { - return ((IDictionary<TKey, TValue>) _inner).Contains(item); - } - - public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) - { - ((IDictionary<TKey, TValue>) _inner).CopyTo(array, arrayIndex); - } - - public bool Remove(KeyValuePair<TKey, TValue> item) - { - StopWrite(); - return false; - } - - public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() - { - foreach (KeyValuePair<TKey, TValue> kvp in _inner) - { - yield return new KeyValuePair<TKey, TValue>(kvp.Key, kvp.Value); //return copy so changes don't affect our copy. - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - return _inner.GetEnumerator(); - } - } -}
\ No newline at end of file + /// <summary> + /// Represent the frontend/backend protocol version. + /// </summary> + public enum ProtocolVersion + { + Unknown, + Version2, + Version3 + } + + /// <summary> + /// Represent the backend server version. + /// </summary> + public sealed class ServerVersion + { + public static readonly Int32 ProtocolVersion2 = 2 << 16; // 131072 + public static readonly Int32 ProtocolVersion3 = 3 << 16; // 196608 + + private Int32 _Major; + private Int32 _Minor; + private Int32 _Patch; + + internal ServerVersion(Int32 Major, Int32 Minor, Int32 Patch) + { + _Major = Major; + _Minor = Minor; + _Patch = Patch; + } + + /// <summary> + /// Server version major number. + /// </summary> + public Int32 Major + { get { return _Major; } } + + /// <summary> + /// Server version minor number. + /// </summary> + public Int32 Minor + { get { return _Minor; } } + + /// <summary> + /// Server version patch level number. + /// </summary> + public Int32 Patch + { get { return _Patch; } } + + 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 && + One._Patch == TheOther._Patch; + } + + public static bool operator != (ServerVersion One, ServerVersion TheOther) + { + if (((Object)One == null) || ((Object)TheOther == null)) + return true; + + return ! (One == TheOther); + } + + public static bool operator > (ServerVersion One, ServerVersion TheOther) + { + return + (One._Major > TheOther._Major) || + (One._Major == TheOther._Major && One._Minor > TheOther._Minor) || + (One._Major == TheOther._Major && One._Minor == TheOther._Minor && One._Patch > TheOther._Patch); + } + + public static bool operator >= (ServerVersion One, ServerVersion TheOther) + { + return + (One._Major > TheOther._Major) || + (One._Major == TheOther._Major && One._Minor > TheOther._Minor) || + (One._Major == TheOther._Major && One._Minor == TheOther._Minor && One._Patch >= TheOther._Patch); + } + + public static bool operator < (ServerVersion One, ServerVersion TheOther) + { + return + (One._Major < TheOther._Major) || + (One._Major == TheOther._Major && One._Minor < TheOther._Minor) || + (One._Major == TheOther._Major && One._Minor == TheOther._Minor && One._Patch < TheOther._Patch); + } + + public static bool operator <= (ServerVersion One, ServerVersion TheOther) + { + return + (One._Major < TheOther._Major) || + (One._Major == TheOther._Major && One._Minor < TheOther._Minor) || + (One._Major == TheOther._Major && One._Minor == TheOther._Minor && One._Patch <= TheOther._Patch); + } + + public override bool Equals(object O) + { + if (O == null) + return false; + + return (O.GetType() == this.GetType() && this == (ServerVersion)O); + } + + public override int GetHashCode() + { + return _Major ^ _Minor ^ _Patch; + } + + /// <summary> + /// Returns the string representation of this version in three place dot notation (Major.Minor.Patch). + /// </summary> + public override String ToString() + { + return string.Format("{0}.{1}.{2}", _Major, _Minor, _Patch); + } + } + + internal enum FormatCode: + short + { + Text = 0, + Binary = 1 + } + + ///<summary> + /// This class provides many util methods to handle + /// reading and writing of PostgreSQL protocol messages. + /// </summary> + internal abstract class PGUtil + { + // Logging related values + private static readonly String CLASSNAME = "PGUtil"; + private static ResourceManager resman = new ResourceManager(typeof(PGUtil)); + + ///<summary> + /// This method takes a ProtocolVersion and returns an integer + /// version number that the Postgres backend will recognize in a + /// startup packet. + /// </summary> + public static Int32 ConvertProtocolVersion(ProtocolVersion Ver) + { + switch (Ver) { + case ProtocolVersion.Version2 : + return ServerVersion.ProtocolVersion2; + + case ProtocolVersion.Version3 : + return ServerVersion.ProtocolVersion3; + + } + + // CHECKME + // should we throw? + return 0; + } + + /// <summary> + /// This method takes a version string as returned by SELECT VERSION() and returns + /// a valid version string ("7.2.2" for example). + /// This is only needed when running protocol version 2. + /// This does not do any validity checks. + /// </summary> + public static string ExtractServerVersion (string VersionString) + { + Int32 Start = 0, End = 0; + + // find the first digit and assume this is the start of the version number + for ( ; Start < VersionString.Length && ! char.IsDigit(VersionString[Start]) ; Start++); + + End = Start; + + // read until hitting whitespace, which should terminate the version number + for ( ; End < VersionString.Length && ! char.IsWhiteSpace(VersionString[End]) ; End++); + + return VersionString.Substring(Start, End - Start + 1); + } + + /// <summary> + /// This method takes a version string ("7.4.1" for example) and produces + /// the required integer version numbers (7, 4, and 1). + /// </summary> + public static ServerVersion ParseServerVersion (string VersionString) + { + String[] Parts; + + Parts = VersionString.Split('.'); + + if (Parts.Length < 2) { + throw new FormatException(String.Format("Internal: Backend sent bad version string: {0}", VersionString)); + } + + try { + if (Parts.Length == 2) { + // Coerce it into a 3-part version. + return new ServerVersion(ConvertBeginToInt32(Parts[0]), ConvertBeginToInt32(Parts[1]), 0); + } else { + // If there are more than 3 parts, just ignore the extras, rather than rejecting it. + return new ServerVersion(ConvertBeginToInt32(Parts[0]), ConvertBeginToInt32(Parts[1]), ConvertBeginToInt32(Parts[2])); + } + } catch (Exception E) { + throw new FormatException(String.Format("Internal: Backend sent bad version string: {0}", VersionString), E); + } + } + + /// <summary> + /// Convert the beginning numeric part of the given string to Int32. + /// For example: + /// Strings "12345ABCD" and "12345.54321" would both be converted to int 12345. + /// </summary> + private static Int32 ConvertBeginToInt32(String Raw) + { + Int32 Length = 0; + for ( ; Length < Raw.Length && Char.IsNumber(Raw[Length]) ; Length++); + return Convert.ToInt32(Raw.Substring(0, Length)); + } + + ///<summary> + /// This method gets a C NULL terminated string from the network stream. + /// It keeps reading a byte in each time until a NULL byte is returned. + /// It returns the resultant string of bytes read. + /// This string is sent from backend. + /// </summary> + public static String ReadString(Stream network_stream, Encoding encoding) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadString"); + + ArrayList buffer = new ArrayList(); + Byte b; + String string_read; + + // [FIXME] Is this cast always safe? + b = (Byte)network_stream.ReadByte(); + while(b != 0) + { + buffer.Add(b); + b = (Byte)network_stream.ReadByte(); + } + + string_read = encoding.GetString((Byte[])buffer.ToArray(typeof(Byte))); + NpgsqlEventLog.LogMsg(resman, "Log_StringRead", LogLevel.Debug, string_read); + + return string_read; + } + + ///<summary> + /// This method gets a length terminated string from a network stream. + /// It returns the resultant string of bytes read. + /// This string is sent from backend. + /// </summary> + public static String ReadString(Stream network_stream, Encoding encoding, Int32 length) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadString"); + + ArrayList buffer = new ArrayList(); + Byte b; + String string_read; + + for (int C = 0 ; C < length ; C++) + { + // [FIXME] Is this cast always safe? + b = (Byte)network_stream.ReadByte(); + buffer.Add(b); + } + + string_read = encoding.GetString((Byte[])buffer.ToArray(typeof(Byte))); + NpgsqlEventLog.LogMsg(resman, "Log_StringRead", LogLevel.Debug, string_read); + + return string_read; + } + + ///<summary> + /// This method writes a C NULL terminated string to the network stream. + /// It appends a NULL terminator to the end of the String. + /// </summary> + public static void WriteString(String the_string, Stream network_stream, Encoding encoding) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteString"); + + NpgsqlEventLog.LogMsg(resman, "Log_StringWritten", LogLevel.Debug, the_string); + + network_stream.Write(encoding.GetBytes(the_string + '\x00') , 0, encoding.GetByteCount(the_string) + 1); + } + + ///<summary> + /// This method writes a C NULL terminated string limited in length to the + /// backend server. + /// It pads the string with null bytes to the size specified. + /// </summary> + public static void WriteLimString(String the_string, Int32 n, Stream network_stream, Encoding encoding) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteLimString"); + + // [FIXME] Parameters should be validated. And what about strings + // larger than or equal to n? + + // Pad the string to the specified value. + String string_padded = the_string.PadRight(n, '\x00'); + + network_stream.Write(encoding.GetBytes(string_padded), 0, n); + } + + public static void CheckedStreamRead(Stream stream, Byte[] buffer, Int32 offset, Int32 size) + { + Int32 bytes_from_stream = 0; + Int32 total_bytes_read = 0; + + do + { + bytes_from_stream = stream.Read(buffer, offset + total_bytes_read, size); + total_bytes_read += bytes_from_stream; + size -= bytes_from_stream; + } + while(size > 0); + } + + + /// <summary> + /// Write a 32-bit integer to the given stream in the correct byte order. + /// </summary> + public static void WriteInt32(Stream stream, Int32 value) + { + stream.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(value)), 0, 4); + } + + /// <summary> + /// Read a 32-bit integer from the given stream in the correct byte order. + /// </summary> + public static Int32 ReadInt32(Stream stream, Byte[] buffer) + { + CheckedStreamRead(stream, buffer, 0, 4); + return IPAddress.NetworkToHostOrder(BitConverter.ToInt32(buffer, 0)); + + } + + /// <summary> + /// Write a 16-bit integer to the given stream in the correct byte order. + /// </summary> + public static void WriteInt16(Stream stream, Int16 value) + { + stream.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(value)), 0, 2); + } + + /// <summary> + /// Read a 16-bit integer from the given stream in the correct byte order. + /// </summary> + public static Int16 ReadInt16(Stream stream, Byte[] buffer) + { + CheckedStreamRead(stream, buffer, 0, 2); + return IPAddress.NetworkToHostOrder(BitConverter.ToInt16(buffer, 0)); + + } + } +} diff --git a/mcs/class/Npgsql/Npgsql/PGUtil.resx b/mcs/class/Npgsql/Npgsql/PGUtil.resx index 82db1c51fce..794c3a407a1 100644 --- a/mcs/class/Npgsql/Npgsql/PGUtil.resx +++ b/mcs/class/Npgsql/Npgsql/PGUtil.resx @@ -1,9 +1,9 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8" ?> <root> - <!-- + <!-- Microsoft ResX Schema - Version 2.0 + Version 1.3 The primary goals of this format is to allow a simple XML format that is mostly human readable. The generation and parsing of the @@ -14,17 +14,16 @@ ... ado.net/XML headers & schema ... <resheader name="resmimetype">text/microsoft-resx</resheader> - <resheader name="version">2.0</resheader> + <resheader name="version">1.3</resheader> <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> - <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Name1">this is my long string</data> <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> - <value>[base64 mime encoded serialized .NET Framework object]</value> + [base64 mime encoded serialized .NET Framework object] </data> <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> - <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> - <comment>This is a comment</comment> + [base64 mime encoded string representing a byte array form of the .NET Framework object] </data> There are any number of "resheader" rows that contain simple @@ -36,7 +35,7 @@ Classes that don't support this are serialized and stored with the mimetype set. - The mimetype is used for serialized objects, and tells the + The mimetype is used forserialized objects, and tells the ResXResourceReader how to depersist the object. This is currently not extensible. For a given mimetype the value must be set accordingly: @@ -46,7 +45,7 @@ mimetype: application/x-microsoft.net.object.binary.base64 value : The object must be serialized with - : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : System.Serialization.Formatters.Binary.BinaryFormatter : and then encoded with base64 encoding. mimetype: application/x-microsoft.net.object.soap.base64 @@ -59,71 +58,50 @@ : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> - <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> - <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> - <xsd:element name="root" msdata:IsDataSet="true"> - <xsd:complexType> - <xsd:choice maxOccurs="unbounded"> - <xsd:element name="metadata"> - <xsd:complexType> - <xsd:sequence> - <xsd:element name="value" type="xsd:string" minOccurs="0" /> - </xsd:sequence> - <xsd:attribute name="name" use="required" type="xsd:string" /> - <xsd:attribute name="type" type="xsd:string" /> - <xsd:attribute name="mimetype" type="xsd:string" /> - <xsd:attribute ref="xml:space" /> - </xsd:complexType> - </xsd:element> - <xsd:element name="assembly"> - <xsd:complexType> - <xsd:attribute name="alias" type="xsd:string" /> - <xsd:attribute name="name" type="xsd:string" /> - </xsd:complexType> - </xsd:element> - <xsd:element name="data"> - <xsd:complexType> - <xsd:sequence> - <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> - <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> - </xsd:sequence> - <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> - <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> - <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> - <xsd:attribute ref="xml:space" /> - </xsd:complexType> - </xsd:element> - <xsd:element name="resheader"> - <xsd:complexType> - <xsd:sequence> - <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> - </xsd:sequence> - <xsd:attribute name="name" type="xsd:string" use="required" /> - </xsd:complexType> - </xsd:element> - </xsd:choice> - </xsd:complexType> - </xsd:element> - </xsd:schema> - <resheader name="resmimetype"> - <value>text/microsoft-resx</value> - </resheader> - <resheader name="version"> - <value>2.0</value> - </resheader> - <resheader name="reader"> - <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> - </resheader> - <resheader name="writer"> - <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> - </resheader> - <data name="Log_StringRead" xml:space="preserve"> - <value>String read: {0}.</value> - </data> - <data name="Log_StringWritten" xml:space="preserve"> - <value>String written: {0}.</value> - </data> - <data name="Read_Only_Write_Error" xml:space="preserve"> - <value>The collection is read-only</value> - </data> -</root>
\ No newline at end of file + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>1.3</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Log_StringRead"> + <value>String read: {0}.</value> + </data> + <data name="Log_StringWritten"> + <value>String written: {0}.</value> + </data> + +</root> diff --git a/mcs/class/Npgsql/NpgsqlTypes/FastPath.cs b/mcs/class/Npgsql/NpgsqlTypes/FastPath.cs index 7dc6650f3f1..ab88f518cde 100644 --- a/mcs/class/Npgsql/NpgsqlTypes/FastPath.cs +++ b/mcs/class/Npgsql/NpgsqlTypes/FastPath.cs @@ -11,78 +11,74 @@ Changed case of method names to conform to .Net names standard. Also changed type names to their true names. i.e. int -> Int32 -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + 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.Collections.Generic; -using System.Data; +using System.Collections; using System.IO; +using System.Data; using Npgsql; namespace NpgsqlTypes { - /* + + + /* * This class implements the Fastpath api. * * * */ + public class Fastpath + { + // This maps the functions names to their id's (possible unique just + // to a connection). + protected Hashtable func = new Hashtable(); - public class Fastpath - { - // This maps the functions names to their id's (possible unique just - // to a connection). - protected Dictionary<string, int> func = new Dictionary<string, int>(); + protected NpgsqlConnection conn; // our connection + protected Stream stream; // the network stream - protected NpgsqlConnection conn; // our connection - protected Stream stream; // the network stream - - /* + /* * Initialises the fastpath system * * @param conn BaseConnection to attach to * @param stream The network stream to the backend */ - - public Fastpath(NpgsqlConnection conn, Stream stream) - { - this.conn = conn; - this.stream = stream; - } + public Fastpath(NpgsqlConnection conn, Stream stream) + { + this.conn = conn; + this.stream = stream; + } - /* + /* * Initialises the fastpath system * * @param conn BaseConnection to attach to * @param stream The network stream to the backend */ - - public Fastpath(NpgsqlConnection conn) - { - this.conn = conn; - // check if the connection is closed ? - this.stream = conn.Connector.Stream; - } - - /* + public Fastpath(NpgsqlConnection conn) + { + this.conn = conn; + // check if the connection is closed ? + this.stream = conn.Connector.Stream; + } + + /* * Send a function call to the PostgreSQL backend * * @param fnid Function id @@ -91,277 +87,271 @@ namespace NpgsqlTypes * @return null if no data, Integer if an integer result, or byte[] otherwise * @exception NpgsqlException if a database-access error occurs. */ - - public Object FastpathCall(Int32 fnid, Boolean resulttype, FastpathArg[] args) - { - try - { - if (conn.BackendProtocolVersion == ProtocolVersion.Version3) - { - return FastpathV3(fnid, resulttype, args); - } - else - { - return FastpathV2(fnid, resulttype, args); - } - } - catch (IOException) - { - conn.ClearPool(); - throw new NpgsqlException("The Connection is broken."); - } - } - - private Object FastpathV3(Int32 fnid, Boolean resulttype, FastpathArg[] args) - { - // give thread safety - lock (stream) - { - // send the function call - - { - Int32 l_msgLen = 0; - l_msgLen += 16; - for (Int32 i = 0; i < args.Length; i++) - { - l_msgLen += args[i].SendSize(); - } - - stream.WriteByte((Byte) 'F'); - PGUtil.WriteInt32(stream, l_msgLen); - PGUtil.WriteInt32(stream, fnid); - PGUtil.WriteInt16(stream, 1); - PGUtil.WriteInt16(stream, 1); - PGUtil.WriteInt16(stream, (short) args.Length); - - for (Int32 i = 0; i < args.Length; i++) - { - args[i].Send(stream); - } - - PGUtil.WriteInt16(stream, 1); - - // This is needed, otherwise data can be lost - stream.Flush(); - } - - - // Now handle the result - - // Now loop, reading the results - Object result = null; // our result - Exception error = null; - Int32 c; - Boolean l_endQuery = false; - - while (!l_endQuery) - { - c = (Char) stream.ReadByte(); - - switch (c) - { - case 'A': // Asynchronous Notify - Int32 msglen = PGUtil.ReadInt32(stream); - Int32 pid = PGUtil.ReadInt32(stream); - String msg = PGUtil.ReadString(stream); - PGUtil.ReadString(stream); - String param = PGUtil.ReadString(stream); - - break; - //------------------------------ - // Error message returned - case 'E': - NpgsqlError e = new NpgsqlError(conn.BackendProtocolVersion, stream); - throw new NpgsqlException(e.ToString()); - - //------------------------------ - // Notice from backend - case 'N': - Int32 l_nlen = PGUtil.ReadInt32(stream); - - conn.Connector.FireNotice(new NpgsqlError(conn.BackendProtocolVersion, stream)); - - break; - - case 'V': - Int32 l_msgLen = PGUtil.ReadInt32(stream); - Int32 l_valueLen = PGUtil.ReadInt32(stream); - - if (l_valueLen == -1) - { - //null value - } - else if (l_valueLen == 0) - { - result = new Byte[0]; - } - else - { - // Return an Integer if - if (resulttype) - { - result = PGUtil.ReadInt32(stream); - } - else - { - Byte[] buf = new Byte[l_valueLen]; - - Int32 bytes_from_stream = 0; - Int32 total_bytes_read = 0; - Int32 size = l_valueLen; - do - { - bytes_from_stream = stream.Read(buf, total_bytes_read, size); - total_bytes_read += bytes_from_stream; - size -= bytes_from_stream; - } - while (size > 0); - - result = buf; - } - } - break; - - case 'Z': - //TODO: use size better - if (PGUtil.ReadInt32(stream) != 5) - { - throw new NpgsqlException("Received Z"); - } - //TODO: handle transaction status - Char l_tStatus = (Char) stream.ReadByte(); - l_endQuery = true; - break; - - default: - throw new NpgsqlException(string.Format("postgresql.fp.protocol received {0}", c)); - } - } - - if (error != null) - { - throw error; - } - - return result; - } - } - - private Object FastpathV2(Int32 fnid, Boolean resulttype, FastpathArg[] args) - { - // added Oct 7 1998 to give us thread safety - lock (stream) - { - // send the function call - - // 70 is 'F' in ASCII. Note: don't use SendChar() here as it adds padding - // that confuses the backend. The 0 terminates the command line. - stream.WriteByte(70); - stream.WriteByte(0); - - PGUtil.WriteInt32(stream, fnid); - PGUtil.WriteInt32(stream, args.Length); - - - for (Int32 i = 0; i < args.Length; i++) - { - args[i].Send(stream); - } - - // This is needed, otherwise data can be lost - stream.Flush(); - - - // Now handle the result - - // Now loop, reading the results - Object result = null; // our result - String errorMessage = ""; - Int32 c; - Boolean l_endQuery = false; - while (!l_endQuery) - { - c = (Char) stream.ReadByte(); - - switch (c) - { - case 'A': // Asynchronous Notify - //TODO: do something with this - Int32 pid = PGUtil.ReadInt32(stream); - String msg = PGUtil.ReadString(stream); - - - break; - - //------------------------------ - // Error message returned - case 'E': - NpgsqlError e = new NpgsqlError(conn.BackendProtocolVersion, stream); - errorMessage += e.Message; - break; - - //------------------------------ - // Notice from backend - case 'N': - NpgsqlError notice = new NpgsqlError(conn.BackendProtocolVersion, stream); - errorMessage += notice.Message; - break; - - case 'V': - Char l_nextChar = (Char) stream.ReadByte(); - if (l_nextChar == 'G') - { - Int32 sz = PGUtil.ReadInt32(stream); - // Return an Integer if - if (resulttype) - { - result = PGUtil.ReadInt32(stream); - } - else - { - Byte[] buf = new Byte[sz]; - - Int32 bytes_from_stream = 0; - Int32 total_bytes_read = 0; - Int32 size = sz; - do - { - bytes_from_stream = stream.Read(buf, total_bytes_read, size); - total_bytes_read += bytes_from_stream; - size -= bytes_from_stream; - } - while (size > 0); - - result = buf; - } - //There should be a trailing '0' - Int32 l_endChar = (Char) stream.ReadByte(); - } - else - { - //it must have been a '0', thus no results - } - break; - - case 'Z': - l_endQuery = true; - break; - - default: - throw new NpgsqlException(string.Format("postgresql.fp.protocol {0}", c)); - } - } - - if (errorMessage != null) - { - throw new NpgsqlException("postgresql.fp.error" + errorMessage); - } - - return result; - } - } - - /* + public Object FastpathCall(Int32 fnid, Boolean resulttype, FastpathArg[] args) + { + try + { + if (conn.BackendProtocolVersion == ProtocolVersion.Version3) + { + return FastpathV3(fnid, resulttype, args); + } + else + { + return FastpathV2(fnid, resulttype, args); + } + } + catch(IOException e) + { + conn.ClearPool(); + throw new NpgsqlException("The Connection is broken."); + } + } + + private Object FastpathV3(Int32 fnid, Boolean resulttype, FastpathArg[] args) + { + // give thread safety + lock (stream) + { + // send the function call + + { + Int32 l_msgLen = 0; + l_msgLen += 16; + for (Int32 i=0;i < args.Length;i++) + l_msgLen += args[i].SendSize(); + + stream.WriteByte((Byte)'F'); + PGUtil.WriteInt32(stream,l_msgLen); + PGUtil.WriteInt32(stream,fnid); + PGUtil.WriteInt16(stream,1); + PGUtil.WriteInt16(stream,1); + PGUtil.WriteInt16(stream,(short)args.Length); + + for (Int32 i = 0;i < args.Length;i++) + args[i].Send(stream); + + PGUtil.WriteInt16(stream,1); + + // This is needed, otherwise data can be lost + stream.Flush(); + } + + + // Now handle the result + + // Now loop, reading the results + Object result = null; // our result + Exception error = null; + Int32 c; + Boolean l_endQuery = false; + Byte[] input_buffer = new Byte[512]; + + while (!l_endQuery) + { + c = (Char)stream.ReadByte(); + + switch (c) + { + case 'A': // Asynchronous Notify + Int32 msglen = PGUtil.ReadInt32(stream,input_buffer); + Int32 pid = PGUtil.ReadInt32(stream,input_buffer); + String msg = PGUtil.ReadString(stream,conn.Connector.Encoding); + PGUtil.ReadString(stream,conn.Connector.Encoding); + String param = PGUtil.ReadString(stream,conn.Connector.Encoding); + + conn.Connector.CheckErrorsAndNotifications(); + break; + //------------------------------ + // Error message returned + case 'E': + NpgsqlError e = new NpgsqlError(conn.BackendProtocolVersion); + e.ReadFromStream(stream,conn.Connector.Encoding); + throw new NpgsqlException(e.ToString()); + + //------------------------------ + // Notice from backend + case 'N': + Int32 l_nlen = PGUtil.ReadInt32(stream,input_buffer); + + NpgsqlError e1 = new NpgsqlError(conn.BackendProtocolVersion); + e1.ReadFromStream(stream,conn.Connector.Encoding); + conn.Connector.Mediator.Errors.Add(e1); + + break; + + case 'V': + Int32 l_msgLen = PGUtil.ReadInt32(stream,input_buffer); + Int32 l_valueLen = PGUtil.ReadInt32(stream,input_buffer); + + if (l_valueLen == -1) + { + //null value + } + else if (l_valueLen == 0) + { + result = new Byte[0]; + } + else + { + // Return an Integer if + if (resulttype) + + result = PGUtil.ReadInt32(stream,input_buffer); + else + { + Byte[] buf = new Byte[l_valueLen]; + + Int32 bytes_from_stream = 0; + Int32 total_bytes_read = 0; + Int32 size = l_valueLen; + do + { + bytes_from_stream = stream.Read(buf, total_bytes_read, size); + total_bytes_read += bytes_from_stream; + size -= bytes_from_stream; + } + while(size > 0); + + result = buf; + } + } + break; + + case 'Z': + //TODO: use size better + if (PGUtil.ReadInt32(stream,input_buffer) != 5) + throw new NpgsqlException("Received Z" ); + //TODO: handle transaction status + Char l_tStatus = (Char)stream.ReadByte(); + l_endQuery = true; + break; + + default: + throw new NpgsqlException("postgresql.fp.protocol received " + c.ToString()); + } + } + + if ( error != null ) + throw error; + + return result; + } + } + + private Object FastpathV2(Int32 fnid, Boolean resulttype, FastpathArg[] args) + { + // added Oct 7 1998 to give us thread safety + lock (stream) + { + // send the function call + + // 70 is 'F' in ASCII. Note: don't use SendChar() here as it adds padding + // that confuses the backend. The 0 terminates the command line. + stream.WriteByte((Byte)70); + stream.WriteByte((Byte)0); + + PGUtil.WriteInt32(stream,fnid); + PGUtil.WriteInt32(stream,args.Length); + + + for (Int32 i = 0;i < args.Length;i++) + args[i].Send(stream); + + // This is needed, otherwise data can be lost + stream.Flush(); + + + // Now handle the result + + // Now loop, reading the results + Object result = null; // our result + String errorMessage = ""; + Byte[] input_buffer = new Byte[512]; + Int32 c; + Boolean l_endQuery = false; + while (!l_endQuery) + { + c = (Char)stream.ReadByte(); + + switch (c) + { + case 'A': // Asynchronous Notify + //TODO: do something with this + Int32 pid = PGUtil.ReadInt32(stream,input_buffer); + String msg = PGUtil.ReadString(stream,conn.Connector.Encoding); + + + conn.Connector.CheckErrorsAndNotifications(); + + break; + + //------------------------------ + // Error message returned + case 'E': + NpgsqlError e = new NpgsqlError(conn.BackendProtocolVersion); + e.ReadFromStream(stream,conn.Connector.Encoding); + errorMessage += e.Message; + break; + + //------------------------------ + // Notice from backend + case 'N': + NpgsqlError notice = new NpgsqlError(conn.BackendProtocolVersion); + notice.ReadFromStream(stream,conn.Connector.Encoding); + errorMessage += notice.Message; + break; + + case 'V': + Char l_nextChar = (Char)stream.ReadByte(); + if (l_nextChar == 'G') + { + Int32 sz = PGUtil.ReadInt32(stream,input_buffer); + // Return an Integer if + if (resulttype) + result = PGUtil.ReadInt32(stream,input_buffer); + else + { + Byte[] buf = new Byte[sz]; + + Int32 bytes_from_stream = 0; + Int32 total_bytes_read = 0; + Int32 size = sz; + do + { + bytes_from_stream = stream.Read(buf, total_bytes_read, size); + total_bytes_read += bytes_from_stream; + size -= bytes_from_stream; + } + while(size > 0); + + result = buf; + } + //There should be a trailing '0' + Int32 l_endChar = (Char)stream.ReadByte(); + } + else + { + //it must have been a '0', thus no results + } + break; + + case 'Z': + l_endQuery = true; + break; + + default: + throw new NpgsqlException("postgresql.fp.protocol " + c.ToString()); + } + } + + if ( errorMessage != null ) + throw new NpgsqlException("postgresql.fp.error" + errorMessage); + + return result; + } + } + + /* * Send a function call to the PostgreSQL backend by name. * * Note: the mapping for the procedure name to function id needs to exist, @@ -381,41 +371,38 @@ namespace NpgsqlTypes * occurs. * @see org.postgresql.largeobject.LargeObject */ + public Object FastpathCall(String name, Boolean resulttype, FastpathArg[] args) + { + return FastpathCall(GetID(name), resulttype, args); + } - public Object FastpathCall(String name, Boolean resulttype, FastpathArg[] args) - { - return FastpathCall(GetID(name), resulttype, args); - } - - /* + /* * This convenience method assumes that the return value is an Integer * @param name Function name * @param args Function arguments * @return integer result * @exception NpgsqlException if a database-access error occurs or no result */ + public Int32 GetInteger(String name, FastpathArg[] args) + { + Int32 i = (Int32)FastpathCall(name, true, args); - public Int32 GetInteger(String name, FastpathArg[] args) - { - Int32 i = (Int32) FastpathCall(name, true, args); - - return i; - } + return i; + } - /* + /* * This convenience method assumes that the return value is an Integer * @param name Function name * @param args Function arguments * @return byte[] array containing result * @exception NpgsqlException if a database-access error occurs or no result */ + public Byte[] GetData(String name, FastpathArg[] args) + { + return (Byte[])FastpathCall(name, false, args); + } - public Byte[] GetData(String name, FastpathArg[] args) - { - return (Byte[]) FastpathCall(name, false, args); - } - - /* + /* * This adds a function to our lookup table. * * <p>User code should use the addFunctions method, which is based upon a @@ -426,13 +413,12 @@ namespace NpgsqlTypes * @param name Function name * @param fnid Function id */ + public void AddFunction(String name, Int32 fnid) + { + func.Add(name, fnid); + } - public void AddFunction(String name, Int32 fnid) - { - func.Add(name, fnid); - } - - /* + /* * This takes a ResultSet containing two columns. Column 1 contains the * function name, Column 2 the oid. * @@ -445,7 +431,7 @@ namespace NpgsqlTypes * * <p>PostgreSQL stores the function id's and their corresponding names in * the pg_proc table. To speed things up locally, instead of querying each - * function from that table when required, a Dictionary is used. Also, only + * function from that table when required, a Hashtable is used. Also, only * the function's required are entered into this table, keeping connection * times as fast as possible. * @@ -464,20 +450,17 @@ namespace NpgsqlTypes * @exception NpgsqlException if a database-access error occurs. * @see org.postgresql.largeobject.LargeObjectManager */ - - public void AddFunctions(IDataReader rs) - { - while (rs.Read()) - { - String key = (String) rs[0]; - if (!func.ContainsKey(key)) - { - func.Add(key, Int32.Parse(rs[1].ToString())); - } - } - } - - /* + public void AddFunctions(IDataReader rs) + { + while (rs.Read()) + { + String key = (String)rs[0]; + if( !func.ContainsKey(key ) ) + func.Add(key, Int32.Parse(rs[1].ToString())); + } + } + + /* * This returns the function id associated by its name * * <p>If addFunction() or addFunctions() have not been called for this name, @@ -487,10 +470,14 @@ namespace NpgsqlTypes * @return Function ID for fastpath call * @exception NpgsqlException is function is unknown. */ + public Int32 GetID(String name) + { + Int32 id = (Int32)func[name]; + + + + return id; + } + } - public Int32 GetID(String name) - { - return func[name]; - } - } -}
\ No newline at end of file +} diff --git a/mcs/class/Npgsql/NpgsqlTypes/FastPathArg.cs b/mcs/class/Npgsql/NpgsqlTypes/FastPathArg.cs index a00fa76f73e..51277265987 100644 --- a/mcs/class/Npgsql/NpgsqlTypes/FastPathArg.cs +++ b/mcs/class/Npgsql/NpgsqlTypes/FastPathArg.cs @@ -11,23 +11,19 @@ Changed case of method names to conform to .Net names standard. Also changed type names to their true names. i.e. int -> Int32 - // Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - + 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 ------------------------------------------------------------------------- */ @@ -37,71 +33,67 @@ using Npgsql; namespace NpgsqlTypes { - public class FastpathArg - { - /* + public class FastpathArg + { + /* * Type of argument, true=integer, false=byte[] */ - public Boolean type; + public Boolean type; - /* + /* * Integer value if type=true */ - public Int32 value; + public Int32 value; - /* + /* * Byte value if type=false; */ - public Byte[] bytes; + public Byte[] bytes; - /* + /* * Constructs an argument that consists of an integer value * @param value int value to set */ + public FastpathArg(Int32 value) + { + type = true; + this.value = value; + } - public FastpathArg(Int32 value) - { - type = true; - this.value = value; - } - - /* + /* * Constructs an argument that consists of an array of bytes * @param bytes array to store */ + public FastpathArg(Byte[] bytes) + { + type = false; + this.bytes = bytes; + } - public FastpathArg(Byte[] bytes) - { - type = false; - this.bytes = bytes; - } - - /* + /* * Constructs an argument that consists of part of a byte array * @param buf source array * @param off offset within array * @param len length of data to include */ - - public FastpathArg(Byte[] buf, Int32 off, Int32 len) - { - type = false; - bytes = new Byte[len]; - //TODO: - bytes = buf; - } - - /* + public FastpathArg(Byte[] buf, Int32 off, Int32 len) + { + type = false; + bytes = new Byte[len]; + //TODO: + bytes = buf; + } + + /* * Constructs an argument that consists of a String. * @param s String to store */ + public FastpathArg(String s) + { + //this(s.ToCharArray()); + } - public FastpathArg(String s) - { - //this(s.ToCharArray()); - } - - /* + /* * This sends this argument down the network stream. * * <p>The stream sent consists of the length.int4 then the contents. @@ -112,33 +104,32 @@ namespace NpgsqlTypes * @param s output stream * @exception IOException if something failed on the network stream */ - - public void Send(Stream s) - { - if (type) - { - // argument is an integer - PGUtil.WriteInt32(s, 4); - PGUtil.WriteInt32(s, value); // integer value of argument - } - else - { - // argument is a byte array - PGUtil.WriteInt32(s, bytes.Length); - s.Write(bytes, 0, bytes.Length); - } - } - - public Int32 SendSize() - { - if (type) - { - return 8; - } - else - { - return 4 + bytes.Length; - } - } - } -}
\ No newline at end of file + public void Send(Stream s) + { + if (type) + { + // argument is an integer + PGUtil.WriteInt32(s, 4); + PGUtil.WriteInt32(s, value); // integer value of argument + } + else + { + // argument is a byte array + PGUtil.WriteInt32(s, bytes.Length); + s.Write(bytes,0,bytes.Length); + } + } + + public Int32 SendSize() + { + if (type) + { + return 8; + } + else + { + return 4+bytes.Length; + } + } + } +} diff --git a/mcs/class/Npgsql/NpgsqlTypes/LargeObject.cs b/mcs/class/Npgsql/NpgsqlTypes/LargeObject.cs index d0595ee296c..bf242a2bd7a 100644 --- a/mcs/class/Npgsql/NpgsqlTypes/LargeObject.cs +++ b/mcs/class/Npgsql/NpgsqlTypes/LargeObject.cs @@ -11,53 +11,53 @@ Changed case of method names to conform to .Net names standard. Also changed type names to their true names. i.e. int -> Int32 -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + 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 Npgsql; namespace NpgsqlTypes { - public class LargeObject - { - /* + + public class LargeObject + { + /* * Indicates a seek from the begining of a file */ - public const Int32 SEEK_SET = 0; + public const Int32 SEEK_SET = 0; - /* + /* * Indicates a seek from the current position */ - public const Int32 SEEK_CUR = 1; + public const Int32 SEEK_CUR = 1; - /* + /* * Indicates a seek from the end of a file */ - public const Int32 SEEK_END = 2; + public const Int32 SEEK_END = 2; - private readonly Fastpath fp; // Fastpath API to use - private readonly Int32 oid; // OID of this object - private readonly Int32 fd; // the descriptor of the open large object + private Fastpath fp; // Fastpath API to use + private Int32 oid; // OID of this object + private Int32 fd; // the descriptor of the open large object - private Boolean closed = false; // true when we are closed + private Boolean closed = false; // true when we are closed - /* + /* * This opens a large object. * * <p>If the object does not exist, then an NpgsqlException is thrown. @@ -69,89 +69,86 @@ namespace NpgsqlTypes * @exception NpgsqlException if a database-access error occurs. * @see org.postgresql.largeobject.LargeObjectManager */ + public LargeObject(Fastpath fp, Int32 oid, Int32 mode) + { + this.fp = fp; + this.oid = oid; - public LargeObject(Fastpath fp, Int32 oid, Int32 mode) - { - this.fp = fp; - this.oid = oid; + FastpathArg[] args = new FastpathArg[2]; + args[0] = new FastpathArg(oid); + args[1] = new FastpathArg(mode); + this.fd = fp.GetInteger("lo_open", args); + } - FastpathArg[] args = new FastpathArg[2]; - args[0] = new FastpathArg(oid); - args[1] = new FastpathArg(mode); - this.fd = fp.GetInteger("lo_open", args); - } - - /* + /* * @return the OID of this LargeObject */ + public Int32 GetOID() + { + return oid; + } - public Int32 GetOID() - { - return oid; - } - - /* + /* * This method closes the object. You must not call methods in this * object after this is called. * @exception NpgsqlException if a database-access error occurs. */ - - public void Close() - { - if (!closed) - { - // finally close - FastpathArg[] args = new FastpathArg[1]; - args[0] = new FastpathArg(fd); - fp.FastpathCall("lo_close", false, args); // true here as we dont care!! - closed = true; - } - } - - /* + public void Close() + { + if (!closed) + { + + // finally close + FastpathArg[] args = new FastpathArg[1]; + args[0] = new FastpathArg(fd); + fp.FastpathCall("lo_close", false, args); // true here as we dont care!! + closed = true; + } + } + + /* * Reads some data from the object, and return as a byte[] array * * @param len number of bytes to read * @return byte[] array containing data read * @exception NpgsqlException if a database-access error occurs. */ - - public Byte[] Read(Int32 len) - { - // This is the original method, where the entire block (len bytes) - // is retrieved in one go. - FastpathArg[] args = new FastpathArg[2]; - args[0] = new FastpathArg(fd); - args[1] = new FastpathArg(len); - return fp.GetData("loread", args); - - // This version allows us to break this down Int32o 4k blocks - //if (len<=4048) { - //// handle as before, return the whole block in one go - //FastpathArg args[] = new FastpathArg[2]; - //args[0] = new FastpathArg(fd); - //args[1] = new FastpathArg(len); - //return fp.getData("loread",args); - //} else { - //// return in 4k blocks - //byte[] buf=new byte[len]; - //int off=0; - //while (len>0) { - //int bs=4048; - //len-=bs; - //if (len<0) { - //bs+=len; - //len=0; - //} - //read(buf,off,bs); - //off+=bs; - //} - //return buf; - //} - } - - /* + public Byte[] Read(Int32 len) + { + // This is the original method, where the entire block (len bytes) + // is retrieved in one go. + FastpathArg[] args = new FastpathArg[2]; + args[0] = new FastpathArg(fd); + args[1] = new FastpathArg(len); + return fp.GetData("loread", args); + + // This version allows us to break this down Int32o 4k blocks + //if (len<=4048) { + //// handle as before, return the whole block in one go + //FastpathArg args[] = new FastpathArg[2]; + //args[0] = new FastpathArg(fd); + //args[1] = new FastpathArg(len); + //return fp.getData("loread",args); + //} else { + //// return in 4k blocks + //byte[] buf=new byte[len]; + //int off=0; + //while (len>0) { + //int bs=4048; + //len-=bs; + //if (len<0) { + //bs+=len; + //len=0; + //} + //read(buf,off,bs); + //off+=bs; + //} + //return buf; + //} + } + + /* * Reads some data from the object into an existing array * * @param buf destination array @@ -160,34 +157,30 @@ namespace NpgsqlTypes * @return the number of bytes actually read * @exception NpgsqlException if a database-access error occurs. */ - - public Int32 Read(Byte[] buf, Int32 off, Int32 len) - { - Byte[] b = Read(len); - if (b.Length < len) - { - len = b.Length; - } - Array.Copy(b, 0, buf, off, len); - return len; - } - - /* + public Int32 Read(Byte[] buf, Int32 off, Int32 len) + { + Byte[] b = Read(len); + if (b.Length < len) + len = b.Length; + Array.Copy(b,0,buf,off,len); + return len; + } + + /* * Writes an array to the object * * @param buf array to write * @exception NpgsqlException if a database-access error occurs. */ - - public void Write(Byte[] buf) - { - FastpathArg[] args = new FastpathArg[2]; - args[0] = new FastpathArg(fd); - args[1] = new FastpathArg(buf); - fp.FastpathCall("lowrite", false, args); - } - - /* + public void Write(Byte[] buf) + { + FastpathArg[] args = new FastpathArg[2]; + args[0] = new FastpathArg(fd); + args[1] = new FastpathArg(buf); + fp.FastpathCall("lowrite", false, args); + } + + /* * Writes some data from an array to the object * * @param buf destination array @@ -195,16 +188,15 @@ namespace NpgsqlTypes * @param len number of bytes to write * @exception NpgsqlException if a database-access error occurs. */ + public void Write(Byte[] buf, Int32 off, Int32 len) + { + Byte[] data = new Byte[len]; - public void Write(Byte[] buf, Int32 off, Int32 len) - { - Byte[] data = new Byte[len]; + System.Array.Copy(buf, off, data, 0, len); + Write(data); + } - Array.Copy(buf, off, data, 0, len); - Write(data); - } - - /* + /* * Sets the current position within the object. * * <p>This is similar to the fseek() call in the standard C library. It @@ -214,17 +206,16 @@ namespace NpgsqlTypes * @param ref Either SEEK_SET, SEEK_CUR or SEEK_END * @exception NpgsqlException if a database-access error occurs. */ - - public void Seek(Int32 pos, Int32 refi) - { - FastpathArg[] args = new FastpathArg[3]; - args[0] = new FastpathArg(fd); - args[1] = new FastpathArg(pos); - args[2] = new FastpathArg(refi); - fp.FastpathCall("lo_lseek", false, args); - } - - /* + public void Seek(Int32 pos, Int32 refi) + { + FastpathArg[] args = new FastpathArg[3]; + args[0] = new FastpathArg(fd); + args[1] = new FastpathArg(pos); + args[2] = new FastpathArg(refi); + fp.FastpathCall("lo_lseek", false, args); + } + + /* * Sets the current position within the object. * * <p>This is similar to the fseek() call in the standard C library. It @@ -233,25 +224,23 @@ namespace NpgsqlTypes * @param pos position within object from begining * @exception NpgsqlException if a database-access error occurs. */ + public void Seek(Int32 pos) + { + Seek(pos, SEEK_SET); + } - public void Seek(Int32 pos) - { - Seek(pos, SEEK_SET); - } - - /* + /* * @return the current position within the object * @exception NpgsqlException if a database-access error occurs. */ - - public Int32 Tell() - { - FastpathArg[] args = new FastpathArg[1]; - args[0] = new FastpathArg(fd); - return fp.GetInteger("lo_tell", args); - } - - /* + public Int32 Tell() + { + FastpathArg[] args = new FastpathArg[1]; + args[0] = new FastpathArg(fd); + return fp.GetInteger("lo_tell", args); + } + + /* * This method is inefficient, as the only way to find out the size of * the object is to seek to the end, record the current position, then * return to the original position. @@ -261,14 +250,14 @@ namespace NpgsqlTypes * @return the size of the large object * @exception NpgsqlException if a database-access error occurs. */ - - public Int32 Size() - { - Int32 cp = Tell(); - Seek(0, SEEK_END); - Int32 sz = Tell(); - Seek(cp, SEEK_SET); - return sz; - } - } -}
\ No newline at end of file + public Int32 Size() + { + Int32 cp = Tell(); + Seek(0, SEEK_END); + Int32 sz = Tell(); + Seek(cp, SEEK_SET); + return sz; + } + + } +} diff --git a/mcs/class/Npgsql/NpgsqlTypes/LargeObjectManager.cs b/mcs/class/Npgsql/NpgsqlTypes/LargeObjectManager.cs index c247b15b19d..26d082fce77 100644 --- a/mcs/class/Npgsql/NpgsqlTypes/LargeObjectManager.cs +++ b/mcs/class/Npgsql/NpgsqlTypes/LargeObjectManager.cs @@ -12,66 +12,59 @@ Changed case of method names to conform to .Net names standard. Also changed type names to their true names. i.e. int -> Int32 - // Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - + 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.Text; using Npgsql; namespace NpgsqlTypes { - /// <summary> - /// Summary description for LargeObjectManager. - /// </summary> - public class LargeObjectManager - { - // the fastpath api for this connection - private readonly Fastpath fp; - - /* + /// <summary> + /// Summary description for LargeObjectManager. + /// </summary> + public class LargeObjectManager + { + // the fastpath api for this connection + private Fastpath fp; + + /* * This mode indicates we want to write to an object */ - public const Int32 WRITE = 0x00020000; + public const Int32 WRITE = 0x00020000; - /* + /* * This mode indicates we want to read an object */ - public static Int32 READ = 0x00040000; + public static Int32 READ = 0x00040000; - /* + /* * This mode is the default. It indicates we want read and write access to * a large object */ - public static Int32 READWRITE = READ | WRITE; - + public static Int32 READWRITE = READ | WRITE; - private LargeObjectManager() - { - /* - * This prevents us being created by mere mortals - */ - } + /* + * This prevents us being created by mere mortals + */ + private LargeObjectManager() + {} - /* + /* * Constructs the LargeObject API. * * <p><b>Important Notice</b> @@ -81,68 +74,55 @@ namespace NpgsqlTypes * org.postgresql.Connection class keeps track of the various extension API's * and it's advised you use those to gain access, and not going direct. */ - - public LargeObjectManager(NpgsqlConnection conn) - { - // We need Fastpath to do anything - // Now get the function oid's for the api - // - // This is an example of Fastpath.addFunctions(); - // - //String sql; - StringBuilder sql = null; - try - { - sql = new StringBuilder(); - if (conn.PostgreSqlVersion > new Version(7, 3, 0)) - { - sql.Append("SELECT p.proname,p.oid "); - sql.Append(" FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n "); - sql.Append(" WHERE p.pronamespace=n.oid AND n.nspname='pg_catalog' AND ("); - } - else - { - sql.Append("SELECT proname,oid FROM pg_proc WHERE "); - } - sql.Append(" proname = 'lo_open'"); - sql.Append(" or proname = 'lo_close'"); - sql.Append(" or proname = 'lo_creat'"); - sql.Append(" or proname = 'lo_unlink'"); - sql.Append(" or proname = 'lo_lseek'"); - sql.Append(" or proname = 'lo_tell'"); - sql.Append(" or proname = 'loread'"); - sql.Append(" or proname = 'lowrite'"); - - if (conn.PostgreSqlVersion > new Version(7, 3, 0)) - { - sql.Append(")"); - } - - using (IDbCommand cmd = new NpgsqlCommand(sql.ToString())) - { - cmd.Connection = conn; - - this.fp = new Fastpath(conn, conn.Connector.Stream); - - using (IDataReader res = cmd.ExecuteReader()) - { - if (res == null) - { - throw new NpgsqlException("postgresql.lo.init"); - } - - - fp.AddFunctions(res); - } - } - } - finally - { - sql = null; - } - } - - /* + public LargeObjectManager(NpgsqlConnection conn) + { + // We need Fastpath to do anything + // Now get the function oid's for the api + // + // This is an example of Fastpath.addFunctions(); + // + String sql; + if (conn.ServerVersion > new ServerVersion(7,3,0) ) + { + + sql = "SELECT p.proname,p.oid "+ + " FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n "+ + " WHERE p.pronamespace=n.oid AND n.nspname='pg_catalog' AND ("; + } + else + { + sql = "SELECT proname,oid FROM pg_proc WHERE "; + } + sql += " proname = 'lo_open'" + + " or proname = 'lo_close'" + + " or proname = 'lo_creat'" + + " or proname = 'lo_unlink'" + + " or proname = 'lo_lseek'" + + " or proname = 'lo_tell'" + + " or proname = 'loread'" + + " or proname = 'lowrite'"; + + if (conn.ServerVersion > new ServerVersion(7,3,0) ) + { + sql += ")"; + } + + IDbCommand cmd = new NpgsqlCommand(sql); + cmd.Connection = conn; + + this.fp = new Fastpath(conn,conn.Connector.Stream); + + IDataReader res = cmd.ExecuteReader(CommandBehavior.CloseConnection); + + + if (res == null) + throw new NpgsqlException("postgresql.lo.init"); + + + fp.AddFunctions(res); + } + + /* * This opens an existing large object, based on its OID. This method * assumes that READ and WRITE access is required (the default). * @@ -150,13 +130,12 @@ namespace NpgsqlTypes * @return LargeObject instance providing access to the object * @exception NpgsqlException on error */ + public LargeObject Open(Int32 oid) + { + return new LargeObject(fp, oid, READWRITE); + } - public LargeObject Open(Int32 oid) - { - return new LargeObject(fp, oid, READWRITE); - } - - /* + /* * This opens an existing large object, based on its OID * * @param oid of large object @@ -164,13 +143,12 @@ namespace NpgsqlTypes * @return LargeObject instance providing access to the object * @exception NpgsqlException on error */ + public LargeObject Open(Int32 oid, Int32 mode) + { + return new LargeObject(fp, oid, mode); + } - public LargeObject Open(Int32 oid, Int32 mode) - { - return new LargeObject(fp, oid, mode); - } - - /* + /* * This creates a large object, returning its OID. * * <p>It defaults to READWRITE for the new object's attributes. @@ -178,44 +156,41 @@ namespace NpgsqlTypes * @return oid of new object * @exception NpgsqlException on error */ - - public Int32 Create() - { - FastpathArg[] args = new FastpathArg[1]; - args[0] = new FastpathArg(READWRITE); - return fp.GetInteger("lo_creat", args); - } - - /* + public Int32 Create() + { + FastpathArg[] args = new FastpathArg[1]; + args[0] = new FastpathArg(READWRITE); + return fp.GetInteger("lo_creat", args); + } + + /* * This creates a large object, returning its OID * * @param mode a bitmask describing different attributes of the new object * @return oid of new object * @exception NpgsqlException on error */ - - public Int32 Create(Int32 mode) - { - FastpathArg[] args = new FastpathArg[1]; - args[0] = new FastpathArg(mode); - return fp.GetInteger("lo_creat", args); - } - - /* + public Int32 Create(Int32 mode) + { + FastpathArg[] args = new FastpathArg[1]; + args[0] = new FastpathArg(mode); + return fp.GetInteger("lo_creat", args); + } + + /* * This deletes a large object. * * @param oid describing object to delete * @exception NpgsqlException on error */ - - public void Delete(Int32 oid) - { - FastpathArg[] args = new FastpathArg[1]; - args[0] = new FastpathArg(oid); - fp.FastpathCall("lo_unlink", false, args); - } - - /* + public void Delete(Int32 oid) + { + FastpathArg[] args = new FastpathArg[1]; + args[0] = new FastpathArg(oid); + fp.FastpathCall("lo_unlink", false, args); + } + + /* * This deletes a large object. * * <p>It is identical to the delete method, and is supplied as the C API uses @@ -224,10 +199,11 @@ namespace NpgsqlTypes * @param oid describing object to delete * @exception NpgsqlException on error */ + public void Unlink(Int32 oid) + { + Delete(oid); + } + + } - public void Unlink(Int32 oid) - { - Delete(oid); - } - } -}
\ No newline at end of file +} diff --git a/mcs/class/Npgsql/NpgsqlTypes/NpgsqlDbType.cs b/mcs/class/Npgsql/NpgsqlTypes/NpgsqlDbType.cs index a5ec51820fa..d3f8c87d19b 100644 --- a/mcs/class/Npgsql/NpgsqlTypes/NpgsqlDbType.cs +++ b/mcs/class/Npgsql/NpgsqlTypes/NpgsqlDbType.cs @@ -7,65 +7,58 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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 Npgsql; namespace NpgsqlTypes { - public enum NpgsqlDbType - { - // This list used to be ordered. But this ordering would break compiled applications - // as enum values would change after each insertion. Now, just append new types. - - // Binary or with other values. E.g. Array of Box is NpgsqlDbType.Array | NpgsqlDbType.Box + public enum NpgsqlDbType + { - Array = int.MinValue, + // This list used to be ordered. But this ordering would break compiled applications + // as enum values would change after each insertion. Now, just append new types. + + Bigint, + Boolean, + Box, + Bytea, + Circle, + Char, + Date, + Double, + Integer, + Line, + LSeg, + Money, + Numeric, + Path, + Point, + Polygon, + Real, + Smallint, + Text, + Time, + Timestamp, + Varchar, + Refcursor, + Inet, + Bit, + TimestampTZ - Bigint = 1, + } - Boolean, - Box, - Bytea, - Circle, - Char, - Date, - Double, - Integer, - Line, - LSeg, - Money, - Numeric, - Path, - Point, - Polygon, - Real, - Smallint, - Text, - Time, - Timestamp, - Varchar, - Refcursor, - Inet, - Bit, - TimestampTZ, - Uuid, - Xml, - Oidvector, - Interval, - TimeTZ - } -}
\ No newline at end of file +} diff --git a/mcs/class/Npgsql/NpgsqlTypes/NpgsqlTypeConverters.cs b/mcs/class/Npgsql/NpgsqlTypes/NpgsqlTypeConverters.cs index 9bedd242a73..0c3f2e7b75d 100644 --- a/mcs/class/Npgsql/NpgsqlTypes/NpgsqlTypeConverters.cs +++ b/mcs/class/Npgsql/NpgsqlTypes/NpgsqlTypeConverters.cs @@ -7,675 +7,574 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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 // This file provides data type converters between PostgreSQL representations // and .NET objects. using System; -using System.Collections.Generic; +using System.Collections; using System.Globalization; -using System.IO; -using System.Net; using System.Text; +using System.IO; using System.Text.RegularExpressions; +using Npgsql; + namespace NpgsqlTypes { - /// <summary> - /// Provide event handlers to convert all native supported basic data types from their backend - /// text representation to a .NET object. - /// </summary> - internal abstract class BasicBackendToNativeTypeConverter - { - private static readonly String[] DateFormats = new String[] { "yyyy-MM-dd", }; - - private static readonly String[] TimeFormats = - new String[] - { - "HH:mm:ss.ffffff", "HH:mm:ss", "HH:mm:ss.ffffffzz", "HH:mm:sszz", "HH:mm:ss.fffff", "HH:mm:ss.ffff", "HH:mm:ss.fff" - , "HH:mm:ss.ff", "HH:mm:ss.f", "HH:mm:ss.fffffzz", "HH:mm:ss.ffffzz", "HH:mm:ss.fffzz", "HH:mm:ss.ffzz", - "HH:mm:ss.fzz", - }; - - private static readonly String[] DateTimeFormats = - new String[] - { - "yyyy-MM-dd HH:mm:ss.ffffff", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm:ss.ffffffzz", "yyyy-MM-dd HH:mm:sszz", - "yyyy-MM-dd HH:mm:ss.fffff", "yyyy-MM-dd HH:mm:ss.ffff", "yyyy-MM-dd HH:mm:ss.fff", "yyyy-MM-dd HH:mm:ss.ff", - "yyyy-MM-dd HH:mm:ss.f", "yyyy-MM-dd HH:mm:ss.fffffzz", "yyyy-MM-dd HH:mm:ss.ffffzz", "yyyy-MM-dd HH:mm:ss.fffzz", - "yyyy-MM-dd HH:mm:ss.ffzz", "yyyy-MM-dd HH:mm:ss.fzz", - }; - - /// <summary> - /// Binary data. - /// </summary> - internal static Object ToBinary(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) - { - Int32 octalValue = 0; - Int32 byteAPosition = 0; - Int32 byteAStringLength = BackendData.Length; - MemoryStream ms = new MemoryStream(); + /// <summary> + /// Provide event handlers to convert all native supported basic data types from their backend + /// text representation to a .NET object. + /// </summary> + internal abstract class BasicBackendToNativeTypeConverter + { + private static readonly String[] DateFormats = new String[] + { + "yyyy-MM-dd", + }; + + private static readonly String[] TimeFormats = new String[] + { + "HH:mm:ss.ffffff", + "HH:mm:ss", + "HH:mm:ss.ffffffzz", + "HH:mm:sszz", + "HH:mm:ss.fffff", + "HH:mm:ss.ffff", + "HH:mm:ss.fff", + "HH:mm:ss.ff", + "HH:mm:ss.f", + "HH:mm:ss.fffffzz", + "HH:mm:ss.ffffzz", + "HH:mm:ss.fffzz", + "HH:mm:ss.ffzz", + "HH:mm:ss.fzz", + }; + + private static readonly String[] DateTimeFormats = new String[] + { + "yyyy-MM-dd HH:mm:ss.ffffff", + "yyyy-MM-dd HH:mm:ss", + "yyyy-MM-dd HH:mm:ss.ffffffzz", + "yyyy-MM-dd HH:mm:sszz", + "yyyy-MM-dd HH:mm:ss.fffff", + "yyyy-MM-dd HH:mm:ss.ffff", + "yyyy-MM-dd HH:mm:ss.fff", + "yyyy-MM-dd HH:mm:ss.ff", + "yyyy-MM-dd HH:mm:ss.f", + "yyyy-MM-dd HH:mm:ss.fffffzz", + "yyyy-MM-dd HH:mm:ss.ffffzz", + "yyyy-MM-dd HH:mm:ss.fffzz", + "yyyy-MM-dd HH:mm:ss.ffzz", + "yyyy-MM-dd HH:mm:ss.fzz", + }; + + /// <summary> + /// Binary data. + /// </summary> + internal static Object ToBinary(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) + { + Int32 octalValue = 0; + Int32 byteAPosition = 0; + Int32 byteAStringLength = BackendData.Length; + MemoryStream ms = new MemoryStream(); + + while (byteAPosition < byteAStringLength) + { + // The IsDigit is necessary in case we receive a \ as the octal value and not + // as the indicator of a following octal value in decimal format. + // i.e.: \201\301P\A + if (BackendData[byteAPosition] == '\\') { + + if (byteAPosition + 1 == byteAStringLength) + { + octalValue = '\\'; + byteAPosition++; + } + else if (Char.IsDigit(BackendData[byteAPosition + 1])) + { + octalValue = (Byte.Parse(BackendData[byteAPosition + 1].ToString()) << 6); + octalValue |= (Byte.Parse(BackendData[byteAPosition + 2].ToString()) << 3); + octalValue |= Byte.Parse(BackendData[byteAPosition + 3].ToString()); + byteAPosition += 4; + + } + else + { + octalValue = '\\'; + byteAPosition += 2; + } + + } else { + octalValue = (Byte)BackendData[byteAPosition]; + byteAPosition++; + } + + + ms.WriteByte((Byte)octalValue); + + } + + return ms.ToArray(); + } - while (byteAPosition < byteAStringLength) - { - // The IsDigit is necessary in case we receive a \ as the octal value and not - // as the indicator of a following octal value in decimal format. - // i.e.: \201\301P\A - if (BackendData[byteAPosition] == '\\') - { - if (byteAPosition + 1 == byteAStringLength) - { - octalValue = '\\'; - byteAPosition++; - } - else if (Char.IsDigit(BackendData[byteAPosition + 1])) - { - octalValue = Convert.ToByte(BackendData.Substring(byteAPosition + 1, 3), 8); - //octalValue = (Byte.Parse(BackendData[byteAPosition + 1].ToString()) << 6); - //octalValue |= (Byte.Parse(BackendData[byteAPosition + 2].ToString()) << 3); - //octalValue |= Byte.Parse(BackendData[byteAPosition + 3].ToString()); - byteAPosition += 4; - } - else - { - octalValue = '\\'; - byteAPosition += 2; - } - } - else - { - octalValue = (Byte)BackendData[byteAPosition]; - byteAPosition++; - } - - - ms.WriteByte((Byte)octalValue); - } - - return ms.ToArray(); - } - - /// <summary> - /// Convert a postgresql boolean to a System.Boolean. - /// </summary> - internal static Object ToBoolean(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, - Int32 TypeModifier) - { - return (BackendData.ToLower() == "t" ? true : false); - } + /// <summary> + /// Convert a postgresql boolean to a System.Boolean. + /// </summary> + internal static Object ToBoolean(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) + { + return (BackendData.ToLower() == "t" ? true : false); + } - /// <summary> - /// Convert a postgresql bit to a System.Boolean. - /// </summary> - internal static Object ToBit(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) - { - return (BackendData.ToLower() == "1" ? true : false); - } - - /// <summary> - /// Convert a postgresql datetime to a System.DateTime. - /// </summary> - internal static Object ToDateTime(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, - Int32 TypeModifier) - { - // Get the date time parsed in all expected formats for timestamp. + /// <summary> + /// Convert a postgresql bit to a System.Boolean. + /// </summary> + internal static Object ToBit(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) + { + return (BackendData.ToLower() == "1" ? true : false); + } - // First check for special values infinity and -infinity. + /// <summary> + /// Convert a postgresql datetime to a System.DateTime. + /// </summary> + internal static Object ToDateTime(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) + { + + // Get the date time parsed in all expected formats for timestamp. - if (BackendData == "infinity") - { - return DateTime.MaxValue; - } + // First check for special values infinity and -infinity. - if (BackendData == "-infinity") - { - return DateTime.MinValue; - } - - return - DateTime.ParseExact(BackendData, DateTimeFormats, DateTimeFormatInfo.InvariantInfo, - DateTimeStyles.NoCurrentDateDefault | DateTimeStyles.AllowWhiteSpaces); - } - - /// <summary> - /// Convert a postgresql date to a System.DateTime. - /// </summary> - internal static Object ToDate(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) - { - return - DateTime.ParseExact(BackendData, DateFormats, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AllowWhiteSpaces); - } - - /// <summary> - /// Convert a postgresql time to a System.DateTime. - /// </summary> - internal static Object ToTime(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) - { - return - DateTime.ParseExact(BackendData, TimeFormats, DateTimeFormatInfo.InvariantInfo, - DateTimeStyles.NoCurrentDateDefault | DateTimeStyles.AllowWhiteSpaces); - } - - /// <summary> - /// Convert a postgresql money to a System.Decimal. - /// </summary> - internal static Object ToMoney(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) - { - // It's a number with a $ on the beginning... - return Convert.ToDecimal(BackendData.Substring(1, BackendData.Length - 1), CultureInfo.InvariantCulture); - } - } - - /// <summary> - /// Provide event handlers to convert the basic native supported data types from - /// native form to backend representation. - /// </summary> - internal abstract class BasicNativeToBackendTypeConverter - { - /// <summary> - /// Binary data. - /// </summary> - internal static String ToBinary(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) - { - Byte[] byteArray = (Byte[])NativeData; - int len = byteArray.Length; - char[] res = new char[len * 5]; + if (BackendData == "infinity") + return DateTime.MaxValue; - for (int i = 0, o = 0; i < len; ++i, o += 5) - { - byte item = byteArray[i]; - res[o] = res[o + 1] = '\\'; - res[o + 2] = (char)('0' + (7 & (item >> 6))); - res[o + 3] = (char)('0' + (7 & (item >> 3))); - res[o + 4] = (char)('0' + (7 & item)); - } - - return new String(res); - } - - /// <summary> - /// Convert to a postgresql boolean. - /// </summary> - internal static String ToBoolean(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) - { - return ((bool)NativeData) ? "TRUE" : "FALSE"; - } + if (BackendData == "-infinity") + return DateTime.MinValue; - /// <summary> - /// Convert to a postgresql bit. - /// </summary> - internal static String ToBit(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) - { - // Convert boolean values to bit or convert int32 values to bit - odd values are 1 and - // even numbers are 0. - if (NativeData is Boolean) - { - return ((Boolean)NativeData) ? "1" : "0"; - } - else - { - return (((Int32)NativeData) % 2 == 1) ? "1" : "0"; - } - } - - /// <summary> - /// Convert to a postgresql timestamp. - /// </summary> - internal static String ToDateTime(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) - { - if (!(NativeData is DateTime)) - { - return ExtendedNativeToBackendTypeConverter.ToTimeStamp(TypeInfo, NativeData); - } - if (DateTime.MaxValue.Equals(NativeData)) - { - return "infinity"; - } - if (DateTime.MinValue.Equals(NativeData)) - { - return "-infinity"; - } - return ((DateTime)NativeData).ToString("yyyy-MM-dd HH:mm:ss.ffffff", DateTimeFormatInfo.InvariantInfo); - } - - /// <summary> - /// Convert to a postgresql date. - /// </summary> - internal static String ToDate(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) - { - if (!(NativeData is DateTime)) - { - return ExtendedNativeToBackendTypeConverter.ToDate(TypeInfo, NativeData); - } - return ((DateTime)NativeData).ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo); - } - - /// <summary> - /// Convert to a postgresql time. - /// </summary> - internal static String ToTime(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) - { - if (!(NativeData is DateTime)) - { - return ExtendedNativeToBackendTypeConverter.ToTime(TypeInfo, NativeData); - } - else - { - return ((DateTime)NativeData).ToString("HH:mm:ss.ffffff", DateTimeFormatInfo.InvariantInfo); - } - } - - /// <summary> - /// Convert to a postgres money. - /// </summary> - internal static String ToMoney(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) - { - return "$" + ((IFormattable)NativeData).ToString(null, CultureInfo.InvariantCulture.NumberFormat); - } - } - - - /// <summary> - /// Provide event handlers to convert extended native supported data types from their backend - /// text representation to a .NET object. - /// </summary> - internal abstract class ExtendedBackendToNativeTypeConverter - { - private static readonly Regex pointRegex = new Regex(@"\((-?\d+.?\d*),(-?\d+.?\d*)\)"); - private static readonly Regex boxlsegRegex = new Regex(@"\((-?\d+.?\d*),(-?\d+.?\d*)\),\((-?\d+.?\d*),(-?\d+.?\d*)\)"); - private static readonly Regex pathpolygonRegex = new Regex(@"\((-?\d+.?\d*),(-?\d+.?\d*)\)"); - private static readonly Regex circleRegex = new Regex(@"<\((-?\d+.?\d*),(-?\d+.?\d*)\),(\d+.?\d*)>"); - - - /// <summary> - /// Convert a postgresql point to a System.NpgsqlPoint. - /// </summary> - internal static Object ToPoint(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) - { - Match m = pointRegex.Match(BackendData); + return DateTime.ParseExact(BackendData, + DateTimeFormats, + DateTimeFormatInfo.InvariantInfo, + DateTimeStyles.NoCurrentDateDefault | DateTimeStyles.AllowWhiteSpaces); + } - return - new NpgsqlPoint(Single.Parse(m.Groups[1].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat), - Single.Parse(m.Groups[2].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat)); - } + /// <summary> + /// Convert a postgresql date to a System.DateTime. + /// </summary> + internal static Object ToDate(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) + { + return DateTime.ParseExact(BackendData, + DateFormats, + DateTimeFormatInfo.InvariantInfo, + DateTimeStyles.AllowWhiteSpaces); + } - /// <summary> - /// Convert a postgresql point to a System.RectangleF. - /// </summary> - internal static Object ToBox(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) - { - Match m = boxlsegRegex.Match(BackendData); - - return - new NpgsqlBox( - new NpgsqlPoint(Single.Parse(m.Groups[1].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat), - Single.Parse(m.Groups[2].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat)), - new NpgsqlPoint(Single.Parse(m.Groups[3].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat), - Single.Parse(m.Groups[4].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat))); - } - - /// <summary> - /// LDeg. - /// </summary> - internal static Object ToLSeg(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) - { - Match m = boxlsegRegex.Match(BackendData); - - return - new NpgsqlLSeg( - new NpgsqlPoint(Single.Parse(m.Groups[1].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat), - Single.Parse(m.Groups[2].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat)), - new NpgsqlPoint(Single.Parse(m.Groups[3].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat), - Single.Parse(m.Groups[4].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat))); - } - - /// <summary> - /// Path. - /// </summary> - internal static Object ToPath(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) - { - Match m = pathpolygonRegex.Match(BackendData); - Boolean open = (BackendData[0] == '['); - List<NpgsqlPoint> points = new List<NpgsqlPoint>(); + /// <summary> + /// Convert a postgresql time to a System.DateTime. + /// </summary> + internal static Object ToTime(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) + { + return DateTime.ParseExact(BackendData, + TimeFormats, + DateTimeFormatInfo.InvariantInfo, + DateTimeStyles.NoCurrentDateDefault | DateTimeStyles.AllowWhiteSpaces); + } - while (m.Success) - { - if (open) - { - points.Add( - new NpgsqlPoint( - Single.Parse(m.Groups[1].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat), - Single.Parse(m.Groups[2].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat))); - } - else - { - // Here we have to do a little hack, because as of 2004-08-11 mono cvs version, the last group is returned with - // a trailling ')' only when the last character of the string is a ')' which is the case for closed paths - // returned by backend. This gives parsing exception when converting to single. - // I still don't know if this is a bug in mono or in my regular expression. - // Check if there is this character and remove it. - - String group2 = m.Groups[2].ToString(); - if (group2.EndsWith(")")) - { - group2 = group2.Remove(group2.Length - 1, 1); - } - - points.Add( - new NpgsqlPoint( - Single.Parse(m.Groups[1].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat), - Single.Parse(group2, NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat))); - } - - m = m.NextMatch(); - } - - NpgsqlPath result = new NpgsqlPath(points.ToArray()); - result.Open = open; - return result; - } - - /// <summary> - /// Polygon. - /// </summary> - internal static Object ToPolygon(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, - Int32 TypeModifier) - { - Match m = pathpolygonRegex.Match(BackendData); - List<NpgsqlPoint> points = new List<NpgsqlPoint>(); + /// <summary> + /// Convert a postgresql money to a System.Decimal. + /// </summary> + internal static Object ToMoney(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) + { + // It's a number with a $ on the beginning... + return Convert.ToDecimal(BackendData.Substring(1, BackendData.Length - 1), CultureInfo.InvariantCulture); + } + + } + + /// <summary> + /// Provide event handlers to convert the basic native supported data types from + /// native form to backend representation. + /// </summary> + internal abstract class BasicNativeToBackendTypeConverter + { + /// <summary> + /// Binary data. + /// </summary> + internal static String ToBinary(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) + { + Byte[] byteArray = (Byte[])NativeData; + int len = byteArray.Length; + char[] res = new char[len * 5]; + + for (int i = 0, o = 0; i < len; ++i, o += 5) + { + byte item = byteArray[i]; + res[o] = res[o + 1] = '\\'; + res[o + 2] = (char)('0' + (7 & (item >> 6))); + res[o + 3] = (char)('0' + (7 & (item >> 3))); + res[o + 4] = (char)('0' + (7 & item)); + } + + return new String(res); + } - while (m.Success) - { - // Here we have to do a little hack, because as of 2004-08-11 mono cvs version, the last group is returned with - // a trailling ')' only when the last character of the string is a ')' which is the case for closed paths - // returned by backend. This gives parsing exception when converting to single. - // I still don't know if this is a bug in mono or in my regular expression. - // Check if there is this character and remove it. + /// <summary> + /// Convert to a postgresql boolean. + /// </summary> + internal static String ToBoolean(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) + { + return ((bool)NativeData) ? "TRUE" : "FALSE"; + } - String group2 = m.Groups[2].ToString(); - if (group2.EndsWith(")")) - { - group2 = group2.Remove(group2.Length - 1, 1); - } + /// <summary> + /// Convert to a postgresql bit. + /// </summary> + internal static String ToBit(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) + { + // Convert boolean values to bit or convert int32 values to bit - odd values are 1 and + // even numbers are 0. + if (NativeData is Boolean) + return ((Boolean)NativeData) ? "1" : "0"; + else + return (((Int32)NativeData) % 2 == 1) ? "1" : "0"; + } - points.Add( - new NpgsqlPoint(Single.Parse(m.Groups[1].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat), - Single.Parse(group2, NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat))); + /// <summary> + /// Convert to a postgresql timestamp. + /// </summary> + internal static String ToDateTime(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) + { + if (DateTime.MaxValue.Equals(NativeData)) + return "infinity"; + if (DateTime.MinValue.Equals(NativeData)) + return "-infinity"; + return ((DateTime)NativeData).ToString("yyyy-MM-dd HH:mm:ss.ffffff", DateTimeFormatInfo.InvariantInfo); + } + /// <summary> + /// Convert to a postgresql date. + /// </summary> + internal static String ToDate(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) + { + return ((DateTime)NativeData).ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo); + } - m = m.NextMatch(); - } + /// <summary> + /// Convert to a postgresql time. + /// </summary> + internal static String ToTime(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) + { + return ((DateTime)NativeData).ToString("HH:mm:ss.ffffff", DateTimeFormatInfo.InvariantInfo); + } - return new NpgsqlPolygon(points); - } + /// <summary> + /// Convert to a postgres money. + /// </summary> + internal static String ToMoney(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) + { + return "$" + ((IFormattable)NativeData).ToString(null, CultureInfo.InvariantCulture.NumberFormat); + } + + + } + + + /// <summary> + /// Provide event handlers to convert extended native supported data types from their backend + /// text representation to a .NET object. + /// </summary> + internal abstract class ExtendedBackendToNativeTypeConverter + { + + private static readonly Regex pointRegex = new Regex(@"\((-?\d+.?\d*),(-?\d+.?\d*)\)"); + private static readonly Regex boxlsegRegex = new Regex(@"\((-?\d+.?\d*),(-?\d+.?\d*)\),\((-?\d+.?\d*),(-?\d+.?\d*)\)"); + private static readonly Regex pathpolygonRegex = new Regex(@"\((-?\d+.?\d*),(-?\d+.?\d*)\)"); + private static readonly Regex circleRegex = new Regex(@"<\((-?\d+.?\d*),(-?\d+.?\d*)\),(\d+.?\d*)>"); + + + /// <summary> + /// Convert a postgresql point to a System.NpgsqlPoint. + /// </summary> + internal static Object ToPoint(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) + { + + Match m = pointRegex.Match(BackendData); + + return new NpgsqlPoint( + Single.Parse(m.Groups[1].ToString(), NumberStyles.Any, + CultureInfo.InvariantCulture.NumberFormat), + Single.Parse(m.Groups[2].ToString(), NumberStyles.Any, + CultureInfo.InvariantCulture.NumberFormat)); + + + + } - /// <summary> - /// Circle. - /// </summary> - internal static Object ToCircle(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) - { - Match m = circleRegex.Match(BackendData); - return - new NpgsqlCircle( - new NpgsqlPoint(Single.Parse(m.Groups[1].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat), - Single.Parse(m.Groups[2].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat)), - Single.Parse(m.Groups[3].ToString(), NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat)); - } - - /// <summary> - /// Inet. - /// </summary> - internal static Object ToInet(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) - { - return new NpgsqlInet(BackendData); - } - - internal static Object ToGuid(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) + /// <summary> + /// Convert a postgresql point to a System.RectangleF. + /// </summary> + internal static Object ToBox(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) { - return new Guid(BackendData); + + Match m = boxlsegRegex.Match(BackendData); + + return new NpgsqlBox( + new NpgsqlPoint( + Single.Parse(m.Groups[1].ToString(), NumberStyles.Any, + CultureInfo.InvariantCulture.NumberFormat), + Single.Parse(m.Groups[2].ToString(), NumberStyles.Any, + CultureInfo.InvariantCulture.NumberFormat)), + new NpgsqlPoint( + Single.Parse(m.Groups[3].ToString(), NumberStyles.Any, + CultureInfo.InvariantCulture.NumberFormat), + Single.Parse(m.Groups[4].ToString(), NumberStyles.Any, + CultureInfo.InvariantCulture.NumberFormat))); } - /// <summary> - /// interval - /// </summary> - internal static object ToInterval(NpgsqlBackendTypeInfo typeInfo, String backendData, Int16 typeSize, - Int32 typeModifier) - { - return NpgsqlInterval.Parse(backendData); - } + /// <summary> + /// LDeg. + /// </summary> + internal static Object ToLSeg(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) + { + Match m = boxlsegRegex.Match(BackendData); + + return new NpgsqlLSeg( + new NpgsqlPoint( + Single.Parse(m.Groups[1].ToString(), NumberStyles.Any, + CultureInfo.InvariantCulture.NumberFormat), + Single.Parse(m.Groups[2].ToString(), NumberStyles.Any, + CultureInfo.InvariantCulture.NumberFormat)), + new NpgsqlPoint( + Single.Parse(m.Groups[3].ToString(), NumberStyles.Any, + CultureInfo.InvariantCulture.NumberFormat), + Single.Parse(m.Groups[4].ToString(), NumberStyles.Any, + CultureInfo.InvariantCulture.NumberFormat))); + } - internal static object ToTime(NpgsqlBackendTypeInfo typeInfo, String backendData, Int16 typeSize, Int32 typeModifier) - { - return NpgsqlTime.Parse(backendData); - } + /// <summary> + /// Path. + /// </summary> + internal static Object ToPath(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) + { + + Match m = pathpolygonRegex.Match(BackendData); + Boolean open = (BackendData[0] == '['); + ArrayList points = new ArrayList(); + + while (m.Success) + { + + if (open) + points.Add(new NpgsqlPoint( + Single.Parse(m.Groups[1].ToString(), NumberStyles.Any, + CultureInfo.InvariantCulture.NumberFormat), + Single.Parse(m.Groups[2].ToString(), NumberStyles.Any, + CultureInfo.InvariantCulture.NumberFormat))); + else + { + // Here we have to do a little hack, because as of 2004-08-11 mono cvs version, the last group is returned with + // a trailling ')' only when the last character of the string is a ')' which is the case for closed paths + // returned by backend. This gives parsing exception when converting to single. + // I still don't know if this is a bug in mono or in my regular expression. + // Check if there is this character and remove it. + + String group2 = m.Groups[2].ToString(); + if (group2.EndsWith(")")) + group2 = group2.Remove(group2.Length - 1, 1); + + points.Add(new NpgsqlPoint( + Single.Parse(m.Groups[1].ToString(), NumberStyles.Any, + CultureInfo.InvariantCulture.NumberFormat), + Single.Parse(group2, NumberStyles.Any, + CultureInfo.InvariantCulture.NumberFormat))); + } + + m = m.NextMatch(); + + } + + NpgsqlPath result = new NpgsqlPath((NpgsqlPoint[]) points.ToArray(typeof(NpgsqlPoint))); + result.IsOpen = open; + return result; + + + } - internal static object ToTimeTZ(NpgsqlBackendTypeInfo typeInfo, String backendData, Int16 typeSize, Int32 typeModifier) - { - return NpgsqlTimeTZ.Parse(backendData); - } + /// <summary> + /// Polygon. + /// </summary> + internal static Object ToPolygon(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) + { + + Match m = pathpolygonRegex.Match(BackendData); + ArrayList points = new ArrayList(); + + while (m.Success) + { + + // Here we have to do a little hack, because as of 2004-08-11 mono cvs version, the last group is returned with + // a trailling ')' only when the last character of the string is a ')' which is the case for closed paths + // returned by backend. This gives parsing exception when converting to single. + // I still don't know if this is a bug in mono or in my regular expression. + // Check if there is this character and remove it. + + String group2 = m.Groups[2].ToString(); + if (group2.EndsWith(")")) + group2 = group2.Remove(group2.Length - 1, 1); + + points.Add(new NpgsqlPoint( + Single.Parse(m.Groups[1].ToString(), NumberStyles.Any, + CultureInfo.InvariantCulture.NumberFormat), + Single.Parse(group2, NumberStyles.Any, + CultureInfo.InvariantCulture.NumberFormat))); + + + m = m.NextMatch(); + + } + + return new NpgsqlPolygon((NpgsqlPoint[]) points.ToArray(typeof(NpgsqlPoint))); + + } - internal static object ToDate(NpgsqlBackendTypeInfo typeInfo, String backendData, Int16 typeSize, Int32 typeModifier) - { - return NpgsqlDate.Parse(backendData); - } + /// <summary> + /// Circle. + /// </summary> + internal static Object ToCircle(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) + { + Match m = circleRegex.Match(BackendData); + return new NpgsqlCircle( + new NpgsqlPoint( + Single.Parse(m.Groups[1].ToString(), NumberStyles.Any, + CultureInfo.InvariantCulture.NumberFormat), + Single.Parse(m.Groups[2].ToString(), NumberStyles.Any, + CultureInfo.InvariantCulture.NumberFormat)), + Single.Parse(m.Groups[3].ToString(), NumberStyles.Any, + CultureInfo.InvariantCulture.NumberFormat)); + + } - internal static object ToTimeStamp(NpgsqlBackendTypeInfo typeInfo, String backendData, Int16 typeSize, - Int32 typeModifier) - { - return NpgsqlTimeStamp.Parse(backendData); - } + /// <summary> + /// Inet. + /// </summary> + internal static Object ToInet(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier) + { + return new NpgsqlInet(BackendData); + + } + } + + /// <summary> + /// Provide event handlers to convert extended native supported data types from + /// native form to backend representation. + /// </summary> + internal abstract class ExtendedNativeToBackendTypeConverter + { + /// <summary> + /// Point. + /// </summary> + internal static String ToPoint(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) + { + if (NativeData is NpgsqlPoint) + { + NpgsqlPoint P = (NpgsqlPoint)NativeData; + return String.Format(CultureInfo.InvariantCulture, "({0},{1})", P.X, P.Y); + } + else + { + throw new InvalidCastException("Unable to cast data to NpgsqlPoint type"); + } + } - internal static object ToTimeStampTZ(NpgsqlBackendTypeInfo typeInfo, String backendData, Int16 typeSize, - Int32 typeModifier) - { - return NpgsqlTimeStampTZ.Parse(backendData); - } - } - - /// <summary> - /// Provide event handlers to convert extended native supported data types from - /// native form to backend representation. - /// </summary> - internal abstract class ExtendedNativeToBackendTypeConverter - { - /// <summary> - /// Point. - /// </summary> - internal static String ToPoint(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) - { - if (NativeData is NpgsqlPoint) - { - NpgsqlPoint P = (NpgsqlPoint)NativeData; - return String.Format(CultureInfo.InvariantCulture, "({0},{1})", P.X, P.Y); - } - else - { - throw new InvalidCastException("Unable to cast data to NpgsqlPoint type"); - } - } - - /// <summary> - /// Box. - /// </summary> - internal static String ToBox(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) - { - /*if (NativeData.GetType() == typeof(Rectangle)) { + /// <summary> + /// Box. + /// </summary> + internal static String ToBox(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) + { + /*if (NativeData.GetType() == typeof(Rectangle)) { Rectangle R = (Rectangle)NativeData; return String.Format(CultureInfo.InvariantCulture, "({0},{1}),({2},{3})", R.Left, R.Top, R.Left + R.Width, R.Top + R.Height); } else if (NativeData.GetType() == typeof(RectangleF)) { RectangleF R = (RectangleF)NativeData; return String.Format(CultureInfo.InvariantCulture, "({0},{1}),({2},{3})", R.Left, R.Top, R.Left + R.Width, R.Top + R.Height);*/ + + if (NativeData is NpgsqlBox) + { + NpgsqlBox box = (NpgsqlBox) NativeData; + return String.Format(CultureInfo.InvariantCulture, "({0},{1}),({2},{3})", box.LowerLeft.X, box.LowerLeft.Y, box.UpperRight.X, box.UpperRight.Y); + + } else { + throw new InvalidCastException("Unable to cast data to Rectangle type"); + } + } - if (NativeData is NpgsqlBox) - { - NpgsqlBox box = (NpgsqlBox)NativeData; - return - String.Format(CultureInfo.InvariantCulture, "({0},{1}),({2},{3})", box.LowerLeft.X, box.LowerLeft.Y, - box.UpperRight.X, box.UpperRight.Y); - } - else - { - throw new InvalidCastException("Unable to cast data to Rectangle type"); - } - } - - /// <summary> - /// LSeg. - /// </summary> - internal static String ToLSeg(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) - { - NpgsqlLSeg S = (NpgsqlLSeg)NativeData; - return String.Format(CultureInfo.InvariantCulture, "{0},{1},{2},{3}", S.Start.X, S.Start.Y, S.End.X, S.End.Y); - } - - /// <summary> - /// Open path. - /// </summary> - internal static String ToPath(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) - { - StringBuilder B = null; - try - { - B =new StringBuilder(); - - foreach (NpgsqlPoint P in ((NpgsqlPath)NativeData)) - { - B.AppendFormat(CultureInfo.InvariantCulture, "{0}({1},{2})", (B.Length > 0 ? "," : ""), P.X, P.Y); - } - - return String.Format("[{0}]", B); - } - finally - { - B = null; - - } - - } - - /// <summary> - /// Polygon. - /// </summary> - internal static String ToPolygon(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) - { - StringBuilder B = new StringBuilder(); - - foreach (NpgsqlPoint P in ((NpgsqlPolygon)NativeData)) - { - B.AppendFormat(CultureInfo.InvariantCulture, "{0}({1},{2})", (B.Length > 0 ? "," : ""), P.X, P.Y); - } + /// <summary> + /// LSeg. + /// </summary> + internal static String ToLSeg(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) + { + NpgsqlLSeg S = (NpgsqlLSeg)NativeData; + return String.Format(CultureInfo.InvariantCulture, "{0},{1},{2},{3}", S.Start.X, S.Start.Y, S.End.X, S.End.Y); + } - return String.Format("({0})", B); - } + /// <summary> + /// Open path. + /// </summary> + internal static String ToPath(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) + { + StringBuilder B = new StringBuilder(); - /// <summary> - /// Circle. - /// </summary> - internal static String ToCircle(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) - { - NpgsqlCircle C = (NpgsqlCircle)NativeData; - return String.Format(CultureInfo.InvariantCulture, "{0},{1},{2}", C.Center.X, C.Center.Y, C.Radius); - } - - /// <summary> - /// Convert to a postgres inet. - /// </summary> - internal static String ToIPAddress(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) - { - if (NativeData is NpgsqlInet) - { - return ((NpgsqlInet)NativeData).ToString(); - } - return NativeData.ToString(); + foreach (NpgsqlPoint P in ((NpgsqlPath)NativeData).Points) { + B.AppendFormat(CultureInfo.InvariantCulture, "{0}({1},{2})", (B.Length > 0 ? "," : ""), P.X, P.Y); + } - } + return String.Format("[{0}]", B.ToString()); + } - /// <summary> - /// Convert to a postgres interval - /// </summary> - internal static String ToInterval(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) - { - return - ((NativeData is TimeSpan) - ? ((NpgsqlInterval)(TimeSpan)NativeData).ToString() - : ((NpgsqlInterval)NativeData).ToString()); - } + /// <summary> + /// Polygon. + /// </summary> + internal static String ToPolygon(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) + { + StringBuilder B = new StringBuilder(); - internal static string ToTime(NpgsqlNativeTypeInfo typeInfo, object nativeData) - { - if (nativeData is DateTime) - { - return BasicNativeToBackendTypeConverter.ToTime(typeInfo, nativeData); - } - NpgsqlTime time; - if (nativeData is TimeSpan) - { - time = (NpgsqlTime)(TimeSpan)nativeData; - } - else - { - time = (NpgsqlTime)nativeData; - } - return time.ToString(); - } + foreach (NpgsqlPoint P in ((NpgsqlPolygon)NativeData).Points) { + B.AppendFormat(CultureInfo.InvariantCulture, "{0}({1},{2})", (B.Length > 0 ? "," : ""), P.X, P.Y); + } - internal static string ToTimeTZ(NpgsqlNativeTypeInfo typeInfo, object nativeData) - { - if (nativeData is DateTime) - { - return BasicNativeToBackendTypeConverter.ToTime(typeInfo, nativeData); - } - NpgsqlTimeTZ time; - if (nativeData is TimeSpan) - { - time = (NpgsqlTimeTZ)(TimeSpan)nativeData; - } - else - { - time = (NpgsqlTimeTZ)nativeData; - } - return time.ToString(); - } + return String.Format("({0})", B.ToString()); + } - internal static string ToDate(NpgsqlNativeTypeInfo typeInfo, object nativeData) - { - if (nativeData is DateTime) - { - return BasicNativeToBackendTypeConverter.ToDate(typeInfo, nativeData); - } - else - { - return nativeData.ToString(); - } - } + /// <summary> + /// Circle. + /// </summary> + internal static String ToCircle(NpgsqlNativeTypeInfo TypeInfo, Object NativeData) + { + NpgsqlCircle C = (NpgsqlCircle)NativeData; + return String.Format(CultureInfo.InvariantCulture, "{0},{1},{2}", C.Center.X, C.Center.Y, C.Radius); + } - internal static string ToTimeStamp(NpgsqlNativeTypeInfo typeInfo, object nativeData) - { - if (nativeData is DateTime) - { - return BasicNativeToBackendTypeConverter.ToDateTime(typeInfo, nativeData); - } - else - { - return nativeData.ToString(); - } - } - } -}
\ No newline at end of file + /// <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(); + } + } +} diff --git a/mcs/class/Npgsql/NpgsqlTypes/NpgsqlTypes.cs b/mcs/class/Npgsql/NpgsqlTypes/NpgsqlTypes.cs index 81991b487a5..1eb660f89a7 100644 --- a/mcs/class/Npgsql/NpgsqlTypes/NpgsqlTypes.cs +++ b/mcs/class/Npgsql/NpgsqlTypes/NpgsqlTypes.cs @@ -1,685 +1,276 @@ // NpgsqlTypes.NpgsqlTypesHelper.cs // // Author: -// Glen Parker <glenebob@nwlink.com> +// Glen Parker <glenebob@nwlink.com> // -// Copyright (C) 2004 The Npgsql Development Team -// npgsql-general@gborg.postgresql.org -// http://gborg.postgresql.org/project/npgsql/projdisplay.php +// Copyright (C) 2004 The Npgsql Development Team +// npgsql-general@gborg.postgresql.org +// http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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 // This file provides implementations of PostgreSQL specific data types that cannot // be mapped to standard .NET classes. using System; using System.Collections; -using System.Collections.Generic; +using System.Globalization; +using System.Data; using System.Net; -using Npgsql; +using System.Text; +using System.IO; +using System.Resources; namespace NpgsqlTypes { + /// <summary> /// Represents a PostgreSQL Point type /// </summary> - public struct NpgsqlPoint : IEquatable<NpgsqlPoint> + + public struct NpgsqlPoint { - private Single _x; - private Single _y; + private Single _X; + private Single _Y; - public NpgsqlPoint(Single x, Single y) + public NpgsqlPoint(Single X, Single Y) { - _x = x; - _y = y; + _X = X; + _Y = Y; } - + public Single X { get { - return _x; + return _X; } - + set { - _x = value; + _X = value; } } - + + public Single Y { get { - return _y; + return _Y; } - + set { - _y = value; + _Y = value; } } - - - public bool Equals(NpgsqlPoint other) - { - return X == other.X && Y == other.Y; - } - - public override bool Equals(object obj) - { - return obj != null && obj is NpgsqlPoint && Equals((NpgsqlPoint) obj); - } - - public static bool operator ==(NpgsqlPoint x, NpgsqlPoint y) - { - return x.Equals(y); - } - - public static bool operator !=(NpgsqlPoint x, NpgsqlPoint y) - { - return !(x == y); - } - - public override int GetHashCode() - { - return X.GetHashCode() ^ PGUtil.RotateShift(Y.GetHashCode(), sizeof (int)/2); - } } - public struct NpgsqlBox : IEquatable<NpgsqlBox> + public struct NpgsqlBox { - - private NpgsqlPoint _upperRight; - private NpgsqlPoint _lowerLeft; - - public NpgsqlBox(NpgsqlPoint upperRight, NpgsqlPoint lowerLeft) - { - _upperRight = upperRight; - _lowerLeft = lowerLeft; - } + private NpgsqlPoint _UpperRight; + private NpgsqlPoint _LowerLeft; - public NpgsqlBox(float Top, float Right, float Bottom, float Left) - : this(new NpgsqlPoint(Right, Top), new NpgsqlPoint(Left, Bottom)) + public NpgsqlBox(NpgsqlPoint UpperRight, NpgsqlPoint LowerLeft) { + _UpperRight = UpperRight; + _LowerLeft = LowerLeft; } + public NpgsqlPoint UpperRight { get { - return _upperRight; + return _UpperRight; } - set { - _upperRight = value; + _UpperRight = value; } - } - + public NpgsqlPoint LowerLeft { get { - return _lowerLeft; + return _LowerLeft; } - set { - _lowerLeft = value; + _LowerLeft = value; } - - } - - public float Left - { - get { return LowerLeft.X; } - } - - public float Right - { - get { return UpperRight.X; } } - public float Bottom - { - get { return LowerLeft.Y; } - } - - public float Top - { - get { return UpperRight.Y; } - } - - public float Width - { - get { return Right - Left; } - } - - public float Height - { - get { return Top - Bottom; } - } - - public bool IsEmpty - { - get { return Width == 0 || Height == 0; } - } - - public bool Equals(NpgsqlBox other) - { - return UpperRight == other.UpperRight && LowerLeft == other.LowerLeft; - } - - public override bool Equals(object obj) - { - return obj != null && obj is NpgsqlBox && Equals((NpgsqlBox) obj); - } - - public static bool operator ==(NpgsqlBox x, NpgsqlBox y) - { - return x.Equals(y); - } - - public static bool operator !=(NpgsqlBox x, NpgsqlBox y) - { - return !(x == y); - } - - public override int GetHashCode() - { - return - Top.GetHashCode() ^ PGUtil.RotateShift(Right.GetHashCode(), sizeof (int)/4) ^ - PGUtil.RotateShift(Bottom.GetHashCode(), sizeof (int)/2) ^ - PGUtil.RotateShift(LowerLeft.GetHashCode(), sizeof (int)*3/4); - } } /// <summary> /// Represents a PostgreSQL Line Segment type. /// </summary> - public struct NpgsqlLSeg : IEquatable<NpgsqlLSeg> + public struct NpgsqlLSeg { - public NpgsqlPoint Start; - public NpgsqlPoint End; + public NpgsqlPoint Start; + public NpgsqlPoint End; - public NpgsqlLSeg(NpgsqlPoint start, NpgsqlPoint end) + public NpgsqlLSeg(NpgsqlPoint Start, NpgsqlPoint End) { - Start = start; - End = end; + this.Start = Start; + this.End = End; } public override String ToString() { return String.Format("({0}, {1})", Start, End); } - - public override int GetHashCode() - { - return - Start.X.GetHashCode() ^ PGUtil.RotateShift(Start.Y.GetHashCode(), sizeof (int)/4) ^ - PGUtil.RotateShift(End.X.GetHashCode(), sizeof (int)/2) ^ PGUtil.RotateShift(End.Y.GetHashCode(), sizeof (int)*3/4); - } - - public bool Equals(NpgsqlLSeg other) - { - return Start == other.Start && End == other.End; - } - - public override bool Equals(object obj) - { - return obj != null && obj is NpgsqlLSeg && Equals((NpgsqlLSeg) obj); - } - - public static bool operator ==(NpgsqlLSeg x, NpgsqlLSeg y) - { - return x.Equals(y); - } - - public static bool operator !=(NpgsqlLSeg x, NpgsqlLSeg y) - { - return !(x == y); - } } /// <summary> /// Represents a PostgreSQL Path type. /// </summary> - public struct NpgsqlPath : IList<NpgsqlPoint>, IEquatable<NpgsqlPath> + public struct NpgsqlPath { - private bool _open; - private readonly List<NpgsqlPoint> _points; + internal NpgsqlPoint[] Points; - public NpgsqlPath(IEnumerable<NpgsqlPoint> points, bool open) - { - _points = new List<NpgsqlPoint>(points); - _open = open; - } + internal Boolean IsOpen; - public NpgsqlPath(IEnumerable<NpgsqlPoint> points) - : this(points, false) - { - } - - public NpgsqlPath(NpgsqlPoint[] points) : this((IEnumerable<NpgsqlPoint>)points, false) + public NpgsqlPath(NpgsqlPoint[] Points) { + this.Points = Points; + IsOpen = false; } - public NpgsqlPath(bool open) - { - _points = new List<NpgsqlPoint>(); - _open = open; - } + public Int32 Count + { get + { + return Points.Length; + } } - public NpgsqlPath(int capacity, bool open) - { - _points = new List<NpgsqlPoint>(capacity); - _open = open; - } + public NpgsqlPoint this [Int32 Index] + { get + { + return Points[Index]; + } } - public NpgsqlPath(int capacity) - : this(capacity, false) - { - } - - public bool Open + public Boolean Open { get { - return _open; + return IsOpen; } - - set - { - _open = value; - } - } - - public NpgsqlPoint this[int index] - { - get { return _points[index]; } - set { _points[index] = value; } - } - - public int Count - { - get { return _points.Count; } - } - - public bool IsReadOnly - { - get { return false; } - } - - public int IndexOf(NpgsqlPoint item) - { - return _points.IndexOf(item); - } - - public void Insert(int index, NpgsqlPoint item) - { - _points.Insert(index, item); - } - - public void RemoveAt(int index) - { - _points.RemoveAt(index); - } - - public void Add(NpgsqlPoint item) - { - _points.Add(item); - } - - public void Clear() - { - _points.Clear(); - } - - public bool Contains(NpgsqlPoint item) - { - return _points.Contains(item); - } - - public void CopyTo(NpgsqlPoint[] array, int arrayIndex) - { - _points.CopyTo(array, arrayIndex); - } - - public bool Remove(NpgsqlPoint item) - { - return _points.Remove(item); - } - - public IEnumerator<NpgsqlPoint> GetEnumerator() - { - return _points.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public bool Equals(NpgsqlPath other) - { - if (Open != other.Open || Count != other.Count) - { - return false; - } - for (int i = 0; i != Count; ++i) - { - if (this[i] != other[i]) - { - return false; - } - } - return true; - } - - public override bool Equals(object obj) - { - return obj != null && obj is NpgsqlPath && Equals((NpgsqlPath) obj); - } - - public static bool operator ==(NpgsqlPath x, NpgsqlPath y) - { - return x.Equals(y); - } - - public static bool operator !=(NpgsqlPath x, NpgsqlPath y) - { - return !(x == y); - } - - public override int GetHashCode() - { - int ret = 0; - foreach (NpgsqlPoint point in this) - { - //The ideal amount to shift each value is one that would evenly spread it throughout - //the resultant bytes. Using the current result % 32 is essentially using a random value - //but one that will be the same on subsequent calls. - ret ^= PGUtil.RotateShift(point.GetHashCode(), ret%sizeof (int)); - } - return Open ? ret : -ret; } } /// <summary> /// Represents a PostgreSQL Polygon type. /// </summary> - public struct NpgsqlPolygon : IList<NpgsqlPoint>, IEquatable<NpgsqlPolygon> + public struct NpgsqlPolygon { - private readonly List<NpgsqlPoint> _points; - - public NpgsqlPolygon(IEnumerable<NpgsqlPoint> points) - { - _points = new List<NpgsqlPoint>(points); - } - - public NpgsqlPolygon(NpgsqlPoint[] points) : this ((IEnumerable<NpgsqlPoint>) points) - { - } - - public NpgsqlPoint this[int index] - { - get { return _points[index]; } - set { _points[index] = value; } - } - - public int Count - { - get { return _points.Count; } - } - - public bool IsReadOnly - { - get { return false; } - } - - public int IndexOf(NpgsqlPoint item) - { - return _points.IndexOf(item); - } - - public void Insert(int index, NpgsqlPoint item) - { - _points.Insert(index, item); - } - - public void RemoveAt(int index) - { - _points.RemoveAt(index); - } - - public void Add(NpgsqlPoint item) - { - _points.Add(item); - } - - public void Clear() - { - _points.Clear(); - } - - public bool Contains(NpgsqlPoint item) - { - return _points.Contains(item); - } - - public void CopyTo(NpgsqlPoint[] array, int arrayIndex) - { - _points.CopyTo(array, arrayIndex); - } - - public bool Remove(NpgsqlPoint item) - { - return _points.Remove(item); - } - - public IEnumerator<NpgsqlPoint> GetEnumerator() - { - return _points.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public bool Equals(NpgsqlPolygon other) - { - if (Count != other.Count) - { - return false; - } - for (int i = 0; i != Count; ++i) - { - if (this[i] != other[i]) - { - return false; - } - } - return true; - } + internal NpgsqlPoint[] Points; - public override bool Equals(object obj) + public NpgsqlPolygon(NpgsqlPoint[] Points) { - return obj != null && obj is NpgsqlPolygon && Equals((NpgsqlPolygon) obj); + this.Points = Points; } - public static bool operator ==(NpgsqlPolygon x, NpgsqlPolygon y) - { - return x.Equals(y); - } - - public static bool operator !=(NpgsqlPolygon x, NpgsqlPolygon y) - { - return !(x == y); - } + public Int32 Count + { get + { + return Points.Length; + } } - public override int GetHashCode() - { - int ret = 0; - foreach (NpgsqlPoint point in this) - { - //The ideal amount to shift each value is one that would evenly spread it throughout - //the resultant bytes. Using the current result % 32 is essentially using a random value - //but one that will be the same on subsequent calls. - ret ^= PGUtil.RotateShift(point.GetHashCode(), ret%sizeof (int)); - } - return ret; - } + public NpgsqlPoint this [Int32 Index] + { get + { + return Points[Index]; + } } } /// <summary> /// Represents a PostgreSQL Circle type. /// </summary> - public struct NpgsqlCircle : IEquatable<NpgsqlCircle> + public struct NpgsqlCircle { - public NpgsqlPoint Center; - public Double Radius; + public NpgsqlPoint Center; + public Double Radius; - public NpgsqlCircle(NpgsqlPoint center, Double radius) + public NpgsqlCircle(NpgsqlPoint Center, Double Radius) { - Center = center; - Radius = radius; - } - - public bool Equals(NpgsqlCircle other) - { - return Center == other.Center && Radius == other.Radius; - } - - public override bool Equals(object obj) - { - return obj != null && obj is NpgsqlCircle && Equals((NpgsqlCircle) obj); + this.Center = Center; + this.Radius = Radius; } public override String ToString() { return string.Format("({0}), {1}", Center, Radius); } - - public static bool operator ==(NpgsqlCircle x, NpgsqlCircle y) - { - return x.Equals(y); - } - - public static bool operator !=(NpgsqlCircle x, NpgsqlCircle y) - { - return !(x == y); - } - - public override int GetHashCode() - { - return - Center.X.GetHashCode() ^ PGUtil.RotateShift(Center.Y.GetHashCode(), sizeof (int)/4) ^ - PGUtil.RotateShift(Radius.GetHashCode(), sizeof (int)/2); - } } - /// <summary> - /// Represents a PostgreSQL inet type. - /// </summary> - public struct NpgsqlInet : IEquatable<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, mask); - } - return addr.ToString(); - - } - - public static implicit operator IPAddress(NpgsqlInet x) - { - if (x.mask != 32) - { - throw new InvalidCastException("Cannot cast CIDR network to address"); - } - return x.addr; - - } - - public bool Equals(NpgsqlInet other) - { - return addr.Equals(other.addr) && mask == other.mask; - } - - public override bool Equals(object obj) - { - return obj != null && obj is NpgsqlInet && Equals((NpgsqlInet) obj); - } - - public override int GetHashCode() - { - return PGUtil.RotateShift(addr.GetHashCode(), mask%32); - } - - public static bool operator ==(NpgsqlInet x, NpgsqlInet y) - { - return x.Equals(y); - } - - public static bool operator !=(NpgsqlInet x, NpgsqlInet y) - { - return !(x == y); - } - } -}
\ No newline at end of file + /// <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; + } + } +} diff --git a/mcs/class/Npgsql/NpgsqlTypes/NpgsqlTypesHelper.cs b/mcs/class/Npgsql/NpgsqlTypes/NpgsqlTypesHelper.cs index 290ae6cdbca..3c25d5d4670 100644 --- a/mcs/class/Npgsql/NpgsqlTypes/NpgsqlTypesHelper.cs +++ b/mcs/class/Npgsql/NpgsqlTypes/NpgsqlTypesHelper.cs @@ -7,199 +7,107 @@ // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written -// agreement is hereby granted, provided that the above copyright notice -// and this paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY -// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS -// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS -// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// 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.Collections.Generic; -using System.Data; +using System.Collections; using System.Globalization; +using System.Data; using System.Net; -using System.Resources; using System.Text; +using System.Resources; using Npgsql; + namespace NpgsqlTypes { - /// <summary> - /// This class contains helper methods for type conversion between - /// the .Net type system and postgresql. - /// </summary> - internal abstract class NpgsqlTypesHelper - { - // Logging related values - private static readonly String CLASSNAME = "NpgsqlTypesHelper"; - private static ResourceManager resman = new ResourceManager(typeof (NpgsqlTypesHelper)); - - private struct MappingKey : IEquatable<MappingKey> - { - public readonly Version Version; - public readonly bool UseExtendedTypes; - - public MappingKey(Version version, bool useExtendedTypes) - { - this.Version = version; - this.UseExtendedTypes = useExtendedTypes; - } - - public bool Equals(MappingKey other) - { - return UseExtendedTypes.Equals(other.UseExtendedTypes) && Version.Equals(other.Version); - } - - public override bool Equals(object obj) - { - //Note that Dictionary<T, U> will call IEquatable<T>.Equals() when possible. - //This is included for completeness (that and second-guessing Mono while coding on .NET!). - return obj != null && obj is MappingKey && Equals((MappingKey) obj); - } - - public override int GetHashCode() - { - return UseExtendedTypes ? ~Version.GetHashCode() : Version.GetHashCode(); - } - } - - /// <summary> - /// A cache of basic datatype mappings keyed by server version. This way we don't - /// have to load the basic type mappings for every connection. - /// </summary> - private static readonly Dictionary<MappingKey, NpgsqlBackendTypeMapping> BackendTypeMappingCache = - new Dictionary<MappingKey, NpgsqlBackendTypeMapping>(); - - private static readonly NpgsqlNativeTypeMapping NativeTypeMapping = PrepareDefaultTypesMap(); - - - /// <summary> - /// Find a NpgsqlNativeTypeInfo in the default types map that can handle objects - /// of the given NpgsqlDbType. - /// </summary> - public static bool TryGetNativeTypeInfo(NpgsqlDbType dbType, out NpgsqlNativeTypeInfo typeInfo) - { - return NativeTypeMapping.TryGetValue(dbType, out typeInfo); - } - - /// <summary> - /// Find a NpgsqlNativeTypeInfo in the default types map that can handle objects - /// of the given DbType. - /// </summary> - public static bool TryGetNativeTypeInfo(DbType dbType, out NpgsqlNativeTypeInfo typeInfo) - { - return NativeTypeMapping.TryGetValue(dbType, out typeInfo); - } - - public static NpgsqlNativeTypeInfo GetNativeTypeInfo(DbType DbType) - { - NpgsqlNativeTypeInfo ret = null; - return TryGetNativeTypeInfo(DbType, out ret) ? ret : null; - } - - private static bool TestTypedEnumerator(Type type, out Type typeOut) - { - if (type.IsArray) - { - typeOut = type.GetElementType(); - return true; - } - //We can only work out the element type for IEnumerable<T> not for IEnumerable - //so we are looking for IEnumerable<T> for any value of T. - //So we want to find an interface type where GetGenericTypeDefinition == typeof(IEnumerable<>); - //And we can only safely call GetGenericTypeDefinition() if IsGenericType is true, but if it's false - //then the interface clearly isn't an IEnumerable<T>. - foreach (Type iface in type.GetInterfaces()) - { - if (iface.IsGenericType && iface.GetGenericTypeDefinition().Equals(typeof (IEnumerable<>))) - { - typeOut = iface.GetGenericArguments()[0]; - return true; - } - } - typeOut = null; - return false; - } - - - /// <summary> - /// Find a NpgsqlNativeTypeInfo in the default types map that can handle objects - /// of the given System.Type. - /// </summary> - public static bool TryGetNativeTypeInfo(Type type, out NpgsqlNativeTypeInfo typeInfo) - { - if (NativeTypeMapping.TryGetValue(type, out typeInfo)) - { - return true; - } - // At this point there is no direct mapping, so we see if we have an array or IEnumerable<T>. - // Note that we checked for a direct mapping first, so if there is a direct mapping of a class - // which implements IEnumerable<T> we will use that (currently this is only string, which - // implements IEnumerable<char>. - - Type elementType = null; - NpgsqlNativeTypeInfo elementTypeInfo = null; - if (TestTypedEnumerator(type, out elementType) && TryGetNativeTypeInfo(elementType, out elementTypeInfo)) - { - typeInfo = NpgsqlNativeTypeInfo.ArrayOf(elementTypeInfo); - return true; - } - return false; - } - - public static NpgsqlNativeTypeInfo GetNativeTypeInfo(Type Type) - { - NpgsqlNativeTypeInfo ret = null; - return TryGetNativeTypeInfo(Type, out ret) ? ret : null; - } - - - public static bool DefinedType(Type type) - - { - return NativeTypeMapping.ContainsType(type); - } - - - public static bool DefinedType(object item) - - { - return DefinedType(item.GetType()); - } - - // CHECKME - // Not sure what to do with this one. I don't believe we ever ask for a binary - // formatting, so this shouldn't even be used right now. - // At some point this will need to be merged into the type converter system somehow? - public static Object ConvertBackendBytesToSystemType(NpgsqlBackendTypeInfo TypeInfo, Byte[] data, Int32 fieldValueSize, - Int32 typeModifier) - { - 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) - { - return data; - } - - switch (TypeInfo.NpgsqlDbType) - { - case NpgsqlDbType.Bytea: - return data; - /*case NpgsqlDbType.Boolean: + /// <summary> + /// This class contains helper methods for type conversion between + /// the .Net type system and postgresql. + /// </summary> + internal abstract class NpgsqlTypesHelper + { + // Logging related values + private static readonly String CLASSNAME = "NpgsqlTypesHelper"; + private static ResourceManager resman = new ResourceManager(typeof(NpgsqlTypesHelper)); + + /// <summary> + /// A cache of basic datatype mappings keyed by server version. This way we don't + /// have to load the basic type mappings for every connection. + /// </summary> + private static Hashtable BackendTypeMappingCache = new Hashtable(); + private static NpgsqlNativeTypeMapping NativeTypeMapping = null; + + + /// <summary> + /// Find a NpgsqlNativeTypeInfo in the default types map that can handle objects + /// of the given NpgsqlDbType. + /// </summary> + public static NpgsqlNativeTypeInfo GetNativeTypeInfo(NpgsqlDbType NpgsqlDbType) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetBackendTypeNameFromNpgsqlDbType"); + + VerifyDefaultTypesMap(); + return NativeTypeMapping[NpgsqlDbType]; + } + + /// <summary> + /// Find a NpgsqlNativeTypeInfo in the default types map that can handle objects + /// of the given DbType. + /// </summary> + public static NpgsqlNativeTypeInfo GetNativeTypeInfo(DbType DbType) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetBackendTypeNameFromNpgsqlDbType"); + + VerifyDefaultTypesMap(); + return NativeTypeMapping[DbType]; + } + + + + /// <summary> + /// Find a NpgsqlNativeTypeInfo in the default types map that can handle objects + /// of the given System.Type. + /// </summary> + public static NpgsqlNativeTypeInfo GetNativeTypeInfo(Type Type) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetBackendTypeNameFromNpgsqlDbType"); + + VerifyDefaultTypesMap(); + return NativeTypeMapping[Type]; + } + + // CHECKME + // Not sure what to do with this one. I don't believe we ever ask for a binary + // formatting, so this shouldn't even be used right now. + // At some point this will need to be merged into the type converter system somehow? + public static Object ConvertBackendBytesToSystemType(NpgsqlBackendTypeInfo TypeInfo, Byte[] data, Encoding encoding, Int32 fieldValueSize, Int32 typeModifier) + { + 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) + return data; + + switch (TypeInfo.NpgsqlDbType) + { + case NpgsqlDbType.Bytea: + return data; + /*case NpgsqlDbType.Boolean: return BitConverter.ToBoolean(data, 0); case NpgsqlDbType.DateTime: return DateTime.MinValue.AddTicks(IPAddress.NetworkToHostOrder(BitConverter.ToInt64(data, 0))); @@ -214,1031 +122,861 @@ namespace NpgsqlTypes case NpgsqlDbType.AnsiString: case NpgsqlDbType.StringFixedLength: return encoding.GetString(data, 0, fieldValueSize);*/ - default: - throw new InvalidCastException("Type not supported in binary format"); - } - } - - ///<summary> - /// This method is responsible to convert the string received from the backend - /// to the corresponding NpgsqlType. - /// The given TypeInfo is called upon to do the conversion. - /// If no TypeInfo object is provided, no conversion is performed. - /// </summary> - public static Object ConvertBackendStringToSystemType(NpgsqlBackendTypeInfo TypeInfo, String data, Int16 typeSize, - Int32 typeModifier) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ConvertBackendStringToSystemType"); - - if (TypeInfo != null) - { - return TypeInfo.ConvertToNative(data, typeSize, typeModifier); - } - else - { - return data; - } - } - - /// <summary> - /// Create the one and only native to backend type map. - /// This map is used when formatting native data - /// types to backend representations. - /// </summary> - private static NpgsqlNativeTypeMapping PrepareDefaultTypesMap() - { - NpgsqlNativeTypeMapping nativeTypeMapping = new NpgsqlNativeTypeMapping(); - - - nativeTypeMapping.AddType("oidvector", NpgsqlDbType.Oidvector, DbType.String, true, null); - - // Conflicting types should have mapped first the non default mappings. - // For example, char, varchar and text map to DbType.String. As the most - // common is to use text with string, it has to be the last mapped, in order - // to type mapping has the last entry, in this case, text, as the map value - // for DbType.String. - - nativeTypeMapping.AddType("refcursor", NpgsqlDbType.Refcursor, DbType.String, true, null); - - nativeTypeMapping.AddType("char", NpgsqlDbType.Char, DbType.String, true, null); - - nativeTypeMapping.AddTypeAlias("char", typeof (Char)); - - nativeTypeMapping.AddType("varchar", NpgsqlDbType.Varchar, DbType.String, true, null); - - nativeTypeMapping.AddType("text", NpgsqlDbType.Text, DbType.String, true, null); - - nativeTypeMapping.AddDbTypeAlias("text", DbType.StringFixedLength); - nativeTypeMapping.AddDbTypeAlias("text", DbType.AnsiString); - nativeTypeMapping.AddDbTypeAlias("text", DbType.AnsiStringFixedLength); - - nativeTypeMapping.AddTypeAlias("text", typeof (String)); - - - nativeTypeMapping.AddType("bytea", NpgsqlDbType.Bytea, DbType.Binary, true, - new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToBinary)); - - nativeTypeMapping.AddTypeAlias("bytea", typeof (Byte[])); - - nativeTypeMapping.AddType("bit", NpgsqlDbType.Bit, DbType.Boolean, false, - new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToBit)); - - nativeTypeMapping.AddType("bool", NpgsqlDbType.Boolean, DbType.Boolean, false, - new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToBoolean)); - - nativeTypeMapping.AddTypeAlias("bool", typeof (Boolean)); - - nativeTypeMapping.AddType("int2", NpgsqlDbType.Smallint, DbType.Int16, false, null); - - nativeTypeMapping.AddTypeAlias("int2", typeof (Int16)); - - nativeTypeMapping.AddDbTypeAlias("int2", DbType.Byte); + default: + throw new InvalidCastException("Type not supported in binary format"); + } + + return null; + } + + ///<summary> + /// This method is responsible to convert the string received from the backend + /// to the corresponding NpgsqlType. + /// The given TypeInfo is called upon to do the conversion. + /// If no TypeInfo object is provided, no conversion is performed. + /// </summary> + public static Object ConvertBackendStringToSystemType(NpgsqlBackendTypeInfo TypeInfo, String data, Int16 typeSize, Int32 typeModifier) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ConvertBackendStringToSystemType"); + + if (TypeInfo != null) { + return TypeInfo.ConvertToNative(data, typeSize, typeModifier); + } else { + return data; + } + } + + /// <summary> + /// Create the one and only native to backend type map. + /// This map is used when formatting native data + /// types to backend representations. + /// </summary> + private static void VerifyDefaultTypesMap() + { + lock(CLASSNAME) { + if (NativeTypeMapping != null) { + return; + } + + NativeTypeMapping = new NpgsqlNativeTypeMapping(); + + + // Conflicting types should have mapped first the non default mappings. + // For example, char, varchar and text map to DbType.String. As the most + // common is to use text with string, it has to be the last mapped, in order + // to type mapping has the last entry, in this case, text, as the map value + // for DbType.String. + + NativeTypeMapping.AddType("refcursor", NpgsqlDbType.Refcursor, DbType.String, true, null); + + NativeTypeMapping.AddType("char", NpgsqlDbType.Char, DbType.String, true, null); + + NativeTypeMapping.AddType("varchar", NpgsqlDbType.Varchar, DbType.String, true, null); - nativeTypeMapping.AddTypeAlias("int2", typeof (Byte)); + NativeTypeMapping.AddType("text", NpgsqlDbType.Text, DbType.String, true, null); - nativeTypeMapping.AddType("int4", NpgsqlDbType.Integer, DbType.Int32, false, null); + NativeTypeMapping.AddDbTypeAlias("text", DbType.StringFixedLength); + NativeTypeMapping.AddDbTypeAlias("text", DbType.AnsiString); + NativeTypeMapping.AddDbTypeAlias("text", DbType.AnsiStringFixedLength); + NativeTypeMapping.AddTypeAlias("text", typeof(String)); - nativeTypeMapping.AddTypeAlias("int4", typeof (Int32)); + + NativeTypeMapping.AddType("bytea", NpgsqlDbType.Bytea, DbType.Binary, true, + new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToBinary)); - nativeTypeMapping.AddType("int8", NpgsqlDbType.Bigint, DbType.Int64, false, null); + NativeTypeMapping.AddTypeAlias("bytea", typeof(Byte[])); + + NativeTypeMapping.AddType("bit", NpgsqlDbType.Bit, DbType.Boolean, true, + new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToBit)); + + NativeTypeMapping.AddType("bool", NpgsqlDbType.Boolean, DbType.Boolean, false, + new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToBoolean)); + + NativeTypeMapping.AddTypeAlias("bool", typeof(Boolean)); + + NativeTypeMapping.AddType("int2", NpgsqlDbType.Smallint, DbType.Int16, false, + null); + + NativeTypeMapping.AddTypeAlias("int2", typeof(Int16)); + + NativeTypeMapping.AddDbTypeAlias("int2", DbType.Byte); + + NativeTypeMapping.AddTypeAlias("int2", typeof(Byte)); + + NativeTypeMapping.AddType("int4", NpgsqlDbType.Integer, DbType.Int32, false, + null); + + NativeTypeMapping.AddTypeAlias("int4", typeof(Int32)); + + NativeTypeMapping.AddType("int8", NpgsqlDbType.Bigint, DbType.Int64, false, + null); + + NativeTypeMapping.AddTypeAlias("int8", typeof(Int64)); + + NativeTypeMapping.AddType("float4", NpgsqlDbType.Real, DbType.Single, true, + null); + + NativeTypeMapping.AddTypeAlias("float4", typeof(Single)); + + NativeTypeMapping.AddType("float8", NpgsqlDbType.Double, DbType.Double, true, + null); + + NativeTypeMapping.AddTypeAlias("float8", typeof(Double)); + + NativeTypeMapping.AddType("numeric", NpgsqlDbType.Numeric, DbType.Decimal, false, + null); + + NativeTypeMapping.AddTypeAlias("numeric", typeof(Decimal)); + + NativeTypeMapping.AddType("currency", NpgsqlDbType.Money, DbType.Currency, true, + new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToMoney)); + + NativeTypeMapping.AddType("date", NpgsqlDbType.Date, DbType.Date, true, + new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToDate)); + + NativeTypeMapping.AddType("time", NpgsqlDbType.Time, DbType.Time, true, + new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToTime)); + + NativeTypeMapping.AddType("timestamp", NpgsqlDbType.Timestamp, DbType.DateTime, true, + new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToDateTime)); + + NativeTypeMapping.AddTypeAlias("timestamp", typeof(DateTime)); + + NativeTypeMapping.AddType("timestamptz", NpgsqlDbType.TimestampTZ, DbType.DateTime, true, + new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToDateTime)); - nativeTypeMapping.AddTypeAlias("int8", typeof (Int64)); + NativeTypeMapping.AddType("point", NpgsqlDbType.Point, DbType.Object, true, + new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToPoint)); - nativeTypeMapping.AddType("float4", NpgsqlDbType.Real, DbType.Single, true, null); + NativeTypeMapping.AddTypeAlias("point", typeof(NpgsqlPoint)); + + NativeTypeMapping.AddType("box", NpgsqlDbType.Box, DbType.Object, true, + new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToBox)); - nativeTypeMapping.AddTypeAlias("float4", typeof (Single)); + NativeTypeMapping.AddTypeAlias("box", typeof(NpgsqlBox)); + + NativeTypeMapping.AddType("lseg", NpgsqlDbType.LSeg, DbType.Object, true, + new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToLSeg)); - nativeTypeMapping.AddType("float8", NpgsqlDbType.Double, DbType.Double, true, null); + NativeTypeMapping.AddTypeAlias("lseg", typeof(NpgsqlLSeg)); - nativeTypeMapping.AddTypeAlias("float8", typeof (Double)); + NativeTypeMapping.AddType("path", NpgsqlDbType.Path, DbType.Object, true, + new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToPath)); - nativeTypeMapping.AddType("numeric", NpgsqlDbType.Numeric, DbType.Decimal, true, null); + NativeTypeMapping.AddTypeAlias("path", typeof(NpgsqlPath)); - nativeTypeMapping.AddTypeAlias("numeric", typeof (Decimal)); + NativeTypeMapping.AddType("polygon", NpgsqlDbType.Polygon, DbType.Object, true, + new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToPolygon)); - nativeTypeMapping.AddType("money", NpgsqlDbType.Money, DbType.Currency, true, - new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToMoney)); + NativeTypeMapping.AddTypeAlias("polygon", typeof(NpgsqlPolygon)); - nativeTypeMapping.AddType("date", NpgsqlDbType.Date, DbType.Date, true, - new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToDate)); + NativeTypeMapping.AddType("circle", NpgsqlDbType.Circle, DbType.Object, true, + new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToCircle)); - nativeTypeMapping.AddTypeAlias("date", typeof (NpgsqlDate)); + NativeTypeMapping.AddTypeAlias("circle", typeof(NpgsqlCircle)); - nativeTypeMapping.AddType("timetz", NpgsqlDbType.TimeTZ, DbType.Time, true, - new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToTimeTZ)); + NativeTypeMapping.AddType("inet", NpgsqlDbType.Inet, DbType.Object, true, + new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToIPAddress)); - nativeTypeMapping.AddTypeAlias("timetz", typeof (NpgsqlTimeTZ)); + NativeTypeMapping.AddTypeAlias("inet", typeof(IPAddress)); + NativeTypeMapping.AddTypeAlias("inet", typeof(NpgsqlInet)); + } + } - nativeTypeMapping.AddType("time", NpgsqlDbType.Time, DbType.Time, true, - new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToTime)); + ///<summary> + /// This method creates (or retrieves from cache) a mapping between type and OID + /// of all natively supported postgresql data types. + /// This is needed as from one version to another, this mapping can be changed and + /// so we avoid hardcoding them. + /// </summary> + /// <returns>NpgsqlTypeMapping containing all known data types. The mapping must be + /// cloned before it is modified because it is cached; changes made by one connection may + /// effect another connection.</returns> + public static NpgsqlBackendTypeMapping CreateAndLoadInitialTypesMapping(NpgsqlConnector conn) + { + NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "LoadTypesMapping"); - nativeTypeMapping.AddTypeAlias("time", typeof (NpgsqlTime)); + // [TODO] Verify another way to get higher concurrency. + lock(CLASSNAME) + { + // Check the cache for an initial types map. + NpgsqlBackendTypeMapping oidToNameMapping = (NpgsqlBackendTypeMapping) BackendTypeMappingCache[conn.ServerVersion]; - nativeTypeMapping.AddType("timestamp", NpgsqlDbType.Timestamp, DbType.DateTime, true, - new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToTimeStamp)); + if (oidToNameMapping != null) + { + return oidToNameMapping; + } - nativeTypeMapping.AddTypeAlias("timestamp", typeof (DateTime)); - nativeTypeMapping.AddTypeAlias("timestamp", typeof (NpgsqlTimeStamp)); + // Not in cache, create a new one. + oidToNameMapping = new NpgsqlBackendTypeMapping(); - nativeTypeMapping.AddType("timestamptz", NpgsqlDbType.TimestampTZ, DbType.DateTime, true, - new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToTimeStamp)); + // Create a list of all natively supported postgresql data types. + NpgsqlBackendTypeInfo[] TypeInfoList = new NpgsqlBackendTypeInfo[] + { + new NpgsqlBackendTypeInfo(0, "unknown", NpgsqlDbType.Text, DbType.String, typeof(String), + null), - nativeTypeMapping.AddTypeAlias("timestamptz", typeof (NpgsqlTimeStampTZ)); - - nativeTypeMapping.AddType("point", NpgsqlDbType.Point, DbType.Object, true, - new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToPoint)); - - nativeTypeMapping.AddTypeAlias("point", typeof (NpgsqlPoint)); - - nativeTypeMapping.AddType("box", NpgsqlDbType.Box, DbType.Object, true, - new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToBox)); - - nativeTypeMapping.AddTypeAlias("box", typeof (NpgsqlBox)); - - nativeTypeMapping.AddType("lseg", NpgsqlDbType.LSeg, DbType.Object, true, - new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToLSeg)); - - nativeTypeMapping.AddTypeAlias("lseg", typeof (NpgsqlLSeg)); - - nativeTypeMapping.AddType("path", NpgsqlDbType.Path, DbType.Object, true, - new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToPath)); - - nativeTypeMapping.AddTypeAlias("path", typeof (NpgsqlPath)); - - nativeTypeMapping.AddType("polygon", NpgsqlDbType.Polygon, DbType.Object, true, - new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToPolygon)); - - nativeTypeMapping.AddTypeAlias("polygon", typeof (NpgsqlPolygon)); - - nativeTypeMapping.AddType("circle", NpgsqlDbType.Circle, DbType.Object, true, - new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToCircle)); - - nativeTypeMapping.AddTypeAlias("circle", typeof (NpgsqlCircle)); - - nativeTypeMapping.AddType("inet", NpgsqlDbType.Inet, DbType.Object, true, - new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToIPAddress)); - - nativeTypeMapping.AddTypeAlias("inet", typeof (IPAddress)); - nativeTypeMapping.AddTypeAlias("inet", typeof (NpgsqlInet)); - - nativeTypeMapping.AddType("uuid", NpgsqlDbType.Uuid, DbType.Guid, true, null); - nativeTypeMapping.AddTypeAlias("uuid", typeof (Guid)); - - nativeTypeMapping.AddType("xml", NpgsqlDbType.Xml, DbType.Xml, true, null); - - nativeTypeMapping.AddType("interval", NpgsqlDbType.Interval, DbType.Object, false, - new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToInterval)); - - nativeTypeMapping.AddTypeAlias("interval", typeof (NpgsqlInterval)); - nativeTypeMapping.AddTypeAlias("interval", typeof (TimeSpan)); - - nativeTypeMapping.AddDbTypeAlias("text", DbType.Object); + new NpgsqlBackendTypeInfo(0, "refcursor", NpgsqlDbType.Refcursor, DbType.String, typeof(String), + null), - - return nativeTypeMapping; - } + new NpgsqlBackendTypeInfo(0, "char", NpgsqlDbType.Char, DbType.String, typeof(String), + null), + + new NpgsqlBackendTypeInfo(0, "bpchar", NpgsqlDbType.Text, DbType.String, typeof(String), + null), - private static IEnumerable<NpgsqlBackendTypeInfo> TypeInfoList(bool useExtendedTypes) - { - yield return new NpgsqlBackendTypeInfo(0, "oidvector", NpgsqlDbType.Text, DbType.String, typeof (String), null); + new NpgsqlBackendTypeInfo(0, "varchar", NpgsqlDbType.Varchar, DbType.String, typeof(String), + null), - yield return new NpgsqlBackendTypeInfo(0, "unknown", NpgsqlDbType.Text, DbType.String, typeof (String), null); + new NpgsqlBackendTypeInfo(0, "text", NpgsqlDbType.Text, DbType.String, typeof(String), + null), + + new NpgsqlBackendTypeInfo(0, "name", NpgsqlDbType.Text, DbType.String, typeof(String), + null), - yield return new NpgsqlBackendTypeInfo(0, "refcursor", NpgsqlDbType.Refcursor, DbType.String, typeof (String), null); + new NpgsqlBackendTypeInfo(0, "bytea", NpgsqlDbType.Bytea, DbType.Binary, typeof(Byte[]), + new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToBinary)), - yield return new NpgsqlBackendTypeInfo(0, "char", NpgsqlDbType.Char, DbType.String, typeof (String), null); + new NpgsqlBackendTypeInfo(0, "bit", NpgsqlDbType.Bit, DbType.Boolean, typeof(Boolean), + new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToBit)), - yield return new NpgsqlBackendTypeInfo(0, "bpchar", NpgsqlDbType.Text, DbType.String, typeof (String), null); + new NpgsqlBackendTypeInfo(0, "bool", NpgsqlDbType.Boolean, DbType.Boolean, typeof(Boolean), + new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToBoolean)), - yield return new NpgsqlBackendTypeInfo(0, "varchar", NpgsqlDbType.Varchar, DbType.String, typeof (String), null); - yield return new NpgsqlBackendTypeInfo(0, "text", NpgsqlDbType.Text, DbType.String, typeof (String), null); + new NpgsqlBackendTypeInfo(0, "int2", NpgsqlDbType.Smallint, DbType.Int16, typeof(Int16), + null), - yield return new NpgsqlBackendTypeInfo(0, "name", NpgsqlDbType.Text, DbType.String, typeof (String), null); + new NpgsqlBackendTypeInfo(0, "int4", NpgsqlDbType.Integer, DbType.Int32, typeof(Int32), + null), - yield return - new NpgsqlBackendTypeInfo(0, "bytea", NpgsqlDbType.Bytea, DbType.Binary, typeof (Byte[]), - new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToBinary)); + new NpgsqlBackendTypeInfo(0, "int8", NpgsqlDbType.Bigint, DbType.Int64, typeof(Int64), + null), - yield return - new NpgsqlBackendTypeInfo(0, "bit", NpgsqlDbType.Bit, DbType.Boolean, typeof (Boolean), - new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToBit)); + new NpgsqlBackendTypeInfo(0, "oid", NpgsqlDbType.Bigint, DbType.Int64, typeof(Int64), + null), - yield return - new NpgsqlBackendTypeInfo(0, "bool", NpgsqlDbType.Boolean, DbType.Boolean, typeof (Boolean), - new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToBoolean)); - yield return new NpgsqlBackendTypeInfo(0, "int2", NpgsqlDbType.Smallint, DbType.Int16, typeof (Int16), null); + new NpgsqlBackendTypeInfo(0, "float4", NpgsqlDbType.Real, DbType.Single, typeof(Single), + null), - yield return new NpgsqlBackendTypeInfo(0, "int4", NpgsqlDbType.Integer, DbType.Int32, typeof (Int32), null); + new NpgsqlBackendTypeInfo(0, "float8", NpgsqlDbType.Double, DbType.Double, typeof(Double), + null), - yield return new NpgsqlBackendTypeInfo(0, "int8", NpgsqlDbType.Bigint, DbType.Int64, typeof (Int64), null); + new NpgsqlBackendTypeInfo(0, "numeric", NpgsqlDbType.Numeric, DbType.Decimal, typeof(Decimal), + null), - yield return new NpgsqlBackendTypeInfo(0, "oid", NpgsqlDbType.Bigint, DbType.Int64, typeof (Int64), null); + new NpgsqlBackendTypeInfo(0, "inet", NpgsqlDbType.Inet, DbType.Object, typeof(NpgsqlInet), new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToInet)), - yield return new NpgsqlBackendTypeInfo(0, "float4", NpgsqlDbType.Real, DbType.Single, typeof (Single), null); + new NpgsqlBackendTypeInfo(0, "money", NpgsqlDbType.Money, DbType.Decimal, typeof(Decimal), + new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToMoney)), - yield return new NpgsqlBackendTypeInfo(0, "float8", NpgsqlDbType.Double, DbType.Double, typeof (Double), null); - yield return new NpgsqlBackendTypeInfo(0, "numeric", NpgsqlDbType.Numeric, DbType.Decimal, typeof (Decimal), null); + new NpgsqlBackendTypeInfo(0, "date", NpgsqlDbType.Date, DbType.Date, typeof(DateTime), + new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToDate)), - yield return - new NpgsqlBackendTypeInfo(0, "inet", NpgsqlDbType.Inet, DbType.Object, typeof (NpgsqlInet), - new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToInet)); + new NpgsqlBackendTypeInfo(0, "time", NpgsqlDbType.Time, DbType.Time, typeof(DateTime), + new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToTime)), - yield return - new NpgsqlBackendTypeInfo(0, "money", NpgsqlDbType.Money, DbType.Currency, typeof (Decimal), - new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToMoney)); + new NpgsqlBackendTypeInfo(0, "timetz", NpgsqlDbType.Time, DbType.Time, typeof(DateTime), + new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToTime)), - yield return - new NpgsqlBackendTypeInfo(0, "point", NpgsqlDbType.Point, DbType.Object, typeof (NpgsqlPoint), - new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToPoint)); + new NpgsqlBackendTypeInfo(0, "timestamp", NpgsqlDbType.Timestamp, DbType.DateTime, typeof(DateTime), + new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToDateTime)), - yield return - new NpgsqlBackendTypeInfo(0, "lseg", NpgsqlDbType.LSeg, DbType.Object, typeof (NpgsqlLSeg), - new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToLSeg)); + new NpgsqlBackendTypeInfo(0, "timestamptz", NpgsqlDbType.Timestamp, DbType.DateTime, typeof(DateTime), + new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToDateTime)), - yield return - new NpgsqlBackendTypeInfo(0, "path", NpgsqlDbType.Path, DbType.Object, typeof (NpgsqlPath), - new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToPath)); - yield return - new NpgsqlBackendTypeInfo(0, "box", NpgsqlDbType.Box, DbType.Object, typeof (NpgsqlBox), - new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToBox)); + new NpgsqlBackendTypeInfo(0, "point", NpgsqlDbType.Point, DbType.Object, typeof(NpgsqlPoint), + new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToPoint)), - yield return - new NpgsqlBackendTypeInfo(0, "circle", NpgsqlDbType.Circle, DbType.Object, typeof (NpgsqlCircle), - new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToCircle)); + new NpgsqlBackendTypeInfo(0, "lseg", NpgsqlDbType.LSeg, DbType.Object, typeof(NpgsqlLSeg), + new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToLSeg)), - yield return - new NpgsqlBackendTypeInfo(0, "polygon", NpgsqlDbType.Polygon, DbType.Object, typeof (NpgsqlPolygon), - new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToPolygon)); + new NpgsqlBackendTypeInfo(0, "path", NpgsqlDbType.Path, DbType.Object, typeof(NpgsqlPath), + new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToPath)), - yield return new NpgsqlBackendTypeInfo(0, "uuid", NpgsqlDbType.Uuid, DbType.Guid, typeof (Guid), new -ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToGuid)); + new NpgsqlBackendTypeInfo(0, "box", NpgsqlDbType.Box, DbType.Object, typeof(NpgsqlBox), + new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToBox)), - yield return new NpgsqlBackendTypeInfo(0, "xml", NpgsqlDbType.Xml, DbType.Xml, typeof (String), null); + new NpgsqlBackendTypeInfo(0, "circle", NpgsqlDbType.Circle, DbType.Object, typeof(NpgsqlCircle), + new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToCircle)), - yield return - new NpgsqlBackendTypeInfo(0, "interval", NpgsqlDbType.Interval, DbType.Object, typeof (NpgsqlInterval), - new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToInterval)); + new NpgsqlBackendTypeInfo(0, "polygon", NpgsqlDbType.Polygon, DbType.Object, typeof(NpgsqlPolygon), + new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToPolygon)), + }; - if (useExtendedTypes) - { - yield return - new NpgsqlBackendTypeInfo(0, "date", NpgsqlDbType.Date, DbType.Date, typeof (NpgsqlDate), - new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToDate)); + // Attempt to map each type info in the list to an OID on the backend and + // add each mapped type to the new type mapping object. + LoadTypesMappings(conn, oidToNameMapping, TypeInfoList); - yield return - new NpgsqlBackendTypeInfo(0, "time", NpgsqlDbType.Time, DbType.Time, typeof (NpgsqlTime), - new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToTime)); + // Add this mapping to the per-server-version cache so we don't have to + // do these expensive queries on every connection startup. + BackendTypeMappingCache.Add(conn.ServerVersion, oidToNameMapping); - yield return - new NpgsqlBackendTypeInfo(0, "timetz", NpgsqlDbType.TimeTZ, DbType.Time, typeof (NpgsqlTimeTZ), - new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToTimeTZ)); + return oidToNameMapping; + } - yield return - new NpgsqlBackendTypeInfo(0, "timestamp", NpgsqlDbType.Timestamp, DbType.DateTime, typeof (NpgsqlTimeStamp), - new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToTimeStamp)); - yield return - new NpgsqlBackendTypeInfo(0, "timestamptz", NpgsqlDbType.TimestampTZ, DbType.DateTime, typeof (NpgsqlTimeStampTZ), - new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToTimeStampTZ)); - } - else - { - yield return - new NpgsqlBackendTypeInfo(0, "date", NpgsqlDbType.Date, DbType.Date, typeof (DateTime), - new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToDate)); - - yield return - new NpgsqlBackendTypeInfo(0, "time", NpgsqlDbType.Time, DbType.Time, typeof (DateTime), - new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToTime)); - - yield return - new NpgsqlBackendTypeInfo(0, "timetz", NpgsqlDbType.TimeTZ, DbType.Time, typeof (DateTime), - new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToTime)); - - yield return - new NpgsqlBackendTypeInfo(0, "timestamp", NpgsqlDbType.Timestamp, DbType.DateTime, typeof (DateTime), - new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToDateTime)); - - yield return - new NpgsqlBackendTypeInfo(0, "timestamptz", NpgsqlDbType.TimestampTZ, DbType.DateTime, typeof (DateTime), - new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToDateTime)); - } - } - - ///<summary> - /// This method creates (or retrieves from cache) a mapping between type and OID - /// of all natively supported postgresql data types. - /// This is needed as from one version to another, this mapping can be changed and - /// so we avoid hardcoding them. - /// </summary> - /// <returns>NpgsqlTypeMapping containing all known data types. The mapping must be - /// cloned before it is modified because it is cached; changes made by one connection may - /// effect another connection.</returns> - public static NpgsqlBackendTypeMapping CreateAndLoadInitialTypesMapping(NpgsqlConnector conn) - { - NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "LoadTypesMapping"); - - // [TODO] Verify another way to get higher concurrency. - lock (CLASSNAME) - { - // Check the cache for an initial types map. - NpgsqlBackendTypeMapping oidToNameMapping = null; - - if ( - BackendTypeMappingCache.TryGetValue(new MappingKey(conn.ServerVersion, conn.UseExtendedTypes), out oidToNameMapping)) - { - return oidToNameMapping; - } - - // Not in cache, create a new one. - oidToNameMapping = new NpgsqlBackendTypeMapping(); - - // Create a list of all natively supported postgresql data types. - - // Attempt to map each type info in the list to an OID on the backend and - // add each mapped type to the new type mapping object. - LoadTypesMappings(conn, oidToNameMapping, TypeInfoList(conn.UseExtendedTypes)); - - // Add this mapping to the per-server-version cache so we don't have to - // do these expensive queries on every connection startup. - BackendTypeMappingCache.Add(new MappingKey(conn.ServerVersion, conn.UseExtendedTypes), oidToNameMapping); - - return oidToNameMapping; - } - } - - //Take a NpgsqlBackendTypeInfo for a type and return the NpgsqlBackendTypeInfo for - - //an array of that type. - - private static NpgsqlBackendTypeInfo ArrayTypeInfo(NpgsqlBackendTypeInfo elementInfo) - - { - return - new NpgsqlBackendTypeInfo(0, "_" + elementInfo.Name, NpgsqlDbType.Array | elementInfo.NpgsqlDbType, DbType.Object, - elementInfo.Type.MakeArrayType(), - new ConvertBackendToNativeHandler( - new ArrayBackendToNativeTypeConverter(elementInfo).ToArray)); - } - - - /// <summary> - /// Attempt to map types by issuing a query against pg_type. - /// This function takes a list of NpgsqlTypeInfo and attempts to resolve the OID field - /// of each by querying pg_type. If the mapping is found, the type info object is - /// updated (OID) and added to the provided NpgsqlTypeMapping object. - /// </summary> - /// <param name="conn">NpgsqlConnector to send query through.</param> - /// <param name="TypeMappings">Mapping object to add types too.</param> - /// <param name="TypeInfoList">List of types that need to have OID's mapped.</param> - public static void LoadTypesMappings(NpgsqlConnector conn, NpgsqlBackendTypeMapping TypeMappings, - IEnumerable<NpgsqlBackendTypeInfo> TypeInfoList) - { - StringBuilder InList = new StringBuilder(); - Dictionary<string, NpgsqlBackendTypeInfo> NameIndex = new Dictionary<string, NpgsqlBackendTypeInfo>(); - - // Build a clause for the SELECT statement. - // Build a name->typeinfo mapping so we can match the results of the query - // with the list of type objects efficiently. - foreach (NpgsqlBackendTypeInfo TypeInfo in TypeInfoList) - { - NameIndex.Add(TypeInfo.Name, TypeInfo); - InList.AppendFormat("{0}'{1}'", ((InList.Length > 0) ? ", " : ""), TypeInfo.Name); - - //do the same for the equivalent array type. - - NameIndex.Add("_" + TypeInfo.Name, ArrayTypeInfo(TypeInfo)); - - InList.Append(", '_").Append(TypeInfo.Name).Append('\''); - } - - if (InList.Length == 0) - { - return; - } - - using ( - NpgsqlCommand command = - new NpgsqlCommand(string.Format("SELECT typname, oid FROM pg_type WHERE typname IN ({0})", InList), conn)) - { - using (NpgsqlDataReader dr = command.GetReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) - { - while (dr.Read()) - { - NpgsqlBackendTypeInfo TypeInfo = NameIndex[dr[0].ToString()]; - - TypeInfo._OID = Convert.ToInt32(dr[1]); - - TypeMappings.AddType(TypeInfo); - } - } - } - } - } - - /// <summary> - /// Delegate called to convert the given backend data to its native representation. - /// </summary> - internal delegate Object ConvertBackendToNativeHandler( - NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier); - - /// <summary> - /// Delegate called to convert the given native data to its backand representation. - /// </summary> - internal delegate String ConvertNativeToBackendHandler(NpgsqlNativeTypeInfo TypeInfo, Object NativeData); - - /// <summary> - /// Represents a backend data type. - /// This class can be called upon to convert a backend field representation to a native object. - /// </summary> - internal class NpgsqlBackendTypeInfo - { - private readonly ConvertBackendToNativeHandler _ConvertBackendToNative; - - internal Int32 _OID; - private readonly String _Name; - private readonly NpgsqlDbType _NpgsqlDbType; - private readonly DbType _DbType; - private readonly Type _Type; - - - /// <summary> - /// Construct a new NpgsqlTypeInfo with the given attributes and conversion handlers. - /// </summary> - /// <param name="OID">Type OID provided by the backend server.</param> - /// <param name="Name">Type name provided by the backend server.</param> - /// <param name="NpgsqlDbType">NpgsqlDbType</param> - /// <param name="Type">System type to convert fields of this type to.</param> - /// <param name="ConvertBackendToNative">Data conversion handler.</param> - public NpgsqlBackendTypeInfo(Int32 OID, String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Type Type, - ConvertBackendToNativeHandler ConvertBackendToNative) - { - if (Type == null) - throw new ArgumentNullException("Type"); - _OID = OID; - _Name = Name; - _NpgsqlDbType = NpgsqlDbType; - _DbType = DbType; - _Type = Type; - _ConvertBackendToNative = ConvertBackendToNative; - } - - /// <summary> - /// Type OID provided by the backend server. - /// </summary> - public Int32 OID - { - get { return _OID; } - } - - /// <summary> - /// Type name provided by the backend server. - /// </summary> - public String Name - { - get { return _Name; } - } - - /// <summary> - /// NpgsqlDbType. - /// </summary> - public NpgsqlDbType NpgsqlDbType - { - get { return _NpgsqlDbType; } - } - - /// <summary> - /// NpgsqlDbType. - /// </summary> - public DbType DbType - { - get { return _DbType; } - } - - /// <summary> - /// System type to convert fields of this type to. - /// </summary> - public Type Type - { - get { return _Type; } - } - - /// <summary> - /// Perform a data conversion from a backend representation to - /// a native object. - /// </summary> - /// <param name="BackendData">Data sent from the backend.</param> - /// <param name="TypeModifier">Type modifier field sent from the backend.</param> - public Object ConvertToNative(String BackendData, Int16 TypeSize, Int32 TypeModifier) - { - if (_ConvertBackendToNative != null) - { - return _ConvertBackendToNative(this, BackendData, TypeSize, TypeModifier); - } - else - { - try - { - return Convert.ChangeType(BackendData, Type, CultureInfo.InvariantCulture); - } - catch - { - return BackendData; - } - } - } - } - - /// <summary> - /// Represents a backend data type. - /// This class can be called upon to convert a native object to its backend field representation, - /// </summary> - internal class NpgsqlNativeTypeInfo - { - private static readonly NumberFormatInfo ni; - - private readonly ConvertNativeToBackendHandler _ConvertNativeToBackend; - - private readonly String _Name; - private readonly string _CastName; - private readonly NpgsqlDbType _NpgsqlDbType; - private readonly DbType _DbType; - private readonly Boolean _Quote; - private readonly Boolean _UseSize; - private Boolean _IsArray = false; - - /// <summary> - /// Returns an NpgsqlNativeTypeInfo for an array where the elements are of the type - /// described by the NpgsqlNativeTypeInfo supplied. - /// </summary> - public static NpgsqlNativeTypeInfo ArrayOf(NpgsqlNativeTypeInfo elementType) - - { - if (elementType._IsArray) - //we've an array of arrays. It's the inner most elements whose type we care about, so the type we have is fine. - { - return elementType; - } - - NpgsqlNativeTypeInfo copy = - new NpgsqlNativeTypeInfo("_" + elementType.Name, NpgsqlDbType.Array | elementType.NpgsqlDbType, elementType.DbType, - false, - new ConvertNativeToBackendHandler( - new ArrayNativeToBackendTypeConverter(elementType).FromArray)); - - copy._IsArray = true; - - return copy; - } - - - static NpgsqlNativeTypeInfo() - { - ni = (NumberFormatInfo) CultureInfo.InvariantCulture.NumberFormat.Clone(); - ni.NumberDecimalDigits = 15; - } - - /// <summary> - /// Construct a new NpgsqlTypeInfo with the given attributes and conversion handlers. - /// </summary> - /// <param name="Name">Type name provided by the backend server.</param> - /// <param name="NpgsqlDbType">NpgsqlDbType</param> - /// <param name="ConvertNativeToBackend">Data conversion handler.</param> - public NpgsqlNativeTypeInfo(String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Boolean Quote, - ConvertNativeToBackendHandler ConvertNativeToBackend) - { - _Name = Name; - _CastName = Name.StartsWith("_") ? Name.Substring(1) + "[]" : Name; - _NpgsqlDbType = NpgsqlDbType; - _DbType = DbType; - _Quote = Quote; - _ConvertNativeToBackend = ConvertNativeToBackend; - - - // The only parameters types which use length currently supported are char and varchar. Check for them. - - if ((NpgsqlDbType == NpgsqlDbType.Char) || (NpgsqlDbType == NpgsqlDbType.Varchar)) - { - _UseSize = true; - } - else - { - _UseSize = false; - } - } - - /// <summary> - /// Type name provided by the backend server. - /// </summary> - public String Name - { - get { return _Name; } - } - - public string CastName - - { - get { return _CastName; } - } - - public bool IsArray - - { - get { return _IsArray; } - } - - /// <summary> - /// NpgsqlDbType. - /// </summary> - public NpgsqlDbType NpgsqlDbType - { - get { return _NpgsqlDbType; } - } - - /// <summary> - /// DbType. - /// </summary> - public DbType DbType - { - get { return _DbType; } - } - - - /// <summary> - /// Apply quoting. - /// </summary> - public Boolean Quote - { - get { return _Quote; } - } - - /// <summary> - /// Use parameter size information. - /// </summary> - public Boolean UseSize - { - get { return _UseSize; } - } - - - /// <summary> - /// Perform a data conversion from a native object to - /// a backend representation. - /// DBNull and null values are handled differently depending if a plain query is used - /// When - /// </summary> - /// <param name="NativeData">Native .NET object to be converted.</param> - /// <param name="ForExtendedQuery">Flag indicating if the conversion has to be done for + } + + /// <summary> + /// Attempt to map types by issuing a query against pg_type. + /// This function takes a list of NpgsqlTypeInfo and attempts to resolve the OID field + /// of each by querying pg_type. If the mapping is found, the type info object is + /// updated (OID) and added to the provided NpgsqlTypeMapping object. + /// </summary> + /// <param name="conn">NpgsqlConnector to send query through.</param> + /// <param name="TypeMappings">Mapping object to add types too.</param> + /// <param name="TypeInfoList">List of types that need to have OID's mapped.</param> + public static void LoadTypesMappings(NpgsqlConnector conn, NpgsqlBackendTypeMapping TypeMappings, IList TypeInfoList) + { + StringBuilder InList = new StringBuilder(); + Hashtable NameIndex = new Hashtable(); + + // Build a clause for the SELECT statement. + // Build a name->typeinfo mapping so we can match the results of the query + /// with the list of type objects efficiently. + foreach (NpgsqlBackendTypeInfo TypeInfo in TypeInfoList) { + NameIndex.Add(TypeInfo.Name, TypeInfo); + InList.AppendFormat("{0}'{1}'", ((InList.Length > 0) ? ", " : ""), TypeInfo.Name); + } + + if (InList.Length == 0) { + return; + } + + NpgsqlCommand command = new NpgsqlCommand("SELECT oid, typname FROM pg_type WHERE typname IN (" + InList.ToString() + ")", conn); + NpgsqlDataReader dr = command.ExecuteReader(); + + while (dr.Read()) { + NpgsqlBackendTypeInfo TypeInfo = (NpgsqlBackendTypeInfo)NameIndex[dr[1].ToString()]; + + TypeInfo._OID = Convert.ToInt32(dr[0]); + + TypeMappings.AddType(TypeInfo); + } + } + } + + /// <summary> + /// Delegate called to convert the given backend data to its native representation. + /// </summary> + internal delegate Object ConvertBackendToNativeHandler(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier); + /// <summary> + /// Delegate called to convert the given native data to its backand representation. + /// </summary> + internal delegate String ConvertNativeToBackendHandler(NpgsqlNativeTypeInfo TypeInfo, Object NativeData); + + /// <summary> + /// Represents a backend data type. + /// This class can be called upon to convert a backend field representation to a native object. + /// </summary> + internal class NpgsqlBackendTypeInfo + { + private ConvertBackendToNativeHandler _ConvertBackendToNative; + + internal Int32 _OID; + private String _Name; + private NpgsqlDbType _NpgsqlDbType; + private DbType _DbType; + private Type _Type; + + /// <summary> + /// Construct a new NpgsqlTypeInfo with the given attributes and conversion handlers. + /// </summary> + /// <param name="OID">Type OID provided by the backend server.</param> + /// <param name="Name">Type name provided by the backend server.</param> + /// <param name="NpgsqlDbType">NpgsqlDbType</param> + /// <param name="Type">System type to convert fields of this type to.</param> + /// <param name="ConvertBackendToNative">Data conversion handler.</param> + public NpgsqlBackendTypeInfo(Int32 OID, String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Type Type, ConvertBackendToNativeHandler ConvertBackendToNative) + { + _OID = OID; + _Name = Name; + _NpgsqlDbType = NpgsqlDbType; + _DbType = DbType; + _Type = Type; + _ConvertBackendToNative = ConvertBackendToNative; + } + + /// <summary> + /// Type OID provided by the backend server. + /// </summary> + public Int32 OID + { + get { return _OID; } + } + + /// <summary> + /// Type name provided by the backend server. + /// </summary> + public String Name + { get { return _Name; } } + + /// <summary> + /// NpgsqlDbType. + /// </summary> + public NpgsqlDbType NpgsqlDbType + { get { return _NpgsqlDbType; } } + + /// <summary> + /// NpgsqlDbType. + /// </summary> + public DbType DbType + { get { return _DbType; } } + + /// <summary> + /// System type to convert fields of this type to. + /// </summary> + public Type Type + { get { return _Type; } } + + /// <summary> + /// Perform a data conversion from a backend representation to + /// a native object. + /// </summary> + /// <param name="BackendData">Data sent from the backend.</param> + /// <param name="TypeModifier">Type modifier field sent from the backend.</param> + public Object ConvertToNative(String BackendData, Int16 TypeSize, Int32 TypeModifier) + { + if (_ConvertBackendToNative != null) { + return _ConvertBackendToNative(this, BackendData, TypeSize, TypeModifier); + } else { + try { + return Convert.ChangeType(BackendData, Type, CultureInfo.InvariantCulture); + } catch { + return BackendData; + } + } + } + } + + /// <summary> + /// Represents a backend data type. + /// This class can be called upon to convert a native object to its backend field representation, + /// </summary> + internal class NpgsqlNativeTypeInfo + { + private static NumberFormatInfo ni; + + private ConvertNativeToBackendHandler _ConvertNativeToBackend; + + private String _Name; + private NpgsqlDbType _NpgsqlDbType; + private DbType _DbType; + private Boolean _Quote; + private Boolean _UseSize; + + static NpgsqlNativeTypeInfo() + { + ni = (NumberFormatInfo) CultureInfo.InvariantCulture.NumberFormat.Clone(); + ni.NumberDecimalDigits = 15; + } + + /// <summary> + /// Construct a new NpgsqlTypeInfo with the given attributes and conversion handlers. + /// </summary> + /// <param name="OID">Type OID provided by the backend server.</param> + /// <param name="Name">Type name provided by the backend server.</param> + /// <param name="NpgsqlDbType">NpgsqlDbType</param> + /// <param name="Type">System type to convert fields of this type to.</param> + /// <param name="ConvertBackendToNative">Data conversion handler.</param> + /// <param name="ConvertNativeToBackend">Data conversion handler.</param> + public NpgsqlNativeTypeInfo(String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Boolean Quote, ConvertNativeToBackendHandler ConvertNativeToBackend) + { + _Name = Name; + _NpgsqlDbType = NpgsqlDbType; + _DbType = DbType; + _Quote = Quote; + _ConvertNativeToBackend = ConvertNativeToBackend; + + + // The only parameters types which use length currently supported are char and varchar. Check for them. + + if ( (NpgsqlDbType == NpgsqlDbType.Char) + || (NpgsqlDbType == NpgsqlDbType.Varchar)) + + _UseSize = true; + else + _UseSize = false; + } + + /// <summary> + /// Type name provided by the backend server. + /// </summary> + public String Name + { get { return _Name; } } + + /// <summary> + /// NpgsqlDbType. + /// </summary> + public NpgsqlDbType NpgsqlDbType + { get { return _NpgsqlDbType; } } + + /// <summary> + /// DbType. + /// </summary> + public DbType DbType + { get { return _DbType; } } + + + /// <summary> + /// Apply quoting. + /// </summary> + public Boolean Quote + { get { return _Quote; } } + + /// <summary> + /// Use parameter size information. + /// </summary> + public Boolean UseSize + { get { return _UseSize; } } + + + /// <summary> + /// Perform a data conversion from a native object to + /// a backend representation. + /// DBNull and null values are handled differently depending if a plain query is used + /// When + /// </summary> + /// <param name="NativeData">Native .NET object to be converted.</param> + /// <param name="ForExtendedQuery">Flag indicating if the conversion has to be done for /// plain queries or extended queries</param> - public String ConvertToBackend(Object NativeData, Boolean ForExtendedQuery) - { - if (ForExtendedQuery) - { - return ConvertToBackendExtendedQuery(NativeData); - } - else - { - return ConvertToBackendPlainQuery(NativeData); - } - } - - private String ConvertToBackendPlainQuery(Object NativeData) - { - if ((NativeData == DBNull.Value) || (NativeData == null)) - { - return "NULL"; // Plain queries exptects null values as string NULL. - } - - if (_ConvertNativeToBackend != null) - { - return - (this.Quote ? QuoteString(_ConvertNativeToBackend(this, NativeData)) : _ConvertNativeToBackend(this, NativeData)); - } - else - { - if (NativeData is Enum) - { - // Do a special handling of Enum values. - // Translate enum value to its underlying type. - return - QuoteString( - (String) - Convert.ChangeType(Enum.Format(NativeData.GetType(), NativeData, "d"), typeof (String), - CultureInfo.InvariantCulture)); - } - else if (NativeData is IFormattable) - { - return - (this.Quote - ? QuoteString(((IFormattable) NativeData).ToString(null, ni).Replace("'", "''").Replace("\\", "\\\\")) - : ((IFormattable) NativeData).ToString(null, ni).Replace("'", "''").Replace("\\", "\\\\")); - } - - // Do special handling of strings when in simple query. Escape quotes and backslashes. - return - (this.Quote - ? QuoteString(NativeData.ToString().Replace("'", "''").Replace("\\", "\\\\").Replace("\0", "\\0")) - : NativeData.ToString().Replace("'", "''").Replace("\\", "\\\\").Replace("\0", "\\0")); - } - } - - private String ConvertToBackendExtendedQuery(Object NativeData) - { - if ((NativeData == DBNull.Value) || (NativeData == null)) - { - return null; // Extended query expects null values be represented as null. - } - - if (_ConvertNativeToBackend != null) - { - return _ConvertNativeToBackend(this, NativeData); - } - else - { - if (NativeData is Enum) - { - // Do a special handling of Enum values. - // Translate enum value to its underlying type. - return - (String) - Convert.ChangeType(Enum.Format(NativeData.GetType(), NativeData, "d"), typeof (String), - CultureInfo.InvariantCulture); - } - else if (NativeData is IFormattable) - { - return ((IFormattable) NativeData).ToString(null, ni); - } - - return NativeData.ToString(); - } - } - - internal static String QuoteString(String S) - { - return String.Format("'{0}'", S); - } - } - - /// <summary> - /// Provide mapping between type OID, type name, and a NpgsqlBackendTypeInfo object that represents it. - /// </summary> - internal class NpgsqlBackendTypeMapping - { - private readonly Dictionary<int, NpgsqlBackendTypeInfo> OIDIndex; - private readonly Dictionary<string, NpgsqlBackendTypeInfo> NameIndex; - - /// <summary> - /// Construct an empty mapping. - /// </summary> - public NpgsqlBackendTypeMapping() - { - OIDIndex = new Dictionary<int, NpgsqlBackendTypeInfo>(); - NameIndex = new Dictionary<string, NpgsqlBackendTypeInfo>(); - } - - /// <summary> - /// Copy constuctor. - /// </summary> - private NpgsqlBackendTypeMapping(NpgsqlBackendTypeMapping Other) - { - OIDIndex = new Dictionary<int, NpgsqlBackendTypeInfo>(Other.OIDIndex); - NameIndex = new Dictionary<string, NpgsqlBackendTypeInfo>(Other.NameIndex); - } - - /// <summary> - /// Add the given NpgsqlBackendTypeInfo to this mapping. - /// </summary> - public void AddType(NpgsqlBackendTypeInfo T) - { - if (OIDIndex.ContainsKey(T.OID)) - { - throw new Exception("Type already mapped"); - } - - OIDIndex[T.OID] = T; - NameIndex[T.Name] = T; - } - - /// <summary> - /// Add a new NpgsqlBackendTypeInfo with the given attributes and conversion handlers to this mapping. - /// </summary> - /// <param name="OID">Type OID provided by the backend server.</param> - /// <param name="Name">Type name provided by the backend server.</param> - /// <param name="NpgsqlDbType">NpgsqlDbType</param> - /// <param name="Type">System type to convert fields of this type to.</param> - /// <param name="BackendConvert">Data conversion handler.</param> - public void AddType(Int32 OID, String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Type Type, - ConvertBackendToNativeHandler BackendConvert) - { - AddType(new NpgsqlBackendTypeInfo(OID, Name, NpgsqlDbType, DbType, Type, BackendConvert)); - } - - /// <summary> - /// Get the number of type infos held. - /// </summary> - public Int32 Count - { - get { return NameIndex.Count; } - } - - public bool TryGetValue(int oid, out NpgsqlBackendTypeInfo value) - { - return OIDIndex.TryGetValue(oid, out value); - } - - /// <summary> - /// Retrieve the NpgsqlBackendTypeInfo with the given backend type OID, or null if none found. - /// </summary> - public NpgsqlBackendTypeInfo this[Int32 OID] - { - get - { - NpgsqlBackendTypeInfo ret = null; - return TryGetValue(OID, out ret) ? ret : null; - } - } - - /// <summary> - /// Retrieve the NpgsqlBackendTypeInfo with the given backend type name, or null if none found. - /// </summary> - public NpgsqlBackendTypeInfo this[String Name] - { - get - { - NpgsqlBackendTypeInfo ret = null; - return NameIndex.TryGetValue(Name, out ret) ? ret : null; - } - } - - /// <summary> - /// Make a shallow copy of this type mapping. - /// </summary> - public NpgsqlBackendTypeMapping Clone() - { - return new NpgsqlBackendTypeMapping(this); - } - - /// <summary> - /// Determine if a NpgsqlBackendTypeInfo with the given backend type OID exists in this mapping. - /// </summary> - public Boolean ContainsOID(Int32 OID) - { - return OIDIndex.ContainsKey(OID); - } - - /// <summary> - /// Determine if a NpgsqlBackendTypeInfo with the given backend type name exists in this mapping. - /// </summary> - public Boolean ContainsName(String Name) - { - return NameIndex.ContainsKey(Name); - } - } - - - /// <summary> - /// Provide mapping between type Type, NpgsqlDbType and a NpgsqlNativeTypeInfo object that represents it. - /// </summary> - internal class NpgsqlNativeTypeMapping - { - private readonly Dictionary<string, NpgsqlNativeTypeInfo> NameIndex = new Dictionary<string, NpgsqlNativeTypeInfo>(); - - private readonly Dictionary<NpgsqlDbType, NpgsqlNativeTypeInfo> NpgsqlDbTypeIndex = - new Dictionary<NpgsqlDbType, NpgsqlNativeTypeInfo>(); - - private readonly Dictionary<DbType, NpgsqlNativeTypeInfo> DbTypeIndex = new Dictionary<DbType, NpgsqlNativeTypeInfo>(); - private readonly Dictionary<Type, NpgsqlNativeTypeInfo> TypeIndex = new Dictionary<Type, NpgsqlNativeTypeInfo>(); - - /// <summary> - /// Add the given NpgsqlNativeTypeInfo to this mapping. - /// </summary> - public void AddType(NpgsqlNativeTypeInfo T) - { - if (NameIndex.ContainsKey(T.Name)) - { - throw new Exception("Type already mapped"); - } - - NameIndex[T.Name] = T; - NpgsqlDbTypeIndex[T.NpgsqlDbType] = T; - DbTypeIndex[T.DbType] = T; - if (!T.IsArray) - - { - NpgsqlNativeTypeInfo arrayType = NpgsqlNativeTypeInfo.ArrayOf(T); - NameIndex[arrayType.Name] = arrayType; - - NameIndex[arrayType.CastName] = arrayType; - NpgsqlDbTypeIndex[arrayType.NpgsqlDbType] = arrayType; - } - } - - /// <summary> - /// Add a new NpgsqlNativeTypeInfo with the given attributes and conversion handlers to this mapping. - /// </summary> - /// <param name="Name">Type name provided by the backend server.</param> - /// <param name="NpgsqlDbType">NpgsqlDbType</param> - /// <param name="NativeConvert">Data conversion handler.</param> - public void AddType(String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Boolean Quote, - ConvertNativeToBackendHandler NativeConvert) - { - AddType(new NpgsqlNativeTypeInfo(Name, NpgsqlDbType, DbType, Quote, NativeConvert)); - } - - public void AddNpgsqlDbTypeAlias(String Name, NpgsqlDbType NpgsqlDbType) - { - if (NpgsqlDbTypeIndex.ContainsKey(NpgsqlDbType)) - { - throw new Exception("NpgsqlDbType already aliased"); - } - - NpgsqlDbTypeIndex[NpgsqlDbType] = NameIndex[Name]; - } - - public void AddDbTypeAlias(String Name, DbType DbType) - { - /*if (DbTypeIndex.ContainsKey(DbType)) - { - throw new Exception("DbType already aliased"); - }*/ - - DbTypeIndex[DbType] = NameIndex[Name]; - } - - public void AddTypeAlias(String Name, Type Type) - { - if (TypeIndex.ContainsKey(Type)) - { - throw new Exception("Type already aliased"); - } - - TypeIndex[Type] = NameIndex[Name]; - } - - /// <summary> - /// Get the number of type infos held. - /// </summary> - public Int32 Count - { - get { return NameIndex.Count; } - } - - public bool TryGetValue(string name, out NpgsqlNativeTypeInfo typeInfo) - { - return NameIndex.TryGetValue(name, out typeInfo); - } - - /// <summary> - /// Retrieve the NpgsqlNativeTypeInfo with the given NpgsqlDbType. - /// </summary> - public bool TryGetValue(NpgsqlDbType dbType, out NpgsqlNativeTypeInfo typeInfo) - { - return NpgsqlDbTypeIndex.TryGetValue(dbType, out typeInfo); - } - - /// <summary> - /// Retrieve the NpgsqlNativeTypeInfo with the given DbType. - /// </summary> - public bool TryGetValue(DbType dbType, out NpgsqlNativeTypeInfo typeInfo) - { - return DbTypeIndex.TryGetValue(dbType, out typeInfo); - } - - - /// <summary> - /// Retrieve the NpgsqlNativeTypeInfo with the given Type. - /// </summary> - public bool TryGetValue(Type type, out NpgsqlNativeTypeInfo typeInfo) - { - return TypeIndex.TryGetValue(type, out typeInfo); - } - - /// <summary> - /// Determine if a NpgsqlNativeTypeInfo with the given backend type name exists in this mapping. - /// </summary> - public Boolean ContainsName(String Name) - { - return NameIndex.ContainsKey(Name); - } - - /// <summary> - /// Determine if a NpgsqlNativeTypeInfo with the given NpgsqlDbType exists in this mapping. - /// </summary> - public Boolean ContainsNpgsqlDbType(NpgsqlDbType NpgsqlDbType) - { - return NpgsqlDbTypeIndex.ContainsKey(NpgsqlDbType); - } - - /// <summary> - /// Determine if a NpgsqlNativeTypeInfo with the given Type name exists in this mapping. - /// </summary> - public Boolean ContainsType(Type Type) - { - return TypeIndex.ContainsKey(Type); - } - } -}
\ No newline at end of file + public String ConvertToBackend(Object NativeData, Boolean ForExtendedQuery) + { + if (ForExtendedQuery) + return ConvertToBackendExtendedQuery(NativeData); + else + return ConvertToBackendPlainQuery(NativeData); + + } + + private String ConvertToBackendPlainQuery(Object NativeData) + { + if ((NativeData == DBNull.Value) || (NativeData == null)) + return "NULL"; // Plain queries exptects null values as string NULL. + + if (_ConvertNativeToBackend != null) + return (this.Quote ? QuoteString(_ConvertNativeToBackend(this, NativeData)) : _ConvertNativeToBackend(this, NativeData)); + else + { + + + if (NativeData is System.Enum) + { + // Do a special handling of Enum values. + // Translate enum value to its underlying type. + return QuoteString((String)Convert.ChangeType(Enum.Format(NativeData.GetType(), NativeData, "d"), typeof(String), CultureInfo.InvariantCulture)); + } + else if (NativeData is IFormattable) + { + return (this.Quote ? QuoteString(((IFormattable) NativeData).ToString(null, ni).Replace("'", "''").Replace("\\", "\\\\")) : + ((IFormattable) NativeData).ToString(null, ni).Replace("'", "''").Replace("\\", "\\\\")); + + } + + // Do special handling of strings when in simple query. Escape quotes and backslashes. + return (this.Quote ? QuoteString(NativeData.ToString().Replace("'", "''").Replace("\\", "\\\\").Replace("\0", "\\0")) : + NativeData.ToString().Replace("'", "''").Replace("\\", "\\\\").Replace("\0", "\\0")); + + } + + } + + private String ConvertToBackendExtendedQuery(Object NativeData) + { + if ((NativeData == DBNull.Value) || (NativeData == null)) + return null; // Extended query expects null values be represented as null. + + if (_ConvertNativeToBackend != null) + return _ConvertNativeToBackend(this, NativeData); + else + { + if (NativeData is System.Enum) + { + // Do a special handling of Enum values. + // Translate enum value to its underlying type. + return (String)Convert.ChangeType(Enum.Format(NativeData.GetType(), NativeData, "d"), typeof(String), CultureInfo.InvariantCulture); + } + else if (NativeData is IFormattable) + { + return ((IFormattable)NativeData).ToString(null, ni); + + } + + return NativeData.ToString(); + + } + + } + + private static String QuoteString(String S) + { + return String.Format("E'{0}'", S); + + } + } + + /// <summary> + /// Provide mapping between type OID, type name, and a NpgsqlBackendTypeInfo object that represents it. + /// </summary> + internal class NpgsqlBackendTypeMapping + { + private Hashtable OIDIndex; + private Hashtable NameIndex; + + /// <summary> + /// Construct an empty mapping. + /// </summary> + public NpgsqlBackendTypeMapping() + { + OIDIndex = new Hashtable(); + NameIndex = new Hashtable(); + } + + /// <summary> + /// Copy constuctor. + /// </summary> + private NpgsqlBackendTypeMapping(NpgsqlBackendTypeMapping Other) + { + OIDIndex = (Hashtable)Other.OIDIndex.Clone(); + NameIndex = (Hashtable)Other.NameIndex.Clone(); + } + + /// <summary> + /// Add the given NpgsqlBackendTypeInfo to this mapping. + /// </summary> + public void AddType(NpgsqlBackendTypeInfo T) + { + if (OIDIndex.Contains(T.OID)) { + throw new Exception("Type already mapped"); + } + + OIDIndex[T.OID] = T; + NameIndex[T.Name] = T; + } + + /// <summary> + /// Add a new NpgsqlBackendTypeInfo with the given attributes and conversion handlers to this mapping. + /// </summary> + /// <param name="OID">Type OID provided by the backend server.</param> + /// <param name="Name">Type name provided by the backend server.</param> + /// <param name="NpgsqlDbType">NpgsqlDbType</param> + /// <param name="Type">System type to convert fields of this type to.</param> + /// <param name="ConvertBackendToNative">Data conversion handler.</param> + public void AddType(Int32 OID, String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Type Type, + ConvertBackendToNativeHandler BackendConvert) + { + AddType(new NpgsqlBackendTypeInfo(OID, Name, NpgsqlDbType, DbType, Type, BackendConvert)); + } + + /// <summary> + /// Get the number of type infos held. + /// </summary> + public Int32 Count + { get { return NameIndex.Count; } } + + /// <summary> + /// Retrieve the NpgsqlBackendTypeInfo with the given backend type OID, or null if none found. + /// </summary> + public NpgsqlBackendTypeInfo this [Int32 OID] + { + get + { + return (NpgsqlBackendTypeInfo)OIDIndex[OID]; + } + } + + /// <summary> + /// Retrieve the NpgsqlBackendTypeInfo with the given backend type name, or null if none found. + /// </summary> + public NpgsqlBackendTypeInfo this [String Name] + { + get + { + return (NpgsqlBackendTypeInfo)NameIndex[Name]; + } + } + + /// <summary> + /// Make a shallow copy of this type mapping. + /// </summary> + public NpgsqlBackendTypeMapping Clone() + { + return new NpgsqlBackendTypeMapping(this); + } + + /// <summary> + /// Determine if a NpgsqlBackendTypeInfo with the given backend type OID exists in this mapping. + /// </summary> + public Boolean ContainsOID(Int32 OID) + { + return OIDIndex.ContainsKey(OID); + } + + /// <summary> + /// Determine if a NpgsqlBackendTypeInfo with the given backend type name exists in this mapping. + /// </summary> + public Boolean ContainsName(String Name) + { + return NameIndex.ContainsKey(Name); + } + } + + + + /// <summary> + /// Provide mapping between type Type, NpgsqlDbType and a NpgsqlNativeTypeInfo object that represents it. + /// </summary> + internal class NpgsqlNativeTypeMapping + { + private Hashtable NameIndex; + private Hashtable NpgsqlDbTypeIndex; + private Hashtable DbTypeIndex; + private Hashtable TypeIndex; + + /// <summary> + /// Construct an empty mapping. + /// </summary> + public NpgsqlNativeTypeMapping() + { + NameIndex = new Hashtable(); + NpgsqlDbTypeIndex = new Hashtable(); + DbTypeIndex = new Hashtable(); + TypeIndex = new Hashtable(); + } + + /// <summary> + /// Add the given NpgsqlNativeTypeInfo to this mapping. + /// </summary> + public void AddType(NpgsqlNativeTypeInfo T) + { + if (NameIndex.Contains(T.Name)) { + throw new Exception("Type already mapped"); + } + + NameIndex[T.Name] = T; + NpgsqlDbTypeIndex[T.NpgsqlDbType] = T; + DbTypeIndex[T.DbType] = T; + } + + /// <summary> + /// Add a new NpgsqlNativeTypeInfo with the given attributes and conversion handlers to this mapping. + /// </summary> + /// <param name="OID">Type OID provided by the backend server.</param> + /// <param name="Name">Type name provided by the backend server.</param> + /// <param name="NpgsqlDbType">NpgsqlDbType</param> + /// <param name="ConvertBackendToNative">Data conversion handler.</param> + /// <param name="ConvertNativeToBackend">Data conversion handler.</param> + public void AddType(String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Boolean Quote, + ConvertNativeToBackendHandler NativeConvert) + { + AddType(new NpgsqlNativeTypeInfo(Name, NpgsqlDbType, DbType, Quote, NativeConvert)); + } + + public void AddNpgsqlDbTypeAlias(String Name, NpgsqlDbType NpgsqlDbType) + { + if (NpgsqlDbTypeIndex.Contains(NpgsqlDbType)) { + throw new Exception("NpgsqlDbType already aliased"); + } + + NpgsqlDbTypeIndex[NpgsqlDbType] = NameIndex[Name]; + } + + public void AddDbTypeAlias(String Name, DbType DbType) + { + if (DbTypeIndex.Contains(DbType)) { + throw new Exception("NpgsqlDbType already aliased"); + } + + DbTypeIndex[DbType] = NameIndex[Name]; + } + + public void AddTypeAlias(String Name, Type Type) + { + if (TypeIndex.Contains(Type)) { + throw new Exception("Type already aliased"); + } + + TypeIndex[Type] = NameIndex[Name]; + } + + /// <summary> + /// Get the number of type infos held. + /// </summary> + public Int32 Count + { get { return NameIndex.Count; } } + + /// <summary> + /// Retrieve the NpgsqlNativeTypeInfo with the given backend type name, or null if none found. + /// </summary> + public NpgsqlNativeTypeInfo this [String Name] + { + get + { + return (NpgsqlNativeTypeInfo)NameIndex[Name]; + } + } + + /// <summary> + /// Retrieve the NpgsqlNativeTypeInfo with the given NpgsqlDbType, or null if none found. + /// </summary> + public NpgsqlNativeTypeInfo this [NpgsqlDbType NpgsqlDbType] + { + get + { + return (NpgsqlNativeTypeInfo)NpgsqlDbTypeIndex[NpgsqlDbType]; + } + } + + /// <summary> + /// Retrieve the NpgsqlNativeTypeInfo with the given DbType, or null if none found. + /// </summary> + public NpgsqlNativeTypeInfo this [DbType DbType] + { + get + { + return (NpgsqlNativeTypeInfo)DbTypeIndex[DbType]; + } + } + + + + /// <summary> + /// Retrieve the NpgsqlNativeTypeInfo with the given Type, or null if none found. + /// </summary> + public NpgsqlNativeTypeInfo this [Type Type] + { + get + { + return (NpgsqlNativeTypeInfo)TypeIndex[Type]; + } + } + + /// <summary> + /// Determine if a NpgsqlNativeTypeInfo with the given backend type name exists in this mapping. + /// </summary> + public Boolean ContainsName(String Name) + { + return NameIndex.ContainsKey(Name); + } + + /// <summary> + /// Determine if a NpgsqlNativeTypeInfo with the given NpgsqlDbType exists in this mapping. + /// </summary> + public Boolean ContainsNpgsqlDbType(NpgsqlDbType NpgsqlDbType) + { + return NpgsqlDbTypeIndex.ContainsKey(NpgsqlDbType); + } + + /// <summary> + /// Determine if a NpgsqlNativeTypeInfo with the given Type name exists in this mapping. + /// </summary> + public Boolean ContainsType(Type Type) + { + return TypeIndex.ContainsKey(Type); + } + } +} |