Skip to content
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Changelog

## 2.4.4

- Added support for varchar arrays. [#39](https://github.com/isoos/postgresql-dart/pull/39) by [paschalisp](https://github.com/paschalisp).

## 2.4.3

- Support for clear text passwords using a boolean parameter in connection as 'allowClearTextPassword' to activate / deactivate the feature. [#20](https://github.com/isoos/postgresql-dart/pull/20).

## 2.4.2
Expand Down
13 changes: 13 additions & 0 deletions lib/src/binary_codec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,17 @@ class PostgresBinaryEncoder extends Converter<dynamic, Uint8List?> {
'Invalid type for parameter value. Expected: List<int> Got: ${value.runtimeType}');
}

case PostgreSQLDataType.varCharArray:
{
if (value is List<String>) {
final bytesArray = value.map((v) => utf8.encode(v));
return writeListBytes<List<int>>(bytesArray, 1043,
(item) => item.length, (writer, item) => writer.write(item));
}
throw FormatException(
'Invalid type for parameter value. Expected: List<String> Got: ${value.runtimeType}');
}

case PostgreSQLDataType.textArray:
{
if (value is List<String>) {
Expand Down Expand Up @@ -487,6 +498,7 @@ class PostgresBinaryDecoder extends Converter<Uint8List, dynamic> {
case PostgreSQLDataType.integerArray:
return readListBytes<int>(value, (reader, _) => reader.readInt32());

case PostgreSQLDataType.varCharArray:
case PostgreSQLDataType.textArray:
return readListBytes<String>(value, (reader, length) {
return utf8.decode(length > 0 ? reader.read(length) : []);
Expand Down Expand Up @@ -555,6 +567,7 @@ class PostgresBinaryDecoder extends Converter<Uint8List, dynamic> {
701: PostgreSQLDataType.double,
1007: PostgreSQLDataType.integerArray,
1009: PostgreSQLDataType.textArray,
1015: PostgreSQLDataType.varCharArray,
1043: PostgreSQLDataType.varChar,
1022: PostgreSQLDataType.doubleArray,
1082: PostgreSQLDataType.date,
Expand Down
1 change: 1 addition & 0 deletions lib/src/query.dart
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ class PostgreSQLFormatIdentifier {
'_text': PostgreSQLDataType.textArray,
'_float8': PostgreSQLDataType.doubleArray,
'varchar': PostgreSQLDataType.varChar,
'_varchar': PostgreSQLDataType.varCharArray,
'_jsonb': PostgreSQLDataType.jsonbArray,
};

Expand Down
2 changes: 2 additions & 0 deletions lib/src/substituter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ class PostgreSQLFormat {
return '_float8';
case PostgreSQLDataType.varChar:
return 'varchar';
case PostgreSQLDataType.varCharArray:
return '_varchar';
case PostgreSQLDataType.jsonbArray:
return '_jsonb';
default:
Expand Down
3 changes: 3 additions & 0 deletions lib/src/types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ enum PostgreSQLDataType {
/// Must be a [String]
varChar,

/// Must be a [List<String>]
varCharArray,

/// Must be a [List] of encodable objects
jsonbArray,
}
19 changes: 12 additions & 7 deletions test/decode_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,34 @@ void main() {
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)
u uuid, v varchar, p point, jj json, ia _int4, ta _text, da _float8, ja _jsonb, va varchar(20)[])
''');

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) '
'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) '
'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\"}', '{}', '{}', '{}', '{}')");
"'abcdef', '(0.01, 12.34)', '{\"key\": \"value\"}', '{}', '{}', '{}', '{}', "
"'{\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"}')");
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) '
'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) '
'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', "
"'2183-11-06 00:00:00.999999', "
"'1000000000000000000000000000.0000000000000000000000000001', "
"'[{\"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\\\"}\"}')");
"'{1, 2, 4.5, 1234.5}', '{1, \"\\\"test\\\"\", \"{\\\"a\\\": \\\"b\\\"}\"}', "
"'{\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"}')");

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) '
'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) '
'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)');
});
tearDown(() async {
await connection.close();
Expand Down Expand Up @@ -79,6 +81,8 @@ void main() {
expect(row1[20], equals(<String>[]));
expect(row1[21], equals(<double>[]));
expect(row1[22], equals([]));
expect(row1[23] is List<String>, true);
expect(row1[23], equals(['a', 'b', 'c', 'd', 'e', 'f']));

// upper bound row
expect(row2[0], equals(2147483647));
Expand Down Expand Up @@ -145,6 +149,7 @@ void main() {
expect(row3[20], isNull);
expect(row3[21], isNull);
expect(row3[22], isNull);
expect(row3[23], isNull);
});

test('Fetch/insert empty string', () async {
Expand Down
13 changes: 13 additions & 0 deletions test/encoding_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,19 @@ void main() {
}
});

test('varCharArray', () async {
await expectInverse(<String>[], PostgreSQLDataType.varCharArray);
await expectInverse(['', 'foo', 'foo\n'], PostgreSQLDataType.varCharArray);
await expectInverse(['foo\nbar;s', '"\'"'], PostgreSQLDataType.varCharArray);
try {
await conn.query('INSERT INTO t (v) VALUES (@v:_varchar(10))',
substitutionValues: {'v': 0});
fail('unreachable');
} on FormatException catch (e) {
expect(e.toString(), contains('Expected: List<String>'));
}
});

test('textArray', () async {
await expectInverse(<String>[], PostgreSQLDataType.textArray);
await expectInverse(['', 'foo', 'foo\n'], PostgreSQLDataType.textArray);
Expand Down
30 changes: 18 additions & 12 deletions test/query_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ 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)');
'v varchar, p point, jj json, ia _int4, ta _text, da _float8, ja _jsonb, va _varchar(20))');
await connection.execute(
'CREATE TEMPORARY TABLE u (i1 int not null, i2 int not null);');
await connection
Expand Down Expand Up @@ -109,7 +109,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) values '
'INSERT INTO t (i, bi, bl, si, t, f, d, dt, ts, tsz, j, u, v, p, jj, ia, ta, da, ja, va) values '
'(${PostgreSQLFormat.id('i')},'
'${PostgreSQLFormat.id('bi')},'
'${PostgreSQLFormat.id('bl')},'
Expand All @@ -128,8 +128,9 @@ void main() {
'${PostgreSQLFormat.id('ia')},'
'${PostgreSQLFormat.id('ta')},'
'${PostgreSQLFormat.id('da')},'
'${PostgreSQLFormat.id('ja')}'
') returning i,s, bi, bs, bl, si, t, f, d, dt, ts, tsz, j, u, v, p, jj, ia, ta, da, ja',
'${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',
substitutionValues: {
'i': 1,
'bi': 2,
Expand All @@ -154,6 +155,7 @@ void main() {
'a"\'\\"',
{'k': 'v"\'\\"'}
],
'va': ['a', 'b', 'c', 'd', 'e', 'f']
});

final expectedRow = [
Expand Down Expand Up @@ -181,22 +183,23 @@ void main() {
1,
'a"\'\\"',
{'k': 'v"\'\\"'}
]
],
['a', 'b', 'c', 'd', 'e', 'f']
];
expect(result.columnDescriptions, hasLength(21));
expect(result.columnDescriptions, hasLength(22));
expect(result.columnDescriptions.first.tableName, 't');
expect(result.columnDescriptions.first.columnName, 'i');
expect(result.columnDescriptions.last.tableName, 't');
expect(result.columnDescriptions.last.columnName, 'ja');
expect(result.columnDescriptions.last.columnName, 'va');
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 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 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) values '
'INSERT INTO t (i, bi, bl, si, t, f, d, dt, ts, tsz, j, u, v, p, jj, ia, ta, da, ja, va) values '
'(${PostgreSQLFormat.id('i', type: PostgreSQLDataType.integer)},'
'${PostgreSQLFormat.id('bi', type: PostgreSQLDataType.bigInteger)},'
'${PostgreSQLFormat.id('bl', type: PostgreSQLDataType.boolean)},'
Expand All @@ -215,8 +218,9 @@ void main() {
'${PostgreSQLFormat.id('ia', type: PostgreSQLDataType.integerArray)},'
'${PostgreSQLFormat.id('ta', type: PostgreSQLDataType.textArray)},'
'${PostgreSQLFormat.id('da', type: PostgreSQLDataType.doubleArray)},'
'${PostgreSQLFormat.id('ja', type: PostgreSQLDataType.jsonbArray)}'
') returning i,s, bi, bs, bl, si, t, f, d, dt, ts, tsz, j, u, v, p, jj, ia, ta, da, ja',
'${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',
substitutionValues: {
'i': 1,
'bi': 2,
Expand All @@ -241,6 +245,7 @@ void main() {
'a',
{'k': 'v'}
],
'va': ['a', 'b', 'c', 'd', 'e', 'f']
});

final expectedRow = [
Expand Down Expand Up @@ -269,11 +274,12 @@ void main() {
'a',
{'k': 'v'}
],
['a', 'b', 'c', 'd', 'e', 'f']
];
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 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 from t');
expect(result, [expectedRow]);
});

Expand Down