Skip to content

Commit

Permalink
Refactor ParameterValue's buffer handling. (#134)
Browse files Browse the repository at this point in the history
  • Loading branch information
isoos authored Sep 24, 2023
1 parent e0c0346 commit ae390ab
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 59 deletions.
54 changes: 23 additions & 31 deletions lib/src/client_messages.dart
Original file line number Diff line number Diff line change
Expand Up @@ -180,42 +180,33 @@ class BindMessage extends ClientMessage {
final List<ParameterValue> _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<int>(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<int>(
0, (len, bytes) => len + 4 + (bytes?.length ?? 0));

buffer.writeUint32(_length(statementName, portalName) - 1);
buffer.writeUint32(length);

// Name of portal.
buffer.writeEncodedString(portalName);
Expand All @@ -224,31 +215,32 @@ 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);
} else {
// 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);
}
}

Expand Down
48 changes: 21 additions & 27 deletions lib/src/query.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -83,7 +84,7 @@ class Query<T> {
formatIdentifiers.map((i) => i.type).toList();

final parameterList = formatIdentifiers
.map((id) => ParameterValue(id, substitutionValues))
.map((id) => ParameterValue.resolve(id, substitutionValues))
.toList();

final messages = [
Expand All @@ -105,7 +106,8 @@ class Query<T> {
Map<String, dynamic>? 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([
Expand Down Expand Up @@ -217,38 +219,30 @@ class CachedQuery {
}

class ParameterValue {
factory ParameterValue(PostgreSQLFormatIdentifier identifier,
Map<String, dynamic>? substitutionValues) {
if (identifier.type == null) {
return ParameterValue.text(substitutionValues?[identifier.name]);
}
final PgDataType<Object>? _type;
final Object? _value;
ParameterValue(this._type, this._value);

return ParameterValue.binary(
substitutionValues?[identifier.name], identifier.type!);
factory ParameterValue.resolve(PostgreSQLFormatIdentifier identifier,
Map<String, dynamic>? 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 {
Expand Down
2 changes: 1 addition & 1 deletion lib/src/v3/connection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit ae390ab

Please sign in to comment.