diff --git a/CHANGELOG.md b/CHANGELOG.md index fe8db30..1feece4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - `TypeCodec` interface is used for encoding/decoding value by OIDs. Gets a reference to `TypeCodecContext` which contains `encoding` and runtime parameters. - `TypeEncoderFn` value converter for generic Dart -> Postgres object encoders (where type is not specified as parameter). +- `RelationTracker` tracks information about relations (currently limited to `RelationMessage` caching). - `RuntimeParameters` 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 4710683..45fcd6d 100644 --- a/lib/src/types/type_codec.dart +++ b/lib/src/types/type_codec.dart @@ -3,6 +3,8 @@ import 'dart:collection'; import 'dart:convert'; import 'dart:typed_data'; +import 'package:postgres/src/v3/relation_tracker.dart'; + import '../buffer.dart'; import 'type_registry.dart'; @@ -85,11 +87,13 @@ class RuntimeParameters { class TypeCodecContext { final Encoding encoding; + final RelationTracker relationTracker; final RuntimeParameters runtimeParameters; final TypeRegistry typeRegistry; TypeCodecContext({ required this.encoding, + required this.relationTracker, required this.runtimeParameters, required this.typeRegistry, }); diff --git a/lib/src/v3/connection.dart b/lib/src/v3/connection.dart index d643ff7..2f454f5 100644 --- a/lib/src/v3/connection.dart +++ b/lib/src/v3/connection.dart @@ -12,10 +12,12 @@ import 'package:stream_channel/stream_channel.dart'; import '../../postgres.dart'; import '../auth/auth.dart'; +import '../messages/logical_replication_messages.dart'; import '../types/type_codec.dart'; import '../types/type_registry.dart'; import 'protocol.dart'; import 'query_description.dart'; +import 'relation_tracker.dart'; import 'resolved_settings.dart'; const _debugLog = false; @@ -348,6 +350,9 @@ class PgConnectionImplementation extends _PgSessionBase implements Connection { late final runtimeParameters = RuntimeParameters(latestValues: UnmodifiableMapView(_parameters)); + // TODO: share this between pooled connections + final _relationTracker = RelationTracker(); + var _statementCounter = 0; var _portalCounter = 0; var _queryCount = 0; @@ -406,6 +411,13 @@ class PgConnectionImplementation extends _PgSessionBase implements Connection { try { message as ServerMessage; + if (message is XLogDataLogicalMessage) { + final embedded = message.message; + if (embedded is RelationMessage) { + _relationTracker.addRelationMessage(embedded); + } + } + if (message is ParameterStatusMessage) { _parameters[message.name] = message.value; } else if (message is BackendKeyMessage || message is NoticeMessage) { @@ -541,6 +553,7 @@ class PgConnectionImplementation extends _PgSessionBase implements Connection { TypeCodecContext createTypeCodecContext() { return TypeCodecContext( encoding: encoding, + relationTracker: _relationTracker, runtimeParameters: runtimeParameters, typeRegistry: _settings.typeRegistry, ); diff --git a/lib/src/v3/relation_tracker.dart b/lib/src/v3/relation_tracker.dart new file mode 100644 index 0000000..30caea7 --- /dev/null +++ b/lib/src/v3/relation_tracker.dart @@ -0,0 +1,32 @@ +import 'package:postgres/src/messages/logical_replication_messages.dart'; + +/// Trackes and caches the type and name info of relations (tables, views, indexes...). +/// +/// Currently it only collects and caches [RelationMessage] instances. +/// +/// TODO: Implement active querying using `pg_class` like the below query: +/// "SELECT relname FROM pg_class WHERE relkind='r' AND oid = ?", +/// https://www.postgresql.org/docs/current/catalog-pg-class.html +class RelationTracker { + final _relationMessages = {}; + + /// Returns the type OID for [relationId] and [columnIndex]. + /// Returns `null` if the [relationId] is unknown or the [columnIndex] + /// is out of bounds. + int? getTypeOid(int relationId, int columnIndex) { + final m = _relationMessages[relationId]; + if (m == null) { + return null; + } + if (columnIndex < 0 || columnIndex > m.columns.length) { + return null; + } + return m.columns[columnIndex].typeOid; + } +} + +extension RelationInfoViewExt on RelationTracker { + void addRelationMessage(RelationMessage message) { + _relationMessages[message.relationId] = message; + } +}