forked from isoos/postgresql-dart
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtypes.dart
455 lines (352 loc) · 13.3 KB
/
types.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
import 'dart:convert';
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/text_search.dart';
import 'types/type_registry.dart';
/// In Postgresql `interval` values are stored as [months], [days], and [microseconds].
/// This is done because the number of days in a month varies, and a day can
/// have 23 or 25 hours if a daylight savings time adjustment is involved.
///
/// Value from one field is never automatically translated to value of
/// another field, so <60*60*24 seconds> != <1 days> and so on.
@immutable
class Interval {
final int months;
final int days;
final int microseconds;
Interval({
this.months = 0,
this.days = 0,
this.microseconds = 0,
});
factory Interval.duration(Duration value) =>
Interval(microseconds: value.inMicroseconds);
late final _asStringParts = [
if (months != 0) '$months months',
if (days != 0) '$days days',
if (microseconds != 0) '$microseconds microseconds',
];
late final _asString =
_asStringParts.isEmpty ? '0 microseconds' : _asStringParts.join(' ');
@override
String toString() => _asString;
@override
int get hashCode => Object.hashAll([months, days, microseconds]);
@override
bool operator ==(Object other) {
return other is Interval &&
other.months == months &&
other.days == days &&
other.microseconds == microseconds;
}
}
/// Describes a generic bytes string value..
@immutable
class UndecodedBytes {
final int typeOid;
final bool isBinary;
final Uint8List bytes;
final Encoding encoding;
UndecodedBytes({
required this.typeOid,
required this.isBinary,
required this.bytes,
required this.encoding,
});
late final asString = encoding.decode(bytes);
}
/// LSN is a PostgreSQL Log Sequence Number.
///
/// For more details, see: https://www.postgresql.org/docs/current/datatype-pg-lsn.html
class LSN {
final int value;
/// Construct an LSN from a 64-bit integer.
LSN(this.value);
/// Construct an LSN from XXX/XXX format used by PostgreSQL
LSN.fromString(String string) : value = _parseLSNString(string);
/// Formats the LSN value into the XXX/XXX format which is the text format
/// used by PostgreSQL.
@override
String toString() {
return '${(value >> 32).toRadixString(16).toUpperCase()}/${value.toUnsigned(32).toRadixString(16).toUpperCase()}';
}
static int _parseLSNString(String string) {
final halves = string.split('/');
if (halves.length != 2) {
throw FormatException('Invalid LSN String was given ($string)');
}
final upperhalf = int.parse(halves[0], radix: 16) << 32;
final lowerhalf = int.parse(halves[1], radix: 16);
return upperhalf + lowerhalf;
}
LSN operator +(LSN other) {
return LSN(value + other.value);
}
LSN operator -(LSN other) {
return LSN(value + other.value);
}
@override
bool operator ==(covariant LSN other) {
if (identical(this, other)) return true;
return other.value == value;
}
@override
int get hashCode => value.hashCode;
}
/// 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
@immutable
@sealed
final 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.
factory Time([
int hour = 0,
int minute = 0,
int second = 0,
int millisecond = 0,
int microsecond = 0,
]) {
return Time.fromMicroseconds(hour * Duration.microsecondsPerHour +
minute * Duration.microsecondsPerMinute +
second * Duration.microsecondsPerSecond +
millisecond * Duration.microsecondsPerMillisecond +
microsecond);
}
_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;
/// The minute `[0...59]`.
int get minute => utcDateTime.minute;
/// 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 Time && microseconds == other.microseconds;
@override
int get hashCode => microseconds;
}
/// Supported data types.
abstract class Type<T extends Object> {
/// Used to represent value without any type representation.
static const unspecified = UnspecifiedType();
/// Must be a [String]
static const character = GenericType<String>(TypeOid.character);
/// Must be a [String].
static const text = GenericType<String>(TypeOid.text);
/// Must be an [int] (4-byte integer)
static const integer = GenericType<int>(TypeOid.integer);
/// Must be an [int] (2-byte integer)
static const smallInteger = GenericType<int>(TypeOid.smallInteger);
/// Must be an [int] (8-byte integer)
static const bigInteger = GenericType<int>(TypeOid.bigInteger);
/// Must be an [int] (autoincrementing 4-byte integer)
static const serial = integer;
/// Must be an [int] (autoincrementing 8-byte integer)
static const bigSerial = bigInteger;
/// Must be a [double] (32-bit floating point value)
static const real = GenericType<core.double>(TypeOid.real);
/// Must be a [double] (64-bit floating point value)
static const double = GenericType<core.double>(TypeOid.double);
/// Must be a [bool]
static const boolean = GenericType<bool>(TypeOid.boolean);
/// Must be a [Time]
static const time = GenericType<Time>(TypeOid.time);
/// Must be a [DateTime] (microsecond date and time precision)
static const timestamp =
GenericType<DateTime>(TypeOid.timestampWithoutTimezone);
/// Please use [Type.timestamp] instead.
static const timestampWithoutTimezone = timestamp;
/// Must be a [DateTime] (microsecond date and time precision)
static const timestampTz =
GenericType<DateTime>(TypeOid.timestampWithTimezone);
/// Please use [Type.timestampTz] instead.
static const timestampWithTimezone = timestampTz;
/// Must be a [Interval]
static const interval = GenericType<Interval>(TypeOid.interval);
/// An arbitrary-precision number.
///
/// This library supports encoding numbers in a textual format, or when
/// passed as [int] or [double]. When decoding values, numeric types are
/// always returned as string.
static const numeric = GenericType<Object>(TypeOid.numeric);
/// Must be a [DateTime] (contains year, month and day only)
static const date = GenericType<DateTime>(TypeOid.date);
/// Must be encodable via [json.encode].
///
/// Values will be encoded via [json.encode] before being sent to the database.
static const jsonb = GenericType(TypeOid.jsonb);
/// Must be encodable via [core.json.encode].
///
/// Values will be encoded via [core.json.encode] before being sent to the database.
static const json = GenericType(TypeOid.json);
/// Must be a [List] of [int].
///
/// Each element of the list must fit into a byte (0-255).
static const byteArray = GenericType<List<int>>(TypeOid.byteArray);
/// Must be a [String]
///
/// Used for internal pg structure names
static const name = GenericType<String>(TypeOid.name);
/// Must be a [String].
///
/// Must contain 32 hexadecimal characters. May contain any number of '-' characters.
/// When returned from database, format will be xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
static const uuid = GenericType<String>(TypeOid.uuid);
/// Must be a [List<String>].
static const uuidArray = GenericType<List<String>>(TypeOid.uuidArray);
/// Must be a [Point]
static const point = GenericType<Point>(TypeOid.point);
/// Must be a [Line]
static const line = GenericType<Line>(TypeOid.line);
/// Must be a [LineSegment]
static const lineSegment = GenericType<LineSegment>(TypeOid.lineSegment);
/// Must be a [Box]
static const box = GenericType<Box>(TypeOid.box);
/// Must be a [Polygon]
static const polygon = GenericType<Polygon>(TypeOid.polygon);
/// Must be a [Path]
static const path = GenericType<Path>(TypeOid.path);
/// Must be a [Circle]
static const circle = GenericType<Circle>(TypeOid.circle);
/// Must be a [List<bool>]
static const booleanArray = GenericType<List<bool>>(TypeOid.booleanArray);
/// Must be a [List<int>]
static const smallIntegerArray =
GenericType<List<int>>(TypeOid.smallIntegerArray);
/// Must be a [List<int>]
static const integerArray = GenericType<List<int>>(TypeOid.integerArray);
/// Must be a [List<int>]
static const bigIntegerArray =
GenericType<List<int>>(TypeOid.bigIntegerArray);
/// Must be a [List<String>]
static const textArray = GenericType<List<String>>(TypeOid.textArray);
/// Must be a [List<double>]
static const doubleArray =
GenericType<List<core.double>>(TypeOid.doubleArray);
/// Must be a [List<DateTime>]
static const dateArray = GenericType<List<DateTime>>(TypeOid.dateArray);
/// Must be a [List<Time>]
static const timeArray = GenericType<List<Time>>(TypeOid.timeArray);
/// Must be a [List<DateTime>] (microsecond date and time precision)
static const timestampArray =
GenericType<List<DateTime>>(TypeOid.timestampArray);
/// Must be a [List<DateTime>] (microsecond date and time precision)
static const timestampTzArray =
GenericType<List<DateTime>>(TypeOid.timestampTzArray);
/// Must be a [TsQuery].
static const tsquery = TsQueryType();
/// Must be a [TsVector].
static const tsvector = TsVectorType();
/// Must be a [String]
static const varChar = GenericType<String>(TypeOid.varChar);
/// Must be a [List<String>]
static const varCharArray = GenericType<List<String>>(TypeOid.varCharArray);
/// Must be a [List] of encodable objects
static const jsonbArray = GenericType<List>(TypeOid.jsonbArray);
/// Must be a [Type].
static const regtype = GenericType<Type>(TypeOid.regtype);
/// Impossible to bind to, always null when read.
static const voidType = GenericType<Object>(TypeOid.voidType);
/// Must be a [IntRange]
static const integerRange = GenericType<IntRange>(TypeOid.integerRange);
/// Must be a [IntRange]
static const bigIntegerRange = GenericType<IntRange>(TypeOid.bigIntegerRange);
/// Must be a [DateRange]
static const dateRange = GenericType<DateRange>(TypeOid.dateRange);
/// Must be a [Range<Object>]
///
/// Supported element types are the same as for [Type.numeric].
// static const numrange =
// GenericType<ContinuousRange<String>>(TypeOid.numrange);
/// Must be a [Range<DateTime>]
static const timestampRange =
GenericType<DateTimeRange>(TypeOid.timestampRange);
/// Must be a [Range<DateTime>]
static const timestampTzRange =
GenericType<DateTimeRange>(TypeOid.timestampTzRange);
/// The object ID of this data type.
final int? oid;
const Type(this.oid);
TypedValue<T> value(T value) => TypedValue<T>(this, value);
@override
String toString() => 'Type(oid:$oid)';
@override
int get hashCode => oid ?? -1;
@override
bool operator ==(Object other) {
return (other is Type) && (other.oid == oid);
}
}
class TypedValue<T extends Object> {
final Type<T> type;
final T? value;
/// Whether this value is deliberately describing a `NULL` value in SQL.
///
/// For some types, in particular `json` and `jsonb`, the `null` value in Dart
/// can be mapped to a non-null SQL value (a textual or binary representation
/// of the `null` value in JSON, respectively).
final bool isSqlNull;
TypedValue(this.type, this.value, {bool? isSqlNull})
: isSqlNull = isSqlNull ?? (value == null) {
if (isSqlNull == true && value != null) {
throw core.ArgumentError(
'Using `isSqlNull: true` indicates that a Dart value should be bound '
'to `NULL` in SQL. This only makes sense for values that are already '
'`null` in Dart.',
);
}
}
@override
int get hashCode => Object.hash(type, value, isSqlNull);
@override
bool operator ==(Object other) {
return other is TypedValue &&
other.type == type &&
other.value == value &&
other.isSqlNull == isSqlNull;
}
@override
String toString() {
return 'TypedValue($type, $value)';
}
}