Add `date` and `timestamp` types for postgres

This commit is contained in:
Simon Binder 2023-10-12 00:03:01 +02:00
parent fae81d0456
commit 6aa1a5d734
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
3 changed files with 107 additions and 0 deletions

View File

@ -16,12 +16,24 @@ typedef UuidColumn = Column<UuidValue>;
typedef IntervalColumn = Column<Duration>; typedef IntervalColumn = Column<Duration>;
typedef JsonColumn = Column<Object>; typedef JsonColumn = Column<Object>;
typedef PointColumn = Column<PgPoint>; typedef PointColumn = Column<PgPoint>;
typedef TimestampColumn = Column<PgDateTime>;
typedef PgDateColumn = Column<PgDate>;
final class PgTypes { final class PgTypes {
PgTypes._(); PgTypes._();
static const CustomSqlType<UuidValue> uuid = UuidType(); static const CustomSqlType<UuidValue> uuid = UuidType();
static const CustomSqlType<Duration> interval = IntervalType(); static const CustomSqlType<Duration> interval = IntervalType();
static const CustomSqlType<PgDate> date = DateType(
PgDataType.date,
'date',
PgDate.fromDateTime,
);
static const CustomSqlType<PgDateTime> timestampNoTimezone = DateType(
PgDataType.timestampWithoutTimezone,
'timestamp without time zone',
PgDateTime.new,
);
static const CustomSqlType<Object> json = static const CustomSqlType<Object> json =
PostgresType(type: PgDataType.json, name: 'json'); PostgresType(type: PgDataType.json, name: 'json');
static const CustomSqlType<Object> jsonb = static const CustomSqlType<Object> jsonb =
@ -29,6 +41,67 @@ final class PgTypes {
static const CustomSqlType<PgPoint> point = PointType(); static const CustomSqlType<PgPoint> point = PointType();
} }
/// A wrapper for values with the Postgres `timestamp without timezone` and
/// `timestamp with timezone` types.
///
/// We can't use [DateTime] directly because drift expects to store them as
/// unix timestamp or text.
final class PgDateTime implements PgTimeValue {
final DateTime dateTime;
PgDateTime(this.dateTime);
@override
int get hashCode => dateTime.hashCode;
@override
bool operator ==(Object other) {
return other is PgDateTime && other.dateTime == dateTime;
}
@override
DateTime toDateTime() => dateTime;
@override
String toString() => dateTime.toString();
}
/// A wrapper for the Postgres `date` type, which stores dates (year, month,
/// days).
final class PgDate implements PgTimeValue {
final int year, month, day;
final DateTime _dateTime;
PgDate({required this.year, required this.month, required this.day})
: _dateTime = DateTime(year, month, day);
PgDate.fromDateTime(DateTime dateTime)
: _dateTime = dateTime,
year = dateTime.year,
month = dateTime.month,
day = dateTime.day;
@override
int get hashCode => Object.hash(year, month, day);
@override
bool operator ==(Object other) {
return other is PgDate &&
other.year == year &&
other.month == month &&
other.day == day;
}
@override
String toString() =>
'${year.toString().padLeft(4, '0')}-${month.toString().padLeft(2, '0')}-${day.toString().padLeft(2, '0')}';
@override
DateTime toDateTime() {
return _dateTime;
}
}
/// Calls the `gen_random_uuid` function in postgres. /// Calls the `gen_random_uuid` function in postgres.
Expression<UuidValue> genRandomUuid() { Expression<UuidValue> genRandomUuid() {
return FunctionCallExpression('gen_random_uuid', []); return FunctionCallExpression('gen_random_uuid', []);

View File

@ -65,3 +65,32 @@ class IntervalType extends PostgresType<Duration> {
return "'${dartValue.inMicroseconds} microseconds'::interval"; return "'${dartValue.inMicroseconds} microseconds'::interval";
} }
} }
abstract interface class PgTimeValue {
DateTime toDateTime();
}
class DateType<T extends PgTimeValue> extends PostgresType<T> {
final T Function(DateTime) _fromDateTime;
const DateType(
PgDataType type,
String name,
this._fromDateTime,
) : super(type: type, name: name);
@override
String mapToSqlLiteral(T dartValue) {
return "${PostgresType._encoder.convert(dartValue.toDateTime())}::$name";
}
@override
Object mapToSqlParameter(T dartValue) {
return PgTypedParameter(type, dartValue.toDateTime());
}
@override
T read(Object fromSql) {
return _fromDateTime(fromSql as DateTime);
}
}

View File

@ -49,5 +49,10 @@ void main() {
group('json', () => testWith(PgTypes.json, {'foo': 'bar'})); group('json', () => testWith(PgTypes.json, {'foo': 'bar'}));
group('jsonb', () => testWith(PgTypes.jsonb, {'foo': 'bar'})); group('jsonb', () => testWith(PgTypes.jsonb, {'foo': 'bar'}));
group('point', () => testWith(PgTypes.point, PgPoint(90, -90))); group('point', () => testWith(PgTypes.point, PgPoint(90, -90)));
group(
'timestamp without timezone',
() => testWith(PgTypes.timestampNoTimezone,
PgDateTime(DateTime.utc(1996, 7, 8, 10, 0, 0))),
);
}); });
} }