Skip to content
This repository has been archived by the owner on Mar 30, 2024. It is now read-only.

Commit

Permalink
Merge pull request #40 from vapor-community/vapor-2
Browse files Browse the repository at this point in the history
Changes for 2.0 (beta 4)
  • Loading branch information
sroebert authored May 13, 2017
2 parents 82b28f6 + 11c76eb commit 6accbb7
Show file tree
Hide file tree
Showing 21 changed files with 1,739 additions and 1,194 deletions.
8 changes: 5 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import PackageDescription

let beta = Version(2,0,0, prereleaseIdentifiers: ["beta"])

let package = Package(
name: "PostgreSQL",
dependencies: [
// Module map for `libpq`
.Package(url: "https://github.com/vapor/cpostgresql.git", Version(2,0,0, prereleaseIdentifiers: ["alpha"])),
.Package(url: "https://github.com/vapor-community/cpostgresql.git", beta),

// Data structure for converting between multiple representations
.Package(url: "https://github.com/vapor/node.git", Version(2,0,0, prereleaseIdentifiers: ["beta"])),
.Package(url: "https://github.com/vapor/node.git", beta),

// Core extensions, type-aliases, and functions that facilitate common tasks
.Package(url: "https://github.com/vapor/core.git", Version(2,0,0, prereleaseIdentifiers: ["beta"])),
.Package(url: "https://github.com/vapor/core.git", beta),
]
)
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,6 @@ extension Float32 {
var bigEndian: Float32 {
return Float32(bitPattern: bitPattern.bigEndian)
}

var littleEndian: Float32 {
return Float32(bitPattern: bitPattern.littleEndian)
}

var byteSwapped: Float32 {
return Float32(bitPattern: bitPattern.byteSwapped)
}
}

extension Float64 {
Expand All @@ -41,57 +33,21 @@ extension Float64 {
var bigEndian: Float64 {
return Float64(bitPattern: bitPattern.bigEndian)
}

var littleEndian: Float64 {
return Float64(bitPattern: bitPattern.littleEndian)
}

var byteSwapped: Float64 {
return Float64(bitPattern: bitPattern.byteSwapped)
}
}

/// Most information for parsing binary formats has been retrieved from the following links:
/// - https://www.postgresql.org/docs/9.6/static/datatype.html (Data types)
/// - https://github.com/postgres/postgres/tree/55c3391d1e6a201b5b891781d21fe682a8c64fe6/src/backend/utils/adt (Backend sending code)
struct PostgresBinaryUtils {
struct BinaryUtils {

// MARK: - Formatters
// MARK: - Formatter

struct Formatters {
private static func formatter(format: String, forceUTC: Bool) -> DateFormatter {
static let timestamptz: DateFormatter = {
let formatter = DateFormatter()
if forceUTC {
formatter.timeZone = TimeZone(abbreviation: "UTC")
}
formatter.dateFormat = format
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSSX"
return formatter
}

private static let timestamp: DateFormatter = formatter(format: "yyyy-MM-dd HH:mm:ss.SSS", forceUTC: true)
private static let timestamptz: DateFormatter = formatter(format: "yyyy-MM-dd HH:mm:ss.SSSX", forceUTC: false)

private static let date: DateFormatter = formatter(format: "yyyy-MM-dd", forceUTC: false)

private static let time: DateFormatter = formatter(format: "HH:mm:ss.SSS", forceUTC: true)
private static let timetz: DateFormatter = formatter(format: "HH:mm:ss.SSSX", forceUTC: false)

static func dateFormatter(for oid: OID) -> DateFormatter {
switch oid {
case .date:
return date
case .time:
return time
case .timetz:
return timetz
case .timestamp:
return timestamp
case .timestamptz:
return timestamptz
default:
return timestamptz
}
}
}()

static let interval: NumberFormatter = {
let formatter = NumberFormatter()
Expand Down Expand Up @@ -133,11 +89,13 @@ struct PostgresBinaryUtils {
return uint8Bytes
}

static func valueToByteArray<T>(_ value: inout T) -> [Int8] {
static func valueToBytes<T>(_ value: inout T) -> (UnsafeMutablePointer<Int8>, Int) {
let size = MemoryLayout.size(ofValue: value)
return withUnsafePointer(to: &value) { valuePointer in
return valuePointer.withMemoryRebound(to: Int8.self, capacity: size) { bytePointer in
return UnsafeBufferPointer(start: bytePointer, count: size).array
let bytes: UnsafeMutablePointer<Int8> = UnsafeMutablePointer.allocate(capacity: size)
bytes.assign(from: bytePointer, count: size)
return (bytes, size)
}
}
}
Expand Down
202 changes: 202 additions & 0 deletions Sources/PostgreSQL/Bind/Bind+Node.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
import CPostgreSQL

extension Bind {
/// Parses a PostgreSQL value from an output binding.
public var value: StructuredData {
// Check if we have data to parse
guard let value = bytes else {
return .null
}

// We only parse binary data, otherwise simply return the data as a string
guard format == .binary else {
let string = BinaryUtils.parseString(value: value, length: length)
return .string(string)
}

// Parse based on the type of data
switch type {
case .null:
return .null

case .supported(let supportedType):
return Bind.parse(type: supportedType, configuration: configuration, value: value, length: length)

case .array(let supportedArrayType):
return Bind.parse(type: supportedArrayType, configuration: configuration, value: value, length: length)

case .unsupported(let oid):
print("Unsupported Oid type for PostgreSQL binding (\(oid)).")

// Fallback to simply passing on the bytes
let bytes = BinaryUtils.parseBytes(value: value, length: length)
return .bytes(bytes)
}
}
}

/**
Parsing data
*/
extension Bind {
fileprivate static func parse(type: FieldType.Supported, configuration: Configuration, value: UnsafeMutablePointer<Int8>, length: Int) -> StructuredData {
switch type {
case .bool:
return .bool(value[0] != 0)

case .char, .name, .text, .json, .xml, .bpchar, .varchar:
let string = BinaryUtils.parseString(value: value, length: length)
return .string(string)

case .jsonb:
// Ignore jsonb version number
let jsonValue = value.advanced(by: 1)
let string = BinaryUtils.parseString(value: jsonValue, length: length - 1)
return .string(string)

case .int2:
let integer = BinaryUtils.parseInt16(value: value)
return .number(.int(Int(integer)))

case .int4:
let integer = BinaryUtils.parseInt32(value: value)
return .number(.int(Int(integer)))

case .int8:
let integer = BinaryUtils.parseInt64(value: value)
if let intValue = Int(exactly: integer) {
return .number(.int(intValue))
} else {
return .number(.double(Double(integer)))
}

case .bytea:
let bytes = BinaryUtils.parseBytes(value: value, length: length)
return .bytes(bytes)

case .float4:
let float = BinaryUtils.parseFloat32(value: value)
return .number(.double(Double(float)))

case .float8:
let float = BinaryUtils.parseFloat64(value: value)
return .number(.double(Double(float)))

case .numeric:
let number = BinaryUtils.parseNumeric(value: value)
return .string(number)

case .uuid:
let uuid = BinaryUtils.parseUUID(value: value)
return .string(uuid)

case .timestamp, .timestamptz, .date, .time, .timetz:
let date = BinaryUtils.parseTimetamp(value: value, isInteger: configuration.hasIntegerDatetimes)
return .date(date)

case .interval:
let interval = BinaryUtils.parseInterval(value: value, timeIsInteger: configuration.hasIntegerDatetimes)
return .string(interval)

case .point:
let point = BinaryUtils.parsePoint(value: value)
return .string(point)

case .lseg:
let lseg = BinaryUtils.parseLineSegment(value: value)
return .string(lseg)

case .path:
let path = BinaryUtils.parsePath(value: value)
return .string(path)

case .box:
let box = BinaryUtils.parseBox(value: value)
return .string(box)

case .polygon:
let polygon = BinaryUtils.parsePolygon(value: value)
return .string(polygon)

case .circle:
let circle = BinaryUtils.parseCircle(value: value)
return .string(circle)

case .inet, .cidr:
let inet = BinaryUtils.parseIPAddress(value: value)
return .string(inet)

case .macaddr:
let macaddr = BinaryUtils.parseMacAddress(value: value)
return .string(macaddr)

case .bit, .varbit:
let bitString = BinaryUtils.parseBitString(value: value, length: length)
return .string(bitString)
}
}

fileprivate static func parse(type: FieldType.ArraySupported, configuration: Configuration, value: UnsafeMutablePointer<Int8>, length: Int) -> StructuredData {
// Get the dimension of the array
let arrayDimension = BinaryUtils.parseInt32(value: value)
guard arrayDimension > 0 else {
return .array([])
}

var pointer = value.advanced(by: 12)

// Get all dimension lengths
var dimensionLengths: [Int] = []
for _ in 0..<arrayDimension {
dimensionLengths.append(Int(BinaryUtils.parseInt32(value: pointer)))
pointer = pointer.advanced(by: 8)
}

// Parse the array
return parse(type: type, configuration: configuration, dimensionLengths: dimensionLengths, pointer: &pointer)
}

private static func parse(type: FieldType.ArraySupported, configuration: Configuration, dimensionLengths: [Int], pointer: inout UnsafeMutablePointer<Int8>) -> StructuredData {
// Get the length of the array
let arrayLength = dimensionLengths[0]

// Create elements array
var values: [StructuredData] = []
values.reserveCapacity(arrayLength)

// Loop through array and convert each item
let supportedType = type.supported
for _ in 0..<arrayLength {

// Check if we need to parse sub arrays
if dimensionLengths.count > 1 {

var subDimensionLengths = dimensionLengths
subDimensionLengths.removeFirst()

let array = parse(type: type, configuration: configuration, dimensionLengths: subDimensionLengths, pointer: &pointer)
values.append(array)

} else {

let elementLength = Int(BinaryUtils.parseInt32(value: pointer))
pointer = pointer.advanced(by: 4)

// Check if the element is null
guard elementLength != -1 else {
values.append(.null)
continue
}

// Parse to node
let item = parse(type: supportedType, configuration: configuration, value: pointer, length: elementLength)
values.append(item)
pointer = pointer.advanced(by: elementLength)
}
}

return .array(values)
}
}


Loading

0 comments on commit 6accbb7

Please sign in to comment.