Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 8e9bb22

Browse files
Merge pull request #1 from vapor/beta
fluent psql beta
2 parents 69319e6 + 726bbf4 commit 8e9bb22

22 files changed

+844
-2
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+
/Packages
4+
/*.xcodeproj
5+
Package.resolved
6+

β€ŽLICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2018 Qutheory, LLC
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

β€ŽPackage.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// swift-tools-version:4.0
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "FluentPostgreSQL",
6+
products: [
7+
// Swift ORM for PostgreSQL (built on top of Fluent ORM framework)
8+
.library(name: "FluentPostgreSQL", targets: ["FluentPostgreSQL"]),
9+
],
10+
dependencies: [
11+
// ⏱ Promises and reactive-streams in Swift built for high-performance and scalability.
12+
.package(url: "https://github.com/vapor/async.git", from: "1.0.0-rc"),
13+
14+
// 🌎 Utility package containing tools for byte manipulation, Codable, OS APIs, and debugging.
15+
.package(url: "https://github.com/vapor/core.git", from: "3.0.0-rc"),
16+
17+
// Swift ORM framework (queries, models, and relations) for building NoSQL and SQL database integrations.
18+
.package(url: "https://github.com/vapor/fluent.git", from: "3.0.0-rc"),
19+
20+
// 🐘 Non-blocking, event-driven Swift client for PostgreSQL.
21+
.package(url: "https://github.com/vapor/postgresql.git", from: "1.0.0-rc"),
22+
],
23+
targets: [
24+
.target(name: "FluentPostgreSQL", dependencies: ["Async", "CodableKit", "Fluent", "FluentSQL", "PostgreSQL"]),
25+
.testTarget(name: "FluentPostgreSQLTests", dependencies: ["FluentBenchmark", "FluentPostgreSQL"]),
26+
]
27+
)

β€ŽREADME.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,20 @@
1-
# fluent-postgresql
2-
Swift ORM for PostgreSQL (built on top of Fluent ORM framework)
1+
<p align="center">
2+
<img src="https://user-images.githubusercontent.com/1342803/36623958-dc6f07f0-18d7-11e8-8c6c-01737f496de9.png" height="64" alt="Fluent PostgreSQL">
3+
<br>
4+
<br>
5+
<a href="http://docs.vapor.codes/3.0/">
6+
<img src="http://img.shields.io/badge/read_the-docs-2196f3.svg" alt="Documentation">
7+
</a>
8+
<a href="http://vapor.team">
9+
<img src="http://vapor.team/badge.svg" alt="Slack Team">
10+
</a>
11+
<a href="LICENSE">
12+
<img src="http://img.shields.io/badge/license-MIT-brightgreen.svg" alt="MIT License">
13+
</a>
14+
<a href="https://circleci.com/gh/vapor/fluent-postgresql">
15+
<img src="https://circleci.com/gh/vapor/fluent-postgresql.svg?style=shield" alt="Continuous Integration">
16+
</a>
17+
<a href="https://swift.org">
18+
<img src="http://img.shields.io/badge/swift-4.1-brightgreen.svg" alt="Swift 4.1">
19+
</a>
20+
</p>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@_exported import Fluent
2+
@_exported import PostgreSQL
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import Service
2+
3+
/// Adds Fluent PostgreSQL's services to your project.
4+
public final class FluentPostgreSQLProvider: Provider {
5+
/// See `Provider.repositoryName`
6+
public static let repositoryName = "fluent-postgresql"
7+
8+
/// Creates a new `FluentPostgreSQLProvider`
9+
///
10+
/// - enableIdentityColumns: If true, `GENERATED BY DEFAULT AS IDENTITY` will be used.
11+
/// `true` by default.
12+
public init(enableIdentityColumns: Bool? = nil) {
13+
if let enableIdentityColumns = enableIdentityColumns {
14+
_globalEnableIdentityColumns = enableIdentityColumns
15+
}
16+
}
17+
18+
/// See `Provider.register(_:)`
19+
public func register(_ services: inout Services) throws {
20+
try services.register(FluentProvider())
21+
try services.register(PostgreSQLProvider())
22+
}
23+
24+
/// See `Provider.boot(_:)`
25+
public func boot(_ worker: Container) throws { }
26+
}
27+
28+
/// Enabled by default
29+
internal var _globalEnableIdentityColumns: Bool = true
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/// A PostgreSQL column type and size.
2+
public struct PostgreSQLColumn {
3+
/// The columns data type.
4+
public let type: PostgreSQLDataType
5+
6+
/// The columns size. Negative values mean varying size.
7+
public let size: Int16
8+
9+
/// Creates a new `PostgreSQLColumn`.
10+
public init(type: PostgreSQLDataType, size: Int16? = nil) {
11+
self.type = type
12+
self.size = size ?? -1
13+
}
14+
}
15+
16+
/// MARK: Representable
17+
18+
/// Capable of being represented statically by a `PostgreSQLColumn`
19+
public protocol PostgreSQLColumnStaticRepresentable {
20+
/// The `PostgreSQLColumn` type that best represents this type.
21+
static var postgreSQLColumn: PostgreSQLColumn { get }
22+
}
23+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
extension PostgreSQLDatabase: JoinSupporting { }
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
extension PostgreSQLDatabase: LogSupporting {
2+
/// See `LogSupporting.enableLogging(using:)`
3+
public func enableLogging(using logger: DatabaseLogger) {
4+
self.logger = logger
5+
}
6+
}
7+
8+
extension DatabaseLogger: PostgreSQLLogger {
9+
/// See `PostgreSQLLogger.log(query:parameters:)`
10+
public func log(query: String, parameters: [PostgreSQLData]) {
11+
let log = DatabaseLog(query: query, values: parameters.map { 0ドル.data?.description ?? "nil" }, dbID: "postgresql", date: .init())
12+
self.record(log: log)
13+
}
14+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import Async
2+
import CodableKit
3+
import FluentSQL
4+
import Foundation
5+
6+
/// Adds ability to do basic Fluent queries using a `PostgreSQLDatabase`.
7+
extension PostgreSQLDatabase: QuerySupporting {
8+
/// See `QuerySupporting.execute`
9+
public static func execute<I, D>(query: DatabaseQuery<PostgreSQLDatabase>, into stream: I, on connection: PostgreSQLConnection)
10+
where I: Async.InputStream, D: Decodable, D == I.Input
11+
{
12+
let future = Future<Void>.flatMap {
13+
// Convert Fluent `DatabaseQuery` to generic FluentSQL `DataQuery`
14+
var (sqlQuery, bindValues) = query.makeDataQuery()
15+
16+
// If the query has an Encodable model attached serialize it.
17+
// Dictionary keys should be added to the DataQuery as columns.
18+
// Dictionary values should be added to the parameterized array.
19+
let modelData: [PostgreSQLData]
20+
if let model = query.data {
21+
let encoder = PostgreSQLRowEncoder()
22+
try model.encode(to: encoder)
23+
sqlQuery.columns += encoder.data.keys.map { key in
24+
return DataColumn(table: query.entity, name: key)
25+
}
26+
modelData = .init(encoder.data.values)
27+
} else {
28+
modelData = []
29+
}
30+
31+
// Create a PostgreSQL-flavored SQL serializer to create a SQL string
32+
let sqlSerializer = PostgreSQLSQLSerializer()
33+
let sqlString = sqlSerializer.serialize(data: sqlQuery)
34+
35+
// Combine the query data with bind values from filters.
36+
// All bind values must come _after_ the columns section of the query.
37+
let parameters = try modelData + bindValues.map { bind in
38+
let encodable = bind.encodable
39+
guard let convertible = encodable as? PostgreSQLDataCustomConvertible else {
40+
let type = Swift.type(of: encodable)
41+
throw PostgreSQLError(
42+
identifier: "convertible",
43+
reason: "Unsupported encodable type: \(type)",
44+
suggestedFixes: [
45+
"Conform \(type) to PostgreSQLDataCustomConvertible"
46+
],
47+
source: .capture()
48+
)
49+
}
50+
return try convertible.convertToPostgreSQLData()
51+
}
52+
53+
// Create a push stream to accept the psql output
54+
// FIXME: connect streams directly instead?
55+
let pushStream = PushStream<D>()
56+
pushStream.output(to: stream)
57+
58+
// Run the query
59+
return try connection.query(sqlString, parameters) { row in
60+
do {
61+
let decoded = try D.init(from: PostgreSQLRowDecoder(row: row))
62+
pushStream.push(decoded)
63+
} catch {
64+
pushStream.error(error)
65+
}
66+
}
67+
}
68+
69+
/// Convert Future completion / error to stream
70+
future.do {
71+
// Query is complete
72+
stream.close()
73+
}.catch { error in
74+
// Query failed
75+
stream.error(error)
76+
stream.close()
77+
}
78+
}
79+
80+
/// See `QuerySupporting.modelEvent`
81+
public static func modelEvent<M>(event: ModelEvent, model: M, on connection: PostgreSQLConnection) -> Future<M>
82+
where PostgreSQLDatabase == M.Database, M: Model
83+
{
84+
switch event {
85+
case .willCreate:
86+
if M.ID.self == UUID.self {
87+
var model = model
88+
model.fluentID = UUID() as? M.ID
89+
return Future(model)
90+
}
91+
case .didCreate:
92+
if M.ID.self == Int.self {
93+
return connection.simpleQuery("SELECT LASTVAL();").map(to: M.self) { row in
94+
var model = model
95+
try model.fluentID = row[0]["lastval"]?.decode(Int.self) as? M.ID
96+
return model
97+
}
98+
}
99+
default: break
100+
}
101+
102+
return Future(model)
103+
}
104+
}

0 commit comments

Comments
(0)

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /