From bea7a6e325d9c88674795ea156eea2130c45793b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20So=C3=B3s?= Date: Thu, 29 Aug 2024 21:44:17 +0200 Subject: [PATCH] RuntimeParameters (#352) --- CHANGELOG.md | 1 + lib/src/types/type_codec.dart | 38 +++++++++++++++++++++++++++++++++++ lib/src/v3/connection.dart | 22 ++++++++++++-------- test/v3_test.dart | 7 +++++++ 4 files changed, 60 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22b1dcf..fa49915 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - (internal) using `EncodedValue` instead of `EncodeOutput` - `TypeCodec` interface (may become public) is used for encoding/decoding value by OIDs. - `TypeEncoderFn` type definition for generic Dart -> Postgres object encoders (where type is not specified as parameter). +- `RuntimeParameters` (accessible through `TypeCodecContext`) to access server-provided parameter status values. ## 3.3.0 diff --git a/lib/src/types/type_codec.dart b/lib/src/types/type_codec.dart index 95d425c..4710683 100644 --- a/lib/src/types/type_codec.dart +++ b/lib/src/types/type_codec.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:collection'; import 'dart:convert'; import 'dart:typed_data'; @@ -47,12 +48,49 @@ abstract class TypeCodec { FutureOr decode(TypeCodecContext context, EncodedValue input); } +/// The read-only, passive view of the Postgresql's runtime/session parameters. +/// +/// Postgresql server reports certain parameter values at opening a connection +/// or whenever their values change. Such parameters may include: +/// - `application_name` +/// - `server_version` +/// - `server_encoding` +/// - `client_encoding` +/// - `is_superuser` +/// - `session_authorization` +/// - `DateStyle` +/// - `TimeZone` +/// - `integer_datetimes` +/// +/// This class holds the latest parameter values send by the server. +/// The values are not queried or updated actively. +/// +/// The available parameters may be discovered following the instructions on these URLs: +/// - https://www.postgresql.org/docs/current/sql-show.html +/// - https://www.postgresql.org/docs/current/runtime-config.html +/// - https://www.postgresql.org/docs/current/libpq-status.html#LIBPQ-PQPARAMETERSTATUS +class RuntimeParameters { + /// The latest values of the runtime parameters. + /// + /// The backing map may be behind an [UnmodifiableMapView], clients may not + /// update these values directly. + final Map latestValues; + + RuntimeParameters({ + required this.latestValues, + }); + + String? get applicationName => latestValues['application_name']; +} + class TypeCodecContext { final Encoding encoding; + final RuntimeParameters runtimeParameters; final TypeRegistry typeRegistry; TypeCodecContext({ required this.encoding, + required this.runtimeParameters, required this.typeRegistry, }); diff --git a/lib/src/v3/connection.dart b/lib/src/v3/connection.dart index 9e5de0d..d643ff7 100644 --- a/lib/src/v3/connection.dart +++ b/lib/src/v3/connection.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:collection'; import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; @@ -343,6 +344,9 @@ class PgConnectionImplementation extends _PgSessionBase implements Connection { _TransactionSession? _activeTransaction; final _parameters = {}; + @internal + late final runtimeParameters = + RuntimeParameters(latestValues: UnmodifiableMapView(_parameters)); var _statementCounter = 0; var _portalCounter = 0; @@ -533,6 +537,14 @@ class PgConnectionImplementation extends _PgSessionBase implements Connection { void _closeAfterError([PgException? cause]) { _close(true, cause); } + + TypeCodecContext createTypeCodecContext() { + return TypeCodecContext( + encoding: encoding, + runtimeParameters: runtimeParameters, + typeRegistry: _settings.typeRegistry, + ); + } } class _PreparedStatement extends Statement { @@ -631,10 +643,7 @@ class _PgResultStreamSubscription connection._pending = this; final encodedFutures = >[]; - final context = TypeCodecContext( - encoding: connection.encoding, - typeRegistry: connection._settings.typeRegistry, - ); + final context = connection.createTypeCodecContext(); for (final e in statement.parameters) { if (e.isSqlNull) { encodedFutures.add(Future.value(null)); @@ -755,10 +764,7 @@ class _PgResultStreamSubscription final columnCount = message.values.length; final futures = []; List? sqlNulls; - final context = TypeCodecContext( - encoding: session.encoding, - typeRegistry: session._connection._settings.typeRegistry, - ); + final context = session._connection.createTypeCodecContext(); for (var i = 0; i < message.values.length; i++) { final field = schema.columns[i]; final input = message.values[i]; diff --git a/test/v3_test.dart b/test/v3_test.dart index 6c46f04..dc04dc7 100644 --- a/test/v3_test.dart +++ b/test/v3_test.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:async/async.dart'; import 'package:postgres/messages.dart'; import 'package:postgres/postgres.dart'; +import 'package:postgres/src/v3/connection.dart'; import 'package:stream_channel/stream_channel.dart'; import 'package:test/test.dart'; @@ -26,6 +27,12 @@ void main() { tearDown(() => connection.close()); + test('runtime parameters', () async { + final c = connection as PgConnectionImplementation; + final p = c.runtimeParameters; + expect(p.applicationName, 'test_app'); + }); + test('simple queries', () async { final rs = await connection.execute("SELECT 'dart', 42, NULL"); expect(rs, [