Example #1
0
 public RowReader(NpgsqlRowDescription rowDesc, Stream stream, byte[] buffer, ByteBuffer bytes)
 {
     _rowDesc    = rowDesc;
     _stream     = stream;
     this.buffer = buffer;
     this.bytes  = bytes;
 }
Example #2
0
 public StringRowReader(NpgsqlRowDescription rowDesc, Stream inputStream, byte[] buffer, ByteBuffer bytes)
     : base(rowDesc, inputStream, buffer, bytes)
 {
     _messageSize = PGUtil.ReadInt32(inputStream, buffer);
     if (PGUtil.ReadInt16(inputStream, buffer) != rowDesc.NumFields)
     {
         throw new DataException();
     }
 }
Example #3
0
 /// <summary>
 /// Constructor.
 /// </summary>
 /// <param name="Shared">Controls whether the connector can be shared.</param>
 public NpgsqlConnector(NpgsqlConnectionStringBuilder ConnectionString, bool Pooled, bool Shared)
 {
     this.settings                = ConnectionString;
     State                        = ConnectionState.Closed;
     _pooled                      = Pooled;
     _shared                      = Shared;
     _isInitialized               = false;
     _state                       = NpgsqlClosedState.Instance;
     _mediator                    = new NpgsqlMediator();
     _oidToNameMapping            = new NpgsqlBackendTypeMapping();
     _planIndex                   = 0;
     _portalIndex                 = 0;
     _notificationThreadStopCount = 1;
     _notificationAutoResetEvent  = new AutoResetEvent(true);
     rowDescription               = new NpgsqlRowDescription(CompatVersion);
     rowReader                    = new StringRowReader(rowDescription, Stream, TmpBuffer, ArrayBuffer);
     forwardReader                = new ForwardsOnlyRow(rowReader);
 }
Example #4
0
        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);
        }
Example #5
0
        protected IEnumerable <IServerResponseObject> ProcessBackendResponses_Ver_3(NpgsqlConnector context)
        {
            using (new ContextResetter(context))
            {
                Stream         stream   = context.Stream;
                NpgsqlMediator mediator = context.Mediator;

                NpgsqlRowDescription lastRowDescription = null;

                var buffer = context.TmpBuffer;
                var queue  = context.ArrayBuffer;
                List <NpgsqlError> errors = null;

                for (; ;)
                {
                    // Check the first Byte of response.
                    BackEndMessageCode message = (BackEndMessageCode)stream.ReadByte();
                    switch (message)
                    {
                    case BackEndMessageCode.ErrorResponse:

                        NpgsqlError error = new NpgsqlError(stream, buffer, queue);
                        error.ErrorSql = mediator.SqlSent;

                        if (errors == null)
                        {
                            errors = new List <NpgsqlError>();
                        }
                        errors.Add(error);

                        // 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 (!context.RequireReadyForQuery)
                        {
                            throw new NpgsqlException(errors);
                        }

                        break;

                    case BackEndMessageCode.AuthenticationRequest:

                        // Get the length in case we're getting AuthenticationGSSContinue
                        int authDataLength = PGUtil.ReadInt32(stream, buffer) - 8;

                        AuthenticationRequestType authType = (AuthenticationRequestType)PGUtil.ReadInt32(stream, buffer);
                        switch (authType)
                        {
                        case AuthenticationRequestType.AuthenticationOk:
                            break;

                        case AuthenticationRequestType.AuthenticationClearTextPassword:
                            // Send the PasswordPacket.

                            ChangeState(context, NpgsqlStartupState.Instance);
                            context.Authenticate(context.Password);

                            break;

                        case AuthenticationRequestType.AuthenticationMD5Password:
                            // 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();


                            // 1.
                            byte[] passwd       = context.Password;
                            byte[] saltUserName = ENCODING_UTF8.GetBytes(context.UserName);

                            byte[] crypt_buf = new byte[passwd.Length + saltUserName.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"));
                            }


                            String prehash = sb.ToString();

                            byte[] prehashbytes = ENCODING_UTF8.GetBytes(prehash);
                            crypt_buf = new byte[prehashbytes.Length + 4];


                            stream.Read(crypt_buf, prehashbytes.Length, 4);
                            // Send the PasswordPacket.
                            ChangeState(context, NpgsqlStartupState.Instance);


                            // 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(ENCODING_UTF8.GetBytes(sb.ToString()));

                            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();
                            }
                        }


                        case AuthenticationRequestType.AuthenticationGSSContinue:
                        {
                            byte[] authData = new byte[authDataLength];
                            PGUtil.CheckedStreamRead(stream, authData, 0, authDataLength);
                            byte[] passwd_read = context.SSPI.Continue(authData);
                            if (passwd_read.Length != 0)
                            {
                                context.Authenticate(passwd_read);
                            }
                            break;
                        }
#endif

                        default:
                            // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now.
                            if (errors == null)
                            {
                                errors = new List <NpgsqlError>();
                            }
                            errors.Add(
                                new NpgsqlError(String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType)));
                            throw new NpgsqlException(errors);
                        }
                        break;

                    case BackEndMessageCode.RowDescription:
                        yield return(lastRowDescription = new NpgsqlRowDescription(stream, context.OidToNameMapping, context.CompatVersion, buffer, queue));

                        break;

                    case BackEndMessageCode.ParameterDescription:

                        // Do nothing,for instance,  just read...
                        int length   = PGUtil.ReadInt32(stream, buffer);
                        int nb_param = PGUtil.ReadInt16(stream, buffer);
                        //WTF
                        for (int i = 0; i < nb_param; i++)
                        {
                            int typeoid = PGUtil.ReadInt32(stream, buffer);
                        }

                        break;

                    case BackEndMessageCode.DataRow:
                        yield return(new ForwardsOnlyRow(new StringRowReader(lastRowDescription, stream, buffer, queue)));

                        break;

                    case BackEndMessageCode.ReadyForQuery:

                        // 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, buffer);
                        stream.ReadByte();

                        ChangeState(context, NpgsqlReadyState.Instance);

                        if (errors != null)
                        {
                            throw new NpgsqlException(errors);
                        }

                        yield break;

                    case BackEndMessageCode.BackendKeyData:
                        // BackendKeyData message.
                        NpgsqlBackEndKeyData backend_keydata = new NpgsqlBackEndKeyData(stream, buffer);
                        context.BackEndKeyData = backend_keydata;


                        // Wait for ReadForQuery message
                        break;

                    case BackEndMessageCode.NoticeResponse:
                        // Notices and errors are identical except that we
                        // just throw notices away completely ignored.
                        context.FireNotice(new NpgsqlError(stream, buffer, queue));
                        break;

                    case BackEndMessageCode.CompletedResponse:
                        PGUtil.ReadInt32(stream, buffer);
                        yield return(new CompletedResponse(stream, queue));

                        break;

                    case BackEndMessageCode.ParseComplete:
                        // Just read up the message length.
                        PGUtil.ReadInt32(stream, buffer);
                        yield break;

                    case BackEndMessageCode.BindComplete:
                        // Just read up the message length.
                        PGUtil.ReadInt32(stream, buffer);
                        yield break;

                    case BackEndMessageCode.EmptyQueryResponse:
                        PGUtil.ReadInt32(stream, buffer);
                        break;

                    case BackEndMessageCode.NotificationResponse:
                        // Eat the length
                        PGUtil.ReadInt32(stream, buffer);
                        context.FireNotification(new NpgsqlNotificationEventArgs(stream, true, buffer, queue));
                        if (context.IsNotificationThreadRunning)
                        {
                            yield break;
                        }
                        break;

                    case BackEndMessageCode.ParameterStatus:
                        NpgsqlParameterStatus parameterStatus = new NpgsqlParameterStatus(stream, queue);

                        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.
                        PGUtil.ReadInt32(stream, buffer);
                        break;

                    case BackEndMessageCode.CopyInResponse:
                        // Enter COPY sub protocol and start pushing data to server
                        ChangeState(context, NpgsqlCopyInState.Instance);
                        PGUtil.ReadInt32(stream, buffer);                                 // length redundant
                        context.CurrentState.StartCopy(context, ReadCopyHeader(stream, buffer));
                        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
                        ChangeState(context, NpgsqlCopyOutState.Instance);
                        PGUtil.ReadInt32(stream, buffer);                                 // length redundant
                        context.CurrentState.StartCopy(context, ReadCopyHeader(stream, buffer));
                        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:
                        Int32  len = PGUtil.ReadInt32(stream, buffer) - 4;
                        byte[] buf = new byte[len];
                        PGUtil.ReadBytes(stream, 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:
                        PGUtil.ReadInt32(stream, buffer);                                 // 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));
                    }
                }
            }
        }
Example #6
0
 public StringRowReader(NpgsqlRowDescription rowDesc, Stream inputStream, byte[] buffer, ByteBuffer bytes)
     : base(rowDesc, inputStream, buffer, bytes)
 {
 }
Example #7
0
		/// <summary>
		/// Creates a prepared version of the command on a PostgreSQL server.
		/// </summary>
		public override void 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;

			if (!m_Connector.SupportsPrepare)
			{
				return; // Do nothing.
			}

			using (m_Connector.BlockNotificationThread())
			{
				try
				{
					// 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++)
						{
							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;
							}
						}
					}
					else
					{
						resultFormatCodes = new Int16[] { 0 };
					}

					bind = new NpgsqlBind("", planName, new Int16[Parameters.Count], null, resultFormatCodes);
				}
				catch (IOException e)
				{
					throw ClearPoolAndCreateException(e);
				}
				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.

					m_Connector.Sync();

					throw;
				}
			}
		}