diff --git a/CHANGELOG.md b/CHANGELOG.md index 44e887a..2793527 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 3.0.9 + +Added the following PostgreSQL builtin types: +- Geometric types `line`, `lseg`,`path`,`polygon`,`box`,`circle` +- Range types `int4range`, `int8range`, `daterange`, `tsrange`,`tstzrange` +- Time type `time` + ## 3.0.8 - Properly react to connection losses by reporting the database connection as diff --git a/lib/postgres.dart b/lib/postgres.dart index 85f2b5c..98e8b58 100644 --- a/lib/postgres.dart +++ b/lib/postgres.dart @@ -17,6 +17,8 @@ export 'src/exceptions.dart'; export 'src/pool/pool_api.dart'; export 'src/replication.dart'; export 'src/types.dart'; +export 'src/types/geo_types.dart'; +export 'src/types/range_types.dart'; export 'src/types/type_registry.dart' show TypeRegistry; /// A description of a SQL query as interpreted by this package. @@ -227,6 +229,7 @@ abstract class ResultStream implements Stream { abstract class ResultStreamSubscription implements StreamSubscription { Future get affectedRows; + Future get schema; } @@ -335,7 +338,9 @@ abstract class Channels { Stream get all; Stream operator [](String channel); + Future notify(String channel, [String? payload]); + Future cancelAll(); } @@ -415,6 +420,7 @@ enum SslMode { ; bool get ignoreCertificateIssues => this == SslMode.require; + bool get allowCleartextPassword => this == SslMode.disable; } diff --git a/lib/src/messages/server_messages.dart b/lib/src/messages/server_messages.dart index 5ee0f51..77edda7 100644 --- a/lib/src/messages/server_messages.dart +++ b/lib/src/messages/server_messages.dart @@ -409,7 +409,7 @@ class UnknownMessage extends ServerMessage { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { if (other is! UnknownMessage) { return false; } diff --git a/lib/src/types.dart b/lib/src/types.dart index 2d7d186..e7ccded 100644 --- a/lib/src/types.dart +++ b/lib/src/types.dart @@ -1,11 +1,13 @@ import 'dart:convert'; -import 'dart:core'; import 'dart:core' as core; +import 'dart:core'; import 'dart:typed_data'; import 'package:meta/meta.dart'; import 'types/generic_type.dart'; +import 'types/geo_types.dart'; +import 'types/range_types.dart'; import 'types/type_registry.dart'; /// In Postgresql `interval` values are stored as [months], [days], and [microseconds]. @@ -119,27 +121,84 @@ class LSN { int get hashCode => value.hashCode; } -/// Describes PostgreSQL's geometric type: `point`. -@immutable -class Point { - /// also referred as `x` - final double latitude; +/// Describes PostgreSQL's `time without time zone` type. +/// +/// See https://www.postgresql.org/docs/current/datatype-datetime.html +/// +/// `time with time zone` is not implemented as Postgres wiki recommends against its use: +/// +/// https://wiki.postgresql.org/wiki/Don't_Do_This#Don.27t_use_timetz +class Time { + /// The time in microseconds + late final int microseconds; + + /// Construct a [Time] instance from [microseconds]. + /// + /// [microseconds] must be positive and not resolve to a time larger than 24:00:00.000000. + Time.fromMicroseconds(this.microseconds) { + _check(); + } + + /// Construct a [Time] instance. + /// + /// [Time] value must be positive and not larger than 24:00:00.000000. + Time( + [int hour = 0, + int minute = 0, + int second = 0, + int millisecond = 0, + int microsecond = 0]) { + microseconds = hour * Duration.microsecondsPerHour + + minute * Duration.microsecondsPerMinute + + second * Duration.microsecondsPerSecond + + millisecond * Duration.microsecondsPerMillisecond + + microsecond; + _check(); + } + + _check() { + if (microseconds > Duration.microsecondsPerDay) { + throw ArgumentError( + 'Time: values greater than 24:00:00.000000 are not allowed'); + } + if (microseconds < 0) { + throw ArgumentError('Time: negative value not allowed'); + } + } + + DateTime get utcDateTime => + DateTime.fromMicrosecondsSinceEpoch(microseconds, isUtc: true); + + /// The hour of the day, expressed as in a 25-hour clock `0...24`. + int get hour => + microseconds == Duration.microsecondsPerDay ? 24 : utcDateTime.hour; - /// also referred as `y` - final double longitude; + /// The minute `[0...59]`. + int get minute => utcDateTime.minute; - const Point(this.latitude, this.longitude); + /// The second `[0...59]`. + int get second => utcDateTime.second; + + /// The millisecond `[0...999]`. + int get millisecond => utcDateTime.millisecond; + + /// The microsecond `[0...999]`. + int get microsecond => utcDateTime.microsecond; + + @override + String toString() => microseconds == Duration.microsecondsPerDay + ? 'Time(24:00:00.000)' + : 'Time(${utcDateTime.toIso8601String().split('T')[1].replaceAll('Z', '')})'; @override bool operator ==(Object other) => identical(this, other) || - other is Point && + other is Time && runtimeType == other.runtimeType && - latitude == other.latitude && - longitude == other.longitude; + microseconds == other.microseconds; @override - int get hashCode => Object.hash(latitude, longitude); + int get hashCode => microseconds; } /// Supported data types. @@ -177,6 +236,9 @@ abstract class Type { /// Must be a [bool] static const boolean = GenericType(TypeOid.boolean); + /// Must be a [Time] + static const time = GenericType