Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for boolean array fields #41

Merged
merged 1 commit into from
Jun 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 2.4.5

- Added support for boolean arrays. [#41](https://github.com/isoos/postgresql-dart/pull/41) by [slightfoot](https://github.com/slightfoot).

## 2.4.4

- Added support for varchar arrays. [#39](https://github.com/isoos/postgresql-dart/pull/39) by [paschalisp](https://github.com/paschalisp).
Expand Down
14 changes: 14 additions & 0 deletions lib/src/binary_codec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,16 @@ class PostgresBinaryEncoder extends Converter<dynamic, Uint8List?> {
'Invalid type for parameter value. Expected: PgPoint Got: ${value.runtimeType}');
}

case PostgreSQLDataType.booleanArray:
{
if (value is List<bool>) {
return writeListBytes<bool>(
value, 16, (_) => 1, (writer, item) => writer.writeUint8(item ? 1 : 0));
}
throw FormatException(
'Invalid type for parameter value. Expected: List<bool> Got: ${value.runtimeType}');
}

case PostgreSQLDataType.integerArray:
{
if (value is List<int>) {
Expand Down Expand Up @@ -495,6 +505,9 @@ class PostgresBinaryDecoder extends Converter<Uint8List, dynamic> {
case PostgreSQLDataType.point:
return PgPoint(buffer.getFloat64(0), buffer.getFloat64(8));

case PostgreSQLDataType.booleanArray:
return readListBytes<bool>(value, (reader, _) => reader.readUint8() != 0);

case PostgreSQLDataType.integerArray:
return readListBytes<int>(value, (reader, _) => reader.readInt32());

Expand Down Expand Up @@ -565,6 +578,7 @@ class PostgresBinaryDecoder extends Converter<Uint8List, dynamic> {
600: PostgreSQLDataType.point,
700: PostgreSQLDataType.real,
701: PostgreSQLDataType.double,
1000: PostgreSQLDataType.booleanArray,
1007: PostgreSQLDataType.integerArray,
1009: PostgreSQLDataType.textArray,
1015: PostgreSQLDataType.varCharArray,
Expand Down
1 change: 1 addition & 0 deletions lib/src/query.dart
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ class PostgreSQLFormatIdentifier {
'uuid': PostgreSQLDataType.uuid,
'json': PostgreSQLDataType.json,
'point': PostgreSQLDataType.point,
'_bool': PostgreSQLDataType.booleanArray,
'_int4': PostgreSQLDataType.integerArray,
'_text': PostgreSQLDataType.textArray,
'_float8': PostgreSQLDataType.doubleArray,
Expand Down
2 changes: 2 additions & 0 deletions lib/src/substituter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ class PostgreSQLFormat {
return 'point';
case PostgreSQLDataType.json:
return 'json';
case PostgreSQLDataType.booleanArray:
return '_bool';
case PostgreSQLDataType.integerArray:
return '_int4';
case PostgreSQLDataType.textArray:
Expand Down
4 changes: 4 additions & 0 deletions lib/src/text_codec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@ class PostgresTextEncoder {
}
});

if (type == bool) {
return '{${value.cast<bool>().map((s) => s.toString()).join(',')}}';
}

if (type == int || type == double) {
return '{${value.cast<num>().map((s) => s is double ? _encodeDouble(s) : _encodeNumber(s)).join(',')}}';
}
Expand Down
7 changes: 6 additions & 1 deletion lib/src/types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

1. add item to this enumeration
2. update all switch statements on this type
3. add pg type code -> enumeration item in PostgresBinaryDecoder.typeMap (lookup type code: https://doxygen.postgresql.org/include_2catalog_2pg__type_8h_source.html)
3. add pg type code -> enumeration item in PostgresBinaryDecoder.typeMap
(lookup type code: https://doxygen.postgresql.org/pg__type_8h_source.html)
4. add identifying key to PostgreSQLFormatIdentifier.typeStringToCodeMap.
5. add identifying key to PostgreSQLFormat.dataTypeStringForDataType
*/

/// Supported data types.
Expand Down Expand Up @@ -80,6 +82,9 @@ enum PostgreSQLDataType {
/// Must be a [PgPoint]
point,

/// Must be a [List<bool>]
booleanArray,

/// Must be a [List<int>]
integerArray,

Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: postgres
description: PostgreSQL database driver. Supports statement reuse and binary protocol.
version: 2.4.4
version: 2.4.5
homepage: https://github.com/isoos/postgresql-dart

environment:
Expand Down
74 changes: 32 additions & 42 deletions test/decode_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,33 @@ import 'package:test/test.dart';
void main() {
late PostgreSQLConnection connection;
setUp(() async {
connection = PostgreSQLConnection('localhost', 5432, 'dart_test',
username: 'dart', password: 'dart');
connection =
PostgreSQLConnection('localhost', 5432, 'dart_test', username: 'dart', password: 'dart');
await connection.open();

await connection.execute('''
CREATE TEMPORARY TABLE t (
i int, s serial, bi bigint, bs bigserial, bl boolean, si smallint,
t text, f real, d double precision, dt date, ts timestamp, tsz timestamptz, n numeric, j jsonb, ba bytea,
u uuid, v varchar, p point, jj json, ia _int4, ta _text, da _float8, ja _jsonb, va varchar(20)[])
u uuid, v varchar, p point, jj json, ia _int4, ta _text, da _float8, ja _jsonb, va varchar(20)[],
boola _bool
)
''');

await connection.execute(
'INSERT INTO t (i, bi, bl, si, t, f, d, dt, ts, tsz, n, j, ba, u, v, p, jj, ia, ta, da, ja, va) '
'INSERT INTO t (i, bi, bl, si, t, f, d, dt, ts, tsz, n, j, ba, u, v, p, jj, ia, ta, da, ja, va, boola) '
'VALUES (-2147483648, -9223372036854775808, TRUE, -32768, '
"'string', 10.0, 10.0, '1983-11-06', "
"'1983-11-06 06:00:00.000000', '1983-11-06 06:00:00.000000', "
"'-1234567890.0987654321', "
"'{\"key\":\"value\"}', E'\\\\000', '00000000-0000-0000-0000-000000000000', "
"'abcdef', '(0.01, 12.34)', '{\"key\": \"value\"}', '{}', '{}', '{}', '{}', "
"'{\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"}')");
"'{\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"}', "
"'{true, false, false}'"
')');

await connection.execute(
'INSERT INTO t (i, bi, bl, si, t, f, d, dt, ts, tsz, n, j, ba, u, v, p, jj, ia, ta, da, ja, va) '
'INSERT INTO t (i, bi, bl, si, t, f, d, dt, ts, tsz, n, j, ba, u, v, p, jj, ia, ta, da, ja, va, boola) '
'VALUES (2147483647, 9223372036854775807, FALSE, 32767, '
"'a significantly longer string to the point where i doubt this actually matters', "
"10.25, 10.125, '2183-11-06', '2183-11-06 00:00:00.111111', "
Expand All @@ -37,18 +42,20 @@ void main() {
"'[{\"key\":1}]', E'\\\\377', 'FFFFFFFF-ffff-ffff-ffff-ffffffffffff', "
"'01234', '(0.2, 100)', '{}', '{-123, 999}', '{\"a\", \"lorem ipsum\", \"\"}', "
"'{1, 2, 4.5, 1234.5}', '{1, \"\\\"test\\\"\", \"{\\\"a\\\": \\\"b\\\"}\"}', "
"'{\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"}')");
"'{\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"}', "
"'{false, false, true}' "
')');

await connection.execute(
'INSERT INTO t (i, bi, bl, si, t, f, d, dt, ts, tsz, n, j, ba, u, v, p, jj, ia, ta, da, ja, va) '
'INSERT INTO t (i, bi, bl, si, t, f, d, dt, ts, tsz, n, j, ba, u, v, p, jj, ia, ta, da, ja, va, boola) '
'VALUES (null, null, null, null, null, null, null, null, null, null, null, null, null, '
'null, null, null, null, null, null, null, null, null)');
'null, null, null, null, null, null, null, null, null, null)');
});
tearDown(() async {
await connection.close();
});

test('Fetch em', () async {
test(' Fetch em', () async {
final res = await connection.query('select * from t');

final row1 = res[0];
Expand Down Expand Up @@ -83,6 +90,8 @@ void main() {
expect(row1[22], equals([]));
expect(row1[23] is List<String>, true);
expect(row1[23], equals(['a', 'b', 'c', 'd', 'e', 'f']));
expect(row1[24] is List<bool>, true);
expect(row1[24], equals([true, false, false]));

// upper bound row
expect(row2[0], equals(2147483647));
Expand All @@ -91,19 +100,16 @@ void main() {
expect(row2[3], equals(2));
expect(row2[4], equals(false));
expect(row2[5], equals(32767));
expect(
row2[6],
equals(
'a significantly longer string to the point where i doubt this actually matters'));
expect(row2[6],
equals('a significantly longer string to the point where i doubt this actually matters'));
expect(row2[7] is double, true);
expect(row2[7], equals(10.25));
expect(row2[8] is double, true);
expect(row2[8], equals(10.125));
expect(row2[9], equals(DateTime.utc(2183, 11, 6)));
expect(row2[10], equals(DateTime.utc(2183, 11, 6, 0, 0, 0, 111, 111)));
expect(row2[11], equals(DateTime.utc(2183, 11, 6, 0, 0, 0, 999, 999)));
expect(row2[12],
equals('1000000000000000000000000000.0000000000000000000000000001'));
expect(row2[12], equals('1000000000000000000000000000.0000000000000000000000000001'));
expect(
row2[13],
equals([
Expand All @@ -124,6 +130,10 @@ void main() {
'test',
{'a': 'b'}
]));
expect(row2[23] is List<String>, true);
expect(row2[23], equals(['a', 'b', 'c', 'd', 'e', 'f']));
expect(row2[24] is List<bool>, true);
expect(row2[24], equals([false, false, true]));

// all null row
expect(row3[0], isNull);
Expand All @@ -150,13 +160,13 @@ void main() {
expect(row3[21], isNull);
expect(row3[22], isNull);
expect(row3[23], isNull);
expect(row3[24], isNull);
});

test('Fetch/insert empty string', () async {
await connection.execute('CREATE TEMPORARY TABLE u (t text)');
var results = await connection.query(
'INSERT INTO u (t) VALUES (@t:text) returning t',
substitutionValues: {'t': ''});
var results = await connection
.query('INSERT INTO u (t) VALUES (@t:text) returning t', substitutionValues: {'t': ''});
expect(results, [
['']
]);
Expand All @@ -169,9 +179,8 @@ void main() {

test('Fetch/insert null value', () async {
await connection.execute('CREATE TEMPORARY TABLE u (t text)');
var results = await connection.query(
'INSERT INTO u (t) VALUES (@t:text) returning t',
substitutionValues: {'t': null});
var results = await connection
.query('INSERT INTO u (t) VALUES (@t:text) returning t', substitutionValues: {'t': null});
expect(results, [
[null]
]);
Expand All @@ -185,26 +194,7 @@ void main() {
test('Decode Numeric to String', () {
final binaries = {
'-123400000.20000': [0, 4, 0, 2, 64, 0, 0, 5, 0, 1, 9, 36, 0, 0, 7, 208],
'-123400001.00002': [
0,
5,
0,
2,
64,
0,
0,
5,
0,
1,
9,
36,
0,
1,
0,
0,
7,
208
],
'-123400001.00002': [0, 5, 0, 2, 64, 0, 0, 5, 0, 1, 9, 36, 0, 1, 0, 0, 7, 208],
'0.00001': [0, 1, 255, 254, 0, 0, 0, 5, 3, 232],
'10000.000000000': [0, 1, 0, 1, 0, 0, 0, 9, 0, 1],
'NaN': [0, 0, 0, 0, 192, 0, 0, 0],
Expand Down
13 changes: 13 additions & 0 deletions test/encoding_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,19 @@ void main() {
}
});

test('booleanArray', () async {
await expectInverse(<bool>[], PostgreSQLDataType.booleanArray);
await expectInverse([false, true], PostgreSQLDataType.booleanArray);
await expectInverse([true], PostgreSQLDataType.booleanArray);
try {
await conn.query('INSERT INTO t (v) VALUES (@v:_bool)',
substitutionValues: {'v': 'not-list-bool'});
fail('unreachable');
} on FormatException catch (e) {
expect(e.toString(), contains('Expected: List<bool>'));
}
});

test('integerArray', () async {
await expectInverse(<int>[], PostgreSQLDataType.integerArray);
await expectInverse([-1, 0, 200], PostgreSQLDataType.integerArray);
Expand Down
38 changes: 23 additions & 15 deletions test/query_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ void main() {
'bs bigserial, bl boolean, si smallint, '
't text, f real, d double precision, '
'dt date, ts timestamp, tsz timestamptz, j jsonb, u uuid, '
'v varchar, p point, jj json, ia _int4, ta _text, da _float8, ja _jsonb, va _varchar(20))');
'v varchar, p point, jj json, ia _int4, ta _text, da _float8, ja _jsonb, va _varchar(20), '
'ba _bool'
')');
await connection.execute(
'CREATE TEMPORARY TABLE u (i1 int not null, i2 int not null);');
await connection
Expand Down Expand Up @@ -109,7 +111,7 @@ void main() {

test('Query without specifying types', () async {
var result = await connection.query(
'INSERT INTO t (i, bi, bl, si, t, f, d, dt, ts, tsz, j, u, v, p, jj, ia, ta, da, ja, va) values '
'INSERT INTO t (i, bi, bl, si, t, f, d, dt, ts, tsz, j, u, v, p, jj, ia, ta, da, ja, va, ba) values '
'(${PostgreSQLFormat.id('i')},'
'${PostgreSQLFormat.id('bi')},'
'${PostgreSQLFormat.id('bl')},'
Expand All @@ -129,8 +131,9 @@ void main() {
'${PostgreSQLFormat.id('ta')},'
'${PostgreSQLFormat.id('da')},'
'${PostgreSQLFormat.id('ja')},'
'${PostgreSQLFormat.id('va')}'
') returning i,s, bi, bs, bl, si, t, f, d, dt, ts, tsz, j, u, v, p, jj, ia, ta, da, ja, va',
'${PostgreSQLFormat.id('va')},'
'${PostgreSQLFormat.id('ba')}'
') returning i,s, bi, bs, bl, si, t, f, d, dt, ts, tsz, j, u, v, p, jj, ia, ta, da, ja, va, ba',
substitutionValues: {
'i': 1,
'bi': 2,
Expand All @@ -155,7 +158,8 @@ void main() {
'a"\'\\"',
{'k': 'v"\'\\"'}
],
'va': ['a', 'b', 'c', 'd', 'e', 'f']
'va': ['a', 'b', 'c', 'd', 'e', 'f'],
'ba': [false, true, false],
});

final expectedRow = [
Expand Down Expand Up @@ -184,22 +188,23 @@ void main() {
'a"\'\\"',
{'k': 'v"\'\\"'}
],
['a', 'b', 'c', 'd', 'e', 'f']
['a', 'b', 'c', 'd', 'e', 'f'],
[false, true, false]
];
expect(result.columnDescriptions, hasLength(22));
expect(result.columnDescriptions, hasLength(23));
expect(result.columnDescriptions.first.tableName, 't');
expect(result.columnDescriptions.first.columnName, 'i');
expect(result.columnDescriptions.last.tableName, 't');
expect(result.columnDescriptions.last.columnName, 'va');
expect(result.columnDescriptions.last.columnName, 'ba');
expect(result, [expectedRow]);
result = await connection.query(
'select i,s, bi, bs, bl, si, t, f, d, dt, ts, tsz, j, u, v, p, jj, ia, ta, da, ja, va from t');
'select i,s, bi, bs, bl, si, t, f, d, dt, ts, tsz, j, u, v, p, jj, ia, ta, da, ja, va, ba from t');
expect(result, [expectedRow]);
});

test('Query by specifying all types', () async {
var result = await connection.query(
'INSERT INTO t (i, bi, bl, si, t, f, d, dt, ts, tsz, j, u, v, p, jj, ia, ta, da, ja, va) values '
'INSERT INTO t (i, bi, bl, si, t, f, d, dt, ts, tsz, j, u, v, p, jj, ia, ta, da, ja, va, ba) values '
'(${PostgreSQLFormat.id('i', type: PostgreSQLDataType.integer)},'
'${PostgreSQLFormat.id('bi', type: PostgreSQLDataType.bigInteger)},'
'${PostgreSQLFormat.id('bl', type: PostgreSQLDataType.boolean)},'
Expand All @@ -219,8 +224,9 @@ void main() {
'${PostgreSQLFormat.id('ta', type: PostgreSQLDataType.textArray)},'
'${PostgreSQLFormat.id('da', type: PostgreSQLDataType.doubleArray)},'
'${PostgreSQLFormat.id('ja', type: PostgreSQLDataType.jsonbArray)},'
'${PostgreSQLFormat.id('va', type: PostgreSQLDataType.varCharArray)}'
') returning i,s, bi, bs, bl, si, t, f, d, dt, ts, tsz, j, u, v, p, jj, ia, ta, da, ja, va',
'${PostgreSQLFormat.id('va', type: PostgreSQLDataType.varCharArray)},'
'${PostgreSQLFormat.id('ba', type: PostgreSQLDataType.booleanArray)}'
') returning i,s, bi, bs, bl, si, t, f, d, dt, ts, tsz, j, u, v, p, jj, ia, ta, da, ja, va, ba',
substitutionValues: {
'i': 1,
'bi': 2,
Expand All @@ -245,7 +251,8 @@ void main() {
'a',
{'k': 'v'}
],
'va': ['a', 'b', 'c', 'd', 'e', 'f']
'va': ['a', 'b', 'c', 'd', 'e', 'f'],
'ba': [false, true, true, false],
});

final expectedRow = [
Expand Down Expand Up @@ -274,12 +281,13 @@ void main() {
'a',
{'k': 'v'}
],
['a', 'b', 'c', 'd', 'e', 'f']
['a', 'b', 'c', 'd', 'e', 'f'],
[false, true, true, false],
];
expect(result, [expectedRow]);

result = await connection.query(
'select i,s, bi, bs, bl, si, t, f, d, dt, ts, tsz, j, u, v, p, jj, ia, ta, da, ja, va from t');
'select i,s, bi, bs, bl, si, t, f, d, dt, ts, tsz, j, u, v, p, jj, ia, ta, da, ja, va, ba from t');
expect(result, [expectedRow]);
});

Expand Down