├── Package.swift ├── Sources ├── Value.swift ├── Row.swift ├── Field.swift ├── Result.swift └── Connection.swift ├── .gitignore ├── LICENSE └── README.md /Package.swift: -------------------------------------------------------------------------------- 1 | import PackageDescription 2 | 3 | let package = Package( 4 | name: "MySQL", 5 | dependencies: [ 6 | .Package(url: "https://github.com/Zewo/SQL.git", majorVersion: 0), 7 | ] 8 | ) 9 | -------------------------------------------------------------------------------- /Sources/Value.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Value.swift 3 | // MySQL 4 | // 5 | // Created by David Ask on 10/12/15. 6 | // Copyright © 2015 Formbound. All rights reserved. 7 | // 8 | 9 | import SQL 10 | 11 | public struct Value: SQL.Value { 12 | 13 | public let data: Data 14 | 15 | public init(data: Data) { 16 | self.data = data 17 | } 18 | } -------------------------------------------------------------------------------- /Sources/Row.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Row.swift 3 | // MySQL 4 | // 5 | // Created by David Ask on 10/12/15. 6 | // Copyright © 2015 Formbound. All rights reserved. 7 | // 8 | 9 | import SQL 10 | 11 | public struct Row: SQL.Row { 12 | public typealias ValueType = Value 13 | 14 | public init(valuesByName: [String: Value]) { 15 | self.valuesByName = valuesByName 16 | } 17 | 18 | public let valuesByName: [String: Value] 19 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #Created by https://www.gitignore.io/api/xcode 2 | 3 | ### Xcode ### 4 | # Xcode 5 | # 6 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 7 | 8 | ## Build generated 9 | .build/ 10 | build/ 11 | DerivedData 12 | Carthage/ 13 | Packages/ 14 | 15 | ## Various settings 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | xcuserdata 25 | 26 | ## Other 27 | *.xccheckout 28 | *.moved-aside 29 | *.xcuserstate 30 | *.xcscmblueprint 31 | XcodeDevelopment/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Zewo 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. -------------------------------------------------------------------------------- /Sources/Field.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Field.swift 3 | // MySQL 4 | // 5 | // Created by David Ask on 10/12/15. 6 | // Copyright © 2015 Formbound. All rights reserved. 7 | // 8 | 9 | import SQL 10 | import CMySQL 11 | 12 | public struct Field: SQL.Field { 13 | 14 | public var name: String 15 | 16 | public var originalName: String 17 | 18 | public var table: String 19 | 20 | public var originalTable: String 21 | 22 | public var database: String 23 | 24 | public var catalog: String 25 | 26 | public var length: UInt 27 | 28 | public var maxLength: UInt 29 | 30 | public init(_ pointer: UnsafePointer) { 31 | name = String(validatingUTF8: pointer.pointee.name)! 32 | originalName = String(validatingUTF8: pointer.pointee.org_name)! 33 | table = String(validatingUTF8: pointer.pointee.table)! 34 | originalTable = String(validatingUTF8: pointer.pointee.org_table)! 35 | database = String(validatingUTF8: pointer.pointee.db)! 36 | catalog = String(validatingUTF8: pointer.pointee.catalog)! 37 | length = pointer.pointee.length 38 | maxLength = pointer.pointee.max_length 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MySQL 2 | 3 | [![Swift][swift-badge]][swift-url] 4 | [![Platform][platform-badge]][platform-url] 5 | [![License][mit-badge]][mit-url] 6 | [![Slack][slack-badge]][slack-url] 7 | 8 | **MySQL** 9 | 10 | ## Usage 11 | 12 | ```swift 13 | // do your magic 14 | ``` 15 | 16 | ## Installation 17 | 18 | ```swift 19 | import PackageDescription 20 | 21 | let package = Package( 22 | dependencies: [ 23 | .Package(url: "https://github.com/Zewo/MySQL.git", majorVersion: 0, minor: 5) 24 | ] 25 | ) 26 | ``` 27 | 28 | ## Community 29 | 30 | [![Slack][slack-image]][slack-url] 31 | 32 | Join us on [Slack](http://slack.zewo.io). 33 | 34 | License 35 | ------- 36 | 37 | **MySQL** is released under the MIT license. See LICENSE for details. 38 | 39 | [swift-badge]: https://img.shields.io/badge/Swift-3.0-orange.svg?style=flat 40 | [swift-url]: https://swift.org 41 | [platform-badge]: https://img.shields.io/badge/Platform-Linux-lightgray.svg?style=flat 42 | [platform-url]: https://swift.org 43 | [mit-badge]: https://img.shields.io/badge/License-MIT-blue.svg?style=flat 44 | [mit-url]: https://tldrlegal.com/license/mit-license 45 | [slack-image]: http://s13.postimg.org/ybwy92ktf/Slack.png 46 | [slack-badge]: https://zewo-slackin.herokuapp.com/badge.svg 47 | [slack-url]: http://slack.zewo.io 48 | -------------------------------------------------------------------------------- /Sources/Result.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Result.swift 3 | // MySQL 4 | // 5 | // Created by David Ask on 10/12/15. 6 | // Copyright © 2015 Formbound. All rights reserved. 7 | // 8 | 9 | #if os(OSX) || os(iOS) || os(watchOS) || os(tvOS) 10 | import Darwin 11 | #elseif os(Linux) 12 | import Glibc 13 | #endif 14 | 15 | import CMySQL 16 | import SQL 17 | 18 | public class Result: SQL.Result { 19 | 20 | public let resultPointer: UnsafeMutablePointer 21 | 22 | public init(_ resultPointer: UnsafeMutablePointer) { 23 | self.resultPointer = resultPointer 24 | } 25 | 26 | deinit { 27 | clear() 28 | } 29 | 30 | public func clear() { 31 | mysql_free_result(resultPointer) 32 | } 33 | 34 | 35 | public subscript(position: Int) -> Row { 36 | 37 | var result: [String: Value] = [:] 38 | 39 | mysql_data_seek(resultPointer, UInt64(position)) 40 | 41 | let row = mysql_fetch_row(resultPointer) 42 | 43 | let lengths = mysql_fetch_lengths(resultPointer) 44 | 45 | for (fieldIndex, field) in fields.enumerated() { 46 | 47 | let val = row[fieldIndex] 48 | let length = Int(lengths[fieldIndex]) 49 | 50 | var buffer = [UInt8](repeating: 0, count: length) 51 | 52 | memcpy(&buffer, val, length) 53 | 54 | result[field.name] = Value(data: Data(uBytes: buffer)) 55 | } 56 | 57 | return Row(valuesByName: result) 58 | } 59 | 60 | public var count: Int { 61 | return Int(mysql_num_rows(resultPointer)) 62 | } 63 | 64 | public lazy var fields: [Field] = { 65 | var result: [Field] = [] 66 | 67 | for i in 0.. 109 | 110 | private(set) public var connectionInfo: Info 111 | 112 | public required init(_ connectionInfo: Info) { 113 | self.connectionInfo = connectionInfo 114 | connection = mysql_init(nil) 115 | } 116 | 117 | deinit { 118 | close() 119 | } 120 | 121 | public var status: Status { 122 | return .OK 123 | } 124 | 125 | public func open() throws { 126 | guard mysql_real_connect( 127 | connection, 128 | connectionInfo.host, 129 | connectionInfo.user ?? "", 130 | connectionInfo.password ?? "", 131 | connectionInfo.database, 132 | UInt32(connectionInfo.port), 133 | nil, 134 | connectionInfo.flags.rawValue 135 | ) != nil else { 136 | throw statusError 137 | } 138 | } 139 | 140 | public func close() { 141 | mysql_close(connection) 142 | } 143 | 144 | public func execute(string: String, parameters: [SQLParameterConvertible]) throws -> Result { 145 | 146 | var statement = string 147 | 148 | for (i, value) in parameters.enumerated() { 149 | let parameterIdentifier = "$\(i + 1)" 150 | 151 | let data: Data 152 | 153 | switch value.SQLParameterData { 154 | case .Binary(let uBytes): 155 | data = Data(uBytes: uBytes) 156 | break 157 | case .Text(let string): 158 | data = Data(string: string) 159 | break 160 | } 161 | 162 | guard let string = data.string else { 163 | throw Error.ParameterError("Failed to convert parameter \(parameterIdentifier) to string") 164 | } 165 | 166 | let escapedPointer = UnsafeMutablePointer(allocatingCapacity: data.length) 167 | 168 | defer { 169 | escapedPointer.destroy() 170 | escapedPointer.deallocateCapacity(data.length) 171 | } 172 | 173 | let len = mysql_real_escape_string(connection, escapedPointer, string, strlen(string)) 174 | escapedPointer[Int(len)] = 0 175 | 176 | guard let escapedString = String(validatingUTF8: escapedPointer) else { 177 | throw Error.ParameterError("Failed to escape parameter \(parameterIdentifier)") 178 | } 179 | 180 | statement = statement.stringByReplacingOccurrencesOfString(parameterIdentifier, withString: "'\(escapedString)'") 181 | 182 | } 183 | 184 | print(statement) 185 | 186 | guard mysql_real_query(connection, statement, UInt(statement.utf8.count)) == 0 else { 187 | throw statusError 188 | } 189 | 190 | let result = mysql_store_result(connection) 191 | 192 | guard result != nil else { 193 | guard mysql_field_count(connection) == 0 else { 194 | throw Error.BadResult 195 | } 196 | 197 | return Result(nil) 198 | } 199 | 200 | return Result(result) 201 | } 202 | 203 | public func createSavePointNamed(name: String) throws { 204 | try execute("SAVEPOINT \(name)") 205 | } 206 | 207 | public func releaseSavePointNamed(name: String) throws { 208 | try execute("RELEASE SAVEPOINT \(name)") 209 | } 210 | 211 | public func rollbackToSavePointNamed(name: String) throws { 212 | try execute("ROLLBACK TO SAVEPOINT \(name)") 213 | } 214 | 215 | private var statusError: Error { 216 | return Error.ErrorCode( 217 | UInt(mysql_errno(connection)), 218 | String(validatingUTF8: mysql_error(connection)) ?? "None" 219 | ) 220 | } 221 | } --------------------------------------------------------------------------------