{"id":16683968,"url":"https://github.com/noppoman/swiftmysql","last_synced_at":"2025-04-09T23:32:27.585Z","repository":{"id":63919326,"uuid":"109151907","full_name":"noppoMan/SwiftMysql","owner":"noppoMan","description":"A pure Swift Client implementing the MySQL protocol. Also supports non-blocking I/O","archived":false,"fork":false,"pushed_at":"2017-11-10T08:18:18.000Z","size":72,"stargazers_count":6,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-24T01:22:43.942Z","etag":null,"topics":["asynchronous","mysql","mysql-client","mysql-client-linux","non-blocking","swift"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/noppoMan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-11-01T15:57:11.000Z","updated_at":"2021-02-26T22:54:14.000Z","dependencies_parsed_at":"2022-11-29T10:45:18.210Z","dependency_job_id":null,"html_url":"https://github.com/noppoMan/SwiftMysql","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noppoMan%2FSwiftMysql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noppoMan%2FSwiftMysql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noppoMan%2FSwiftMysql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noppoMan%2FSwiftMysql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/noppoMan","download_url":"https://codeload.github.com/noppoMan/SwiftMysql/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247666016,"owners_count":20975788,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asynchronous","mysql","mysql-client","mysql-client-linux","non-blocking","swift"],"created_at":"2024-10-12T14:27:33.666Z","updated_at":"2025-04-09T23:32:27.566Z","avatar_url":"https://github.com/noppoMan.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SwiftMysql\n\n[![Build Status](https://travis-ci.org/noppoMan/SwiftMysql.svg?branch=master)](https://travis-ci.org/noppoMan/SwiftMysql)\n\nA pure Swift Client implementing the MySQL protocol. this is not depend on libmysql.\n\n## Features\n\n* [x] Thread safe\n* [x] Pooling connections\n* [x] Prepared statements\n* [x] Transactions\n* [x] JSON Data Type in MySQL 5.7\n* [x] Streaming query rows\n* [x] Non-Blocking Querying\n\n## Instllation\n\n```swift\nlet package = Package(\n    name: \"MyApp\",\n    dependencies: [\n        .package(url: \"https://github.com/noppoMan/SwiftMysql.git\", .upToNextMajor(from: \"0.1.0\"))\n    ],\n)\n```\n\n## Basic querying\n\n```swift\nlet url = URL(string: \"mysql://localhost:3306\")\nlet con = try Connection(url: url!, user: \"root\", password: \"password\", database: \"swift_mysql\")\n\nlet result = try con.query(\"selct * from users\")\nif let rows = result.asRows() {\n    for row in rows {\n      print(row) // [\"id\": 1, \"name\": \"Luke\", \"email\": \"test@example.com\"]\n    }\n}\n```\n\n## Prepared statements\n\nYou can easy to use prepared statement as following.\n\n```swift\nlet url = URL(string: \"mysql://localhost:3306\")\nlet con = try Connection(url: url!, user: \"root\", password: \"password\", database: \"swift_mysql\")\n\nlet result = try con.query(\"selct * from books where published_at \u003e ? and category_id = ?\", [2017, 3])\nif let rows = result.asRows() {\n    for row in rs {\n      print(row)\n    }\n}\n```\n\n\n## Pooling connections\n\nRather than creating and managing connections one-by-one, this module also provides built-in connection pooling using `ConnectionPool(url:user:database:minPoolSize:maxPoolSize)`\n\n```swift\nlet pool = try ConnectionPool(\n    url: URL(string: \"mysql://localhost:3306\")!,\n    user: \"root\",\n    database: \"swift_mysql\",\n    minPoolSize: 3,\n    maxPoolSize: 10\n)\n\ntry pool.query(\"select * from users\") // the connection is released after finishing query.\n```\n\n### failedToGetConnectionFromPool Error\n\n`failedToGetConnectionFromPool` will be thrown when the number of connections that are used in internally reaches `maxPoolSize`, and then `query` is called. But It's recoverable, all developers can retry to perform `query` as like following.\n\n```swift\ndo {\n    try pool.query(\"select * from users\")\n} catch ConnectionPoolError.failedToGetConnectionFromPool {\n    // may need to wait a moment...\n\n    // try again.\n    try pool.query(\"select * from users\")\n}\n```\n\n## Transactions\n\nSimple transaction support is available at the connection level\n\n### Commit\n\nIf the program that in transaction block is finished without throwing error, transaction should be committed automatically.\n\n```swift\ntry con.transaction {\n    $0.query(\"insert into users (name, email), value (\\\"Foo\\\", \\\"foo@example.com\\\")\")\n}\n```\n\n### Rollback\n\nif the error is thrown in transaction block, `rollback` should be performed.\n\n```swift\ntry con.transaction {\n    throw FooError\n}\n```\n\n## Streaming query rows\n\nSometimes you may want to select large quantities of rows and process each of them as they are received. This can be done like this\n\n```swift\nlet result = try con.query(\"selct * from large_tables\")\nif let resultFetcher = result.asResultSet() {\n    print(resultFetcher.columns) // [String]\n\n    // resultFetcher.rows is a infinity Sequence\n    // you can loop it until the Mysql sends EOF packet.\n    for row in resultFetcher.rows {\n        print(row)\n    }\n}\n```\n\n## Terminating connections\n\nonce call `close` method, the Mysql connection is terminated safely.\n\n```swift\ntry con.close()\n```\n\n# Non-Blocking Querying\n\nSwiftMysql supports Non-Blocking querying with `AsyncConnection`. Non-Blocking means event-driven non-blocking I/O using OS native asynchronous system calls (epoll/kqueue). It doesn't concurrent execution by worker threads.\n\nCurrently all of non-blocking features are **not thread safe**. So you should use them on the single thread.\n\n## Non-Blocking Querying with AsyncConnection\n\nYou can asynchronously connect to the mysql with `AsyncConnection(url:user:password:database:queue)`,\n\nOnce call initializer of `AsyncConnection`, connection is automatically opened on a specified thread(queue). Then, all of your operations(query) are queued and processed in order.\nThe thread will create event loop on the own thread to observe the file descriptor of the connection.\n\n```swift\nimport Foundation\nimport SwiftMysql\n\nlet url = URL(string: \"mysql://localhost:3306\")!\nlet con = try SwiftMysql.AsyncConnection(\n    url: url,\n    user: \"root\",\n    password: nil,\n    database: \"swift_mysql\"\n)\n\ncon.onConnect {\n    print(\"connected to \\(url)\")\n}\n\ncon.onError { error in\n    print(\"Error: \\(error)\")\n}\n\ncon.query(\"select * from users where id = 1\") { result in\n    if let error = result.asError() {\n        print(error)\n        return\n    }\n\n    result.asRows {\n        print($0) // [[\"id\": 1, \"name\": \"Jack....]]\n    }\n}\n\ncon.query(\"select * from users where id = 2\") { result in\n    if let error = result.asError() {\n        print(error)\n        return\n    }\n\n    result.asRows {\n        print($0) // [[\"id\": 2, \"name\": \"Tonny....]]\n    }\n}\n\nRunLoop.main.run()\n```\n\n### Event Loop Thread\n\nIf you didn't care the thread for running event loop, it's automatically determined by `DispatchQueue(attributes: .serial)` internally.\n\nOr you can provide it by `queue` label of initializer like following.\n\n```swift\nlet url = URL(string: \"mysql://localhost:3306\")!\nlet con = try SwiftMysql.AsyncConnection(\n    url: url,\n    user: \"root\",\n    password: nil,\n    database: \"swift_mysql\",\n    queue: DispatchQueue.main\n)\n\ncon.connect {\n    print(Thread.current == Thread.main) // true\n}\n\ncon.query(\"...\") { _ in\n    print(Thread.current == Thread.main) // true\n}\n```\n\n### Event Driven Query rows and fields\n\nYou can improve memory efficiency for fetching records to use `ResultSetEvent`.\n\n`ResultSetEvent` provides two methods to fetch fields and rows streamly.\n\n* `onFields`: onFields is called when the all fields packets are received.\n* `onRow`: onRow is called when the per row packets are received.\n\n```swift\ncon.query(\"select * from users limit ?\", bindParams: [100]) { result in\n    let rs = result.asResultSet() // get ResultSetEvent\n\n    rs.onFields { fields in\n        print(fields) // [\"id\", \"name\", \"age\"...]\n    }\n\n    event.onRow { row in\n        print(row) // [[1, \"Jack\", 35...]]\n    }\n}\n```\n\n\n## Pooling connections\nAlso you can use pooling connections for non-blocking querying with `AsyncConnectionPool`. The usage is roughly same as sync version. The number of connections using at the same time are reached `maxPoolSize`, the next query queue should wait for a connection is available.\n\n```swift\nlet pool = try AsyncConnectionPool(\n    url: url,\n    user: \"root\",\n    database: \"swift_mysql\",\n    minPoolSize: 2,\n    maxPoolSize: 10\n)\n\npool.onReady {\n    print(\"The initial connections are ready\")\n}\n\npool.onNewConnectionIsReady {\n    print(\"new Connection is ready\")\n}\n\n// Uses a existing connection\npool.query(\"select * from users where id = ?\", bindParams: [1]) { result in\n    result.asRows()\n    // connection will be released automatically,\n    // when the all of packets of this query are received.\n}\n\n// Uses a existing connection\npool.query(\"select * from users where id = ?\", bindParams: [2]) { result in\n    result.asRows()\n}\n\n// May create a new connection asynchronously.\n// Depends on the timing of first query finished.\npool.query(\"select * from users where id = ?\", bindParams: [3]) { result in\n    result.asRows()\n}\n```\n\n## Transactions\n\n```swift\npool.transaction { error, con in\n    con?.query(\"insert into ....\") { result in\n        if let error = result.asError() {\n            con?.rollback { _ in\n                done(error)\n            }\n            return\n        }\n\n        con?.query(\"update users set name = ....\") { result in\n          if let error = result.asError() {\n              con?.rollback { _ in\n                  done(error)\n              }\n              return\n          }\n\n          con?.commit { _ in\n              done(nil)\n          }\n        }\n    }\n}\n```\n\n\n## License\nSwiftMysql is released under the MIT license. See LICENSE for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoppoman%2Fswiftmysql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnoppoman%2Fswiftmysql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoppoman%2Fswiftmysql/lists"}