diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a984d32..f31844a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/lib/src/binary_codec.dart b/lib/src/binary_codec.dart index c391898d..8234024b 100644 --- a/lib/src/binary_codec.dart +++ b/lib/src/binary_codec.dart @@ -254,6 +254,17 @@ class PostgresBinaryEncoder extends Converter { 'Invalid type for parameter value. Expected: List Got: ${value.runtimeType}'); } + case PostgreSQLDataType.varCharArray: + { + if (value is List) { + final bytesArray = value.map((v) => utf8.encode(v)); + return writeListBytes>(bytesArray, 1043, + (item) => item.length, (writer, item) => writer.write(item)); + } + throw FormatException( + 'Invalid type for parameter value. Expected: List Got: ${value.runtimeType}'); + } + case PostgreSQLDataType.textArray: { if (value is List) { @@ -487,6 +498,7 @@ class PostgresBinaryDecoder extends Converter { case PostgreSQLDataType.integerArray: return readListBytes(value, (reader, _) => reader.readInt32()); + case PostgreSQLDataType.varCharArray: case PostgreSQLDataType.textArray: return readListBytes(value, (reader, length) { return utf8.decode(length > 0 ? reader.read(length) : []); @@ -555,6 +567,7 @@ class PostgresBinaryDecoder extends Converter { 701: PostgreSQLDataType.double, 1007: PostgreSQLDataType.integerArray, 1009: PostgreSQLDataType.textArray, + 1015: PostgreSQLDataType.varCharArray, 1043: PostgreSQLDataType.varChar, 1022: PostgreSQLDataType.doubleArray, 1082: PostgreSQLDataType.date, diff --git a/lib/src/query.dart b/lib/src/query.dart index 982dd95b..eb2642a6 100644 --- a/lib/src/query.dart +++ b/lib/src/query.dart @@ -336,6 +336,7 @@ class PostgreSQLFormatIdentifier { '_text': PostgreSQLDataType.textArray, '_float8': PostgreSQLDataType.doubleArray, 'varchar': PostgreSQLDataType.varChar, + '_varchar': PostgreSQLDataType.varCharArray, '_jsonb': PostgreSQLDataType.jsonbArray, }; diff --git a/lib/src/substituter.dart b/lib/src/substituter.dart index bf84827d..16f29684 100644 --- a/lib/src/substituter.dart +++ b/lib/src/substituter.dart @@ -63,6 +63,8 @@ class PostgreSQLFormat { return '_float8'; case PostgreSQLDataType.varChar: return 'varchar'; + case PostgreSQLDataType.varCharArray: + return '_varchar'; case PostgreSQLDataType.jsonbArray: return '_jsonb'; default: diff --git a/lib/src/types.dart b/lib/src/types.dart index 0f7abb05..452a936b 100644 --- a/lib/src/types.dart +++ b/lib/src/types.dart @@ -92,6 +92,9 @@ enum PostgreSQLDataType { /// Must be a [String] varChar, + /// Must be a [List] + varCharArray, + /// Must be a [List] of encodable objects jsonbArray, } diff --git a/test/decode_test.dart b/test/decode_test.dart index a6ec4453..e9b89ec8 100644 --- a/test/decode_test.dart +++ b/test/decode_test.dart @@ -15,19 +15,20 @@ 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', " @@ -35,12 +36,13 @@ void main() { "'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(); @@ -79,6 +81,8 @@ void main() { expect(row1[20], equals([])); expect(row1[21], equals([])); expect(row1[22], equals([])); + expect(row1[23] is List, true); + expect(row1[23], equals(['a', 'b', 'c', 'd', 'e', 'f'])); // upper bound row expect(row2[0], equals(2147483647)); @@ -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 { diff --git a/test/encoding_test.dart b/test/encoding_test.dart index da3fafdf..529f08ab 100644 --- a/test/encoding_test.dart +++ b/test/encoding_test.dart @@ -401,6 +401,19 @@ void main() { } }); + test('varCharArray', () async { + await expectInverse([], 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')); + } + }); + test('textArray', () async { await expectInverse([], PostgreSQLDataType.textArray); await expectInverse(['', 'foo', 'foo\n'], PostgreSQLDataType.textArray); diff --git a/test/query_test.dart b/test/query_test.dart index 504221ca..f5bad919 100644 --- a/test/query_test.dart +++ b/test/query_test.dart @@ -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 @@ -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')},' @@ -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, @@ -154,6 +155,7 @@ void main() { 'a"\'\\"', {'k': 'v"\'\\"'} ], + 'va': ['a', 'b', 'c', 'd', 'e', 'f'] }); final expectedRow = [ @@ -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)},' @@ -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, @@ -241,6 +245,7 @@ void main() { 'a', {'k': 'v'} ], + 'va': ['a', 'b', 'c', 'd', 'e', 'f'] }); final expectedRow = [ @@ -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]); });