Skip to content

Commit a408069

Browse files
committed
Initial commit
0 parents  commit a408069

18 files changed

+800
-0
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.DS_Store
2+
/.build
3+
/.swiftpm
4+
/Packages
5+
/*.xcodeproj
6+
/Package.resolved

LICENSE

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
This is free and unencumbered software released into the public domain.
2+
3+
Anyone is free to copy, modify, publish, use, compile, sell, or
4+
distribute this software, either in source code form or as a compiled
5+
binary, for any purpose, commercial or non-commercial, and by any
6+
means.
7+
8+
In jurisdictions that recognize copyright laws, the author or authors
9+
of this software dedicate any and all copyright interest in the
10+
software to the public domain. We make this dedication for the benefit
11+
of the public at large and to the detriment of our heirs and
12+
successors. We intend this dedication to be an overt act of
13+
relinquishment in perpetuity of all present and future rights to this
14+
software under copyright law.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22+
OTHER DEALINGS IN THE SOFTWARE.
23+
24+
For more information, please refer to <http://unlicense.org>

Package.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// swift-tools-version:5.0
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "PostgreSQL",
6+
dependencies: [
7+
.package(
8+
url: "https://github.com/swift-stack/aio.git",
9+
.branch("master")),
10+
.package(
11+
url: "https://github.com/swift-stack/test.git",
12+
.branch("master"))
13+
],
14+
targets: [
15+
.target(
16+
name: "PostgreSQL",
17+
dependencies: ["Network"]),
18+
.testTarget(
19+
name: "PostgreSQLTests",
20+
dependencies: ["Test", "PostgreSQL"])
21+
]
22+
)
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import Stream
2+
3+
enum BackendMessage {
4+
enum RawType: UInt8 {
5+
case authentication = 82 // 'R'
6+
case backendKeyData = 75 // 'K'
7+
case bindComplete = 50 // '2'
8+
case closeComplete = 51 // '3'
9+
case commandComplete = 67 // 'C'
10+
case copyData = 100 // 'c'
11+
case copyInResponse = 71 // 'G'
12+
case copyOutResponse = 72 // 'H'
13+
case copyBothResponse = 87 // 'W'
14+
case dataRow = 68 // 'D'
15+
case emptyQueryResponse = 73 // 'I'
16+
case errorResponse = 69 // 'E'
17+
case functionCallResponse = 86 // 'V'
18+
case negotiateProtocolVersion = 118 // 'v'
19+
case noData = 110 // 'n'
20+
case noticeResponse = 78 // 'N'
21+
case notificationResponse = 65 // 'A'
22+
case parameterDescription = 116 // 't'
23+
case parameterStatus = 83 // 'S'
24+
case parseComplete = 49 // '1'
25+
case postalSuspended = 115 // 's'
26+
case readyForQuery = 90 // 'Z'
27+
case rowDescription = 84 // 'T'
28+
}
29+
30+
case authentication(Authentication)
31+
case parameterStatus(ParameterStatus)
32+
case backendKeyData(BackendKeyData)
33+
case readyForQuery(TransactionStatus)
34+
case rowDescription(RowDescription)
35+
case dataRow(DataRow)
36+
case commandComplete(CommandComplete)
37+
case error(Error)
38+
39+
init(from stream: StreamReader) throws {
40+
let rawType = try stream.read(UInt8.self)
41+
guard let messageType = RawType(rawValue: rawType) else {
42+
fatalError("unknown message type: \(rawType)")
43+
}
44+
self = try stream.withSubStreamReader(
45+
sizedBy: Int32.self,
46+
includingHeader: true)
47+
{ stream in
48+
switch messageType {
49+
case .authentication:
50+
return .authentication(try .init(from: stream))
51+
case .parameterStatus:
52+
return .parameterStatus(try .init(from: stream))
53+
case .backendKeyData:
54+
return .backendKeyData(try .init(from: stream))
55+
case .readyForQuery:
56+
return .readyForQuery(try .init(from: stream))
57+
case .rowDescription:
58+
return .rowDescription(try .init(from: stream))
59+
case .dataRow:
60+
return .dataRow(try .init(from: stream))
61+
case .commandComplete:
62+
return .commandComplete(try .init(from: stream))
63+
case .errorResponse:
64+
return .error(try .init(from: stream))
65+
default:
66+
print("type: \(rawType) size: \(stream.limit)")
67+
print(try stream.readUntilEnd())
68+
fatalError()
69+
}
70+
}
71+
}
72+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import Stream
2+
3+
extension BackendMessage {
4+
enum Authentication {
5+
case ok
6+
case clearTextPassword
7+
case md5Password
8+
9+
enum RawType: UInt32 {
10+
case ok = 0
11+
case kerberos = 2
12+
case clearTextPassword = 3
13+
case md5Password = 5
14+
case scmCredential = 6
15+
case gss = 7
16+
case sspi = 9
17+
case gssContinue = 8
18+
case sasl = 10
19+
case saslContinue = 11
20+
case saslFinal = 12
21+
}
22+
23+
init(from stream: SubStreamReader) throws {
24+
guard stream.limit == 4 else {
25+
fatalError("Authentication: invalid size")
26+
}
27+
let rawType = try stream.read(UInt32.self)
28+
guard let status = RawType(rawValue: rawType) else {
29+
fatalError("Authentication: unknown status \(rawType)")
30+
}
31+
switch status {
32+
case .ok: self = .ok
33+
// case .clearTextPassword: self = .clearTextPassword
34+
// case .md5Password: self = .md5Password
35+
default: fatalError("Authentication: unsupported method: \(status)")
36+
}
37+
}
38+
}
39+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import Stream
2+
3+
extension BackendMessage {
4+
struct BackendKeyData {
5+
let processId: Int
6+
let secretKey: Int
7+
8+
init(from stream: SubStreamReader) throws {
9+
guard stream.limit == 8 else {
10+
fatalError("BackendKeyData: invalid size")
11+
}
12+
self.processId = Int(try stream.read(Int32.self))
13+
self.secretKey = Int(try stream.read(Int32.self))
14+
}
15+
}
16+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import Stream
2+
3+
extension BackendMessage {
4+
struct CommandComplete {
5+
let tag: String
6+
7+
init(from stream: SubStreamReader) throws {
8+
self.tag = try stream.readCString()
9+
}
10+
}
11+
}
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import Stream
2+
3+
extension BackendMessage {
4+
struct Error {
5+
let fields: [Field]
6+
7+
struct Field {
8+
let type: FieldType
9+
let value: String
10+
11+
init(from stream: SubStreamReader) throws {
12+
let rawFieldType = Int(try stream.read(UInt8.self))
13+
self.type = Field.FieldType(rawValue: rawFieldType)
14+
self.value = try stream.readCString()
15+
}
16+
17+
enum FieldType {
18+
case severity
19+
case severityEN
20+
case code
21+
case message
22+
case detail
23+
case hint
24+
case position
25+
case internalPosition
26+
case internalQuery
27+
case `where`
28+
case schemaName
29+
case tableName
30+
case columnName
31+
case dataTypeName
32+
case constraintName
33+
case file
34+
case line
35+
case routine
36+
case unknown(Int)
37+
}
38+
}
39+
40+
init(from stream: SubStreamReader) throws {
41+
var fields = [Field]()
42+
while !stream.isEmpty {
43+
// end of error message
44+
guard try stream.peek() != 0 else {
45+
try stream.consume(count: 1)
46+
break
47+
}
48+
fields.append(try .init(from: stream))
49+
}
50+
self.fields = fields
51+
}
52+
}
53+
}
54+
55+
extension BackendMessage.Error.Field.FieldType: RawRepresentable {
56+
enum RawType: Int {
57+
case severity = 83 // 'S'
58+
case severityEN = 86 // 'V'
59+
case code = 67 // 'C'
60+
case message = 77 // 'M'
61+
case detail = 68 // 'D'
62+
case hint = 72 // 'H'
63+
case position = 80 // 'P'
64+
case internalPosition = 112 // 'p'
65+
case internalQuery = 113 // 'q'
66+
case `where` = 87 // 'W'
67+
case schemaName = 115 // 's'
68+
case tableName = 116 // 't'
69+
case columnName = 99 // 'c'
70+
case dataTypeName = 100 // 'd'
71+
case constraintName = 110 // 'n'
72+
case file = 70 // 'F'
73+
case line = 76 // 'L'
74+
case routine = 82 // 'R'
75+
}
76+
77+
var rawValue: Int {
78+
switch self {
79+
case .severity: return RawType.severity.rawValue
80+
case .severityEN: return RawType.severityEN.rawValue
81+
case .code: return RawType.code.rawValue
82+
case .message: return RawType.message.rawValue
83+
case .detail: return RawType.detail.rawValue
84+
case .hint: return RawType.hint.rawValue
85+
case .position: return RawType.position.rawValue
86+
case .internalPosition: return RawType.internalPosition.rawValue
87+
case .internalQuery: return RawType.internalQuery.rawValue
88+
case .where: return RawType.where.rawValue
89+
case .schemaName: return RawType.schemaName.rawValue
90+
case .tableName: return RawType.tableName.rawValue
91+
case .columnName: return RawType.columnName.rawValue
92+
case .dataTypeName: return RawType.dataTypeName.rawValue
93+
case .constraintName: return RawType.constraintName.rawValue
94+
case .file: return RawType.file.rawValue
95+
case .line: return RawType.line.rawValue
96+
case .routine: return RawType.routine.rawValue
97+
case .unknown(let value): return value
98+
}
99+
}
100+
101+
init(rawValue: Int) {
102+
guard let rawType = RawType(rawValue: rawValue) else {
103+
self = .unknown(rawValue)
104+
return
105+
}
106+
switch rawType {
107+
case .severity: self = .severity
108+
case .severityEN: self = .severityEN
109+
case .code: self = .code
110+
case .message: self = .message
111+
case .detail: self = .detail
112+
case .hint: self = .hint
113+
case .position: self = .position
114+
case .internalPosition: self = .internalPosition
115+
case .internalQuery: self = .internalQuery
116+
case .where: self = .where
117+
case .schemaName: self = .schemaName
118+
case .tableName: self = .tableName
119+
case .columnName: self = .columnName
120+
case .dataTypeName: self = .dataTypeName
121+
case .constraintName: self = .constraintName
122+
case .file: self = .file
123+
case .line: self = .line
124+
case .routine: self = .routine
125+
}
126+
}
127+
}
128+
129+
extension BackendMessage.Error.Field: CustomStringConvertible {
130+
var description: String {
131+
return "(type: \(type), \"\(value)\")"
132+
}
133+
}
134+
135+
extension BackendMessage.Error.Field.FieldType: CustomStringConvertible {
136+
var description: String {
137+
switch self {
138+
case .severity: return ".severity"
139+
case .severityEN: return ".severityEN"
140+
case .code: return ".code"
141+
case .message: return ".message"
142+
case .detail: return ".detail"
143+
case .hint: return ".hint"
144+
case .position: return ".position"
145+
case .internalPosition: return ".internalPosition"
146+
case .internalQuery: return ".internalQuery"
147+
case .where: return ".where"
148+
case .schemaName: return ".schemaName"
149+
case .tableName: return ".tableName"
150+
case .columnName: return ".columnName"
151+
case .dataTypeName: return ".dataTypeName"
152+
case .constraintName: return ".constraintName"
153+
case .file: return ".file"
154+
case .line: return ".line"
155+
case .routine: return ".routine"
156+
case .unknown(let value): return ".unknown(\(value))"
157+
}
158+
}
159+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import Stream
2+
3+
extension BackendMessage {
4+
struct ParameterStatus {
5+
let name: String
6+
let value: String
7+
8+
init(from stream: SubStreamReader) throws {
9+
self.name = try stream.readCString()
10+
self.value = try stream.readCString()
11+
}
12+
}
13+
}

0 commit comments

Comments
 (0)