async Task Authenticate(string username, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken) { Log.Authenticating(Id); var msg = await ReadExpecting <AuthenticationRequestMessage>(async); timeout.Check(); switch (msg.AuthRequestType) { case AuthenticationRequestType.AuthenticationOk: return; case AuthenticationRequestType.AuthenticationCleartextPassword: await AuthenticateCleartext(async, cancellationToken); return; case AuthenticationRequestType.AuthenticationMD5Password: await AuthenticateMD5(username, ((AuthenticationMD5PasswordMessage)msg).Salt, async, cancellationToken); return; case AuthenticationRequestType.AuthenticationGSS: case AuthenticationRequestType.AuthenticationSSPI: await AuthenticateGSS(async, cancellationToken); return; case AuthenticationRequestType.AuthenticationGSSContinue: throw new NpgsqlException("Can't start auth cycle with AuthenticationGSSContinue"); default: throw new NotSupportedException($"Authentication method not supported (Received: {msg.AuthRequestType})"); } }
static async Task <AvailablePostgresTypes> LoadBackendTypes(NpgsqlConnector connector, NpgsqlTimeout timeout, bool async) { var commandTimeout = 0; // Default to infinity if (timeout.IsSet) { commandTimeout = (int)timeout.TimeLeft.TotalSeconds; if (commandTimeout <= 0) { throw new TimeoutException(); } } var types = new AvailablePostgresTypes(); using (var command = new NpgsqlCommand(connector.SupportsRangeTypes ? TypesQueryWithRange : TypesQueryWithoutRange, connector.Connection)) { command.CommandTimeout = commandTimeout; command.AllResultTypesAreUnknown = true; using (var reader = async ? await command.ExecuteReaderAsync() : command.ExecuteReader()) { while (async ? await reader.ReadAsync() : reader.Read()) { timeout.Check(); LoadBackendType(reader, types, connector); } } } return(types); }
async Task Authenticate(string username, NpgsqlTimeout timeout, bool async) { Log.Trace("Authenticating...", Id); var msg = Expect <AuthenticationRequestMessage>(await ReadMessage(async), this); timeout.Check(); switch (msg.AuthRequestType) { case AuthenticationRequestType.AuthenticationOk: return; case AuthenticationRequestType.AuthenticationCleartextPassword: await AuthenticateCleartext(username, async); return; case AuthenticationRequestType.AuthenticationMD5Password: await AuthenticateMD5(username, ((AuthenticationMD5PasswordMessage)msg).Salt, async); return; case AuthenticationRequestType.AuthenticationSASL: await AuthenticateSASL(((AuthenticationSASLMessage)msg).Mechanisms, username, async); return; case AuthenticationRequestType.AuthenticationGSS: case AuthenticationRequestType.AuthenticationSSPI: await AuthenticateGSS(async); return; case AuthenticationRequestType.AuthenticationGSSContinue: throw new NpgsqlException("Can't start auth cycle with AuthenticationGSSContinue"); default: throw new NotSupportedException($"Authentication method not supported (Received: {msg.AuthRequestType})"); } }
/// <summary> /// Called exactly once per multiplexing pool, when the first connection is opened, with two goals: /// 1. Load types and bind the pool-wide type mapper (necessary for binding parameters) /// 2. Cause any connection exceptions (e.g. bad username) to be thrown from NpgsqlConnection.Open /// </summary> internal async Task BootstrapMultiplexing(NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken = default) { Debug.Assert(_multiplexing); var hasSemaphore = async ? await _bootstrapSemaphore !.WaitAsync(timeout.TimeLeft, cancellationToken) : _bootstrapSemaphore !.Wait(timeout.TimeLeft, cancellationToken); // We've timed out - calling Check, to throw the correct exception if (!hasSemaphore) { timeout.Check(); } try { if (IsBootstrapped) { return; } var connector = await conn.StartBindingScope(ConnectorBindingScope.Connection, timeout, async, cancellationToken); using var _ = Defer(() => conn.EndBindingScope(ConnectorBindingScope.Connection)); // Somewhat hacky. Extract the connector's type mapper as our pool-wide mapper, // and have the connector rebind to ensure it has a different instance. // The latter isn't strictly necessary (type mappers should always be usable // concurrently) but just in case. MultiplexingTypeMapper = connector.TypeMapper; connector.RebindTypeMapper(); IsBootstrapped = true; } finally { _bootstrapSemaphore !.Release(); } }
/// <summary> /// Called exactly once per multiplexing pool, when the first connection is opened, with two goals: /// 1. Load types and bind the pool-wide type mapper (necessary for binding parameters) /// 2. Cause any connection exceptions (e.g. bad username) to be thrown from NpgsqlConnection.Open /// </summary> internal async Task BootstrapMultiplexing(NpgsqlConnection conn, NpgsqlTimeout timeout, bool async, CancellationToken cancellationToken = default) { Debug.Assert(_multiplexing); var hasSemaphore = async ? await _bootstrapSemaphore !.WaitAsync(timeout.TimeLeft, cancellationToken) : _bootstrapSemaphore !.Wait(timeout.TimeLeft, cancellationToken); // We've timed out - calling Check, to throw the correct exception if (!hasSemaphore) { timeout.Check(); } try { if (IsBootstrapped) { return; } var connector = await conn.StartBindingScope(ConnectorBindingScope.Connection, timeout, async, cancellationToken); using var _ = Defer(static conn => conn.EndBindingScope(ConnectorBindingScope.Connection), conn);
internal async Task <List <PostgresType> > LoadBackendTypes(NpgsqlConnection conn, NpgsqlTimeout timeout, bool async) { var commandTimeout = 0; // Default to infinity if (timeout.IsSet) { commandTimeout = (int)timeout.TimeLeft.TotalSeconds; if (commandTimeout <= 0) { throw new TimeoutException(); } } var typeLoadingQuery = GenerateTypesQuery(SupportsRangeTypes, SupportsEnumTypes, conn.Settings.LoadTableComposites); using (var command = new NpgsqlCommand(typeLoadingQuery, conn)) { command.CommandTimeout = commandTimeout; command.AllResultTypesAreUnknown = true; using (var reader = async ? await command.ExecuteReaderAsync() : command.ExecuteReader()) { var byOID = new Dictionary <uint, PostgresType>(); // First load the types themselves while (async ? await reader.ReadAsync() : reader.Read()) { timeout.Check(); var ns = reader.GetString(reader.GetOrdinal("nspname")); var internalName = reader.GetString(reader.GetOrdinal("typname")); var oid = Convert.ToUInt32(reader[reader.GetOrdinal("oid")]); Debug.Assert(internalName != null); Debug.Assert(oid != 0); var typeChar = reader.GetString(reader.GetOrdinal("type"))[0]; switch (typeChar) { case 'b': // Normal base type var baseType = new PostgresBaseType(ns, internalName, oid); byOID[baseType.OID] = baseType; continue; case 'a': // Array { var elementOID = Convert.ToUInt32(reader[reader.GetOrdinal("elemoid")]); Debug.Assert(elementOID > 0); if (!byOID.TryGetValue(elementOID, out var elementPostgresType)) { Log.Trace($"Array type '{internalName}' refers to unknown element with OID {elementOID}, skipping", conn.ProcessID); continue; } var arrayType = new PostgresArrayType(ns, internalName, oid, elementPostgresType); byOID[arrayType.OID] = arrayType; continue; } case 'r': // Range { var elementOID = Convert.ToUInt32(reader[reader.GetOrdinal("elemoid")]); Debug.Assert(elementOID > 0); if (!byOID.TryGetValue(elementOID, out var subtypePostgresType)) { Log.Trace($"Range type '{internalName}' refers to unknown subtype with OID {elementOID}, skipping", conn.ProcessID); continue; } var rangeType = new PostgresRangeType(ns, internalName, oid, subtypePostgresType); byOID[rangeType.OID] = rangeType; continue; } case 'e': // Enum var enumType = new PostgresEnumType(ns, internalName, oid); byOID[enumType.OID] = enumType; continue; case 'c': // Composite // Unlike other types, we don't var compositeType = new PostgresCompositeType(ns, internalName, oid); byOID[compositeType.OID] = compositeType; continue; case 'd': // Domain var baseTypeOID = Convert.ToUInt32(reader[reader.GetOrdinal("typbasetype")]); Debug.Assert(baseTypeOID > 0); if (!byOID.TryGetValue(baseTypeOID, out var basePostgresType)) { Log.Trace($"Domain type '{internalName}' refers to unknown base type with OID {baseTypeOID}, skipping", conn.ProcessID); continue; } var domainType = new PostgresDomainType(ns, internalName, oid, basePostgresType); byOID[domainType.OID] = domainType; continue; case 'p': // pseudo-type (record, void) // Hack this as a base type goto case 'b'; default: throw new ArgumentOutOfRangeException($"Unknown typtype for type '{internalName}' in pg_type: {typeChar}"); } } if (async) { await reader.NextResultAsync(); } else { reader.NextResult(); } LoadCompositeFields(reader, byOID); if (SupportsEnumTypes) { if (async) { await reader.NextResultAsync(); } else { reader.NextResult(); } LoadEnumLabels(reader, byOID); } return(byOID.Values.ToList()); } } }