@@ -137,16 +137,18 @@ function pqparse end
137
137
# Fallback method
138
138
pqparse (:: Type{T} , str:: AbstractString ) where T = parse (T, str)
139
139
140
+ function pqparse (:: Type{T} , ptr:: Ptr{UInt8} ) where T<: Number
141
+ return ntoh (unsafe_load (Ptr {T} (ptr)))
142
+ end
143
+
140
144
# allow parsing as a Symbol anything which works as a String
141
145
pqparse (:: Type{Symbol} , str:: AbstractString ) = Symbol (str)
142
146
143
147
function generate_binary_parser (symbol)
144
148
@eval function Base. parse (
145
149
:: Type{T} , pqv:: PQBinaryValue{$(oid(symbol))}
146
150
) where T<: Number
147
- return convert (
148
- T, ntoh (unsafe_load (Ptr {$(_DEFAULT_TYPE_MAP[symbol])} (data_pointer (pqv))))
149
- )
151
+ return convert (T, pqparse ($ (_DEFAULT_TYPE_MAP[symbol]), data_pointer (pqv)))
150
152
end
151
153
end
152
154
@@ -316,14 +318,92 @@ function pqparse(::Type{InfExtendedTime{T}}, str::AbstractString) where T<:Dates
316
318
end
317
319
318
320
# UNIX timestamps
319
- function Base. parse (:: Type{DateTime} , pqv:: PQTextValue {PQ_SYSTEM_TYPES[:int8]} )
321
+ function Base. parse (:: Type{DateTime} , pqv:: PQValue {PQ_SYSTEM_TYPES[:int8]} )
320
322
return unix2datetime (parse (Int64, pqv))
321
323
end
322
324
323
- function Base. parse (:: Type{ZonedDateTime} , pqv:: PQTextValue {PQ_SYSTEM_TYPES[:int8]} )
325
+ function Base. parse (:: Type{ZonedDateTime} , pqv:: PQValue {PQ_SYSTEM_TYPES[:int8]} )
324
326
return TimeZones. unix2zdt (parse (Int64, pqv))
325
327
end
326
328
329
+ # All postgresql timestamptz are stored in UTC time with the epoch of 2000-01-01.
330
+ const POSTGRES_EPOCH_DATE = Date (" 2000-01-01" )
331
+ const POSTGRES_EPOCH_DATETIME = DateTime (" 2000-01-01" )
332
+
333
+ # Note: Because postgresql stores the values as a Microsecond in Int64, the max (infinite)
334
+ # value of date time in postgresql when querying binary is 294277-01-09T04:00:54.775
335
+ # and the minimum is -290278-12-22T19:59:05.225.
336
+ function pqparse (:: Type{ZonedDateTime} , ptr:: Ptr{UInt8} )
337
+ value = ntoh (unsafe_load (Ptr {Int64} (ptr)))
338
+ if value == typemax (Int64)
339
+ depwarn_timetype_inf ()
340
+ return ZonedDateTime (typemax (DateTime), tz " UTC" )
341
+ elseif value == typemin (Int64)
342
+ depwarn_timetype_inf ()
343
+ return ZonedDateTime (typemin (DateTime), tz " UTC" )
344
+ end
345
+ dt = POSTGRES_EPOCH_DATETIME + Microsecond (value)
346
+ return ZonedDateTime (dt, tz " UTC" ; from_utc= true )
347
+ end
348
+
349
+ function pqparse (:: Type{DateTime} , ptr:: Ptr{UInt8} )
350
+ value = ntoh (unsafe_load (Ptr {Int64} (ptr)))
351
+ if value == typemax (Int64)
352
+ depwarn_timetype_inf ()
353
+ return typemax (DateTime)
354
+ elseif value == typemin (Int64)
355
+ depwarn_timetype_inf ()
356
+ return typemin (DateTime)
357
+ end
358
+ return POSTGRES_EPOCH_DATETIME + Microsecond (ntoh (unsafe_load (Ptr {Int64} (ptr))))
359
+ end
360
+
361
+ function pqparse (:: Type{Date} , ptr:: Ptr{UInt8} )
362
+ value = ntoh (unsafe_load (Ptr {Int32} (ptr)))
363
+ if value == typemax (Int32)
364
+ depwarn_timetype_inf ()
365
+ return typemax (Date)
366
+ elseif value == typemin (Int32)
367
+ depwarn_timetype_inf ()
368
+ return typemin (Date)
369
+ end
370
+ return POSTGRES_EPOCH_DATE + Day (value)
371
+ end
372
+
373
+ function pqparse (
374
+ :: Type{InfExtendedTime{T}} , ptr:: Ptr{UInt8}
375
+ ) where T<: Dates.AbstractDateTime
376
+ microseconds = ntoh (unsafe_load (Ptr {Int64} (ptr)))
377
+ if microseconds == typemax (Int64)
378
+ return InfExtendedTime {T} (∞)
379
+ elseif microseconds == typemin (Int64)
380
+ return InfExtendedTime {T} (- ∞)
381
+ end
382
+
383
+ return InfExtendedTime {T} (pqparse (T, ptr))
384
+ end
385
+
386
+ function pqparse (:: Type{InfExtendedTime{T}} , ptr:: Ptr{UInt8} ) where T<: Date
387
+ microseconds = ntoh (unsafe_load (Ptr {Int32} (ptr)))
388
+ if microseconds == typemax (Int32)
389
+ return InfExtendedTime {T} (∞)
390
+ elseif microseconds == typemin (Int32)
391
+ return InfExtendedTime {T} (- ∞)
392
+ end
393
+
394
+ return InfExtendedTime {T} (pqparse (T, ptr))
395
+ end
396
+
397
+ function generate_binary_date_parser (symbol)
398
+ @eval function Base. parse (
399
+ :: Type{T} , pqv:: PQBinaryValue{$(oid(symbol))}
400
+ ) where T<: TimeType
401
+ return pqparse (T, data_pointer (pqv))
402
+ end
403
+ end
404
+
405
+ foreach (generate_binary_date_parser, (:timestamptz , :timestamp , :date ))
406
+
327
407
# # intervals
328
408
# iso_8601
329
409
_DEFAULT_TYPE_MAP[:interval ] = Dates. CompoundPeriod
@@ -417,6 +497,56 @@ function pqparse(::Type{Interval{T}}, str::AbstractString) where T
417
497
return parse (Interval{T}, str; element_parser= pqparse)
418
498
end
419
499
500
+ # How to parse range binary fetch is shown here
501
+ # https://github.com/postgres/postgres/blob/31079a4a8e66e56e48bad94d380fa6224e9ffa0d/src/backend/utils/adt/rangetypes.c#L162
502
+ const RANGE_EMPTY = 0b00000001
503
+ const RANGE_LOWER_BOUND_INCLUSIVE = 0b00000010
504
+ const RANGE_UPPER_BOUND_INCLUSIVE = 0b00000100
505
+ const RANGE_LOWER_BOUND_INFINITIY = 0b00001000
506
+ const RANGE_UPPER_BOUND_INFINITIY = 0b00010000
507
+ const RANGE_LOWER_BOUND_NULL = 0b00100000
508
+ const RANGE_UPPER_BOUND_NULL = 0b01000000
509
+
510
+ function generate_range_binary_parser (symbol)
511
+ @eval function Base. parse (
512
+ :: Type{Interval{T}} , pqv:: PQBinaryValue{$(oid(symbol))}
513
+ ) where T
514
+ current_pointer = data_pointer (pqv)
515
+ flags = ntoh (unsafe_load (Ptr {UInt8} (current_pointer)))
516
+ current_pointer += sizeof (UInt8)
517
+
518
+ Bool (flags & RANGE_EMPTY) && return Interval {T} ()
519
+
520
+ lower_value = nothing
521
+ lower_bound = Unbounded
522
+ # if there is a lower bound
523
+ if iszero (flags & (RANGE_LOWER_BOUND_INFINITIY | RANGE_LOWER_BOUND_NULL))
524
+ lower_value_length = ntoh (unsafe_load (Ptr {UInt32} (current_pointer)))
525
+ current_pointer += sizeof (UInt32)
526
+ lower_value = pqparse (T, current_pointer)
527
+ current_pointer += lower_value_length
528
+ lower_bound = ! iszero (flags & RANGE_LOWER_BOUND_INCLUSIVE) ? Closed : Open
529
+ end
530
+
531
+ upper_value = nothing
532
+ upper_bound = Unbounded
533
+ # if there is a upper bound
534
+ if iszero (flags & (RANGE_UPPER_BOUND_INFINITIY | RANGE_UPPER_BOUND_NULL))
535
+ upper_value_length = ntoh (unsafe_load (Ptr {UInt32} (current_pointer)))
536
+ current_pointer += sizeof (UInt32)
537
+ upper_value = pqparse (T, current_pointer)
538
+ current_pointer += upper_value_length
539
+ upper_bound = ! iszero (flags & RANGE_UPPER_BOUND_INCLUSIVE) ? Closed : Open
540
+ end
541
+
542
+ return Interval {T,lower_bound,upper_bound} (lower_value, upper_value)
543
+ end
544
+ end
545
+
546
+ foreach (
547
+ generate_range_binary_parser, (:int4range , :int8range , :tsrange , :tstzrange , :daterange )
548
+ )
549
+
420
550
# # arrays
421
551
# numeric arrays never have double quotes and always use ',' as a separator
422
552
parse_numeric_element (:: Type{T} , str) where T = parse (T, str)
0 commit comments