From ae390ab6645c1a8b67eafde31c0dcdca41e5bc9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20So=C3=B3s?= Date: Sun, 24 Sep 2023 11:12:18 +0200 Subject: [PATCH] Refactor ParameterValue's buffer handling. (#134) --- lib/src/client_messages.dart | 54 +++++++++++++++--------------------- lib/src/query.dart | 48 ++++++++++++++------------------ lib/src/v3/connection.dart | 2 +- 3 files changed, 45 insertions(+), 59 deletions(-) diff --git a/lib/src/client_messages.dart b/lib/src/client_messages.dart index 3876ec36..36e7818d 100644 --- a/lib/src/client_messages.dart +++ b/lib/src/client_messages.dart @@ -180,42 +180,33 @@ class BindMessage extends ClientMessage { final List _parameters; final String _portalName; final String _statementName; - final int _typeSpecCount; BindMessage(this._parameters, {String portalName = '', String statementName = ''}) - : _typeSpecCount = _parameters.where((p) => p.isBinary).length, - _portalName = portalName, + : _portalName = portalName, _statementName = statementName; - int _length(EncodedString statementName, EncodedString portalName) { - var length = 0; + @override + void applyToBuffer(PgByteDataWriter buffer) { + buffer.writeUint8(ClientMessage.BindIdentifier); + final portalName = buffer.prepareString(_portalName); + final statementName = buffer.prepareString(_statementName); + + final parameterBytes = _parameters.map((p) => p.encodeAsBytes()).toList(); + final typeSpecCount = _parameters.where((p) => p.hasKnownType).length; var inputParameterElementCount = _parameters.length; - if (_typeSpecCount == _parameters.length || _typeSpecCount == 0) { + if (typeSpecCount == _parameters.length || typeSpecCount == 0) { inputParameterElementCount = 1; } - length = 15; + var length = 14; length += statementName.bytesLength; length += portalName.bytesLength; length += inputParameterElementCount * 2; - length += _parameters.fold(0, (len, ParameterValue paramValue) { - if (paramValue.bytes == null) { - return len + 4; - } else { - return len + 4 + paramValue.length; - } - }); - return length; - } - - @override - void applyToBuffer(PgByteDataWriter buffer) { - buffer.writeUint8(ClientMessage.BindIdentifier); - final portalName = buffer.prepareString(_portalName); - final statementName = buffer.prepareString(_statementName); + length += parameterBytes.fold( + 0, (len, bytes) => len + 4 + (bytes?.length ?? 0)); - buffer.writeUint32(_length(statementName, portalName) - 1); + buffer.writeUint32(length); // Name of portal. buffer.writeEncodedString(portalName); @@ -224,11 +215,11 @@ class BindMessage extends ClientMessage { // OK, if we have no specified types at all, we can use 0. If we have all specified types, we can use 1. If we have a mix, we have to individually // call out each type. - if (_typeSpecCount == _parameters.length) { + if (typeSpecCount == _parameters.length) { buffer.writeUint16(1); // Apply following format code for all parameters by indicating 1 buffer.writeUint16(ClientMessage.FormatBinary); - } else if (_typeSpecCount == 0) { + } else if (typeSpecCount == 0) { buffer.writeUint16(1); // Apply following format code for all parameters by indicating 1 buffer.writeUint16(ClientMessage.FormatText); @@ -236,19 +227,20 @@ class BindMessage extends ClientMessage { // Well, we have some text and some binary, so we have to be explicit about each one buffer.writeUint16(_parameters.length); for (final p in _parameters) { - buffer.writeUint16( - p.isBinary ? ClientMessage.FormatBinary : ClientMessage.FormatText); + buffer.writeUint16(p.hasKnownType + ? ClientMessage.FormatBinary + : ClientMessage.FormatText); } } // This must be the number of $n's in the query. buffer.writeUint16(_parameters.length); - for (final p in _parameters) { - if (p.bytes == null) { + for (final bytes in parameterBytes) { + if (bytes == null) { buffer.writeInt32(-1); } else { - buffer.writeInt32(p.length); - buffer.write(p.bytes!); + buffer.writeInt32(bytes.length); + buffer.write(bytes); } } diff --git a/lib/src/query.dart b/lib/src/query.dart index 1d6a3d7f..b8465c65 100644 --- a/lib/src/query.dart +++ b/lib/src/query.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'dart:typed_data'; import 'package:buffer/buffer.dart'; +import 'package:postgres/src/v3/types.dart'; import 'binary_codec.dart'; import 'client_messages.dart'; @@ -83,7 +84,7 @@ class Query { formatIdentifiers.map((i) => i.type).toList(); final parameterList = formatIdentifiers - .map((id) => ParameterValue(id, substitutionValues)) + .map((id) => ParameterValue.resolve(id, substitutionValues)) .toList(); final messages = [ @@ -105,7 +106,8 @@ class Query { Map? substitutionValues) { final statementName = cacheQuery.preparedStatementName; final parameterList = cacheQuery.orderedParameters! - .map((identifier) => ParameterValue(identifier, substitutionValues)) + .map((identifier) => + ParameterValue.resolve(identifier, substitutionValues)) .toList(); final bytes = ClientMessage.aggregateBytes([ @@ -217,38 +219,30 @@ class CachedQuery { } class ParameterValue { - factory ParameterValue(PostgreSQLFormatIdentifier identifier, - Map? substitutionValues) { - if (identifier.type == null) { - return ParameterValue.text(substitutionValues?[identifier.name]); - } + final PgDataType? _type; + final Object? _value; + ParameterValue(this._type, this._value); - return ParameterValue.binary( - substitutionValues?[identifier.name], identifier.type!); + factory ParameterValue.resolve(PostgreSQLFormatIdentifier identifier, + Map? substitutionValues) { + final value = substitutionValues?[identifier.name]; + final type = identifier.type; + return ParameterValue(type, value); } - factory ParameterValue.binary( - dynamic value, PostgreSQLDataType postgresType) { - final bytes = postgresType.binaryCodec.encoder.convert(value); - return ParameterValue._(true, bytes, bytes?.length ?? 0); - } + bool get hasKnownType => _type != null; - factory ParameterValue.text(dynamic value) { - Uint8List? bytes; - if (value != null) { + Uint8List? encodeAsBytes() { + if (_type != null) { + return _type!.binaryCodec.encoder.convert(_value); + } + if (_value != null) { const converter = PostgresTextEncoder(); - bytes = castBytes( - utf8.encode(converter.convert(value, escapeStrings: false))); + return castBytes( + utf8.encode(converter.convert(_value, escapeStrings: false))); } - final length = bytes?.length ?? 0; - return ParameterValue._(false, bytes, length); + return null; } - - ParameterValue._(this.isBinary, this.bytes, this.length); - - final bool isBinary; - final Uint8List? bytes; - final int length; } class FieldDescription implements ColumnDescription { diff --git a/lib/src/v3/connection.dart b/lib/src/v3/connection.dart index 119c21e3..0e1ba5cf 100644 --- a/lib/src/v3/connection.dart +++ b/lib/src/v3/connection.dart @@ -478,7 +478,7 @@ class _PgResultStreamSubscription BindMessage( [ for (final parameter in statement.parameters) - ParameterValue.binary(parameter.value, parameter.type) + ParameterValue(parameter.type, parameter.value) ], portalName: _portalName, statementName: statement.statement._name,