From 04d7e8458431e67e5d2de93d3a1eb65054538d93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Str=C3=B6mstedt?= Date: Fri, 17 May 2024 06:29:00 +0200 Subject: [PATCH 1/4] Fixed double/real + added timestamp, numeric, date & json --- lib/src/types/text_codec.dart | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/src/types/text_codec.dart b/lib/src/types/text_codec.dart index c6911d5..db88867 100644 --- a/lib/src/types/text_codec.dart +++ b/lib/src/types/text_codec.dart @@ -138,8 +138,7 @@ class PostgresTextEncoder { final timezoneMinuteOffset = value.timeZoneOffset.inMinutes % 60; var hourComponent = timezoneHourOffset.abs().toString().padLeft(2, '0'); - final minuteComponent = - timezoneMinuteOffset.abs().toString().padLeft(2, '0'); + final minuteComponent = timezoneMinuteOffset.abs().toString().padLeft(2, '0'); if (timezoneHourOffset >= 0) { hourComponent = '+$hourComponent'; @@ -210,8 +209,7 @@ class PostgresTextEncoder { if (type == Map) { return '{${value.map((s) { - final escaped = - json.encode(s).replaceAll(r'\', r'\\').replaceAll('"', r'\"'); + final escaped = json.encode(s).replaceAll(r'\', r'\\').replaceAll('"', r'\"'); return '"$escaped"'; }).join(',')}}'; @@ -240,7 +238,7 @@ class PostgresTextDecoder { return int.parse(di.asText); case TypeOid.real: case TypeOid.double: - return num.parse(di.asText); + return double.parse(di.asText); case TypeOid.boolean: // In text data format when using simple query protocol, "true" & "false" // are represented as `t` and `f`, respectively. @@ -255,12 +253,27 @@ class PostgresTextDecoder { case TypeOid.timestampWithTimezone: case TypeOid.timestampWithoutTimezone: + final String strTimeStamp = utf8.decode(di.bytes) + 'Z'; + return DateTime.parse(strTimeStamp); + case TypeOid.interval: + return utf8.decode(di.bytes); + case TypeOid.numeric: - case TypeOid.byteArray: + return double.parse(di.asText); + case TypeOid.date: + final String strTimeStamp = utf8.decode(di.bytes) + 'T00:00:00Z'; + final DateTime dt = DateTime.parse(strTimeStamp); + return dt; + case TypeOid.json: case TypeOid.jsonb: + final String strJSON = utf8.decode(di.bytes); + return (jsonDecode(strJSON)); + break; + + case TypeOid.byteArray: case TypeOid.uuid: case TypeOid.point: case TypeOid.booleanArray: From 229f9f31ed42a0da856f493311b97339a1fb147c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Str=C3=B6mstedt?= Date: Fri, 17 May 2024 11:36:56 +0200 Subject: [PATCH 2/4] Bumped version, added changelog + fixed lints --- CHANGELOG.md | 8 ++++++++ lib/src/types/text_codec.dart | 7 +++---- pubspec.yaml | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6764994..0fde92f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 3.2.1 + +- Added some conversions for queryMode: QueryMode.simple (not complete): + `timestampWithTimezone`, `timestampWithoutTimezone`, `date`, `interval`, `numeric`, `json`, `jsonb` + +- Fixed some conversions for queryMode: QueryMode.simple: + `real`, `double` + ## 3.2.0 - Support for `tsvector` and `tsquery` types. diff --git a/lib/src/types/text_codec.dart b/lib/src/types/text_codec.dart index db88867..01fa090 100644 --- a/lib/src/types/text_codec.dart +++ b/lib/src/types/text_codec.dart @@ -253,7 +253,7 @@ class PostgresTextDecoder { case TypeOid.timestampWithTimezone: case TypeOid.timestampWithoutTimezone: - final String strTimeStamp = utf8.decode(di.bytes) + 'Z'; + final String strTimeStamp = '${utf8.decode(di.bytes)}Z'; return DateTime.parse(strTimeStamp); case TypeOid.interval: @@ -263,15 +263,14 @@ class PostgresTextDecoder { return double.parse(di.asText); case TypeOid.date: - final String strTimeStamp = utf8.decode(di.bytes) + 'T00:00:00Z'; + final String strTimeStamp = '${utf8.decode(di.bytes)}T00:00:00Z'; final DateTime dt = DateTime.parse(strTimeStamp); return dt; case TypeOid.json: case TypeOid.jsonb: final String strJSON = utf8.decode(di.bytes); - return (jsonDecode(strJSON)); - break; + return jsonDecode(strJSON); case TypeOid.byteArray: case TypeOid.uuid: diff --git a/pubspec.yaml b/pubspec.yaml index 304bd71..922aab0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: postgres description: PostgreSQL database driver. Supports statement reuse and binary protocol and connection pooling. -version: 3.2.0 +version: 3.2.1 homepage: https://github.com/isoos/postgresql-dart topics: - sql From 3d6f62305611fd0114dffb63baa8029c5d9b2fbf Mon Sep 17 00:00:00 2001 From: Istvan Soos Date: Fri, 17 May 2024 19:51:44 +0200 Subject: [PATCH 3/4] Tests + fixes --- CHANGELOG.md | 8 ++--- lib/src/types/text_codec.dart | 22 +++++------- test/decode_test.dart | 63 +++++++++++++++++++++++++++++++++++ test/docker.dart | 2 ++ 4 files changed, 77 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fde92f..092ea70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,9 @@ ## 3.2.1 -- Added some conversions for queryMode: QueryMode.simple (not complete): - `timestampWithTimezone`, `timestampWithoutTimezone`, `date`, `interval`, `numeric`, `json`, `jsonb` - -- Fixed some conversions for queryMode: QueryMode.simple: - `real`, `double` +- Added or fixed decoders support for `QueryMode.simple`: + `double`, `real`, `timestampWithTimezone`, `timestampWithoutTimezone`, + `date`, `numeric`, `json`, `jsonb` [#338](https://github.com/isoos/postgresql-dart/pull/338) by [pst9354](https://github.com/pst9354). ## 3.2.0 diff --git a/lib/src/types/text_codec.dart b/lib/src/types/text_codec.dart index 01fa090..acd385e 100644 --- a/lib/src/types/text_codec.dart +++ b/lib/src/types/text_codec.dart @@ -138,7 +138,8 @@ class PostgresTextEncoder { final timezoneMinuteOffset = value.timeZoneOffset.inMinutes % 60; var hourComponent = timezoneHourOffset.abs().toString().padLeft(2, '0'); - final minuteComponent = timezoneMinuteOffset.abs().toString().padLeft(2, '0'); + final minuteComponent = + timezoneMinuteOffset.abs().toString().padLeft(2, '0'); if (timezoneHourOffset >= 0) { hourComponent = '+$hourComponent'; @@ -209,7 +210,8 @@ class PostgresTextEncoder { if (type == Map) { return '{${value.map((s) { - final escaped = json.encode(s).replaceAll(r'\', r'\\').replaceAll('"', r'\"'); + final escaped = + json.encode(s).replaceAll(r'\', r'\\').replaceAll('"', r'\"'); return '"$escaped"'; }).join(',')}}'; @@ -253,25 +255,19 @@ class PostgresTextDecoder { case TypeOid.timestampWithTimezone: case TypeOid.timestampWithoutTimezone: - final String strTimeStamp = '${utf8.decode(di.bytes)}Z'; - return DateTime.parse(strTimeStamp); - - case TypeOid.interval: - return utf8.decode(di.bytes); + return DateTime.parse(di.asText); case TypeOid.numeric: - return double.parse(di.asText); + return di.asText; case TypeOid.date: - final String strTimeStamp = '${utf8.decode(di.bytes)}T00:00:00Z'; - final DateTime dt = DateTime.parse(strTimeStamp); - return dt; + return DateTime.parse(di.asText); case TypeOid.json: case TypeOid.jsonb: - final String strJSON = utf8.decode(di.bytes); - return jsonDecode(strJSON); + return jsonDecode(di.asText); + case TypeOid.interval: case TypeOid.byteArray: case TypeOid.uuid: case TypeOid.point: diff --git a/test/decode_test.dart b/test/decode_test.dart index 8597523..ad7ebba 100644 --- a/test/decode_test.dart +++ b/test/decode_test.dart @@ -4,6 +4,69 @@ import 'package:test/test.dart'; import 'docker.dart'; void main() { + withPostgresServer('simple query protocol decode', (server) { + late Connection conn; + setUp(() async { + conn = await server.newConnection(queryMode: QueryMode.simple); + }); + tearDown(() async { + await conn.close(); + }); + + test('double', () async { + final rs = await conn.execute('SELECT 3.14::DOUBLE PRECISION'); + expect(rs.single.single, 3.14); + }); + + test('timestamp with time zones', () async { + final rs = await conn.execute( + "SELECT '1999-01-08 04:05:06 -8:00'::TIMESTAMP WITH TIME ZONE"); + final item = rs.single.single as DateTime; + expect(item.toIso8601String(), '1999-01-08T12:05:06.000Z'); + }); + + test('timestamp without time zones', () async { + final rs = await conn + .execute("SELECT '1999-01-08 04:05:06'::TIMESTAMP WITHOUT TIME ZONE"); + final item = rs.single.single as DateTime; + expect(item.toIso8601String(), '1999-01-08T04:05:06.000'); + }); + + test('interval', () async { + final rs = await conn.execute( + "SELECT '2 years 15 months 100 weeks 99 hours 123456789 milliseconds'::INTERVAL"); + final item = rs.single.single; + expect((item as UndecodedBytes).asString, + '3 years 3 mons 700 days 133:17:36.789'); + // should be: + // expect(item, Interval(months: 39, days: 700, microseconds: 479856789000)); + }); + + test('numeric', () async { + final rs = await conn.execute('SELECT 3.141::NUMERIC(5,2)'); + final item = rs.single.single; + expect(item, '3.14'); + }); + + test('date', () async { + final rs = await conn.execute("SELECT '1999-01-08'::DATE"); + final item = rs.single.single as DateTime; + expect(item.toIso8601String(), '1999-01-08T00:00:00.000'); + }); + + test('json', () async { + final rs = await conn.execute("SELECT '{\"a\": 1}'::JSON"); + final item = rs.single.single; + expect(item, {'a': 1}); + }); + + test('jsonb', () async { + final rs = await conn.execute("SELECT '{\"a\": 1}'::JSONB"); + final item = rs.single.single; + expect(item, {'a': 1}); + }); + }); + withPostgresServer('decode', (server) { late Connection connection; setUp(() async { diff --git a/test/docker.dart b/test/docker.dart index d8b6373..6338fc6 100644 --- a/test/docker.dart +++ b/test/docker.dart @@ -62,6 +62,7 @@ class PostgresServer { Future newConnection({ ReplicationMode replicationMode = ReplicationMode.none, SslMode? sslMode, + QueryMode? queryMode, }) async { return Connection.open( await endpoint(), @@ -71,6 +72,7 @@ class PostgresServer { replicationMode: replicationMode, transformer: loggingTransformer('conn'), sslMode: sslMode, + queryMode: queryMode, ), ); } From 57c05d177ef600d642fb380bedc18ec0420c8446 Mon Sep 17 00:00:00 2001 From: Istvan Soos Date: Sat, 18 May 2024 09:47:54 +0200 Subject: [PATCH 4/4] Converting DateTimes to UTC without string modification. --- lib/src/types/text_codec.dart | 15 +++++++++++++-- test/decode_test.dart | 4 ++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/src/types/text_codec.dart b/lib/src/types/text_codec.dart index acd385e..2d38740 100644 --- a/lib/src/types/text_codec.dart +++ b/lib/src/types/text_codec.dart @@ -255,13 +255,24 @@ class PostgresTextDecoder { case TypeOid.timestampWithTimezone: case TypeOid.timestampWithoutTimezone: - return DateTime.parse(di.asText); + final raw = DateTime.parse(di.asText); + return DateTime.utc( + raw.year, + raw.month, + raw.day, + raw.hour, + raw.minute, + raw.second, + raw.millisecond, + raw.microsecond, + ); case TypeOid.numeric: return di.asText; case TypeOid.date: - return DateTime.parse(di.asText); + final raw = DateTime.parse(di.asText); + return DateTime.utc(raw.year, raw.month, raw.day); case TypeOid.json: case TypeOid.jsonb: diff --git a/test/decode_test.dart b/test/decode_test.dart index ad7ebba..1bcce74 100644 --- a/test/decode_test.dart +++ b/test/decode_test.dart @@ -29,7 +29,7 @@ void main() { final rs = await conn .execute("SELECT '1999-01-08 04:05:06'::TIMESTAMP WITHOUT TIME ZONE"); final item = rs.single.single as DateTime; - expect(item.toIso8601String(), '1999-01-08T04:05:06.000'); + expect(item.toIso8601String(), '1999-01-08T04:05:06.000Z'); }); test('interval', () async { @@ -51,7 +51,7 @@ void main() { test('date', () async { final rs = await conn.execute("SELECT '1999-01-08'::DATE"); final item = rs.single.single as DateTime; - expect(item.toIso8601String(), '1999-01-08T00:00:00.000'); + expect(item.toIso8601String(), '1999-01-08T00:00:00.000Z'); }); test('json', () async {