@@ -4,59 +4,52 @@ import FluentSQL
44import Foundation
55
66/// Adds ability to do basic Fluent queries using a `PostgreSQLDatabase`.
7- extension PostgreSQLDatabase : QuerySupporting {
8- 7+ extension PostgreSQLDatabase : QuerySupporting , CustomSQLSupporting {
98 /// See `QuerySupporting.execute`
10- public static func execute< D > (
9+ public static func execute(
1110 query: DatabaseQuery < PostgreSQLDatabase > ,
12- into handler: @escaping ( D , PostgreSQLConnection ) throws -> ( ) ,
11+ into handler: @escaping ( [ QueryField : PostgreSQLDataConvertible ] , PostgreSQLConnection ) throws -> ( ) ,
1312 on connection: PostgreSQLConnection
14- ) -> EventLoopFuture < Void > where D : Decodable {
13+ ) -> EventLoopFuture < Void > {
1514 return Future< Void> . flatMap( on: connection) {
1615 // Convert Fluent `DatabaseQuery` to generic FluentSQL `DataQuery`
1716 var ( sqlQuery, bindValues) = query. makeDataQuery ( )
1817
1918 // If the query has an Encodable model attached serialize it.
2019 // Dictionary keys should be added to the DataQuery as columns.
2120 // Dictionary values should be added to the parameterized array.
22- let modelData : [ PostgreSQLData ]
23- if let model = query. data{
24- let encoder = PostgreSQLRowEncoder ( )
25- try model . encode ( to : encoder )
26- sqlQuery . columns += encoder . data. keys . map { key in
27- return DataColumn ( table : query . entity , name : key )
28- }
29- modelData = . init ( encoder . data . values )
30- } else {
31- modelData = [ ]
21+ var modelData : [ PostgreSQLDataConvertible ] = [ ]
22+ modelData . reserveCapacity ( query. data. count )
23+ for (field , data ) in query . data {
24+ sqlQuery . columns . append ( DataColumn ( table : field . entity , name : field . name ) )
25+ modelData . append ( data)
26+ }
27+ 28+ /// Apply custom sql transformations
29+ for customSQL in query . customSQL {
30+ customSQL . closure ( & sqlQuery )
3231 }
3332
3433 // Create a PostgreSQL-flavored SQL serializer to create a SQL string
3534 let sqlSerializer = PostgreSQLSQLSerializer ( )
3635 let sqlString = sqlSerializer. serialize ( data: sqlQuery)
3736
38- // Combine the query data with bind values from filters.
39- // All bind values must come _after_ the columns section of the query.
40- let parameters = try modelData + bindValues. map { bind in
41- let encodable = bind. encodable
42- guard let convertible = encodable as? PostgreSQLDataCustomConvertible else {
43- let type = Swift . type ( of: encodable)
44- throw PostgreSQLError (
45- identifier: " convertible " ,
46- reason: " Unsupported encodable type: \( type) " ,
47- suggestedFixes: [
48- " Conform \( type) to PostgreSQLDataCustomConvertible "
49- ] ,
50- source: . capture( )
51- )
52- }
53- return try convertible. convertToPostgreSQLData ( )
37+ /// Convert params
38+ let parameters : [ PostgreSQLData ] = try ( modelData + bindValues) . map { try 0ドル. convertToPostgreSQLData ( ) }
39+ 40+ /// Log supporting
41+ if let logger = connection. logger {
42+ logger. log ( query: sqlString, parameters: parameters)
5443 }
5544
5645 // Run the query
5746 return try connection. query ( sqlString, parameters) { row in
58- let decoded = try D . init ( from: PostgreSQLRowDecoder ( row: row) )
59- try handler ( decoded, connection)
47+ var res : [ QueryField : PostgreSQLDataConvertible ] = [ : ]
48+ for (col, data) in row {
49+ let field = QueryField ( entity: col. table, name: col. name)
50+ res [ field] = data
51+ }
52+ try handler ( res, connection)
6053 }
6154 }
6255 }
@@ -76,7 +69,7 @@ extension PostgreSQLDatabase: QuerySupporting {
7669 if M . ID. self == Int . self {
7770 return connection. simpleQuery ( " SELECT LASTVAL(); " ) . map ( to: M . self) { row in
7871 var model = model
79- try model. fluentID = row [ 0 ] [ " lastval " ] ? . decode ( Int . self) as? M . ID
72+ try model. fluentID = row [ 0 ] . firstValue ( forField : " lastval " ) ? . decode ( Int . self) as? M . ID
8073 return model
8174 }
8275 }
@@ -85,4 +78,29 @@ extension PostgreSQLDatabase: QuerySupporting {
8578
8679 return Future . map ( on: connection) { model }
8780 }
81+ 82+ 83+ 84+ /// See `QuerySupporting.queryDataParse(_:from:)`
85+ public static func queryDataParse< T> ( _ type: T . Type , from data: PostgreSQLDataConvertible ) throws -> T {
86+ guard let convertibleType = T . self as? PostgreSQLDataConvertible . Type else {
87+ throw PostgreSQLError ( identifier: " queryDataParse " , reason: " Cannot parse \( T . self) from PostgreSQLData " , source: . capture( ) )
88+ }
89+ return try convertibleType. convertFromPostgreSQLData ( data. convertToPostgreSQLData ( ) ) as! T
90+ }
91+ 92+ /// See `QuerySupporting.queryDataSerialize(data:)`
93+ public static func queryDataSerialize< T> ( data: T ? ) throws -> PostgreSQLDataConvertible {
94+ if let data = data {
95+ guard let convertible = data as? PostgreSQLDataConvertible else {
96+ throw PostgreSQLError ( identifier: " queryDataSerialize " , reason: " Cannot serialize \( T . self) to PostgreSQLData " , source: . capture( ) )
97+ }
98+ return try convertible. convertToPostgreSQLData ( )
99+ } else {
100+ guard let convertibleType = T . self as? PostgreSQLDataConvertible . Type else {
101+ throw PostgreSQLError ( identifier: " queryDataParse " , reason: " Cannot parse \( T . self) from PostgreSQLData " , source: . capture( ) )
102+ }
103+ return PostgreSQLData ( type: convertibleType. postgreSQLDataType, format: . binary, data: nil )
104+ }
105+ }
88106}
0 commit comments