├── .build-ubuntu1404 ├── .codecov.yml ├── .gitignore ├── .jazzy.yaml ├── .swift-build-linux ├── .swift-build-macos ├── .swift-version ├── .swift-xcodeproj ├── .test-ubuntu1404 ├── .travis.yml ├── Config.xcconfig ├── LICENSE.txt ├── Package.swift ├── Package@swift-4.0.swift ├── Package@swift-4.1.swift ├── Package@swift-4.2.swift ├── README.md ├── Sources ├── CMySQL │ ├── module.modulemap │ └── shim.h └── SwiftKueryMySQL │ ├── MySQLConnection.swift │ ├── MySQLPreparedStatement.swift │ └── MySQLResultFetcher.swift ├── Tests ├── LinuxMain.swift └── SwiftKueryMySQLTests │ ├── CommonUtils.swift │ ├── MySQLTest.swift │ ├── TestAlias.swift │ ├── TestColumnTypes.swift │ ├── TestInsert.swift │ ├── TestJoin.swift │ ├── TestParameters.swift │ ├── TestSchema.swift │ ├── TestSelect.swift │ ├── TestSubquery.swift │ ├── TestTimingWindows.swift │ ├── TestTransaction.swift │ ├── TestUpdate.swift │ └── connection.json ├── docker-compose.yml └── docs ├── Classes.html ├── Classes ├── MySQLConnection.html ├── MySQLResultFetcher.html └── MySQLThreadSafeConnection.html ├── css ├── highlight.css └── jazzy.css ├── docsets ├── SwiftKueryMySQL.docset │ └── Contents │ │ ├── Info.plist │ │ └── Resources │ │ ├── Documents │ │ ├── Classes.html │ │ ├── Classes │ │ │ ├── MySQLConnection.html │ │ │ ├── MySQLResultFetcher.html │ │ │ └── MySQLThreadSafeConnection.html │ │ ├── css │ │ │ ├── highlight.css │ │ │ └── jazzy.css │ │ ├── img │ │ │ ├── carat.png │ │ │ ├── dash.png │ │ │ └── gh.png │ │ ├── index.html │ │ ├── js │ │ │ ├── jazzy.js │ │ │ └── jquery.min.js │ │ └── undocumented.json │ │ └── docSet.dsidx └── SwiftKueryMySQL.tgz ├── img ├── carat.png ├── dash.png └── gh.png ├── index.html ├── js ├── jazzy.js └── jquery.min.js └── undocumented.json /.build-ubuntu1404: -------------------------------------------------------------------------------- 1 | swift build -Xcc -I/usr/include/mysql/ 2 | -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | token: d57c3a3c-3410-455a-bbcd-bc0614608929 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | build/ 4 | /Packages 5 | /*.xcodeproj 6 | Package.resolved 7 | -------------------------------------------------------------------------------- /.jazzy.yaml: -------------------------------------------------------------------------------- 1 | module: SwiftKueryMySQL 2 | author: IBM 3 | github_url: https://github.com/IBM-Swift/SwiftKueryMySQL/ 4 | 5 | theme: fullwidth 6 | clean: true 7 | exclude: [Packages] 8 | 9 | readme: README.md 10 | 11 | skip_undocumented: false 12 | hide_documentation_coverage: false 13 | 14 | xcodebuild_arguments: [-project, SwiftKueryMySQL.xcodeproj, -target, SwiftKueryMySQL, LIBRARY_SEARCH_PATHS=.build/debug] 15 | -------------------------------------------------------------------------------- /.swift-build-linux: -------------------------------------------------------------------------------- 1 | MYSQL_V8_PACKAGE="mysql-apt-config_0.8.12-1_all.deb" 2 | export DEBIAN_FRONTEND="noninteractive" 3 | if [[ $MYSQL_VER == 5 ]]; then 4 | mysql --version || { apt-get update && apt-get install -y pkg-config mysql-server libmysqlclient-dev && service mysql start && mysql --version; } 5 | else 6 | sudo -E apt-get install -y gnupg lsb-release 7 | cd /tmp 8 | wget https://dev.mysql.com/get/${MYSQL_V8_PACKAGE} 9 | cd - 10 | sudo -E echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | debconf-set-selections 11 | sudo -E dpkg -i /tmp/${MYSQL_V8_PACKAGE} 12 | sudo -E echo mysql-community-server mysql-community-server/root-pass password | debconf-set-selections 13 | sudo -E apt-get update -y 14 | sudo -E apt-get install -y pkg-config 15 | sudo -E apt-get install -q -y mysql-server 16 | sudo -E apt-get install -y libmysqlclient-dev 17 | /usr/bin/mysqld_safe --user=mysql & 18 | sleep 5 19 | mysql --version 20 | fi 21 | 22 | mysql_upgrade -uroot || echo "No need to upgrade" 23 | mysql -uroot -e "CREATE USER 'swift'@'localhost' IDENTIFIED BY 'kuery';" 24 | mysql -uroot -e "CREATE DATABASE IF NOT EXISTS test;" 25 | mysql -uroot -e "GRANT ALL ON test.* TO 'swift'@'localhost';" 26 | 27 | swift build 28 | -------------------------------------------------------------------------------- /.swift-build-macos: -------------------------------------------------------------------------------- 1 | if [[ $MYSQL_VER == 5 ]]; then 2 | mysql --version || { brew update && brew install mysql@5.7 && brew link mysql@5.7 --force && mysql.server start && mysql --version; } 3 | else 4 | mysql --version || { brew update && brew install mysql && mysql.server start && mysql --version; } 5 | fi 6 | 7 | mysql_upgrade -uroot || echo "No need to upgrade" 8 | mysql -uroot -e "CREATE USER 'swift'@'localhost' IDENTIFIED BY 'kuery';" 9 | mysql -uroot -e "CREATE DATABASE IF NOT EXISTS test;" 10 | mysql -uroot -e "GRANT ALL ON test.* TO 'swift'@'localhost';" 11 | 12 | swift build 13 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 5.1 2 | -------------------------------------------------------------------------------- /.swift-xcodeproj: -------------------------------------------------------------------------------- 1 | swift package generate-xcodeproj --xcconfig-overrides Config.xcconfig 2 | -------------------------------------------------------------------------------- /.test-ubuntu1404: -------------------------------------------------------------------------------- 1 | swift test -Xcc -I/usr/include/mysql/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Travis CI build file for SwiftKueryMySQL. 2 | # SwiftKueryMySQL runs on OS X and Linux (Ubuntu). 3 | # See the following URLs for further details on Travis CI 4 | # https://docs.travis-ci.com/user/customizing-the-build/ 5 | # https://docs.travis-ci.com/user/docker/ 6 | # https://docs.travis-ci.com/user/multi-os/ 7 | 8 | # whitelist (branches that should be built) 9 | branches: 10 | only: 11 | - master 12 | - next 13 | - /^issue.*$/ 14 | 15 | matrix: 16 | include: 17 | - os: linux 18 | dist: xenial 19 | sudo: required 20 | services: docker 21 | env: DOCKER_IMAGE=swift:4.0.3 SWIFT_SNAPSHOT=4.0.3 MYSQL_VER=5 DOCKER_ENVIRONMENT="MYSQL_VER" 22 | - os: linux 23 | dist: xenial 24 | sudo: required 25 | services: docker 26 | env: DOCKER_IMAGE=swift:4.1.3 SWIFT_SNAPSHOT=4.1.3 MYSQL_VER=5 DOCKER_ENVIRONMENT="MYSQL_VER" 27 | - os: linux 28 | dist: xenial 29 | sudo: required 30 | services: docker 31 | env: DOCKER_IMAGE=swift:4.2.4 SWIFT_SNAPSHOT=4.2.4 MYSQL_VER=5 DOCKER_ENVIRONMENT="MYSQL_VER" 32 | - os: linux 33 | dist: xenial 34 | sudo: required 35 | services: docker 36 | env: DOCKER_IMAGE=swift:5.0.3-xenial SWIFT_SNAPSHOT=5.0.3 MYSQL_VER=5 DOCKER_ENVIRONMENT="MYSQL_VER" 37 | - os: linux 38 | dist: xenial 39 | sudo: required 40 | services: docker 41 | env: DOCKER_IMAGE=swift:5.0.3-xenial SWIFT_SNAPSHOT=5.0.3 MYSQL_VER=8 DOCKER_ENVIRONMENT="MYSQL_VER" 42 | - os: linux 43 | dist: xenial 44 | sudo: required 45 | services: docker 46 | env: DOCKER_IMAGE=swift:5.1 MYSQL_VER=5 DOCKER_ENVIRONMENT="MYSQL_VER" 47 | - os: linux 48 | dist: xenial 49 | sudo: required 50 | services: docker 51 | env: DOCKER_IMAGE=swift:5.1 MYSQL_VER=8 DOCKER_ENVIRONMENT="MYSQL_VER" 52 | - os: linux 53 | dist: xenial 54 | sudo: required 55 | services: docker 56 | env: DOCKER_IMAGE=swift:5.1 MYSQL_VER=8 SWIFT_SNAPSHOT=$SWIFT_DEVELOPMENT_SNAPSHOT DOCKER_ENVIRONMENT="MYSQL_VER" 57 | - os: osx 58 | osx_image: xcode9.2 59 | sudo: required 60 | env: SWIFT_SNAPSHOT=4.0.3 MYSQL_VER=5 61 | - os: osx 62 | osx_image: xcode9.4 63 | sudo: required 64 | env: SWIFT_SNAPSHOT=4.1.2 MYSQL_VER=5 65 | - os: osx 66 | osx_image: xcode10.1 67 | sudo: required 68 | env: MYSQL_VER=5 SWIFT_SNAPSHOT=4.2.1 69 | - os: osx 70 | osx_image: xcode10.2 71 | sudo: required 72 | env: MYSQL_VER=5 SWIFT_SNAPSHOT=5.0.1 JAZZY_ELIGIBLE=true 73 | - os: osx 74 | osx_image: xcode10.2 75 | sudo: required 76 | env: MYSQL_VER=8 SWIFT_SNAPSHOT=5.0.1 77 | - os: osx 78 | osx_image: xcode11 79 | sudo: required 80 | env: MYSQL_VER=5 81 | - os: osx 82 | osx_image: xcode11 83 | sudo: required 84 | env: MYSQL_VER=8 85 | - os: osx 86 | osx_image: xcode11 87 | sudo: required 88 | env: SWIFT_SNAPSHOT=$SWIFT_DEVELOPMENT_SNAPSHOT MYSQL_VER=8 89 | 90 | before_install: 91 | - git clone https://github.com/IBM-Swift/Package-Builder.git 92 | 93 | script: 94 | - ./Package-Builder/build-package.sh -projectDir $TRAVIS_BUILD_DIR 95 | -------------------------------------------------------------------------------- /Config.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Config.xcconfig 3 | // SwiftKueryMySQL 4 | // 5 | // Created by Matthew Kilner on 02/07/2018. 6 | // 7 | //** 8 | //* Copyright IBM Corporation 2018 9 | //* 10 | //* Licensed under the Apache License, Version 2.0 (the "License"); 11 | //* you may not use this file except in compliance with the License. 12 | //* You may obtain a copy of the License at 13 | //* 14 | //* http://www.apache.org/licenses/LICENSE-2.0 15 | //* 16 | //* Unless required by applicable law or agreed to in writing, software 17 | //* distributed under the License is distributed on an "AS IS" BASIS, 18 | //* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | //* See the License for the specific language governing permissions and 20 | //* limitations under the License. 21 | //** 22 | //:configuration = Debug 23 | HEADER_SEARCH_PATHS = /usr/local/include/** 24 | //:configuration = Release 25 | HEADER_SEARCH_PATHS = /usr/local/include/** 26 | //:configuration = Debug 27 | OTHER_LDFLAGS = -L/usr/local/lib -lmysqlclient 28 | //:configuration = Release 29 | OTHER_LDFLAGS = -L/usr/local/lib -lmysqlclient 30 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | /** 5 | * Copyright IBM Corporation 2017,2018,2019 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | **/ 19 | 20 | import PackageDescription 21 | 22 | let package = Package( 23 | name: "SwiftKueryMySQL", 24 | products: [ 25 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 26 | .library( 27 | name: "SwiftKueryMySQL", 28 | targets: ["SwiftKueryMySQL"] 29 | ) 30 | ], 31 | dependencies: [ 32 | //.package(url: "https://github.com/IBM-Swift/Swift-Kuery.git", from: "3.1.0"), 33 | .package(url: "https://github.com/IBM-Swift/Swift-Kuery.git", .branch("master")), 34 | ], 35 | targets: [ 36 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 37 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 38 | .systemLibrary( 39 | name: "CMySQL", 40 | pkgConfig: "mysqlclient", 41 | providers: [ 42 | .brew(["mysql"]), 43 | .apt(["libmysqlclient-dev"]) 44 | ] 45 | ), 46 | .target( 47 | name: "SwiftKueryMySQL", 48 | dependencies: ["SwiftKuery","CMySQL"] 49 | ), 50 | .testTarget( 51 | name: "SwiftKueryMySQLTests", 52 | dependencies: ["SwiftKueryMySQL","CMySQL"] 53 | ) 54 | ] 55 | ) 56 | 57 | -------------------------------------------------------------------------------- /Package@swift-4.0.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | /** 5 | * Copyright IBM Corporation 2017 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | **/ 19 | 20 | import PackageDescription 21 | 22 | let package = Package( 23 | name: "SwiftKueryMySQL", 24 | products: [ 25 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 26 | .library( 27 | name: "SwiftKueryMySQL", 28 | targets: ["SwiftKueryMySQL"] 29 | ) 30 | ], 31 | dependencies: [ 32 | .package(url: "https://github.com/IBM-Swift/CMySQL.git", .upToNextMinor(from: "0.2.200")), 33 | //.package(url: "https://github.com/IBM-Swift/Swift-Kuery.git", from: "3.1.0"), 34 | .package(url: "https://github.com/IBM-Swift/Swift-Kuery.git", .branch("master")), 35 | ], 36 | targets: [ 37 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 38 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 39 | .target( 40 | name: "SwiftKueryMySQL", 41 | dependencies: ["SwiftKuery", "CMySQL"] 42 | ), 43 | .testTarget( 44 | name: "SwiftKueryMySQLTests", 45 | dependencies: ["SwiftKueryMySQL"] 46 | ) 47 | ] 48 | ) 49 | -------------------------------------------------------------------------------- /Package@swift-4.1.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | /** 5 | * Copyright IBM Corporation 2017 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | **/ 19 | 20 | import PackageDescription 21 | 22 | let package = Package( 23 | name: "SwiftKueryMySQL", 24 | products: [ 25 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 26 | .library( 27 | name: "SwiftKueryMySQL", 28 | targets: ["SwiftKueryMySQL"] 29 | ) 30 | ], 31 | dependencies: [ 32 | .package(url: "https://github.com/IBM-Swift/CMySQL.git", .upToNextMinor(from: "0.2.200")), 33 | //.package(url: "https://github.com/IBM-Swift/Swift-Kuery.git", from: "3.1.0"), 34 | .package(url: "https://github.com/IBM-Swift/Swift-Kuery.git", .branch("master")), 35 | ], 36 | targets: [ 37 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 38 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 39 | .target( 40 | name: "SwiftKueryMySQL", 41 | dependencies: ["SwiftKuery", "CMySQL"] 42 | ), 43 | .testTarget( 44 | name: "SwiftKueryMySQLTests", 45 | dependencies: ["SwiftKueryMySQL"] 46 | ) 47 | ] 48 | ) 49 | -------------------------------------------------------------------------------- /Package@swift-4.2.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.2 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | /** 5 | * Copyright IBM Corporation 2017,2018,2019 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | **/ 19 | 20 | import PackageDescription 21 | 22 | let package = Package( 23 | name: "SwiftKueryMySQL", 24 | products: [ 25 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 26 | .library( 27 | name: "SwiftKueryMySQL", 28 | targets: ["SwiftKueryMySQL"] 29 | ) 30 | ], 31 | dependencies: [ 32 | //.package(url: "https://github.com/IBM-Swift/Swift-Kuery.git", from: "3.1.0"), 33 | .package(url: "https://github.com/IBM-Swift/Swift-Kuery.git", .branch("master")), 34 | ], 35 | targets: [ 36 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 37 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 38 | .systemLibrary( 39 | name: "CMySQL", 40 | pkgConfig: "mysqlclient", 41 | providers: [ 42 | .brew(["mysql"]), 43 | .apt(["libmysqlclient-dev"]) 44 | ] 45 | ), 46 | .target( 47 | name: "SwiftKueryMySQL", 48 | dependencies: ["SwiftKuery","CMySQL"] 49 | ), 50 | .testTarget( 51 | name: "SwiftKueryMySQLTests", 52 | dependencies: ["SwiftKueryMySQL","CMySQL"] 53 | ) 54 | ] 55 | ) 56 | 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Kitura 4 | 5 |

6 | 7 | 8 |

9 | 10 | APIDoc 11 | 12 | 13 | Build Status - Master 14 | 15 | macOS 16 | Linux 17 | Apache 2 18 | 19 | Slack Status 20 | 21 |

22 | 23 | # SwiftKueryMySQL 24 | 25 | [MySQL](https://dev.mysql.com/) plugin for the [Swift-Kuery](https://github.com/IBM-Swift/Swift-Kuery) framework. It enables you to use Swift-Kuery to manipulate data in a MySQL database. 26 | 27 | ## Swift version 28 | The latest version of SwiftKueryMySQL requires **Swift 4.0 or newer**. You can download this version of the Swift binaries by following this [link](https://swift.org/download/). Compatibility with other Swift versions is not guaranteed. 29 | 30 | ## Install MySQL 31 | 32 | #### macOS 33 | ``` 34 | brew install mysql 35 | mysql.server start 36 | ``` 37 | 38 | #### Linux 39 | ``` 40 | sudo apt-get update 41 | sudo apt-get install mysql-server libmysqlclient-dev pkg-config 42 | sudo service mysql start 43 | ``` 44 | 45 | ## Usage 46 | 47 | On macOS, regular swift commands can be used for build and test. Use the example command below for generating an Xcode project. 48 | 49 | For example, 50 | ``` 51 | swift build 52 | swift test 53 | swift package generate-xcodeproj --xcconfig-overrides Config.xcconfig 54 | ``` 55 | On linux standard swift commands will also work provided your mysql installation is version 5.7 or greater. If using an earlier version of mysql add ` -Xcc -I/usr/include/mysql` to swift commands to point the compiler at the mysql header files: 56 | 57 | For example, 58 | ``` 59 | swift build -Xcc -I/usr/include/mysql/ 60 | swift test -Xcc -I/usr/include/mysql/ 61 | ``` 62 | 63 | #### Add dependencies 64 | 65 | Add the `SwiftKueryMySQL` package to the dependencies within your application’s `Package.swift` file. Substitute `"x.x.x"` with the latest `SwiftKueryMySQL` [release](https://github.com/IBM-Swift/SwiftKueryMySQL/releases). 66 | 67 | ```swift 68 | .package(url: "https://github.com/IBM-Swift/SwiftKueryMySQL.git", from: "x.x.x") 69 | ``` 70 | 71 | Add `SwiftKueryMySQL` to your target's dependencies: 72 | 73 | ```swift 74 | .target(name: "example", dependencies: ["SwiftKueryMySQL"]), 75 | ``` 76 | 77 | #### Import package 78 | 79 | ```swift 80 | import SwiftKueryMySQL 81 | ``` 82 | 83 | ## Using SwiftKueryMySQL 84 | 85 | Create an instance of `MySQLConnection` by calling: 86 | 87 | ```swift 88 | let connection = MySQLConnection(host: host, user: user, password: password, database: database, 89 | port: port, characterSet: characterSet) 90 | ``` 91 | **Where:** 92 | - *host* - hostname or IP of the MySQL server, defaults to localhost 93 | - *user* - the user name, defaults to current user 94 | - *password* - the user password, defaults to no password 95 | - *database* - default database to use, if specified 96 | - *port* - port number for the TCP/IP connection if connecting to server on a non-standard port (i.e. not 3306) 97 | - *characterSet* - MySQL character set to use for the connection 98 | 99 | All the connection parameters are optional, so if you were using a standard local MySQL server as the current user, you could simply use: 100 | ```swift 101 | let connection = MySQLConnection(password: password) 102 | ``` 103 | *password* is also optional, but recommended. 104 | 105 | Alternatively, call: 106 | ```swift 107 | let connection = MySQLConnection(url: URL(string: "mysql://\(user):\(password)@\(host):\(port)/\(database)")!)) 108 | ``` 109 | You now have a connection that can be used to execute SQL queries created using Swift-Kuery. 110 | 111 | To connect to the server and execute a query: 112 | ```swift 113 | connection.connect() { result in 114 | guard result.success else { 115 | // Connection unsuccessful 116 | return 117 | } 118 | // Connection succesful 119 | // Use connection 120 | connection.execute(query: query) { queryResult in 121 | guard queryResult.success else { 122 | // Check for Error and handle 123 | return 124 | } 125 | // Process queryResult 126 | } 127 | } 128 | ``` 129 | 130 | MySQLConnections should not be used to execute concurrent operations and therefore should not be shared across threads without proper synchronisation in place. It is recommended to use a connection pool if you wish to share connections between multiple threads as the connection pool will ensure your connection is not used concurrently. 131 | 132 | The example below creates a `ConnectionPool` containing a single connection and uses it to perform an insert on multiple threads: 133 | 134 | ```swift 135 | var connectionPoolOptions = ConnectionPoolOptions.init(initialCapacity: 1, maxCapacity: 1) 136 | let connectionPool = MySQLConnection.createPool(host: host, user: user, password: password, database: database, port: port, characterSet: nil, connectionTimeout: 10000, poolOptions: connectionPoolOptions) 137 | ....... 138 | let insertQuery = Insert(into: infos, values: "firstname", "surname", Parameter()) 139 | let insertGroup = DispatchGroup() 140 | for age in 0 ... 5 { 141 | insertGroup.enter() 142 | connectionPool.getConnection() { connection, error in 143 | guard let connection = connection else { 144 | // Error Handling and return 145 | } 146 | connection.execute(query: insertQuery, parameters: [age]) { result in 147 | guard result.success else { 148 | // Error handling and return 149 | } 150 | print("Successfully inserted age: \(age)") 151 | return insertGroup.leave() 152 | } 153 | } 154 | } 155 | insertGroup.wait() 156 | ``` 157 | When executing this example code you see output similar to: 158 | ``` 159 | Successfully inserted age: 0 160 | Successfully inserted age: 1 161 | Successfully inserted age: 2 162 | Successfully inserted age: 3 163 | Successfully inserted age: 4 164 | Successfully inserted age: 5 165 | ``` 166 | This is because the connection pool only allows that connection to be obtained by a single task. Because the connection pool is now empty, additional tasks are queued for later execution. As each task completes, the single connection is returned to the pool, and the next task is then invoked. 167 | 168 | In the example above, a DispatchGroup is used to pause the main thread until all of the tasks complete. This is necessary because the call to connectionPool.getConnection() returns immediately - the task is invoked later, once a connection is available. 169 | 170 | If you increase the capacity of the thread pool, then the order of inserts will be unpredictable, as they are able to execute concurrently on different connections: 171 | ``` 172 | Successfully inserted age: 0 173 | Successfully inserted age: 1 174 | Successfully inserted age: 3 175 | Successfully inserted age: 2 176 | Successfully inserted age: 5 177 | Successfully inserted age: 4 178 | ``` 179 | 180 | View the [Swift-Kuery](https://github.com/IBM-Swift/Swift-Kuery) documentation for detailed information on using the Swift-Kuery framework. 181 | 182 | 183 | ## For testing purposes - MySQL test setup 184 | 185 | To run `swift test` to validate your MySQL installation, you must first run the following commands to set up your MySQL: 186 | ``` 187 | mysql_upgrade -uroot || echo "No need to upgrade" 188 | mysql -uroot -e "CREATE USER 'swift'@'localhost' IDENTIFIED BY 'kuery';" 189 | mysql -uroot -e "CREATE DATABASE IF NOT EXISTS test;" 190 | mysql -uroot -e "GRANT ALL ON test.* TO 'swift'@'localhost';" 191 | ``` 192 | 193 | ## API Documentation 194 | For more information visit our [API reference](https://ibm-swift.github.io/SwiftKueryMySQL/index.html). 195 | 196 | ## Community 197 | 198 | We love to talk server-side Swift, and Kitura. Join our [Slack](http://swift-at-ibm-slack.mybluemix.net/) to meet the team! 199 | 200 | ## Deployment via Cloud Foundry 201 | 202 | If you include SwiftKueryMySQL as a dependancy in an application you are deploying using the Cloud Foundry Swift buildpack then you will need to specify some additional flags when compiling your application. This is best achieved by adding a file to the root of your application named .swift-build-linux-options with the content: 203 | 204 | ``` 205 | $ cat .swift-build-options-linux 206 | -Xcc -I/usr/include/mysql/ 207 | ``` 208 | These flags tell the compiler where to find the MySQL header files required to build the CMySQL library. 209 | 210 | ## License 211 | This library is licensed under Apache 2.0. Full license text is available in [LICENSE](https://github.com/IBM-Swift/SwiftKueryMySQL/blob/master/LICENSE.txt). 212 | -------------------------------------------------------------------------------- /Sources/CMySQL/module.modulemap: -------------------------------------------------------------------------------- 1 | module CMySQL [system] { 2 | header "shim.h" 3 | link "mysqlclient" 4 | export * 5 | } 6 | -------------------------------------------------------------------------------- /Sources/CMySQL/shim.h: -------------------------------------------------------------------------------- 1 | #ifndef __CMYSQL_SHIM_H__ 2 | #define __CMYSQL_SHIM_H__ 3 | #include 4 | #include 5 | 6 | #ifdef __linux__ 7 | #include 8 | #include 9 | #else 10 | #include 11 | #endif 12 | 13 | #include 14 | #include 15 | 16 | #if LIBMYSQL_VERSION_ID < 80000 17 | 18 | typedef my_bool mysql_bool; 19 | 20 | static inline mysql_bool mysql_true(){ 21 | return 1; 22 | } 23 | 24 | static inline mysql_bool mysql_false(){ 25 | return 0; 26 | } 27 | 28 | #else 29 | 30 | typedef bool mysql_bool; 31 | 32 | static inline mysql_bool mysql_true(){ 33 | return true; 34 | } 35 | 36 | static inline mysql_bool mysql_false(){ 37 | return false; 38 | } 39 | 40 | #endif 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /Sources/SwiftKueryMySQL/MySQLPreparedStatement.swift: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright IBM Corporation 2017,2108,2019 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import Foundation 18 | import SwiftKuery 19 | 20 | import CMySQL 21 | 22 | /// MySQL implementation for prepared statements. 23 | public class MySQLPreparedStatement: PreparedStatement { 24 | var statement: UnsafeMutablePointer? 25 | internal let query: Query? 26 | 27 | private var binds = [MYSQL_BIND]() 28 | internal var bindsCapacity = 0 29 | internal var bindPtr: UnsafeMutablePointer? = nil 30 | private var mysql: UnsafeMutablePointer? 31 | 32 | init(query: Query? = nil, mysql: UnsafeMutablePointer?, statement: UnsafeMutablePointer?) { 33 | self.mysql = mysql 34 | self.statement = statement 35 | self.query = query 36 | } 37 | 38 | deinit { 39 | if self.statement != nil { 40 | print("WARNING: Deinitialising a prepared statement that has not been explictly released. Failing to release prepared statements will leak memory.") 41 | } 42 | } 43 | 44 | func release(onCompletion: @escaping ((QueryResult) -> ())) { 45 | deallocateBinds() 46 | 47 | if let statement = self.statement { 48 | self.statement = nil 49 | mysql_stmt_close(statement) 50 | } 51 | onCompletion(.successNoData) 52 | } 53 | 54 | internal func getError(_ statement: UnsafeMutablePointer) -> String { 55 | return "ERROR \(mysql_stmt_errno(statement)): " + String(cString: mysql_stmt_error(statement)) 56 | } 57 | 58 | internal func allocateBinds(parameters: [Any?]) -> Bool { 59 | var cols: [Column]? 60 | switch query { 61 | case let insert as Insert: 62 | cols = insert.columns ?? insert.table.columns 63 | case let update as Update: 64 | cols = update.table.columns 65 | default: 66 | break 67 | } 68 | 69 | let columns: [Column]? 70 | if cols?.count == parameters.count { 71 | columns = cols 72 | } else { 73 | columns = nil 74 | } 75 | 76 | if binds.isEmpty { // first parameter set, create new bind and bind it to the parameter 77 | for (index, parameter) in parameters.enumerated() { 78 | var bind = MYSQL_BIND() 79 | setBind(&bind, parameter, columns?[index]) 80 | binds.append(bind) 81 | bindPtr![index] = bind 82 | } 83 | } else { // bind was previously created, re-initialize value 84 | for (index, parameter) in parameters.enumerated() { 85 | var bind = binds[index] 86 | setBind(&bind, parameter, columns?[index]) 87 | binds[index] = bind 88 | bindPtr![index] = bind 89 | } 90 | } 91 | 92 | guard mysql_stmt_bind_param(statement, bindPtr) == mysql_false() else { 93 | return false 94 | } 95 | return true 96 | } 97 | 98 | private func deallocateBinds() { 99 | guard let bindPtr = self.bindPtr else { 100 | return 101 | } 102 | 103 | self.bindPtr = nil 104 | 105 | for bind in binds { 106 | if bind.buffer != nil { 107 | #if swift(>=4.1) 108 | bind.buffer.deallocate() 109 | #else 110 | bind.buffer.deallocate(bytes: Int(bind.buffer_length), alignedTo: 1) 111 | #endif 112 | } 113 | if bind.length != nil { 114 | #if swift(>=4.1) 115 | bind.length.deallocate() 116 | #else 117 | bind.length.deallocate(capacity: 1) 118 | #endif 119 | } 120 | if bind.is_null != nil { 121 | #if swift(>=4.1) 122 | bind.is_null.deallocate() 123 | #else 124 | bind.is_null.deallocate(capacity: 1) 125 | #endif 126 | } 127 | } 128 | #if swift(>=4.1) 129 | bindPtr.deallocate() 130 | #else 131 | bindPtr.deallocate(capacity: bindsCapacity) 132 | #endif 133 | binds.removeAll() 134 | } 135 | 136 | private func setBind(_ bind: inout MYSQL_BIND, _ parameter: Any?, _ column: Column?) { 137 | if bind.is_null == nil { 138 | bind.is_null = UnsafeMutablePointer.allocate(capacity: 1) 139 | } 140 | 141 | guard let parameter = parameter else { 142 | bind.buffer_type = MYSQL_TYPE_NULL 143 | bind.is_null.initialize(to: mysql_true()) 144 | return 145 | } 146 | 147 | bind.buffer_type = getType(parameter: parameter) 148 | bind.is_null.initialize(to: mysql_false()) 149 | bind.is_unsigned = mysql_false() 150 | 151 | switch parameter { 152 | case let string as String: 153 | initialize(string: string, &bind) 154 | case let date as Date: 155 | let formatter: DateFormatter 156 | switch column?.type { 157 | case is SQLDate.Type: 158 | formatter = MySQLConnection.dateFormatter 159 | case is Time.Type: 160 | formatter = MySQLConnection.timeFormatter 161 | default: 162 | formatter = MySQLConnection.dateTimeFormatter 163 | } 164 | let formattedDate = formatter.string(from: date) 165 | initialize(string: formattedDate, &bind) 166 | case let byteArray as [UInt8]: 167 | let typedBuffer = allocate(type: UInt8.self, capacity: byteArray.count, bind: &bind) 168 | typedBuffer.initialize(from: byteArray, count: byteArray.count) 169 | case let data as Data: 170 | let typedBuffer = allocate(type: UInt8.self, capacity: data.count, bind: &bind) 171 | data.copyBytes(to: typedBuffer, count: data.count) 172 | case let dateTime as MYSQL_TIME: 173 | initialize(dateTime, &bind) 174 | case let float as Float: 175 | initialize(float, &bind) 176 | case let double as Double: 177 | initialize(double, &bind) 178 | case let bool as Bool: 179 | initialize(bool, &bind) 180 | case let int as Int: 181 | initialize(int, &bind) 182 | case let int as Int8: 183 | initialize(int, &bind) 184 | case let int as Int16: 185 | initialize(int, &bind) 186 | case let int as Int32: 187 | initialize(int, &bind) 188 | case let int as Int64: 189 | initialize(int, &bind) 190 | case let uint as UInt: 191 | initialize(uint, &bind) 192 | bind.is_unsigned = mysql_true() 193 | case let uint as UInt8: 194 | initialize(uint, &bind) 195 | bind.is_unsigned = mysql_true() 196 | case let uint as UInt16: 197 | initialize(uint, &bind) 198 | bind.is_unsigned = mysql_true() 199 | case let uint as UInt32: 200 | initialize(uint, &bind) 201 | bind.is_unsigned = mysql_true() 202 | case let uint as UInt64: 203 | initialize(uint, &bind) 204 | bind.is_unsigned = mysql_true() 205 | case let unicodeScalar as UnicodeScalar: 206 | initialize(unicodeScalar, &bind) 207 | bind.is_unsigned = mysql_true() 208 | default: 209 | print("WARNING: Unhandled parameter \(parameter) (type: \(type(of: parameter))). Will attempt to convert it to a String") 210 | initialize(string: String(describing: parameter), &bind) 211 | } 212 | } 213 | 214 | private func allocate(type: T.Type, capacity: Int, bind: inout MYSQL_BIND) -> UnsafeMutablePointer { 215 | 216 | let length = UInt(capacity * MemoryLayout.size) 217 | 218 | if bind.length == nil { 219 | bind.length = UnsafeMutablePointer.allocate(capacity: 1) 220 | } 221 | bind.length.initialize(to: length) 222 | 223 | let typedBuffer: UnsafeMutablePointer 224 | if let buffer = bind.buffer, bind.buffer_length >= length { 225 | typedBuffer = buffer.assumingMemoryBound(to: type) 226 | } else { 227 | if bind.buffer != nil { 228 | // deallocate existing smaller buffer 229 | #if swift(>=4.1) 230 | bind.buffer.deallocate() 231 | #else 232 | bind.buffer.deallocate(bytes: Int(bind.buffer_length), alignedTo: 1) 233 | #endif 234 | } 235 | 236 | typedBuffer = UnsafeMutablePointer.allocate(capacity: capacity) 237 | bind.buffer = UnsafeMutableRawPointer(typedBuffer) 238 | bind.buffer_length = length 239 | } 240 | 241 | return typedBuffer 242 | } 243 | 244 | private func initialize(_ parameter: T, _ bind: inout MYSQL_BIND) { 245 | let typedBuffer = allocate(type: type(of: parameter), capacity: 1, bind: &bind) 246 | typedBuffer.initialize(to: parameter) 247 | } 248 | 249 | private func initialize(string: String, _ bind: inout MYSQL_BIND) { 250 | let utf8 = Array(string.utf8) 251 | let typedBuffer = allocate(type: UInt8.self, capacity: utf8.count, bind: &bind) 252 | typedBuffer.initialize(from: utf8, count: utf8.count) 253 | } 254 | 255 | private func getType(parameter: Any) -> enum_field_types { 256 | switch parameter { 257 | case is String, 258 | is Date: 259 | return MYSQL_TYPE_STRING 260 | case is Data, 261 | is [UInt8]: 262 | return MYSQL_TYPE_BLOB 263 | case is Int8, 264 | is UInt8, 265 | is Bool: 266 | return MYSQL_TYPE_TINY 267 | case is Int16, 268 | is UInt16: 269 | return MYSQL_TYPE_SHORT 270 | case is Int32, 271 | is UInt32, 272 | is UnicodeScalar: 273 | return MYSQL_TYPE_LONG 274 | case is Int, 275 | is UInt, 276 | is Int64, 277 | is UInt64: 278 | return MYSQL_TYPE_LONGLONG 279 | case is Float: 280 | return MYSQL_TYPE_FLOAT 281 | case is Double: 282 | return MYSQL_TYPE_DOUBLE 283 | case is MYSQL_TIME: 284 | return MYSQL_TYPE_DATETIME 285 | default: 286 | return MYSQL_TYPE_STRING 287 | } 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /Sources/SwiftKueryMySQL/MySQLResultFetcher.swift: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright IBM Corporation 2017 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import SwiftKuery 18 | import Foundation 19 | import Dispatch 20 | 21 | import CMySQL 22 | 23 | /// An implementation of query result fetcher. 24 | public class MySQLResultFetcher: ResultFetcher { 25 | 26 | private var preparedStatement: MySQLPreparedStatement 27 | private var bindPtr: UnsafeMutablePointer? 28 | private var binds: [MYSQL_BIND] 29 | 30 | private var fieldNames: [String] 31 | private var charsetnr: [UInt32] 32 | 33 | private var hasMoreRows = true 34 | 35 | private var resultMetadata: UnsafeMutablePointer? = nil 36 | 37 | init(preparedStatement: MySQLPreparedStatement, resultMetadata: UnsafeMutablePointer) { 38 | self.resultMetadata = resultMetadata 39 | self.preparedStatement = preparedStatement 40 | self.binds = [MYSQL_BIND]() 41 | self.fieldNames = [String]() 42 | self.charsetnr = [UInt32]() 43 | } 44 | 45 | internal func initialize() -> Bool { 46 | guard let fields = mysql_fetch_fields(resultMetadata) else { 47 | return initError(preparedStatement) 48 | } 49 | 50 | let numFields = Int(mysql_num_fields(resultMetadata)) 51 | var binds = [MYSQL_BIND]() 52 | var fieldNames = [String]() 53 | var charsetnr = [UInt32]() 54 | 55 | for i in 0 ..< numFields { 56 | let field = fields[i] 57 | binds.append(MySQLResultFetcher.getOutputBind(field)) 58 | fieldNames.append(String(cString: field.name)) 59 | charsetnr.append(field.charsetnr) 60 | } 61 | 62 | let bindPtr = UnsafeMutablePointer.allocate(capacity: binds.count) 63 | for i in 0 ..< binds.count { 64 | bindPtr[i] = binds[i] 65 | } 66 | 67 | guard mysql_stmt_bind_result(preparedStatement.statement, bindPtr) == mysql_false() else { 68 | return initError(preparedStatement, bindPtr: bindPtr, binds: binds) 69 | } 70 | 71 | guard mysql_stmt_execute(preparedStatement.statement) == 0 else { 72 | return initError(preparedStatement, bindPtr: bindPtr, binds: binds) 73 | } 74 | 75 | self.bindPtr = bindPtr 76 | self.binds = binds 77 | self.fieldNames = fieldNames 78 | self.charsetnr = charsetnr 79 | 80 | return true 81 | } 82 | 83 | deinit { 84 | close() 85 | } 86 | 87 | private func initError(_ preparedStatement: MySQLPreparedStatement, bindPtr: UnsafeMutablePointer? = nil, binds: [MYSQL_BIND]? = nil) -> Bool { 88 | 89 | if let binds = binds { 90 | for bind in binds { 91 | 92 | #if swift(>=4.1) 93 | bind.buffer.deallocate() 94 | bind.length.deallocate() 95 | bind.is_null.deallocate() 96 | bind.error.deallocate() 97 | #else 98 | bind.buffer.deallocate(bytes: Int(bind.buffer_length), alignedTo: 1) 99 | bind.length.deallocate(capacity: 1) 100 | bind.is_null.deallocate(capacity: 1) 101 | bind.error.deallocate(capacity: 1) 102 | #endif 103 | } 104 | 105 | if let bindPtr = bindPtr { 106 | #if swift(>=4.1) 107 | bindPtr.deallocate() 108 | #else 109 | bindPtr.deallocate(capacity: binds.count) 110 | #endif 111 | } 112 | } 113 | return false 114 | } 115 | 116 | private func close() { 117 | if let bindPtr = bindPtr { 118 | self.bindPtr = nil 119 | 120 | for bind in binds { 121 | #if swift(>=4.1) 122 | bind.buffer.deallocate() 123 | bind.length.deallocate() 124 | bind.is_null.deallocate() 125 | bind.error.deallocate() 126 | #else 127 | bind.buffer.deallocate(bytes: Int(bind.buffer_length), alignedTo: 1) 128 | bind.length.deallocate(capacity: 1) 129 | bind.is_null.deallocate(capacity: 1) 130 | bind.error.deallocate(capacity: 1) 131 | #endif 132 | } 133 | #if swift(>=4.1) 134 | bindPtr.deallocate() 135 | #else 136 | bindPtr.deallocate(capacity: binds.count) 137 | #endif 138 | 139 | mysql_free_result(resultMetadata) 140 | preparedStatement.release() { _ in } 141 | } 142 | } 143 | 144 | /// Indicate no further calls will be made to this ResultFetcher allowing the connection in use to be released. 145 | /// 146 | public func done() { 147 | close() 148 | } 149 | 150 | /// Fetch the next row of the query result. This function is non-blocking. 151 | /// 152 | /// - Parameter callback: A callback to call when the next row of the query result is ready. 153 | public func fetchNext(callback: @escaping (([Any?]?, Error?)) -> ()) { 154 | DispatchQueue.global().async { 155 | mysql_thread_init() 156 | guard self.hasMoreRows else { 157 | mysql_thread_end() 158 | return callback((nil, nil)) 159 | } 160 | 161 | if let row = self.buildRow() { 162 | mysql_thread_end() 163 | return callback((row, nil)) 164 | } else { 165 | self.hasMoreRows = false 166 | self.close() 167 | mysql_thread_end() 168 | return callback((nil, nil)) 169 | } 170 | } 171 | } 172 | 173 | /// Fetch the titles of the query result. This function is non-blocking. 174 | /// 175 | /// - Parameter callback: A closure that accepts a tuple containing an optional array of column titles of type String and an optional Error 176 | public func fetchTitles(callback: @escaping (([String]?, Error?)) -> ()) { 177 | // As the titles are prepared during intialisation we can return without needing to offload. 178 | return callback((fieldNames, nil)) 179 | } 180 | 181 | private static func getOutputBind(_ field: MYSQL_FIELD) -> MYSQL_BIND { 182 | let size = getSize(field: field) 183 | 184 | var bind = MYSQL_BIND() 185 | bind.buffer_type = field.type 186 | bind.buffer_length = UInt(size) 187 | bind.is_unsigned = mysql_false() 188 | 189 | #if swift(>=4.1) 190 | bind.buffer = UnsafeMutableRawPointer.allocate(byteCount: size, alignment: 1) 191 | #else 192 | bind.buffer = UnsafeMutableRawPointer.allocate(bytes: size, alignedTo: 1) 193 | #endif 194 | 195 | bind.length = UnsafeMutablePointer.allocate(capacity: 1) 196 | bind.is_null = UnsafeMutablePointer.allocate(capacity: 1) 197 | bind.error = UnsafeMutablePointer.allocate(capacity: 1) 198 | 199 | return bind 200 | } 201 | 202 | private static func getSize(field: MYSQL_FIELD) -> Int { 203 | switch field.type { 204 | case MYSQL_TYPE_TINY: 205 | return MemoryLayout.size 206 | case MYSQL_TYPE_SHORT: 207 | return MemoryLayout.size 208 | case MYSQL_TYPE_INT24, 209 | MYSQL_TYPE_LONG: 210 | return MemoryLayout.size 211 | case MYSQL_TYPE_LONGLONG: 212 | return MemoryLayout.size 213 | case MYSQL_TYPE_FLOAT: 214 | return MemoryLayout.size 215 | case MYSQL_TYPE_DOUBLE: 216 | return MemoryLayout.size 217 | case MYSQL_TYPE_TIME, 218 | MYSQL_TYPE_DATE, 219 | MYSQL_TYPE_DATETIME, 220 | MYSQL_TYPE_TIMESTAMP: 221 | return MemoryLayout.size 222 | default: 223 | return Int(field.length) 224 | } 225 | } 226 | 227 | private func buildRow() -> [Any?]? { 228 | let fetchStatus = mysql_stmt_fetch(preparedStatement.statement) 229 | if fetchStatus == MYSQL_NO_DATA { 230 | return nil 231 | } 232 | 233 | if fetchStatus == 1 { 234 | // use a logger or add throws to the fetchNext signature? 235 | print("ERROR: while fetching row: \(preparedStatement.getError(preparedStatement.statement!))") 236 | return nil 237 | } 238 | 239 | var row = [Any?]() 240 | for (index, bind) in binds.enumerated() { 241 | guard let buffer = bind.buffer else { 242 | row.append("bind buffer not set") 243 | continue 244 | } 245 | 246 | guard bind.is_null.pointee == mysql_false() else { 247 | row.append(nil) 248 | continue 249 | } 250 | 251 | let type = bind.buffer_type 252 | switch type { 253 | case MYSQL_TYPE_TINY: 254 | row.append(buffer.load(as: Int8.self)) 255 | case MYSQL_TYPE_SHORT: 256 | row.append(buffer.load(as: Int16.self)) 257 | case MYSQL_TYPE_INT24, 258 | MYSQL_TYPE_LONG: 259 | row.append(buffer.load(as: Int32.self)) 260 | case MYSQL_TYPE_LONGLONG: 261 | row.append(buffer.load(as: Int64.self)) 262 | case MYSQL_TYPE_FLOAT: 263 | row.append(buffer.load(as: Float.self)) 264 | case MYSQL_TYPE_DOUBLE: 265 | row.append(buffer.load(as: Double.self)) 266 | case MYSQL_TYPE_NEWDECIMAL, 267 | MYSQL_TYPE_STRING, 268 | MYSQL_TYPE_VAR_STRING: 269 | row.append(String(bytesNoCopy: buffer, length: getLength(bind), encoding: .utf8, freeWhenDone: false)) 270 | case MYSQL_TYPE_TINY_BLOB, 271 | MYSQL_TYPE_BLOB, 272 | MYSQL_TYPE_MEDIUM_BLOB, 273 | MYSQL_TYPE_LONG_BLOB: 274 | if charsetnr[index] == 63 { 275 | // Value 63 is used to denote binary data 276 | // see https://dev.mysql.com/doc/refman/5.7/en/c-api-prepared-statement-type-conversions.html 277 | row.append(Data(bytes: buffer, count: getLength(bind))) 278 | } else { 279 | // We are assuming that the returned data 280 | // is encoded in UTF-8 281 | row.append(String(bytesNoCopy: buffer, length: getLength(bind), encoding: .utf8, freeWhenDone: false)) 282 | } 283 | case MYSQL_TYPE_BIT: 284 | row.append(Data(bytes: buffer, count: getLength(bind))) 285 | case MYSQL_TYPE_TIME: 286 | let time = buffer.load(as: MYSQL_TIME.self) 287 | row.append("\(pad(time.hour)):\(pad(time.minute)):\(pad(time.second))") 288 | case MYSQL_TYPE_DATE: 289 | let time = buffer.load(as: MYSQL_TIME.self) 290 | row.append("\(time.year)-\(pad(time.month))-\(pad(time.day))") 291 | case MYSQL_TYPE_DATETIME, 292 | MYSQL_TYPE_TIMESTAMP: 293 | let time = buffer.load(as: MYSQL_TIME.self) 294 | let formattedDate = "\(time.year)-\(time.month)-\(time.day) \(time.hour):\(time.minute):\(time.second)" 295 | row.append(MySQLConnection.dateTimeFormatter.date(from: formattedDate)) 296 | default: 297 | print("Using string for unhandled enum_field_type: \(type.rawValue)") 298 | row.append(String(bytesNoCopy: buffer, length: getLength(bind), encoding: .utf8, freeWhenDone: false)) 299 | } 300 | } 301 | return row 302 | } 303 | 304 | private func getLength(_ bind: MYSQL_BIND) -> Int { 305 | return Int(bind.length.pointee > bind.buffer_length ? bind.buffer_length : bind.length.pointee) 306 | } 307 | 308 | private func pad(_ uInt: UInt32) -> String { 309 | return String(format: "%02u", uInt) 310 | } 311 | } 312 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import SwiftKueryMySQLTests 3 | 4 | XCTMain([ 5 | testCase(TestSelect.allTests), 6 | testCase(TestInsert.allTests), 7 | testCase(TestUpdate.allTests), 8 | testCase(TestAlias.allTests), 9 | testCase(TestParameters.allTests), 10 | testCase(TestJoin.allTests), 11 | testCase(TestSubquery.allTests), 12 | testCase(TestTransaction.allTests), 13 | testCase(TestColumnTypes.allTests), 14 | testCase(TestSchema.allTests) 15 | ]) 16 | -------------------------------------------------------------------------------- /Tests/SwiftKueryMySQLTests/CommonUtils.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright IBM Corporation 2017 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | import XCTest 18 | import Foundation 19 | import SwiftKuery 20 | import SwiftKueryMySQL 21 | 22 | func read(fileName: String) -> String { 23 | // Read in a configuration file into an NSData 24 | do { 25 | var pathToTests = #file 26 | if pathToTests.hasSuffix("CommonUtils.swift") { 27 | pathToTests = pathToTests.replacingOccurrences(of: "CommonUtils.swift", with: "") 28 | } 29 | let fileData = try Data(contentsOf: URL(fileURLWithPath: "\(pathToTests)\(fileName)")) 30 | XCTAssertNotNil(fileData, "Failed to read in the \(fileName) file") 31 | 32 | let resultString = String(data: fileData, encoding: String.Encoding.utf8) 33 | 34 | guard 35 | let resultLiteral = resultString 36 | else { 37 | XCTFail("Error in \(fileName).") 38 | exit(1) 39 | } 40 | return resultLiteral.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) 41 | } catch { 42 | XCTFail("Error in \(fileName).") 43 | exit(1) 44 | } 45 | } 46 | 47 | func executeQuery(query: Query, connection: Connection, callback: @escaping (QueryResult, [[Any?]]?)->()) { 48 | do { 49 | try print("=======\(connection.descriptionOf(query: query))=======") 50 | } 51 | catch {} 52 | connection.execute(query: query) { result in 53 | printResultAndGetRowsAsArray(result, callback: callback) 54 | } 55 | } 56 | 57 | func executeQueryWithParameters(query: Query, connection: Connection, parameters: [Any?], callback: @escaping (QueryResult, [[Any?]]?)->()) { 58 | do { 59 | try print("=======\(connection.descriptionOf(query: query))=======") 60 | } 61 | catch {} 62 | connection.execute(query: query, parameters: parameters) { result in 63 | printResultAndGetRowsAsArray(result, callback: callback) 64 | } 65 | } 66 | 67 | func executeQueryWithNamedParameters(query: Query, connection: Connection, parameters: [String:Any?], callback: @escaping (QueryResult, [[Any?]]?)->()) { 68 | do { 69 | try print("=======\(connection.descriptionOf(query: query))=======") 70 | } 71 | catch {} 72 | connection.execute(query: query, parameters: parameters) { result in 73 | printResultAndGetRowsAsArray(result, callback: callback) 74 | } 75 | } 76 | 77 | func executeRawQueryWithParameters(_ raw: String, connection: Connection, parameters: [Any?], callback: @escaping (QueryResult, [[Any?]]?)->()) { 78 | print("=======\(raw)=======") 79 | connection.execute(raw, parameters: parameters) { result in 80 | printResultAndGetRowsAsArray(result, callback: callback) 81 | } 82 | } 83 | 84 | func executeQueryWithParameters(query: Query, connection: Connection, parameters: [String:Any?], callback: @escaping (QueryResult, [[Any?]]?)->()) { 85 | do { 86 | try print("=======\(connection.descriptionOf(query: query))=======") 87 | } 88 | catch {} 89 | connection.execute(query: query, parameters: parameters) { result in 90 | printResultAndGetRowsAsArray(result, callback: callback) 91 | } 92 | } 93 | 94 | func executeRawQueryWithParameters(_ raw: String, connection: Connection, parameters: [String:Any?], callback: @escaping (QueryResult, [[Any?]]?)->()) { 95 | print("=======\(raw)=======") 96 | connection.execute(raw, parameters: parameters) { result in 97 | printResultAndGetRowsAsArray(result, callback: callback) 98 | } 99 | } 100 | 101 | func executeRawQuery(_ raw: String, connection: Connection, callback: @escaping (QueryResult, [[Any?]]?)->()) { 102 | print("=======\(raw)=======") 103 | connection.execute(raw) { result in 104 | printResultAndGetRowsAsArray(result, callback: callback) 105 | } 106 | } 107 | 108 | func cleanUp(table: String, connection: Connection, callback: @escaping (QueryResult)->()) { 109 | connection.execute("DROP TABLE " + packName(table)) { result in 110 | callback(result) 111 | } 112 | } 113 | 114 | private func printResultAndGetRowsAsArray(_ result: QueryResult, callback: @escaping (QueryResult, [[Any?]]?)->()) { 115 | var rows: [[Any?]] = [[Any?]]() 116 | if let resultSet = result.asResultSet { 117 | resultSet.getColumnTitles() { titles, error in 118 | guard let titles = titles else { 119 | return callback(result, nil) 120 | } 121 | for title in titles { 122 | print(title.padding(toLength: 11, withPad: " ", startingAt: 0), terminator: "") 123 | } 124 | print() 125 | resultSet.forEach() { row, error in 126 | guard let row = row else { 127 | // No more rows 128 | return callback(result, rows) 129 | } 130 | for value in row { 131 | if let value = value { 132 | print(value, terminator: " ") 133 | } else { 134 | print("nil", terminator: " ") 135 | } 136 | } 137 | print() 138 | rows.append(row) 139 | } 140 | } 141 | } else if let value = result.asValue { 142 | print("Result: ", value) 143 | callback(result, nil) 144 | } else if result.success { 145 | print("Success") 146 | callback(result, nil) 147 | } else if let queryError = result.asError { 148 | print("Error in query: ", queryError) 149 | callback(result, nil) 150 | } 151 | } 152 | 153 | func packName(_ name: String) -> String { 154 | var result = name 155 | let identifierQuoteCharacter = "`" 156 | if !result.hasPrefix(identifierQuoteCharacter) { 157 | result = identifierQuoteCharacter + result + identifierQuoteCharacter 158 | } 159 | return result 160 | } 161 | 162 | class CommonUtils { 163 | private var pool: ConnectionPool? 164 | static let sharedInstance = CommonUtils() 165 | private init() {} 166 | 167 | func getConnectionPool(characterSet: String? = nil) -> ConnectionPool { 168 | if let pool = pool { 169 | return pool 170 | } 171 | do { 172 | let connectionFile = #file.replacingOccurrences(of: "CommonUtils.swift", with: "connection.json") 173 | let data = Data(referencing: try NSData(contentsOfFile: connectionFile)) 174 | let json = try JSONSerialization.jsonObject(with: data) 175 | 176 | if let dictionary = json as? [String: String] { 177 | let host = dictionary["host"] 178 | let username = dictionary["username"] 179 | let password = dictionary["password"] 180 | let database = dictionary["database"] 181 | var port: Int? = nil 182 | if let portString = dictionary["port"] { 183 | port = Int(portString) 184 | } 185 | 186 | let randomBinary: UInt32 187 | #if os(Linux) 188 | randomBinary = UInt32(random() % 2) 189 | #else 190 | randomBinary = arc4random_uniform(2) 191 | #endif 192 | 193 | let poolOptions = ConnectionPoolOptions(initialCapacity: 1, maxCapacity: 1) 194 | 195 | if characterSet != nil || randomBinary == 0 { 196 | pool = MySQLConnection.createPool(host: host, user: username, password: password, database: database, port: port, characterSet: characterSet, connectionTimeout: 10000, poolOptions: poolOptions) 197 | } else { 198 | var urlString = "mysql://" 199 | if let username = username, let password = password { 200 | urlString += "\(username):\(password)@" 201 | } 202 | urlString += host ?? "localhost" 203 | if let port = port { 204 | urlString += ":\(port)" 205 | } 206 | if let database = database { 207 | urlString += "/\(database)" 208 | } 209 | 210 | if let url = URL(string: urlString) { 211 | pool = MySQLConnection.createPool(url: url, poolOptions: poolOptions) 212 | } else { 213 | pool = nil 214 | XCTFail("Invalid URL format: \(urlString)") 215 | } 216 | } 217 | } else { 218 | pool = nil 219 | XCTFail("Invalid format for connection.json contents: \(json)") 220 | } 221 | } catch { 222 | print("caught throw") 223 | pool = nil 224 | XCTFail(error.localizedDescription) 225 | } 226 | return pool! 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /Tests/SwiftKueryMySQLTests/MySQLTest.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright IBM Corporation 2017 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | import XCTest 18 | import Dispatch 19 | import Foundation 20 | 21 | import SwiftKuery 22 | import SwiftKueryMySQL 23 | 24 | protocol Test { 25 | func expectation(_ index: Int) -> XCTestExpectation 26 | func waitExpectation(timeout t: TimeInterval, handler: XCWaitCompletionHandler?) 27 | } 28 | 29 | extension Test { 30 | 31 | func doSetUp() { 32 | } 33 | 34 | func doTearDown() { 35 | // sleep(10) 36 | } 37 | 38 | func performTest(asyncTasks: (XCTestExpectation) -> Void...) { 39 | let queue = DispatchQueue(label: "Query queue") 40 | 41 | for (index, asyncTask) in asyncTasks.enumerated() { 42 | let expectation = self.expectation(index) 43 | queue.async() { 44 | asyncTask(expectation) 45 | } 46 | } 47 | 48 | waitExpectation(timeout: 30) { error in 49 | // blocks test until request completes 50 | XCTAssertNil(error) 51 | } 52 | } 53 | } 54 | 55 | extension XCTestCase: Test { 56 | func expectation(_ index: Int) -> XCTestExpectation { 57 | let expectationDescription = "\(type(of: self))-\(index)" 58 | return self.expectation(description: expectationDescription) 59 | } 60 | 61 | func waitExpectation(timeout t: TimeInterval, handler: XCWaitCompletionHandler?) { 62 | self.waitForExpectations(timeout: t, handler: handler) 63 | } 64 | } 65 | 66 | /*class MySQLTest: XCTestCase { 67 | private static var threadSafePool: ConnectionPool? 68 | private static var threadUnsafePool: ConnectionPool? 69 | 70 | func performTest(characterSet: String? = nil, timeout: TimeInterval = 10, line: Int = #line, asyncTasks: (Connection) -> Void...) { 71 | 72 | var connection: Connection 73 | guard let pool = getPool(taskCount: asyncTasks.count, characterSet: characterSet).pool else { 74 | XCTFail("Failed to get connection pool") 75 | return 76 | } 77 | 78 | guard let conn = pool.getConnection() else { 79 | XCTFail("Failed to get connection") 80 | return 81 | } 82 | connection = conn 83 | 84 | defer { 85 | connection.closeConnection() 86 | } 87 | 88 | var connectError: QueryError? = nil 89 | connection.connect() { error in 90 | if let error = error { 91 | connectError = error 92 | return 93 | } 94 | 95 | // use a concurrent queue so we can test connection is thread-safe 96 | let queue = DispatchQueue(label: "Test tasks queue", attributes: .concurrent) 97 | queue.suspend() // don't start executing tasks when queued 98 | 99 | for (index, asyncTask) in asyncTasks.enumerated() { 100 | let exp = self.expectation(description: "\(type(of: self)):\(line)[\(index)]") 101 | queue.async() { 102 | asyncTask(connection) 103 | exp.fulfill() 104 | } 105 | } 106 | 107 | queue.resume() // all tasks are queued, execute them 108 | } 109 | 110 | if let error = connectError { 111 | XCTFail(error.description) 112 | } else { 113 | // wait for all async tasks to finish 114 | waitForExpectations(timeout: timeout) { error in 115 | XCTAssertNil(error) 116 | } 117 | } 118 | } 119 | 120 | private func getPool(taskCount: Int, characterSet: String?) -> (connection: Connection?, pool: ConnectionPool?) { 121 | if characterSet == nil { 122 | if let pool = (taskCount > 1 ? MySQLTest.threadSafePool : MySQLTest.threadUnsafePool) { 123 | return (nil, pool) 124 | } 125 | } 126 | 127 | let pool: ConnectionPool? 128 | do { 129 | let connectionFile = #file.replacingOccurrences(of: "MySQLTest.swift", with: "connection.json") 130 | let data = Data(referencing: try NSData(contentsOfFile: connectionFile)) 131 | let json = try JSONSerialization.jsonObject(with: data) 132 | 133 | if let dictionary = json as? [String: String] { 134 | let host = dictionary["host"] 135 | let username = dictionary["username"] 136 | let password = dictionary["password"] 137 | let database = dictionary["database"] 138 | var port: Int? = nil 139 | if let portString = dictionary["port"] { 140 | port = Int(portString) 141 | } 142 | 143 | let randomBinary: UInt32 144 | #if os(Linux) 145 | randomBinary = UInt32(random() % 2) 146 | #else 147 | randomBinary = arc4random_uniform(2) 148 | #endif 149 | 150 | let poolOptions = ConnectionPoolOptions(initialCapacity: 1, maxCapacity: 1, timeout: 10000) 151 | 152 | if characterSet != nil || randomBinary == 0 { 153 | if taskCount > 1 { 154 | pool = MySQLThreadSafeConnection.createPool(host: host, user: username, password: password, database: database, port: port, characterSet: characterSet, poolOptions: poolOptions) 155 | } else { 156 | pool = MySQLConnection.createPool(host: host, user: username, password: password, database: database, port: port, characterSet: characterSet, poolOptions: poolOptions) 157 | } 158 | } else { 159 | var urlString = "mysql://" 160 | if let username = username, let password = password { 161 | urlString += "\(username):\(password)@" 162 | } 163 | urlString += host ?? "localhost" 164 | if let port = port { 165 | urlString += ":\(port)" 166 | } 167 | if let database = database { 168 | urlString += "/\(database)" 169 | } 170 | 171 | if let url = URL(string: urlString) { 172 | if taskCount > 1 { 173 | pool = MySQLThreadSafeConnection.createPool(url: url, poolOptions: poolOptions) 174 | } else { 175 | pool = MySQLConnection.createPool(url: url, poolOptions: poolOptions) 176 | } 177 | } else { 178 | pool = nil 179 | XCTFail("Invalid URL format: \(urlString)") 180 | } 181 | } 182 | } else { 183 | pool = nil 184 | XCTFail("Invalid format for connection.json contents: \(json)") 185 | } 186 | } catch { 187 | pool = nil 188 | XCTFail(error.localizedDescription) 189 | } 190 | 191 | if taskCount > 1 { 192 | MySQLTest.threadSafePool = pool 193 | } else { 194 | MySQLTest.threadUnsafePool = pool 195 | } 196 | 197 | return (nil, pool) 198 | } 199 | }*/ 200 | -------------------------------------------------------------------------------- /Tests/SwiftKueryMySQLTests/TestAlias.swift: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright IBM Corporation 2017 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import XCTest 18 | import SwiftKuery 19 | 20 | #if os(Linux) 21 | let tableAlias = "tableAliasLinux" 22 | #else 23 | let tableAlias = "tableAliasOSX" 24 | #endif 25 | 26 | class TestAlias: XCTestCase { 27 | 28 | static var allTests: [(String, (TestAlias) -> () throws -> Void)] { 29 | return [ 30 | ("testAlias", testAlias), 31 | ] 32 | } 33 | 34 | class MyTable : Table { 35 | let a = Column("a") 36 | let b = Column("b") 37 | 38 | let tableName = tableAlias 39 | } 40 | 41 | func testAlias() { 42 | let t = MyTable() 43 | 44 | let pool = CommonUtils.sharedInstance.getConnectionPool() 45 | performTest(asyncTasks: { expectation in 46 | 47 | pool.getConnection { connection, error in 48 | guard let connection = connection else { 49 | XCTFail("Failed to get connection") 50 | return 51 | } 52 | cleanUp(table: t.tableName, connection: connection) { _ in 53 | 54 | executeRawQuery("CREATE TABLE " + packName(t.tableName) + " (a varchar(40), b integer)", connection: connection) { result, rows in 55 | XCTAssertEqual(result.success, true, "CREATE TABLE failed") 56 | XCTAssertNil(result.asError, "Error in CREATE TABLE: \(result.asError!)") 57 | 58 | let i1 = Insert(into: t, rows: [["apple", 10], ["apricot", 3], ["banana", 17], ["apple", 17], ["banana", -7], ["banana", 27]]) 59 | executeQuery(query: i1, connection: connection) { result, rows in 60 | XCTAssertEqual(result.success, true, "INSERT failed") 61 | XCTAssertNil(result.asError, "Error in INSERT: \(result.asError!)") 62 | 63 | let s1 = Select(t.a.as("fruit name"), t.b.as("number"), from: t) 64 | executeQuery(query: s1, connection: connection) { result, rows in 65 | XCTAssertEqual(result.success, true, "SELECT failed") 66 | XCTAssertNotNil(result.asResultSet, "SELECT returned no rows") 67 | XCTAssertNotNil(rows, "SELECT returned no rows") 68 | let resultSet = result.asResultSet! 69 | XCTAssertEqual(rows?.count, 6, "SELECT returned wrong number of rows: \(String(describing: rows?.count)) instead of 6") 70 | resultSet.getColumnTitles() { titles, error in 71 | guard let titles = titles else { 72 | XCTFail("Unable to retrieve column names") 73 | return 74 | } 75 | XCTAssertEqual(titles[1], "number", "Wrong column name: \(titles[1]) instead of 'number'") 76 | XCTAssertEqual(titles[0], "fruit name", "Wrong column name: \(titles[0]) instead of 'fruit name'") 77 | 78 | let s2 = Select(from: t.as("new")) 79 | executeQuery(query: s2, connection: connection) { result, rows in 80 | XCTAssertEqual(result.success, true, "SELECT failed") 81 | XCTAssertNotNil(rows, "SELECT returned no rows") 82 | let resultSet = result.asResultSet! 83 | XCTAssertEqual(rows?.count, 6, "SELECT returned wrong number of rows: \(String(describing: rows?.count)) instead of 6") 84 | resultSet.getColumnTitles() { titles, error in 85 | guard let titles = titles else { 86 | XCTFail("Unable to retrieve column names") 87 | return 88 | } 89 | XCTAssertEqual(titles[0], "a", "Wrong column name: \(titles[0]) instead of 'a'") 90 | XCTAssertEqual(titles[1], "b", "Wrong column name: \(titles[1]) instead of 'b'") 91 | 92 | let t2 = t.as("t 2") 93 | let s3 = Select(t2.a, from: t2) 94 | executeQuery(query: s3, connection: connection) { result, rows in 95 | XCTAssertEqual(result.success, true, "SELECT failed") 96 | XCTAssertNotNil(rows, "SELECT returned no rows") 97 | let resultSet = result.asResultSet! 98 | XCTAssertEqual(rows?.count, 6, "SELECT returned wrong number of rows: \(String(describing: rows?.count)) instead of 6") 99 | resultSet.getColumnTitles() { titles, error in 100 | guard let titles = titles else { 101 | XCTFail("Unable to retrieve column names") 102 | return 103 | } 104 | XCTAssertEqual(titles[0], "a", "Wrong column name: \(titles[0]) instead of 'a'") 105 | expectation.fulfill() 106 | } 107 | } 108 | } 109 | } 110 | } 111 | } 112 | } 113 | } 114 | } 115 | } 116 | }) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /Tests/SwiftKueryMySQLTests/TestSubquery.swift: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright IBM Corporation 2017 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import XCTest 18 | import SwiftKuery 19 | 20 | #if os(Linux) 21 | let tableSubquery = "tableSubqueryLinux" 22 | #else 23 | let tableSubquery = "tableSubqueryOSX" 24 | #endif 25 | 26 | class TestSubquery: XCTestCase { 27 | 28 | static var allTests: [(String, (TestSubquery) -> () throws -> Void)] { 29 | return [ 30 | ("testSubquery", testSubquery), 31 | ] 32 | } 33 | 34 | class MyTable : Table { 35 | let a = Column("a") 36 | let b = Column("b") 37 | 38 | let tableName = tableSubquery 39 | } 40 | 41 | func testSubquery() { 42 | let t = MyTable() 43 | let pool = CommonUtils.sharedInstance.getConnectionPool() 44 | performTest(asyncTasks: { expectation in 45 | 46 | pool.getConnection { connection, error in 47 | guard let connection = connection else { 48 | XCTFail("Failed to get connection") 49 | return 50 | } 51 | cleanUp(table: t.tableName, connection: connection) { _ in 52 | executeRawQuery("CREATE TABLE " + packName(t.tableName) + " (a varchar(40), b integer)", connection: connection) { result, rows in 53 | XCTAssertEqual(result.success, true, "CREATE TABLE failed") 54 | XCTAssertNil(result.asError, "Error in CREATE TABLE: \(result.asError!)") 55 | 56 | let i1 = Insert(into: t, rows: [["apple", 10], ["apricot", 3], ["banana", 17], ["apple", 17], ["banana", -7], ["banana", 27]]) 57 | executeQuery(query: i1, connection: connection) { result, rows in 58 | XCTAssertEqual(result.success, true, "INSERT failed") 59 | XCTAssertNil(result.asError, "Error in INSERT: \(result.asError!)") 60 | 61 | var s = Select(from: t) 62 | .where(t.b == any(Select(t.b, from: t).where(t.b == 17))) 63 | executeQuery(query: s, connection: connection) { result, rows in 64 | XCTAssertEqual(result.success, true, "SELECT failed") 65 | XCTAssertNotNil(result.asResultSet, "SELECT returned no rows") 66 | XCTAssertNotNil(rows, "SELECT returned no rows") 67 | XCTAssertEqual(rows?.count, 2, "SELECT returned wrong number of rows: \(String(describing: rows?.count)) instead of 2") 68 | 69 | s = Select(t.a, from: t) 70 | .group(by: t.a) 71 | .having(sum(t.b) > any(Select(t.b, from: t).where(t.b == 27))) 72 | executeQuery(query: s, connection: connection) { result, rows in 73 | XCTAssertEqual(result.success, true, "SELECT failed") 74 | XCTAssertNotNil(result.asResultSet, "SELECT returned no rows") 75 | XCTAssertNotNil(rows, "SELECT returned no rows") 76 | XCTAssertEqual(rows?.count, 1, "SELECT returned wrong number of rows: \(String(describing: rows?.count)) instead of 1") 77 | 78 | s = Select(from: t) 79 | .where(t.b > (Select(t.b, from: t).where(t.b == 3))) 80 | executeQuery(query: s, connection: connection) { result, rows in 81 | XCTAssertEqual(result.success, true, "SELECT failed") 82 | XCTAssertNotNil(result.asResultSet, "SELECT returned no rows") 83 | XCTAssertNotNil(rows, "SELECT returned no rows") 84 | XCTAssertEqual(rows?.count, 4, "SELECT returned wrong number of rows: \(String(describing: rows?.count)) instead of 4") 85 | 86 | s = Select(from: t) 87 | .where(exists(Select(t.b, from: t).where(t.b == 10))) 88 | executeQuery(query: s, connection: connection) { result, rows in 89 | XCTAssertEqual(result.success, true, "SELECT failed") 90 | XCTAssertNotNil(result.asResultSet, "SELECT returned no rows") 91 | XCTAssertNotNil(rows, "SELECT returned no rows") 92 | XCTAssertEqual(rows?.count, 6, "SELECT returned wrong number of rows: \(String(describing: rows?.count)) instead of 6") 93 | 94 | s = Select(from: t) 95 | .where(8.in(1,6,8)) 96 | executeQuery(query: s, connection: connection) { result, rows in 97 | XCTAssertEqual(result.success, true, "SELECT failed") 98 | XCTAssertNotNil(result.asResultSet, "SELECT returned no rows") 99 | XCTAssertNotNil(rows, "SELECT returned no rows") 100 | XCTAssertEqual(rows?.count, 6, "SELECT returned wrong number of rows: \(String(describing: rows?.count)) instead of 6") 101 | 102 | s = Select(from: t) 103 | .having("apple".notIn("plum")) 104 | .group(by: t.a, t.b) 105 | executeQuery(query: s, connection: connection) { result, rows in 106 | XCTAssertEqual(result.success, true, "SELECT failed") 107 | XCTAssertNotNil(result.asResultSet, "SELECT returned no rows") 108 | XCTAssertNotNil(rows, "SELECT returned no rows") 109 | XCTAssertEqual(rows?.count, 6, "SELECT returned wrong number of rows: \(String(describing: rows?.count)) instead of 6") 110 | 111 | s = Select(from: t) 112 | .where((-7).in(Select(t.b, from: t).where(t.b == -1))) 113 | executeQuery(query: s, connection: connection) { result, rows in 114 | XCTAssertEqual(result.success, true, "SELECT failed") 115 | XCTAssertEqual(rows?.count, 0, "SELECT should not return any rows") 116 | 117 | s = Select(from: t) 118 | .group(by: t.a, t.b) 119 | .having(exists(Select(t.b, from: t).where(t.b == 17))) 120 | executeQuery(query: s, connection: connection) { result, rows in 121 | XCTAssertEqual(result.success, true, "SELECT failed") 122 | XCTAssertNotNil(result.asResultSet, "SELECT returned no rows") 123 | XCTAssertNotNil(rows, "SELECT returned no rows") 124 | XCTAssertEqual(rows?.count, 6, "SELECT returned wrong number of rows: \(String(describing: rows?.count)) instead of 6") 125 | 126 | s = Select(from: t) 127 | .where(notExists(Select(t.b, from: t).where(t.b == 8))) 128 | executeQuery(query: s, connection: connection) { result, rows in 129 | XCTAssertEqual(result.success, true, "SELECT failed") 130 | XCTAssertNotNil(result.asResultSet, "SELECT returned no rows") 131 | XCTAssertNotNil(rows, "SELECT returned no rows") 132 | XCTAssertEqual(rows?.count, 6, "SELECT returned wrong number of rows: \(String(describing: rows?.count)) instead of 6") 133 | 134 | s = Select(from: t) 135 | .group(by: t.a, t.b) 136 | .having(Parameter().in(Parameter(), Parameter())) 137 | executeQueryWithParameters(query: s, connection: connection, parameters: [true, true, false]) { result, rows in 138 | XCTAssertEqual(result.success, true, "SELECT failed") 139 | XCTAssertNotNil(result.asResultSet, "SELECT returned no rows") 140 | XCTAssertNotNil(rows, "SELECT returned no rows") 141 | XCTAssertEqual(rows?.count, 6, "SELECT returned wrong number of rows: \(String(describing: rows?.count)) instead of 6") 142 | 143 | s = Select(from: t) 144 | .where(false.notIn(Parameter(), Parameter())) 145 | executeQueryWithParameters(query: s, connection: connection, parameters: [true, true]) { result, rows in 146 | XCTAssertEqual(result.success, true, "SELECT failed") 147 | XCTAssertNotNil(result.asResultSet, "SELECT returned no rows") 148 | XCTAssertNotNil(rows, "SELECT returned no rows") 149 | XCTAssertEqual(rows?.count, 6, "SELECT returned wrong number of rows: \(String(describing: rows?.count)) instead of 6") 150 | 151 | expectation.fulfill() 152 | } 153 | } 154 | } 155 | } 156 | } 157 | } 158 | } 159 | } 160 | } 161 | } 162 | } 163 | } 164 | } 165 | } 166 | } 167 | }) 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /Tests/SwiftKueryMySQLTests/TestTimingWindows.swift: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright IBM Corporation 2018 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import XCTest 18 | import Foundation 19 | import SwiftKuery 20 | 21 | #if os(Linux) 22 | let tableTimings = "tableTimingsLinux" 23 | #else 24 | let tableTimings = "tableTimingsOSX" 25 | #endif 26 | 27 | class TestTimingWindows: XCTestCase { 28 | 29 | static var allTests: [(String, (TestTimingWindows) -> () throws -> Void)] { 30 | return [ 31 | ("testExecutePassingQueryWithNoParameters", testExecutePassingQueryWithNoParameters), 32 | ("testExecutePassingQueryWithParameters", testExecutePassingQueryWithParameters), 33 | ("testExecutePassingRawQueryWithNoParameters", testExecutePassingRawQueryWithNoParameters), 34 | ("testExecutePassingRawQueryWithParameters",testExecutePassingRawQueryWithParameters), 35 | ] 36 | } 37 | 38 | class MyTable : Table { 39 | let a = Column("a", Varchar.self, length: 10, defaultValue: "qiwi", collate: "utf8_general_ci") 40 | let b = Column("b", Int32.self) 41 | 42 | let tableName = tableTimings 43 | } 44 | 45 | // public func execute(query: Query, onCompletion: @escaping ((QueryResult) -> ())) { 46 | func testExecutePassingQueryWithNoParameters() { 47 | let t = MyTable() 48 | 49 | let pool = CommonUtils.sharedInstance.getConnectionPool() 50 | performTest(asyncTasks: { expectation in 51 | 52 | pool.getConnection { connection, error in 53 | guard let connection = connection else { 54 | XCTFail("Failed to get connection") 55 | return 56 | } 57 | cleanUp(table: t.tableName, connection: connection) { _ in 58 | t.create(connection: connection) { result in 59 | if let error = result.asError { 60 | XCTFail("Error in CREATE TABLE: \(error)") 61 | return 62 | } 63 | 64 | let insertCount = 1000 65 | let query = Insert(into: t, values: "apple", 10) 66 | self.executeInsertQuery(count: insertCount, query: query, connection: connection) { result in 67 | XCTAssertEqual(result.success, true, "INSERT failed") 68 | XCTAssertNil(result.asError, "Error in INSERT: \(result.asError!)") 69 | 70 | cleanUp(table: t.tableName, connection: connection) { _ in 71 | expectation.fulfill() 72 | } 73 | } // self.executeInsertQuery 74 | } // t.create 75 | } // cleanup 76 | } 77 | }) 78 | } 79 | 80 | func executeInsertQuery(count index: Int, query: Query, connection: Connection, onCompletion: @escaping ((QueryResult) -> ())) { 81 | connection.execute(query: query) { result in 82 | if result.asError != nil { 83 | onCompletion(result) 84 | return 85 | } 86 | if index >= 0 { 87 | self.executeInsertQuery(count: index - 1, query: query, connection: connection, onCompletion: onCompletion) 88 | } else { 89 | onCompletion(result) 90 | } 91 | } 92 | } 93 | 94 | // public func execute(query: Query, parameters: [Any?], onCompletion: @escaping ((QueryResult) -> ())) { 95 | func testExecutePassingQueryWithParameters() { 96 | let t = MyTable() 97 | 98 | let pool = CommonUtils.sharedInstance.getConnectionPool() 99 | performTest(asyncTasks: { expectation in 100 | 101 | pool.getConnection { connection, error in 102 | guard let connection = connection else { 103 | XCTFail("Failed to get connection") 104 | return 105 | } 106 | cleanUp(table: t.tableName, connection: connection) { _ in 107 | t.create(connection: connection) { result in 108 | if let error = result.asError { 109 | XCTFail("Error in CREATE TABLE: \(error)") 110 | return 111 | } 112 | 113 | let insertCount = 1000 114 | let query = Insert(into: t, values: Parameter(), Parameter()) 115 | self.executeInsertQueryWithParameters(count: insertCount, query: query, parameters: ["apple", 10], connection: connection) { result in 116 | XCTAssertEqual(result.success, true, "INSERT failed") 117 | XCTAssertNil(result.asError, "Error in INSERT: \(result.asError!)") 118 | 119 | cleanUp(table: t.tableName, connection: connection) { _ in 120 | expectation.fulfill() 121 | } 122 | } // self.executeInsertQuery 123 | } // t.create 124 | } // cleanup 125 | } 126 | }) 127 | } 128 | 129 | func executeInsertQueryWithParameters(count index: Int, query: Query, parameters: [Any?], connection: Connection, onCompletion: @escaping ((QueryResult) -> ())) { 130 | connection.execute(query: query, parameters: parameters) { result in 131 | if result.asError != nil { 132 | onCompletion(result) 133 | return 134 | } 135 | if index >= 0 { 136 | self.executeInsertQueryWithParameters(count: index - 1, query: query, parameters: parameters, connection: connection, onCompletion: onCompletion) 137 | } else { 138 | onCompletion(result) 139 | } 140 | } 141 | } 142 | 143 | // public func execute(_ raw: String, onCompletion: @escaping ((QueryResult) -> ())) { 144 | func testExecutePassingRawQueryWithNoParameters() { 145 | let t = MyTable() 146 | 147 | let pool = CommonUtils.sharedInstance.getConnectionPool() 148 | performTest(asyncTasks: { expectation in 149 | 150 | pool.getConnection { connection, error in 151 | guard let connection = connection else { 152 | XCTFail("Failed to get connection") 153 | return 154 | } 155 | cleanUp(table: t.tableName, connection: connection) { _ in 156 | t.create(connection: connection) { result in 157 | if let error = result.asError { 158 | XCTFail("Error in CREATE TABLE: \(error)") 159 | return 160 | } 161 | 162 | let insertCount = 1000 163 | let raw = "insert into " + t.tableName + " values(\"apple\", 10)" 164 | self.executeRawInsertQuery(count: insertCount, raw: raw, connection: connection) { result in 165 | XCTAssertEqual(result.success, true, "INSERT failed") 166 | XCTAssertNil(result.asError, "Error in INSERT: \(result.asError!)") 167 | 168 | cleanUp(table: t.tableName, connection: connection) { _ in 169 | expectation.fulfill() 170 | } 171 | } // self.executeInsertQuery 172 | } // t.create 173 | } // cleanup 174 | } 175 | }) 176 | } 177 | 178 | func executeRawInsertQuery(count index: Int, raw: String, connection: Connection, onCompletion: @escaping ((QueryResult) -> ())) { 179 | connection.execute(raw) { result in 180 | if result.asError != nil { 181 | onCompletion(result) 182 | return 183 | } 184 | if index >= 0 { 185 | self.executeRawInsertQuery(count: index - 1, raw: raw, connection: connection, onCompletion: onCompletion) 186 | } else { 187 | onCompletion(result) 188 | } 189 | } 190 | } 191 | 192 | // public func execute(_ raw: String, parameters: [Any?], onCompletion: @escaping ((QueryResult) -> ())) { 193 | func testExecutePassingRawQueryWithParameters() { 194 | let t = MyTable() 195 | 196 | let pool = CommonUtils.sharedInstance.getConnectionPool() 197 | performTest(asyncTasks: { expectation in 198 | 199 | pool.getConnection { connection, error in 200 | guard let connection = connection else { 201 | XCTFail("Failed to get connection") 202 | return 203 | } 204 | cleanUp(table: t.tableName, connection: connection) { _ in 205 | t.create(connection: connection) { result in 206 | if let error = result.asError { 207 | XCTFail("Error in CREATE TABLE: \(error)") 208 | return 209 | } 210 | 211 | let insertCount = 1000 212 | let raw = "insert into " + t.tableName + " values(?, ?)" 213 | self.executeRawInsertQueryWithParameters(count: insertCount, raw: raw, parameters: ["apple", 10], connection: connection) { result in 214 | XCTAssertEqual(result.success, true, "INSERT failed") 215 | XCTAssertNil(result.asError, "Error in INSERT: \(result.asError!)") 216 | 217 | cleanUp(table: t.tableName, connection: connection) { _ in 218 | expectation.fulfill() 219 | } 220 | } // self.executeInsertQuery 221 | } // t.create 222 | } // cleanup 223 | } 224 | }) 225 | } 226 | 227 | func executeRawInsertQueryWithParameters(count index: Int, raw: String, parameters: [Any?], connection: Connection, onCompletion: @escaping ((QueryResult) -> ())) { 228 | connection.execute(raw, parameters: parameters) { result in 229 | if result.asError != nil { 230 | onCompletion(result) 231 | return 232 | } 233 | if index >= 0 { 234 | self.executeRawInsertQueryWithParameters(count: index - 1, raw: raw, parameters: parameters, connection: connection, onCompletion: onCompletion) 235 | } else { 236 | onCompletion(result) 237 | } 238 | } 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /Tests/SwiftKueryMySQLTests/TestUpdate.swift: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright IBM Corporation 2017, 2018 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import XCTest 18 | import SwiftKuery 19 | 20 | #if os(Linux) 21 | let tableUpdate = "tableUpdateLinux" 22 | #else 23 | let tableUpdate = "tableUpdateOSX" 24 | #endif 25 | 26 | class TestUpdate: XCTestCase { 27 | 28 | static var allTests: [(String, (TestUpdate) -> () throws -> Void)] { 29 | return [ 30 | ("testUpdateAndDelete", testUpdateAndDelete), 31 | ("testUpdateNilValue", testUpdateNilValue), 32 | ] 33 | } 34 | 35 | class MyTable : Table { 36 | let a = Column("a") 37 | let b = Column("b") 38 | 39 | let tableName = tableUpdate 40 | } 41 | 42 | func testUpdateAndDelete () { 43 | let t = MyTable() 44 | 45 | let pool = CommonUtils.sharedInstance.getConnectionPool() 46 | performTest(asyncTasks: { expectation in 47 | 48 | pool.getConnection { connection, error in 49 | guard let connection = connection else { 50 | XCTFail("Failed to get connection") 51 | return 52 | } 53 | cleanUp(table: t.tableName, connection: connection) { _ in 54 | 55 | executeRawQuery("CREATE TABLE " + packName(t.tableName) + " (a varchar(40), b integer)", connection: connection) { result, rows in 56 | XCTAssertEqual(result.success, true, "CREATE TABLE failed") 57 | XCTAssertNil(result.asError, "Error in CREATE TABLE: \(result.asError!)") 58 | 59 | let i1 = Insert(into: t, rows: [["apple", 10], ["apricot", 3], ["banana", 17], ["apple", 17], ["banana", -7], ["banana", 27]]) 60 | executeQuery(query: i1, connection: connection) { result, rows in 61 | XCTAssertEqual(result.success, true, "INSERT failed") 62 | XCTAssertNil(result.asError, "Error in INSERT: \(result.asError!)") 63 | 64 | let s1 = Select(from: t) 65 | executeQuery(query: s1, connection: connection) { result, rows in 66 | XCTAssertEqual(result.success, true, "SELECT failed") 67 | XCTAssertNotNil(result.asResultSet, "SELECT returned no rows") 68 | XCTAssertNotNil(rows, "SELECT returned no rows") 69 | XCTAssertEqual(rows?.count, 6, "SELECT returned wrong number of rows: \(String(describing: rows?.count)) instead of 6") 70 | 71 | let u1 = Update(t, set: [(t.a, "peach"), (t.b, 2)]) 72 | .where(t.a == "banana") 73 | executeQuery(query: u1, connection: connection) { result, rows in 74 | XCTAssertEqual(result.success, true, "UPDATE failed") 75 | XCTAssertNil(result.asError, "Error in UPDATE: \(result.asError!)") 76 | 77 | let u2 = Update(t, set: [(t.a, "peach"), (t.b, 2)]) 78 | .where(t.a == "apple") 79 | executeQuery(query: u2, connection: connection) { result, rows in 80 | XCTAssertEqual(result.success, true, "UPDATE failed") 81 | XCTAssertNil(result.asError, "Error in UPDATE: \(result.asError!)") 82 | XCTAssert((result.asValue as! String).contains("2"), "UPDATE affected wrong number of rows: \(result.asValue!)") 83 | 84 | let s2 = Select(t.a, t.b, from: t) 85 | .where(t.a == "banana") 86 | executeQuery(query: s2, connection: connection) { result, rows in 87 | XCTAssertEqual(result.success, true, "SELECT failed") 88 | XCTAssertEqual(rows?.count, 0, "SELECT should not return any rows") 89 | 90 | let d1 = Delete(from: t) 91 | .where(t.b == "2") 92 | executeQuery(query: d1, connection: connection) { result, rows in 93 | XCTAssertEqual(result.success, true, "DELETE failed") 94 | XCTAssertNil(result.asError, "Error in DELETE: \(result.asError!)") 95 | XCTAssert((result.asValue as! String).contains("5"), "DELETE affected wrong number of rows: \(result.asValue!)") 96 | 97 | executeQuery(query: s1, connection: connection) { result, rows in 98 | XCTAssertEqual(result.success, true, "SELECT failed") 99 | XCTAssertNotNil(result.asResultSet, "SELECT returned no rows") 100 | XCTAssertNotNil(rows, "SELECT returned no rows") 101 | XCTAssertEqual(rows?.count, 1, "SELECT returned wrong number of rows: \(String(describing: rows?.count)) instead of 1") 102 | 103 | let d2 = Delete(from: t) 104 | executeQuery(query: d2, connection: connection) { result, rows in 105 | XCTAssertEqual(result.success, true, "DELETE failed") 106 | XCTAssertNil(result.asError, "Error in DELETE: \(result.asError!)") 107 | 108 | executeQuery(query: s1, connection: connection) { result, rows in 109 | XCTAssertEqual(result.success, true, "SELECT failed") 110 | XCTAssertEqual(rows?.count, 0, "SELECT should not return any rows") 111 | 112 | expectation.fulfill() 113 | } 114 | } 115 | } 116 | } 117 | } 118 | } 119 | } 120 | } 121 | } 122 | } 123 | } 124 | } 125 | }) 126 | } 127 | 128 | func testUpdateNilValue() { 129 | let t = MyTable() 130 | 131 | let pool = CommonUtils.sharedInstance.getConnectionPool() 132 | performTest(asyncTasks: { expectation in 133 | 134 | pool.getConnection { connection, error in 135 | guard let connection = connection else { 136 | XCTFail("Failed to get connection") 137 | return 138 | } 139 | cleanUp(table: t.tableName, connection: connection) { _ in 140 | 141 | executeRawQuery("CREATE TABLE " + packName(t.tableName) + " (a varchar(40), b integer)", connection: connection) { result, rows in 142 | XCTAssertEqual(result.success, true, "CREATE TABLE failed") 143 | XCTAssertNil(result.asError, "Error in CREATE TABLE: \(result.asError!)") 144 | 145 | let insert = Insert(into: t, rows: [["apple", 10], ["apricot", 3], ["banana", 17], ["apple", 17], ["banana", -7], ["banana", 27]]) 146 | executeQuery(query: insert, connection: connection) { result, rows in 147 | XCTAssertEqual(result.success, true, "INSERT failed") 148 | XCTAssertNil(result.asError, "Error in INSERT: \(result.asError!)") 149 | 150 | let nilString: String? = nil 151 | let update = Update(t, set:[(t.a, nilString as Any)], where: t.a == "apple") 152 | executeQuery(query: update, connection: connection) { result, rows in 153 | XCTAssertEqual(result.success, true, "UPDATE failed") 154 | XCTAssertNil(result.asError, "Error in UPDATE: \(result.asError!)") 155 | 156 | let select = Select(from: t) 157 | executeQuery(query: select, connection: connection) { result, rows in 158 | XCTAssertEqual(result.success, true, "SELECT failed") 159 | XCTAssertNil(result.asError, "Error in SELECT: \(result.asError!)") 160 | XCTAssertNotNil(rows, "Expected rows but none returned") 161 | for row in rows! { 162 | XCTAssertNotEqual(row[0] as? String, "apple", "Row returned with \"apple\" instead of expected value \"nil\"") 163 | } 164 | 165 | expectation.fulfill() 166 | } 167 | } 168 | } 169 | } 170 | } 171 | } 172 | }) 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /Tests/SwiftKueryMySQLTests/connection.json: -------------------------------------------------------------------------------- 1 | { 2 | "host": "localhost", 3 | "port": "3306", 4 | "username": "swift", 5 | "password": "kuery", 6 | "database": "test" 7 | } 8 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | test: 2 | image: ibmcom/swift-ubuntu:4.1.2 3 | volumes: 4 | - .:/SwiftKueryMySQL 5 | command: bash -c "cd /SwiftKueryMySQL && swift package clean && swift build" 6 | -------------------------------------------------------------------------------- /docs/Classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Classes Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |

19 | 20 | SwiftKueryMySQL Docs 21 | 22 | (100% documented) 23 |

24 | 25 |

26 | 27 | 28 | View on GitHub 29 | 30 |

31 | 32 |
33 | 34 | 39 | 40 |
41 | 59 |
60 | 61 |
62 |
63 |

Classes

64 |

The following classes are available globally.

65 | 66 |
67 |
68 | 69 |
70 |
71 |
72 |
    73 |
  • 74 |
    75 | 76 | 77 | 78 | MySQLConnection 79 | 80 |
    81 |
    82 |
    83 |
    84 |
    85 |
    86 |

    An implementation of SwiftKuery.Connection protocol for MySQL. 87 | Instances of MySQLConnection are NOT thread-safe and should not be shared between threads. 88 | Use MySQLThreadSafeConnection to share connection instances between multiple threads.

    89 | 90 | See more 91 |
    92 |
    93 |

    Declaration

    94 |
    95 |

    Swift

    96 |
    public class MySQLConnection: Connection
    97 | 98 |
    99 |
    100 |
    101 |
    102 |
  • 103 |
104 |
105 |
106 |
    107 |
  • 108 |
    109 | 110 | 111 | 112 | MySQLResultFetcher 113 | 114 |
    115 |
    116 |
    117 |
    118 |
    119 |
    120 |

    An implementation of query result fetcher.

    121 | 122 | See more 123 |
    124 |
    125 |

    Declaration

    126 |
    127 |

    Swift

    128 |
    public class MySQLResultFetcher: ResultFetcher
    129 | 130 |
    131 |
    132 |
    133 |
    134 |
  • 135 |
136 |
137 |
138 |
    139 |
  • 140 |
    141 | 142 | 143 | 144 | MySQLThreadSafeConnection 145 | 146 |
    147 |
    148 |
    149 |
    150 |
    151 |
    152 |

    A thread-safe implementation of SwiftKuery.Connection protocol for MySQL. 153 | Instances of MySQLThreadSafeConnection can be safely shared between multiple threads.

    154 | 155 | See more 156 |
    157 |
    158 |

    Declaration

    159 |
    160 |

    Swift

    161 |
    public class MySQLThreadSafeConnection: MySQLConnection
    162 | 163 |
    164 |
    165 |
    166 |
    167 |
  • 168 |
169 |
170 |
171 |
172 | 173 |
174 |
175 | 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /docs/Classes/MySQLResultFetcher.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MySQLResultFetcher Class Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |

20 | 21 | SwiftKueryMySQL Docs 22 | 23 | (100% documented) 24 |

25 | 26 |

27 | 28 | 29 | View on GitHub 30 | 31 |

32 | 33 |
34 | 35 | 40 | 41 |
42 | 60 |
61 | 62 |
63 |
64 |

MySQLResultFetcher

65 |
66 |
67 |
public class MySQLResultFetcher: ResultFetcher
68 | 69 |
70 |
71 |

An implementation of query result fetcher.

72 | 73 |
74 |
75 | 76 |
77 |
78 |
79 |
    80 |
  • 81 |
    82 | 83 | 84 | 85 | fetchNext() 86 | 87 |
    88 |
    89 |
    90 |
    91 |
    92 |
    93 |

    Fetch the next row of the query result. This function is blocking.

    94 | 95 |
    96 |

    Returns

    97 | An array of values of type Any? representing the next row from the query result. 98 | 99 |
    100 | 101 |
    102 |
    103 |

    Declaration

    104 |
    105 |

    Swift

    106 |
    public func fetchNext() -> [Any?]?
    107 | 108 |
    109 |
    110 |
    111 |

    Return Value

    112 |

    An array of values of type Any? representing the next row from the query result.

    113 | 114 |
    115 |
    116 |
    117 |
  • 118 |
  • 119 |
    120 | 121 | 122 | 123 | fetchNext(callback:) 124 | 125 |
    126 |
    127 |
    128 |
    129 |
    130 |
    131 |

    Fetch the next row of the query result. This function is non-blocking.

    132 | 133 |
    134 |

    Parameter

    135 | Parameter callback: A callback to call when the next row of the query result is ready. 136 | 137 |
    138 | 139 |
    140 |
    141 |

    Declaration

    142 |
    143 |

    Swift

    144 |
    public func fetchNext(callback: ([Any?]?) ->())
    145 | 146 |
    147 |
    148 |
    149 |

    Parameters

    150 | 151 | 152 | 153 | 158 | 164 | 165 | 166 |
    154 | 155 | callback 156 | 157 | 159 |
    160 |

    A callback to call when the next row of the query result is ready.

    161 | 162 |
    163 |
    167 |
    168 |
    169 |
    170 |
  • 171 |
  • 172 |
    173 | 174 | 175 | 176 | fetchTitles() 177 | 178 |
    179 |
    180 |
    181 |
    182 |
    183 |
    184 |

    Fetch the titles of the query result.

    185 | 186 |
    187 |

    Returns

    188 | An array of column titles of type String. 189 | 190 |
    191 | 192 |
    193 |
    194 |

    Declaration

    195 |
    196 |

    Swift

    197 |
    public func fetchTitles() -> [String]
    198 | 199 |
    200 |
    201 |
    202 |

    Return Value

    203 |

    An array of column titles of type String.

    204 | 205 |
    206 |
    207 |
    208 |
  • 209 |
210 |
211 |
212 |
213 | 214 |
215 |
216 | 220 | 221 | 222 | 223 | -------------------------------------------------------------------------------- /docs/Classes/MySQLThreadSafeConnection.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MySQLThreadSafeConnection Class Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |

20 | 21 | SwiftKueryMySQL Docs 22 | 23 | (100% documented) 24 |

25 | 26 |

27 | 28 | 29 | View on GitHub 30 | 31 |

32 | 33 |
34 | 35 | 40 | 41 |
42 | 60 |
61 | 62 |
63 |
64 |

MySQLThreadSafeConnection

65 |
66 |
67 |
public class MySQLThreadSafeConnection: MySQLConnection
68 | 69 |
70 |
71 |

A thread-safe implementation of SwiftKuery.Connection protocol for MySQL. 72 | Instances of MySQLThreadSafeConnection can be safely shared between multiple threads.

73 | 74 |
75 |
76 | 77 |
78 |
79 |
80 |
    81 |
  • 82 |
    83 | 84 | 85 | 86 | connect(onCompletion:) 87 | 88 |
    89 |
    90 |
    91 |
    92 |
    93 |
    94 |

    Establish a connection with the database.

    95 | 96 |
    97 |

    Parameter

    98 | Parameter onCompletion: The function to be called when the connection is established. 99 | 100 |
    101 | 102 |
    103 |
    104 |

    Declaration

    105 |
    106 |

    Swift

    107 |
    public override func connect(onCompletion: (QueryError?) -> ())
    108 | 109 |
    110 |
    111 |
    112 |

    Parameters

    113 | 114 | 115 | 116 | 121 | 127 | 128 | 129 |
    117 | 118 | onCompletion 119 | 120 | 122 |
    123 |

    The function to be called when the connection is established.

    124 | 125 |
    126 |
    130 |
    131 |
    132 |
    133 |
  • 134 |
  • 135 |
    136 | 137 | 138 | 139 | closeConnection() 140 | 141 |
    142 |
    143 |
    144 |
    145 |
    146 |
    147 |

    Close the connection to the database.

    148 | 149 |
    150 |
    151 |

    Declaration

    152 |
    153 |

    Swift

    154 |
    public override func closeConnection()
    155 | 156 |
    157 |
    158 |
    159 |
    160 |
  • 161 |
162 |
163 |
164 |
165 | 166 |
167 |
168 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /docs/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/css/jazzy.css: -------------------------------------------------------------------------------- 1 | *, *:before, *:after { 2 | box-sizing: inherit; } 3 | 4 | body { 5 | margin: 0; 6 | background: #fff; 7 | color: #333; 8 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | letter-spacing: .2px; 10 | -webkit-font-smoothing: antialiased; 11 | box-sizing: border-box; } 12 | 13 | h1 { 14 | font-size: 2rem; 15 | font-weight: 700; 16 | margin: 1.275em 0 0.6em; } 17 | 18 | h2 { 19 | font-size: 1.75rem; 20 | font-weight: 700; 21 | margin: 1.275em 0 0.3em; } 22 | 23 | h3 { 24 | font-size: 1.5rem; 25 | font-weight: 700; 26 | margin: 1em 0 0.3em; } 27 | 28 | h4 { 29 | font-size: 1.25rem; 30 | font-weight: 700; 31 | margin: 1.275em 0 0.85em; } 32 | 33 | h5 { 34 | font-size: 1rem; 35 | font-weight: 700; 36 | margin: 1.275em 0 0.85em; } 37 | 38 | h6 { 39 | font-size: 1rem; 40 | font-weight: 700; 41 | margin: 1.275em 0 0.85em; 42 | color: #777; } 43 | 44 | p { 45 | margin: 0 0 1em; } 46 | 47 | ul, ol { 48 | padding: 0 0 0 2em; 49 | margin: 0 0 0.85em; } 50 | 51 | blockquote { 52 | margin: 0 0 0.85em; 53 | padding: 0 15px; 54 | color: #858585; 55 | border-left: 4px solid #e5e5e5; } 56 | 57 | img { 58 | max-width: 100%; } 59 | 60 | a { 61 | color: #4183c4; 62 | text-decoration: none; } 63 | a:hover, a:focus { 64 | outline: 0; 65 | text-decoration: underline; } 66 | 67 | table { 68 | background: #fff; 69 | width: 100%; 70 | border-collapse: collapse; 71 | border-spacing: 0; 72 | overflow: auto; 73 | margin: 0 0 0.85em; } 74 | 75 | tr:nth-child(2n) { 76 | background-color: #fbfbfb; } 77 | 78 | th, td { 79 | padding: 6px 13px; 80 | border: 1px solid #ddd; } 81 | 82 | pre { 83 | margin: 0 0 1.275em; 84 | padding: .85em 1em; 85 | overflow: auto; 86 | background: #f7f7f7; 87 | font-size: .85em; 88 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } 89 | 90 | code { 91 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } 92 | 93 | p > code, li > code { 94 | background: #f7f7f7; 95 | padding: .2em; } 96 | p > code:before, p > code:after, li > code:before, li > code:after { 97 | letter-spacing: -.2em; 98 | content: "\00a0"; } 99 | 100 | pre code { 101 | padding: 0; 102 | white-space: pre; } 103 | 104 | .content-wrapper { 105 | display: flex; 106 | flex-direction: column; } 107 | @media (min-width: 768px) { 108 | .content-wrapper { 109 | flex-direction: row; } } 110 | 111 | .header { 112 | display: flex; 113 | padding: 8px; 114 | font-size: 0.875em; 115 | background: #444; 116 | color: #999; } 117 | 118 | .header-col { 119 | margin: 0; 120 | padding: 0 8px; } 121 | 122 | .header-col--primary { 123 | flex: 1; } 124 | 125 | .header-link { 126 | color: #fff; } 127 | 128 | .header-icon { 129 | padding-right: 6px; 130 | vertical-align: -4px; 131 | height: 16px; } 132 | 133 | .breadcrumbs { 134 | font-size: 0.875em; 135 | padding: 8px 16px; 136 | margin: 0; 137 | background: #fbfbfb; 138 | border-bottom: 1px solid #ddd; } 139 | 140 | .carat { 141 | height: 10px; 142 | margin: 0 5px; } 143 | 144 | .navigation { 145 | order: 2; } 146 | @media (min-width: 768px) { 147 | .navigation { 148 | order: 1; 149 | width: 25%; 150 | max-width: 300px; 151 | padding-bottom: 64px; 152 | overflow: hidden; 153 | word-wrap: normal; 154 | background: #fbfbfb; 155 | border-right: 1px solid #ddd; } } 156 | 157 | .nav-groups { 158 | list-style-type: none; 159 | padding-left: 0; } 160 | 161 | .nav-group-name { 162 | border-bottom: 1px solid #ddd; 163 | padding: 8px 0 8px 16px; } 164 | 165 | .nav-group-name-link { 166 | color: #333; } 167 | 168 | .nav-group-tasks { 169 | margin: 8px 0; 170 | padding: 0 0 0 8px; } 171 | 172 | .nav-group-task { 173 | font-size: 1em; 174 | list-style-type: none; 175 | white-space: nowrap; } 176 | 177 | .nav-group-task-link { 178 | color: #808080; } 179 | 180 | .main-content { 181 | order: 1; } 182 | @media (min-width: 768px) { 183 | .main-content { 184 | order: 2; 185 | flex: 1; 186 | padding-bottom: 60px; } } 187 | 188 | .section { 189 | padding: 0 32px; 190 | border-bottom: 1px solid #ddd; } 191 | 192 | .section-content { 193 | max-width: 834px; 194 | margin: 0 auto; 195 | padding: 16px 0; } 196 | 197 | .section-name { 198 | color: #666; 199 | display: block; } 200 | 201 | .declaration .highlight { 202 | overflow-x: initial; 203 | padding: 8px 0; 204 | margin: 0; 205 | background-color: transparent; 206 | border: none; } 207 | 208 | .task-group-section { 209 | border-top: 1px solid #ddd; } 210 | 211 | .task-group { 212 | padding-top: 0px; } 213 | 214 | .task-name-container a[name]:before { 215 | content: ""; 216 | display: block; } 217 | 218 | .item-container { 219 | padding: 0; } 220 | 221 | .item { 222 | padding-top: 8px; 223 | width: 100%; 224 | list-style-type: none; } 225 | .item a[name]:before { 226 | content: ""; 227 | display: block; } 228 | .item .token { 229 | padding-left: 3px; 230 | margin-left: 0px; 231 | font-size: 1rem; } 232 | .item .declaration-note { 233 | font-size: .85em; 234 | color: #808080; 235 | font-style: italic; } 236 | 237 | .pointer-container { 238 | border-bottom: 1px solid #ddd; 239 | left: -23px; 240 | padding-bottom: 13px; 241 | position: relative; 242 | width: 110%; } 243 | 244 | .pointer { 245 | left: 21px; 246 | top: 7px; 247 | display: block; 248 | position: absolute; 249 | width: 12px; 250 | height: 12px; 251 | border-left: 1px solid #ddd; 252 | border-top: 1px solid #ddd; 253 | background: #fff; 254 | transform: rotate(45deg); } 255 | 256 | .height-container { 257 | display: none; 258 | position: relative; 259 | width: 100%; 260 | overflow: hidden; } 261 | .height-container .section { 262 | background: #fff; 263 | border: 1px solid #ddd; 264 | border-top-width: 0; 265 | padding-top: 10px; 266 | padding-bottom: 5px; 267 | padding: 8px 16px; } 268 | 269 | .aside, .language { 270 | padding: 6px 12px; 271 | margin: 12px 0; 272 | border-left: 5px solid #dddddd; 273 | overflow-y: hidden; } 274 | .aside .aside-title, .language .aside-title { 275 | font-size: 9px; 276 | letter-spacing: 2px; 277 | text-transform: uppercase; 278 | padding-bottom: 0; 279 | margin: 0; 280 | color: #aaa; 281 | -webkit-user-select: none; } 282 | .aside p:last-child, .language p:last-child { 283 | margin-bottom: 0; } 284 | 285 | .language { 286 | border-left: 5px solid #cde9f4; } 287 | .language .aside-title { 288 | color: #4183c4; } 289 | 290 | .aside-warning { 291 | border-left: 5px solid #ff6666; } 292 | .aside-warning .aside-title { 293 | color: #ff0000; } 294 | 295 | .graybox { 296 | border-collapse: collapse; 297 | width: 100%; } 298 | .graybox p { 299 | margin: 0; 300 | word-break: break-word; 301 | min-width: 50px; } 302 | .graybox td { 303 | border: 1px solid #ddd; 304 | padding: 5px 25px 5px 10px; 305 | vertical-align: middle; } 306 | .graybox tr td:first-of-type { 307 | text-align: right; 308 | padding: 7px; 309 | vertical-align: top; 310 | word-break: normal; 311 | width: 40px; } 312 | 313 | .slightly-smaller { 314 | font-size: 0.9em; } 315 | 316 | .footer { 317 | padding: 8px 16px; 318 | background: #444; 319 | color: #ddd; 320 | font-size: 0.8em; } 321 | .footer p { 322 | margin: 8px 0; } 323 | .footer a { 324 | color: #fff; } 325 | 326 | html.dash .header, html.dash .breadcrumbs, html.dash .navigation { 327 | display: none; } 328 | html.dash .height-container { 329 | display: block; } 330 | -------------------------------------------------------------------------------- /docs/docsets/SwiftKueryMySQL.docset/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | com.jazzy.swiftkuerymysql 7 | CFBundleName 8 | SwiftKueryMySQL 9 | DocSetPlatformFamily 10 | swiftkuerymysql 11 | isDashDocset 12 | 13 | dashIndexFilePath 14 | index.html 15 | isJavaScriptEnabled 16 | 17 | DashDocSetFamily 18 | dashtoc 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/docsets/SwiftKueryMySQL.docset/Contents/Resources/Documents/Classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Classes Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |

19 | 20 | SwiftKueryMySQL Docs 21 | 22 | (100% documented) 23 |

24 | 25 |

26 | 27 | 28 | View on GitHub 29 | 30 |

31 | 32 |
33 | 34 | 39 | 40 |
41 | 59 |
60 | 61 |
62 |
63 |

Classes

64 |

The following classes are available globally.

65 | 66 |
67 |
68 | 69 |
70 |
71 |
72 |
    73 |
  • 74 |
    75 | 76 | 77 | 78 | MySQLConnection 79 | 80 |
    81 |
    82 |
    83 |
    84 |
    85 |
    86 |

    An implementation of SwiftKuery.Connection protocol for MySQL. 87 | Instances of MySQLConnection are NOT thread-safe and should not be shared between threads. 88 | Use MySQLThreadSafeConnection to share connection instances between multiple threads.

    89 | 90 | See more 91 |
    92 |
    93 |

    Declaration

    94 |
    95 |

    Swift

    96 |
    public class MySQLConnection: Connection
    97 | 98 |
    99 |
    100 |
    101 |
    102 |
  • 103 |
104 |
105 |
106 |
    107 |
  • 108 |
    109 | 110 | 111 | 112 | MySQLResultFetcher 113 | 114 |
    115 |
    116 |
    117 |
    118 |
    119 |
    120 |

    An implementation of query result fetcher.

    121 | 122 | See more 123 |
    124 |
    125 |

    Declaration

    126 |
    127 |

    Swift

    128 |
    public class MySQLResultFetcher: ResultFetcher
    129 | 130 |
    131 |
    132 |
    133 |
    134 |
  • 135 |
136 |
137 |
138 |
    139 |
  • 140 |
    141 | 142 | 143 | 144 | MySQLThreadSafeConnection 145 | 146 |
    147 |
    148 |
    149 |
    150 |
    151 |
    152 |

    A thread-safe implementation of SwiftKuery.Connection protocol for MySQL. 153 | Instances of MySQLThreadSafeConnection can be safely shared between multiple threads.

    154 | 155 | See more 156 |
    157 |
    158 |

    Declaration

    159 |
    160 |

    Swift

    161 |
    public class MySQLThreadSafeConnection: MySQLConnection
    162 | 163 |
    164 |
    165 |
    166 |
    167 |
  • 168 |
169 |
170 |
171 |
172 | 173 |
174 |
175 | 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /docs/docsets/SwiftKueryMySQL.docset/Contents/Resources/Documents/Classes/MySQLResultFetcher.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MySQLResultFetcher Class Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |

20 | 21 | SwiftKueryMySQL Docs 22 | 23 | (100% documented) 24 |

25 | 26 |

27 | 28 | 29 | View on GitHub 30 | 31 |

32 | 33 |
34 | 35 | 40 | 41 |
42 | 60 |
61 | 62 |
63 |
64 |

MySQLResultFetcher

65 |
66 |
67 |
public class MySQLResultFetcher: ResultFetcher
68 | 69 |
70 |
71 |

An implementation of query result fetcher.

72 | 73 |
74 |
75 | 76 |
77 |
78 |
79 |
    80 |
  • 81 |
    82 | 83 | 84 | 85 | fetchNext() 86 | 87 |
    88 |
    89 |
    90 |
    91 |
    92 |
    93 |

    Fetch the next row of the query result. This function is blocking.

    94 | 95 |
    96 |

    Returns

    97 | An array of values of type Any? representing the next row from the query result. 98 | 99 |
    100 | 101 |
    102 |
    103 |

    Declaration

    104 |
    105 |

    Swift

    106 |
    public func fetchNext() -> [Any?]?
    107 | 108 |
    109 |
    110 |
    111 |

    Return Value

    112 |

    An array of values of type Any? representing the next row from the query result.

    113 | 114 |
    115 |
    116 |
    117 |
  • 118 |
  • 119 |
    120 | 121 | 122 | 123 | fetchNext(callback:) 124 | 125 |
    126 |
    127 |
    128 |
    129 |
    130 |
    131 |

    Fetch the next row of the query result. This function is non-blocking.

    132 | 133 |
    134 |

    Parameter

    135 | Parameter callback: A callback to call when the next row of the query result is ready. 136 | 137 |
    138 | 139 |
    140 |
    141 |

    Declaration

    142 |
    143 |

    Swift

    144 |
    public func fetchNext(callback: ([Any?]?) ->())
    145 | 146 |
    147 |
    148 |
    149 |

    Parameters

    150 | 151 | 152 | 153 | 158 | 164 | 165 | 166 |
    154 | 155 | callback 156 | 157 | 159 |
    160 |

    A callback to call when the next row of the query result is ready.

    161 | 162 |
    163 |
    167 |
    168 |
    169 |
    170 |
  • 171 |
  • 172 |
    173 | 174 | 175 | 176 | fetchTitles() 177 | 178 |
    179 |
    180 |
    181 |
    182 |
    183 |
    184 |

    Fetch the titles of the query result.

    185 | 186 |
    187 |

    Returns

    188 | An array of column titles of type String. 189 | 190 |
    191 | 192 |
    193 |
    194 |

    Declaration

    195 |
    196 |

    Swift

    197 |
    public func fetchTitles() -> [String]
    198 | 199 |
    200 |
    201 |
    202 |

    Return Value

    203 |

    An array of column titles of type String.

    204 | 205 |
    206 |
    207 |
    208 |
  • 209 |
210 |
211 |
212 |
213 | 214 |
215 |
216 | 220 | 221 | 222 | 223 | -------------------------------------------------------------------------------- /docs/docsets/SwiftKueryMySQL.docset/Contents/Resources/Documents/Classes/MySQLThreadSafeConnection.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MySQLThreadSafeConnection Class Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |

20 | 21 | SwiftKueryMySQL Docs 22 | 23 | (100% documented) 24 |

25 | 26 |

27 | 28 | 29 | View on GitHub 30 | 31 |

32 | 33 |
34 | 35 | 40 | 41 |
42 | 60 |
61 | 62 |
63 |
64 |

MySQLThreadSafeConnection

65 |
66 |
67 |
public class MySQLThreadSafeConnection: MySQLConnection
68 | 69 |
70 |
71 |

A thread-safe implementation of SwiftKuery.Connection protocol for MySQL. 72 | Instances of MySQLThreadSafeConnection can be safely shared between multiple threads.

73 | 74 |
75 |
76 | 77 |
78 |
79 |
80 |
    81 |
  • 82 |
    83 | 84 | 85 | 86 | connect(onCompletion:) 87 | 88 |
    89 |
    90 |
    91 |
    92 |
    93 |
    94 |

    Establish a connection with the database.

    95 | 96 |
    97 |

    Parameter

    98 | Parameter onCompletion: The function to be called when the connection is established. 99 | 100 |
    101 | 102 |
    103 |
    104 |

    Declaration

    105 |
    106 |

    Swift

    107 |
    public override func connect(onCompletion: (QueryError?) -> ())
    108 | 109 |
    110 |
    111 |
    112 |

    Parameters

    113 | 114 | 115 | 116 | 121 | 127 | 128 | 129 |
    117 | 118 | onCompletion 119 | 120 | 122 |
    123 |

    The function to be called when the connection is established.

    124 | 125 |
    126 |
    130 |
    131 |
    132 |
    133 |
  • 134 |
  • 135 |
    136 | 137 | 138 | 139 | closeConnection() 140 | 141 |
    142 |
    143 |
    144 |
    145 |
    146 |
    147 |

    Close the connection to the database.

    148 | 149 |
    150 |
    151 |

    Declaration

    152 |
    153 |

    Swift

    154 |
    public override func closeConnection()
    155 | 156 |
    157 |
    158 |
    159 |
    160 |
  • 161 |
162 |
163 |
164 |
165 | 166 |
167 |
168 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /docs/docsets/SwiftKueryMySQL.docset/Contents/Resources/Documents/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/docsets/SwiftKueryMySQL.docset/Contents/Resources/Documents/css/jazzy.css: -------------------------------------------------------------------------------- 1 | *, *:before, *:after { 2 | box-sizing: inherit; } 3 | 4 | body { 5 | margin: 0; 6 | background: #fff; 7 | color: #333; 8 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | letter-spacing: .2px; 10 | -webkit-font-smoothing: antialiased; 11 | box-sizing: border-box; } 12 | 13 | h1 { 14 | font-size: 2rem; 15 | font-weight: 700; 16 | margin: 1.275em 0 0.6em; } 17 | 18 | h2 { 19 | font-size: 1.75rem; 20 | font-weight: 700; 21 | margin: 1.275em 0 0.3em; } 22 | 23 | h3 { 24 | font-size: 1.5rem; 25 | font-weight: 700; 26 | margin: 1em 0 0.3em; } 27 | 28 | h4 { 29 | font-size: 1.25rem; 30 | font-weight: 700; 31 | margin: 1.275em 0 0.85em; } 32 | 33 | h5 { 34 | font-size: 1rem; 35 | font-weight: 700; 36 | margin: 1.275em 0 0.85em; } 37 | 38 | h6 { 39 | font-size: 1rem; 40 | font-weight: 700; 41 | margin: 1.275em 0 0.85em; 42 | color: #777; } 43 | 44 | p { 45 | margin: 0 0 1em; } 46 | 47 | ul, ol { 48 | padding: 0 0 0 2em; 49 | margin: 0 0 0.85em; } 50 | 51 | blockquote { 52 | margin: 0 0 0.85em; 53 | padding: 0 15px; 54 | color: #858585; 55 | border-left: 4px solid #e5e5e5; } 56 | 57 | img { 58 | max-width: 100%; } 59 | 60 | a { 61 | color: #4183c4; 62 | text-decoration: none; } 63 | a:hover, a:focus { 64 | outline: 0; 65 | text-decoration: underline; } 66 | 67 | table { 68 | background: #fff; 69 | width: 100%; 70 | border-collapse: collapse; 71 | border-spacing: 0; 72 | overflow: auto; 73 | margin: 0 0 0.85em; } 74 | 75 | tr:nth-child(2n) { 76 | background-color: #fbfbfb; } 77 | 78 | th, td { 79 | padding: 6px 13px; 80 | border: 1px solid #ddd; } 81 | 82 | pre { 83 | margin: 0 0 1.275em; 84 | padding: .85em 1em; 85 | overflow: auto; 86 | background: #f7f7f7; 87 | font-size: .85em; 88 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } 89 | 90 | code { 91 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } 92 | 93 | p > code, li > code { 94 | background: #f7f7f7; 95 | padding: .2em; } 96 | p > code:before, p > code:after, li > code:before, li > code:after { 97 | letter-spacing: -.2em; 98 | content: "\00a0"; } 99 | 100 | pre code { 101 | padding: 0; 102 | white-space: pre; } 103 | 104 | .content-wrapper { 105 | display: flex; 106 | flex-direction: column; } 107 | @media (min-width: 768px) { 108 | .content-wrapper { 109 | flex-direction: row; } } 110 | 111 | .header { 112 | display: flex; 113 | padding: 8px; 114 | font-size: 0.875em; 115 | background: #444; 116 | color: #999; } 117 | 118 | .header-col { 119 | margin: 0; 120 | padding: 0 8px; } 121 | 122 | .header-col--primary { 123 | flex: 1; } 124 | 125 | .header-link { 126 | color: #fff; } 127 | 128 | .header-icon { 129 | padding-right: 6px; 130 | vertical-align: -4px; 131 | height: 16px; } 132 | 133 | .breadcrumbs { 134 | font-size: 0.875em; 135 | padding: 8px 16px; 136 | margin: 0; 137 | background: #fbfbfb; 138 | border-bottom: 1px solid #ddd; } 139 | 140 | .carat { 141 | height: 10px; 142 | margin: 0 5px; } 143 | 144 | .navigation { 145 | order: 2; } 146 | @media (min-width: 768px) { 147 | .navigation { 148 | order: 1; 149 | width: 25%; 150 | max-width: 300px; 151 | padding-bottom: 64px; 152 | overflow: hidden; 153 | word-wrap: normal; 154 | background: #fbfbfb; 155 | border-right: 1px solid #ddd; } } 156 | 157 | .nav-groups { 158 | list-style-type: none; 159 | padding-left: 0; } 160 | 161 | .nav-group-name { 162 | border-bottom: 1px solid #ddd; 163 | padding: 8px 0 8px 16px; } 164 | 165 | .nav-group-name-link { 166 | color: #333; } 167 | 168 | .nav-group-tasks { 169 | margin: 8px 0; 170 | padding: 0 0 0 8px; } 171 | 172 | .nav-group-task { 173 | font-size: 1em; 174 | list-style-type: none; 175 | white-space: nowrap; } 176 | 177 | .nav-group-task-link { 178 | color: #808080; } 179 | 180 | .main-content { 181 | order: 1; } 182 | @media (min-width: 768px) { 183 | .main-content { 184 | order: 2; 185 | flex: 1; 186 | padding-bottom: 60px; } } 187 | 188 | .section { 189 | padding: 0 32px; 190 | border-bottom: 1px solid #ddd; } 191 | 192 | .section-content { 193 | max-width: 834px; 194 | margin: 0 auto; 195 | padding: 16px 0; } 196 | 197 | .section-name { 198 | color: #666; 199 | display: block; } 200 | 201 | .declaration .highlight { 202 | overflow-x: initial; 203 | padding: 8px 0; 204 | margin: 0; 205 | background-color: transparent; 206 | border: none; } 207 | 208 | .task-group-section { 209 | border-top: 1px solid #ddd; } 210 | 211 | .task-group { 212 | padding-top: 0px; } 213 | 214 | .task-name-container a[name]:before { 215 | content: ""; 216 | display: block; } 217 | 218 | .item-container { 219 | padding: 0; } 220 | 221 | .item { 222 | padding-top: 8px; 223 | width: 100%; 224 | list-style-type: none; } 225 | .item a[name]:before { 226 | content: ""; 227 | display: block; } 228 | .item .token { 229 | padding-left: 3px; 230 | margin-left: 0px; 231 | font-size: 1rem; } 232 | .item .declaration-note { 233 | font-size: .85em; 234 | color: #808080; 235 | font-style: italic; } 236 | 237 | .pointer-container { 238 | border-bottom: 1px solid #ddd; 239 | left: -23px; 240 | padding-bottom: 13px; 241 | position: relative; 242 | width: 110%; } 243 | 244 | .pointer { 245 | left: 21px; 246 | top: 7px; 247 | display: block; 248 | position: absolute; 249 | width: 12px; 250 | height: 12px; 251 | border-left: 1px solid #ddd; 252 | border-top: 1px solid #ddd; 253 | background: #fff; 254 | transform: rotate(45deg); } 255 | 256 | .height-container { 257 | display: none; 258 | position: relative; 259 | width: 100%; 260 | overflow: hidden; } 261 | .height-container .section { 262 | background: #fff; 263 | border: 1px solid #ddd; 264 | border-top-width: 0; 265 | padding-top: 10px; 266 | padding-bottom: 5px; 267 | padding: 8px 16px; } 268 | 269 | .aside, .language { 270 | padding: 6px 12px; 271 | margin: 12px 0; 272 | border-left: 5px solid #dddddd; 273 | overflow-y: hidden; } 274 | .aside .aside-title, .language .aside-title { 275 | font-size: 9px; 276 | letter-spacing: 2px; 277 | text-transform: uppercase; 278 | padding-bottom: 0; 279 | margin: 0; 280 | color: #aaa; 281 | -webkit-user-select: none; } 282 | .aside p:last-child, .language p:last-child { 283 | margin-bottom: 0; } 284 | 285 | .language { 286 | border-left: 5px solid #cde9f4; } 287 | .language .aside-title { 288 | color: #4183c4; } 289 | 290 | .aside-warning { 291 | border-left: 5px solid #ff6666; } 292 | .aside-warning .aside-title { 293 | color: #ff0000; } 294 | 295 | .graybox { 296 | border-collapse: collapse; 297 | width: 100%; } 298 | .graybox p { 299 | margin: 0; 300 | word-break: break-word; 301 | min-width: 50px; } 302 | .graybox td { 303 | border: 1px solid #ddd; 304 | padding: 5px 25px 5px 10px; 305 | vertical-align: middle; } 306 | .graybox tr td:first-of-type { 307 | text-align: right; 308 | padding: 7px; 309 | vertical-align: top; 310 | word-break: normal; 311 | width: 40px; } 312 | 313 | .slightly-smaller { 314 | font-size: 0.9em; } 315 | 316 | .footer { 317 | padding: 8px 16px; 318 | background: #444; 319 | color: #ddd; 320 | font-size: 0.8em; } 321 | .footer p { 322 | margin: 8px 0; } 323 | .footer a { 324 | color: #fff; } 325 | 326 | html.dash .header, html.dash .breadcrumbs, html.dash .navigation { 327 | display: none; } 328 | html.dash .height-container { 329 | display: block; } 330 | -------------------------------------------------------------------------------- /docs/docsets/SwiftKueryMySQL.docset/Contents/Resources/Documents/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/SwiftKueryMySQL/ad6355e6fe75149efc5dc270f726edf8af867944/docs/docsets/SwiftKueryMySQL.docset/Contents/Resources/Documents/img/carat.png -------------------------------------------------------------------------------- /docs/docsets/SwiftKueryMySQL.docset/Contents/Resources/Documents/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/SwiftKueryMySQL/ad6355e6fe75149efc5dc270f726edf8af867944/docs/docsets/SwiftKueryMySQL.docset/Contents/Resources/Documents/img/dash.png -------------------------------------------------------------------------------- /docs/docsets/SwiftKueryMySQL.docset/Contents/Resources/Documents/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/SwiftKueryMySQL/ad6355e6fe75149efc5dc270f726edf8af867944/docs/docsets/SwiftKueryMySQL.docset/Contents/Resources/Documents/img/gh.png -------------------------------------------------------------------------------- /docs/docsets/SwiftKueryMySQL.docset/Contents/Resources/Documents/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SwiftKueryMySQL Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |

19 | 20 | SwiftKueryMySQL Docs 21 | 22 | (100% documented) 23 |

24 | 25 |

26 | 27 | 28 | View on GitHub 29 | 30 |

31 | 32 |
33 | 34 | 39 | 40 |
41 | 59 |
60 | 61 |
62 |
63 | 64 |

SwiftKueryMySQL

65 | 66 |

MySQL plugin for Swift-Kuery framework

67 | 68 |

Build Status - Master 69 | macOS 70 | Linux 71 | Apache 2

72 |

Summary

73 | 74 |

MySQL plugin for the Swift-Kuery framework. It enables you to use Swift-Kuery to manipulate data in a MySQL database.

75 |

Install MySQL

76 |

macOS

77 |
brew install mysql
 78 | mysql.server start
 79 | 
80 | 81 |

Other install options: https://dev.mysql.com/doc/refman/5.7/en/osx-installation.html

82 | 83 |

On macOS, add -Xlinker -L/usr/local/lib to swift commands to point the linker to the MySQL library location. 84 | For example, 85 | 86 | swift build -Xlinker -L/usr/local/lib 87 | swift test -Xlinker -L/usr/local/lib 88 | swift package generate-xcodeproj -Xlinker -L/usr/local/lib 89 |

90 |

Linux

91 | 92 |

Download the release package for your Linux distribution from http://dev.mysql.com/downloads/repo/apt/ 93 | For example: wget https://repo.mysql.com//mysql-apt-config_0.8.2-1_all.deb 94 | 95 | sudo dpkg -i mysql-apt-config_0.8.2-1_all.deb 96 | sudo apt-get update 97 | sudo apt-get install mysql-server 98 | sudo service mysql start 99 | 100 | More details: https://dev.mysql.com/doc/refman/5.7/en/linux-installation.html

101 | 102 |

On linux, regular swift commands should work fine: 103 | 104 | swift build 105 | swift test 106 | swift package generate-xcodeproj 107 |

108 |

Using Swift-Kuery-MySQL

109 | 110 |

First create an instance of MySQLConnection by calling:

111 |
let connection = MySQLConnection(host: host, user: user, password: password, database: database, 
112 |                                  port: port, characterSet: characterSet)
113 | 
114 | 115 |

Where: 116 | - host - hostname or IP of the MySQL server, defaults to localhost 117 | - user - the user name, defaults to current user 118 | - password - the user password, defaults to no password 119 | - database - default database to use if specified 120 | - port - port number for the TCP/IP connection if connecting to server on a non-standard port (not 3306) 121 | - characterSet - MySQL character set to use for the connection

122 | 123 |

All the connection parameters are optional, so if you were using a standard local MySQL server as the current user, you could simply use: 124 | swift 125 | let connection = MySQLConnection(password: password) 126 | 127 | password is also optional, but recommended.

128 | 129 |

Alternatively, call: 130 | swift 131 | let connection = MySQLConnection(url: URL(string: "mysql://\(user):\(password)@\(host):\(port)/\(database)")!)) 132 | 133 | You now have a connection that can be used to execute SQL queries created using Swift-Kuery.

134 | 135 |

If you want to share a connection instance between multiple threads use MySQLThreadSafeConnection instead of MySQLConnection: 136 | swift 137 | let connection = MySQLThreadSafeConnection(host: host, user: user, password: password, database: database, 138 | port: port, characterSet: characterSet) 139 | 140 | A MySQLThreadSafeConnection instance can be used to safely execute queries on multiple threads sharing the same connection.

141 | 142 |

To connect to the server and execute a query: 143 | swift 144 | connection.connect() { error in 145 | // if error is nil, connect() was successful 146 | let query = Select(from: table) 147 | connection.execute(query: query) { queryResult in 148 | if let resultSet = queryResult.asResultSet { 149 | for row in resultSet.rows { 150 | // process each row 151 | } 152 | } 153 | } 154 | } 155 |

156 | 157 |

View the Kuery documentation for detailed information on using the Kuery framework.

158 |

License

159 | 160 |

This library is licensed under Apache 2.0. Full license text is available in LICENSE.

161 | 162 |
163 |
164 | 165 | 166 |
167 |
168 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /docs/docsets/SwiftKueryMySQL.docset/Contents/Resources/Documents/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | // On doc load, toggle the URL hash discussion if present 12 | $(document).ready(function() { 13 | if (!window.jazzy.docset) { 14 | var linkToHash = $('a[href="' + window.location.hash +'"]'); 15 | linkToHash.trigger("click"); 16 | } 17 | }); 18 | 19 | // On token click, toggle its discussion and animate token.marginLeft 20 | $(".token").click(function(event) { 21 | if (window.jazzy.docset) { 22 | return; 23 | } 24 | var link = $(this); 25 | var animationDuration = 300; 26 | $content = link.parent().parent().next(); 27 | $content.slideToggle(animationDuration); 28 | 29 | // Keeps the document from jumping to the hash. 30 | var href = $(this).attr('href'); 31 | if (history.pushState) { 32 | history.pushState({}, '', href); 33 | } else { 34 | location.hash = href; 35 | } 36 | event.preventDefault(); 37 | }); 38 | -------------------------------------------------------------------------------- /docs/docsets/SwiftKueryMySQL.docset/Contents/Resources/Documents/undocumented.json: -------------------------------------------------------------------------------- 1 | { 2 | "warnings": [ 3 | 4 | ], 5 | "source_directory": "/Users/navneet/development/SwiftKueryMySQL" 6 | } -------------------------------------------------------------------------------- /docs/docsets/SwiftKueryMySQL.docset/Contents/Resources/docSet.dsidx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/SwiftKueryMySQL/ad6355e6fe75149efc5dc270f726edf8af867944/docs/docsets/SwiftKueryMySQL.docset/Contents/Resources/docSet.dsidx -------------------------------------------------------------------------------- /docs/docsets/SwiftKueryMySQL.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/SwiftKueryMySQL/ad6355e6fe75149efc5dc270f726edf8af867944/docs/docsets/SwiftKueryMySQL.tgz -------------------------------------------------------------------------------- /docs/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/SwiftKueryMySQL/ad6355e6fe75149efc5dc270f726edf8af867944/docs/img/carat.png -------------------------------------------------------------------------------- /docs/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/SwiftKueryMySQL/ad6355e6fe75149efc5dc270f726edf8af867944/docs/img/dash.png -------------------------------------------------------------------------------- /docs/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kitura/SwiftKueryMySQL/ad6355e6fe75149efc5dc270f726edf8af867944/docs/img/gh.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SwiftKueryMySQL Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |

19 | 20 | SwiftKueryMySQL Docs 21 | 22 | (100% documented) 23 |

24 | 25 |

26 | 27 | 28 | View on GitHub 29 | 30 |

31 | 32 |
33 | 34 | 39 | 40 |
41 | 59 |
60 | 61 |
62 |
63 | 64 |

SwiftKueryMySQL

65 | 66 |

MySQL plugin for Swift-Kuery framework

67 | 68 |

Build Status - Master 69 | macOS 70 | Linux 71 | Apache 2

72 |

Summary

73 | 74 |

MySQL plugin for the Swift-Kuery framework. It enables you to use Swift-Kuery to manipulate data in a MySQL database.

75 |

Install MySQL

76 |

macOS

77 |
brew install mysql
 78 | mysql.server start
 79 | 
80 | 81 |

Other install options: https://dev.mysql.com/doc/refman/5.7/en/osx-installation.html

82 | 83 |

On macOS, add -Xlinker -L/usr/local/lib to swift commands to point the linker to the MySQL library location. 84 | For example, 85 | 86 | swift build -Xlinker -L/usr/local/lib 87 | swift test -Xlinker -L/usr/local/lib 88 | swift package generate-xcodeproj -Xlinker -L/usr/local/lib 89 |

90 |

Linux

91 | 92 |

Download the release package for your Linux distribution from http://dev.mysql.com/downloads/repo/apt/ 93 | For example: wget https://repo.mysql.com//mysql-apt-config_0.8.2-1_all.deb 94 | 95 | sudo dpkg -i mysql-apt-config_0.8.2-1_all.deb 96 | sudo apt-get update 97 | sudo apt-get install mysql-server 98 | sudo service mysql start 99 | 100 | More details: https://dev.mysql.com/doc/refman/5.7/en/linux-installation.html

101 | 102 |

On linux, regular swift commands should work fine: 103 | 104 | swift build 105 | swift test 106 | swift package generate-xcodeproj 107 |

108 |

Using Swift-Kuery-MySQL

109 | 110 |

First create an instance of MySQLConnection by calling:

111 |
let connection = MySQLConnection(host: host, user: user, password: password, database: database, 
112 |                                  port: port, characterSet: characterSet)
113 | 
114 | 115 |

Where: 116 | - host - hostname or IP of the MySQL server, defaults to localhost 117 | - user - the user name, defaults to current user 118 | - password - the user password, defaults to no password 119 | - database - default database to use if specified 120 | - port - port number for the TCP/IP connection if connecting to server on a non-standard port (not 3306) 121 | - characterSet - MySQL character set to use for the connection

122 | 123 |

All the connection parameters are optional, so if you were using a standard local MySQL server as the current user, you could simply use: 124 | swift 125 | let connection = MySQLConnection(password: password) 126 | 127 | password is also optional, but recommended.

128 | 129 |

Alternatively, call: 130 | swift 131 | let connection = MySQLConnection(url: URL(string: "mysql://\(user):\(password)@\(host):\(port)/\(database)")!)) 132 | 133 | You now have a connection that can be used to execute SQL queries created using Swift-Kuery.

134 | 135 |

If you want to share a connection instance between multiple threads use MySQLThreadSafeConnection instead of MySQLConnection: 136 | swift 137 | let connection = MySQLThreadSafeConnection(host: host, user: user, password: password, database: database, 138 | port: port, characterSet: characterSet) 139 | 140 | A MySQLThreadSafeConnection instance can be used to safely execute queries on multiple threads sharing the same connection.

141 | 142 |

To connect to the server and execute a query: 143 | swift 144 | connection.connect() { error in 145 | // if error is nil, connect() was successful 146 | let query = Select(from: table) 147 | connection.execute(query: query) { queryResult in 148 | if let resultSet = queryResult.asResultSet { 149 | for row in resultSet.rows { 150 | // process each row 151 | } 152 | } 153 | } 154 | } 155 |

156 | 157 |

View the Kuery documentation for detailed information on using the Kuery framework.

158 |

License

159 | 160 |

This library is licensed under Apache 2.0. Full license text is available in LICENSE.

161 | 162 |
163 |
164 | 165 | 166 |
167 |
168 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /docs/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | // On doc load, toggle the URL hash discussion if present 12 | $(document).ready(function() { 13 | if (!window.jazzy.docset) { 14 | var linkToHash = $('a[href="' + window.location.hash +'"]'); 15 | linkToHash.trigger("click"); 16 | } 17 | }); 18 | 19 | // On token click, toggle its discussion and animate token.marginLeft 20 | $(".token").click(function(event) { 21 | if (window.jazzy.docset) { 22 | return; 23 | } 24 | var link = $(this); 25 | var animationDuration = 300; 26 | $content = link.parent().parent().next(); 27 | $content.slideToggle(animationDuration); 28 | 29 | // Keeps the document from jumping to the hash. 30 | var href = $(this).attr('href'); 31 | if (history.pushState) { 32 | history.pushState({}, '', href); 33 | } else { 34 | location.hash = href; 35 | } 36 | event.preventDefault(); 37 | }); 38 | -------------------------------------------------------------------------------- /docs/undocumented.json: -------------------------------------------------------------------------------- 1 | { 2 | "warnings": [ 3 | 4 | ], 5 | "source_directory": "/Users/navneet/development/SwiftKueryMySQL" 6 | } --------------------------------------------------------------------------------