├── .github └── cover.png ├── .gitignore ├── .gitmodules ├── .swift-format ├── CONTRIBUTING.md ├── Examples ├── Batch │ ├── README.md │ └── main.swift ├── Local │ ├── .gitignore │ ├── README.md │ └── main.swift ├── Memory │ ├── README.md │ └── main.swift ├── Query │ └── main.swift ├── Remote │ ├── .gitignore │ ├── README.md │ └── main.swift ├── Sync │ ├── .gitignore │ ├── README.md │ └── main.swift ├── Transaction │ └── main.swift ├── Transactions │ ├── .gitignore │ ├── README.md │ └── main.swift ├── Vector │ ├── README.md │ └── main.swift └── local.db ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── CLibsql │ ├── CLibsql.xcframework │ │ ├── Info.plist │ │ ├── ios-arm64 │ │ │ ├── Headers │ │ │ │ ├── libsql.h │ │ │ │ └── module.modulemap │ │ │ └── liblibsql.a │ │ ├── ios-arm64_x86_64-simulator │ │ │ ├── Headers │ │ │ │ ├── libsql.h │ │ │ │ └── module.modulemap │ │ │ └── liblibsql.a │ │ └── macos-arm64_x86_64 │ │ │ ├── Headers │ │ │ ├── libsql.h │ │ │ └── module.modulemap │ │ │ └── liblibsql.a │ ├── build.sh │ └── module.modulemap └── Libsql │ └── Libsql.swift └── Tests └── LibsqlTests └── LibsqlTests.swift /.github/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tursodatabase/libsql-swift/d6c16af9a8bf5a096c5eaca1fd4dac845242ef87/.github/cover.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | test.db* 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Sources/CLibsql/libsql-c"] 2 | path = Sources/CLibsql/libsql-c 3 | url = https://github.com/tursodatabase/libsql-c 4 | -------------------------------------------------------------------------------- /.swift-format: -------------------------------------------------------------------------------- 1 | { 2 | "fileScopedDeclarationPrivacy" : { 3 | "accessLevel" : "private" 4 | }, 5 | "indentation" : { 6 | "spaces" : 4 7 | }, 8 | "indentConditionalCompilationBlocks" : true, 9 | "indentSwitchCaseLabels" : false, 10 | "lineBreakAroundMultilineExpressionChainComponents" : false, 11 | "lineBreakBeforeControlFlowKeywords" : false, 12 | "lineBreakBeforeEachArgument" : false, 13 | "lineBreakBeforeEachGenericRequirement" : false, 14 | "lineLength" : 100, 15 | "maximumBlankLines" : 1, 16 | "prioritizeKeepingFunctionOutputTogether" : false, 17 | "respectsExistingLineBreaks" : true, 18 | "rules" : { 19 | "AllPublicDeclarationsHaveDocumentation" : false, 20 | "AlwaysUseLowerCamelCase" : true, 21 | "AmbiguousTrailingClosureOverload" : true, 22 | "BeginDocumentationCommentWithOneLineSummary" : false, 23 | "DoNotUseSemicolons" : true, 24 | "DontRepeatTypeInStaticProperties" : true, 25 | "FileScopedDeclarationPrivacy" : true, 26 | "FullyIndirectEnum" : true, 27 | "GroupNumericLiterals" : true, 28 | "IdentifiersMustBeASCII" : true, 29 | "NeverForceUnwrap" : false, 30 | "NeverUseForceTry" : false, 31 | "NeverUseImplicitlyUnwrappedOptionals" : false, 32 | "NoAccessLevelOnExtensionDeclaration" : true, 33 | "NoAssignmentInExpressions" : true, 34 | "NoBlockComments" : true, 35 | "NoCasesWithOnlyFallthrough" : true, 36 | "NoEmptyTrailingClosureParentheses" : true, 37 | "NoLabelsInCasePatterns" : true, 38 | "NoLeadingUnderscores" : false, 39 | "NoParensAroundConditions" : true, 40 | "NoVoidReturnOnFunctionSignature" : true, 41 | "OneCasePerLine" : true, 42 | "OneVariableDeclarationPerLine" : true, 43 | "OnlyOneTrailingClosureArgument" : true, 44 | "OrderedImports" : true, 45 | "ReturnVoidInsteadOfEmptyTuple" : true, 46 | "UseEarlyExits" : false, 47 | "UseLetInEveryBoundCaseVariable" : true, 48 | "UseShorthandTypeNames" : true, 49 | "UseSingleLinePropertyGetter" : true, 50 | "UseSynthesizedInitializer" : true, 51 | "UseTripleSlashForDocumentationComments" : true, 52 | "UseWhereClausesInForLoops" : false, 53 | "ValidateDocumentationComments" : false 54 | }, 55 | "spacesAroundRangeFormationOperators" : false, 56 | "tabWidth" : 4, 57 | "version" : 1 58 | } 59 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributor Manual 2 | 3 | Turso welcomes contributions from the community. This manual provides guidelines for contributing to `libsql-swift` SDK. 4 | 5 | https://github.com/firstcontributions/first-contributions 6 | 7 | ## Prerequisites 8 | 9 | ## Development 10 | 11 | ## Running Tests 12 | -------------------------------------------------------------------------------- /Examples/Batch/README.md: -------------------------------------------------------------------------------- 1 | # Vector 2 | 3 | This example demonstrates how to use libSQL vector search with a local database. 4 | 5 | ## Running 6 | 7 | Execute the example: 8 | 9 | ```bash 10 | swift run Vector 11 | ``` 12 | 13 | This will setup a local SQLite database, insert some data, and then query the results using the vector similarity search function. 14 | -------------------------------------------------------------------------------- /Examples/Batch/main.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Libsql 3 | 4 | let db = try Database(":memory:") 5 | let conn = try db.connect() 6 | 7 | _ = try conn.executeBatch(""" 8 | DROP TABLE IF EXISTS users; 9 | CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT); 10 | INSERT INTO users VALUES (1, 'first@example.com'); 11 | INSERT INTO users VALUES (2, 'second@example.com'); 12 | INSERT INTO users VALUES (3, 'third@example.com'); 13 | 14 | """) 15 | 16 | for row in try conn.query("select * from users", [1]) { 17 | print(try row.getInt(0), try row.getString(1)) 18 | } 19 | -------------------------------------------------------------------------------- /Examples/Local/.gitignore: -------------------------------------------------------------------------------- 1 | local.db* 2 | -------------------------------------------------------------------------------- /Examples/Local/README.md: -------------------------------------------------------------------------------- 1 | # Local 2 | 3 | This example demonstrates how to use libSQL with a local SQLite file. 4 | 5 | ## Running 6 | 7 | Execute the example: 8 | 9 | ```bash 10 | swift run Local 11 | ``` 12 | 13 | This will setup a local SQLite database, insert some data, and then query the results. 14 | -------------------------------------------------------------------------------- /Examples/Local/main.swift: -------------------------------------------------------------------------------- 1 | import Libsql 2 | 3 | let db = try Database("./local.db") 4 | let conn = try db.connect() 5 | 6 | _ = try conn.executeBatch(""" 7 | DROP TABLE IF EXISTS users; 8 | CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT); 9 | INSERT INTO users VALUES (1, 'first@example.com'); 10 | INSERT INTO users VALUES (2, 'second@example.com'); 11 | INSERT INTO users VALUES (3, 'third@example.com'); 12 | 13 | """) 14 | 15 | for row in try conn.query("select * from users", [1]) { 16 | print(try row.getInt(0), try row.getString(1)) 17 | } 18 | -------------------------------------------------------------------------------- /Examples/Memory/README.md: -------------------------------------------------------------------------------- 1 | # Memory 2 | 3 | This example demonstrates how to use libsql with an in-memory SQLite database. 4 | 5 | ## Running 6 | 7 | Execute the example: 8 | 9 | ```bash 10 | swift run Memory 11 | ``` 12 | 13 | This will create an in-memory SQLite database, insert some data, and then query the results. 14 | -------------------------------------------------------------------------------- /Examples/Memory/main.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Libsql 3 | 4 | let db = try Database(":memory:") 5 | let conn = try db.connect() 6 | 7 | _ = try conn.executeBatch(""" 8 | DROP TABLE IF EXISTS users; 9 | CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT); 10 | INSERT INTO users VALUES (1, 'first@example.com'); 11 | INSERT INTO users VALUES (2, 'second@example.com'); 12 | INSERT INTO users VALUES (3, 'third@example.com'); 13 | 14 | """) 15 | 16 | for row in try conn.query("select * from users", [1]) { 17 | print(try row.getInt(0), try row.getString(1)) 18 | } 19 | -------------------------------------------------------------------------------- /Examples/Query/main.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Libsql 3 | 4 | let db = try Database(":memory:") 5 | let conn = try db.connect() 6 | 7 | _ = try conn.executeBatch(""" 8 | create table users(id integer primary key autoincrement, name text); 9 | insert into users (name) values ('Iku Turso'); 10 | """) 11 | 12 | let forenames = ["John", "Mary", "Alice", "Mark"] 13 | let surnames = ["Doe", "Smith", "Jones", "Taylor"] 14 | 15 | for forename in forenames { 16 | for surname in surnames { 17 | _ = try conn.execute( 18 | "insert into users (name) values (?)", 19 | ["\(forename) \(surname)"] 20 | ); 21 | } 22 | } 23 | 24 | for row in try conn.query("select * from users", [1]) { 25 | print(try row.getInt(0), try row.getString(1)) 26 | } 27 | -------------------------------------------------------------------------------- /Examples/Remote/.gitignore: -------------------------------------------------------------------------------- 1 | local.db* 2 | -------------------------------------------------------------------------------- /Examples/Remote/README.md: -------------------------------------------------------------------------------- 1 | # Remote 2 | 3 | This example demonstrates how to use libSQL with a remote database. 4 | 5 | ## Running 6 | 7 | Execute the example: 8 | 9 | ```bash 10 | TURSO_DATABASE_URL="..." TURSO_AUTH_TOKEN="..." swift run Remote 11 | ``` 12 | 13 | This will connect to a remote SQLite database, insert some data, and then query the results. 14 | -------------------------------------------------------------------------------- /Examples/Remote/main.swift: -------------------------------------------------------------------------------- 1 | import Libsql 2 | import Foundation 3 | 4 | let db = try Database( 5 | path: "./local.db", 6 | url: ProcessInfo.processInfo.environment["TURSO_DATABASE_URL"] ?? "", 7 | authToken: ProcessInfo.processInfo.environment["TURSO_AUTH_TOKEN"] ?? "" 8 | ) 9 | let conn = try db.connect() 10 | 11 | _ = try conn.executeBatch(""" 12 | DROP TABLE IF EXISTS users; 13 | CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT); 14 | INSERT INTO users VALUES (1, 'first@example.com'); 15 | INSERT INTO users VALUES (2, 'second@example.com'); 16 | INSERT INTO users VALUES (3, 'third@example.com'); 17 | 18 | """) 19 | 20 | for row in try conn.query("select * from users", [1]) { 21 | print(try row.getInt(0), try row.getString(1)) 22 | } 23 | -------------------------------------------------------------------------------- /Examples/Sync/.gitignore: -------------------------------------------------------------------------------- 1 | local.db* 2 | -------------------------------------------------------------------------------- /Examples/Sync/README.md: -------------------------------------------------------------------------------- 1 | # Sync 2 | 3 | This example demonstrates how to use libSQL with a synced database (local file synced with a remote database). 4 | 5 | ## Running 6 | 7 | Execute the example: 8 | 9 | ```bash 10 | TURSO_DATABASE_URL="..." TURSO_AUTH_TOKEN="..." swift run Sync 11 | ``` 12 | 13 | This will connect to a remote SQLite database, insert some data, and then query the results. 14 | -------------------------------------------------------------------------------- /Examples/Sync/main.swift: -------------------------------------------------------------------------------- 1 | import Libsql 2 | import Foundation 3 | 4 | let db = try Database( 5 | path: "./local.db", 6 | url: ProcessInfo.processInfo.environment["TURSO_DATABASE_URL"] ?? "", 7 | authToken: ProcessInfo.processInfo.environment["TURSO_AUTH_TOKEN"] ?? "", 8 | syncInterval: 1000 9 | 10 | ) 11 | let conn = try db.connect() 12 | 13 | _ = try conn.executeBatch(""" 14 | DROP TABLE IF EXISTS users; 15 | CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT); 16 | INSERT INTO users VALUES (1, 'first@example.com'); 17 | INSERT INTO users VALUES (2, 'second@example.com'); 18 | INSERT INTO users VALUES (3, 'third@example.com'); 19 | 20 | """) 21 | 22 | for row in try conn.query("select * from users", [1]) { 23 | print(try row.getInt(0), try row.getString(1)) 24 | } 25 | -------------------------------------------------------------------------------- /Examples/Transaction/main.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Libsql 3 | 4 | let db = try Database(":memory:") 5 | let conn = try db.connect() 6 | 7 | _ = try conn.executeBatch(""" 8 | create table users(id integer primary key autoincrement, name text); 9 | insert into users (name) values ('Iku Turso'); 10 | """) 11 | 12 | let tx = try conn.transaction(); 13 | 14 | let forenames = ["John", "Mary", "Alice", "Mark"] 15 | let surnames = ["Doe", "Smith", "Jones", "Taylor"] 16 | 17 | for forename in forenames { 18 | for surname in surnames { 19 | _ = try tx.execute( 20 | "insert into users (name) values (?)", 21 | ["\(forename) \(surname)"] 22 | ); 23 | } 24 | } 25 | 26 | tx.rollback() // Discards all inserts 27 | 28 | 29 | // Only returns "1 Iku turso", since the transaction was rollbacked. 30 | for row in try conn.query("select * from users", [1]) { 31 | print(try row.getInt(0), try row.getString(1)) 32 | } 33 | -------------------------------------------------------------------------------- /Examples/Transactions/.gitignore: -------------------------------------------------------------------------------- 1 | local.db* 2 | -------------------------------------------------------------------------------- /Examples/Transactions/README.md: -------------------------------------------------------------------------------- 1 | # Transactions 2 | 3 | This example demonstrates how to use transactions with libSQL. 4 | 5 | ## Running 6 | 7 | Execute the example: 8 | 9 | ```bash 10 | swift run Transactions 11 | ``` 12 | 13 | This example will: 14 | 15 | 1. Create a new table called `users`. 16 | 2. Start a transaction. 17 | 3. Insert multiple users within the transaction. 18 | 4. Demonstrate how to rollback a transaction. 19 | 5. Start another transaction. 20 | 6. Insert more users and commit the transaction. 21 | 7. Query and display the final state of the `users` table. 22 | 23 | -------------------------------------------------------------------------------- /Examples/Transactions/main.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Libsql 3 | 4 | let db = try Database(":memory:") 5 | let conn = try db.connect() 6 | 7 | _ = try conn.executeBatch(""" 8 | create table users(id integer primary key autoincrement, name text); 9 | insert into users (name) values ('First Iku Turso'); 10 | """) 11 | 12 | let tx = try conn.transaction(); 13 | 14 | let fullNames = ["John Doe", "Mary Smith", "Alice Jones", "Mark Taylor"] 15 | 16 | for fullName in fullNames { 17 | _ = try tx.execute( 18 | "insert into users (name) values (?)", 19 | [fullName] 20 | ); 21 | } 22 | 23 | tx.rollback() // Discards all inserts 24 | 25 | _ = try conn.execute("insert into users (name) values (?)", ["Second Iku Turso"]) 26 | 27 | // Only returns "1 Iku turso", since the transaction was rollbacked. 28 | for row in try conn.query("select * from users", [1]) { 29 | print(try row.getInt(0), try row.getString(1)) 30 | } 31 | -------------------------------------------------------------------------------- /Examples/Vector/README.md: -------------------------------------------------------------------------------- 1 | # Remote 2 | 3 | This example demonstrates how to use libSQL with a remote database. 4 | 5 | ## Running 6 | 7 | Execute the example: 8 | 9 | ```bash 10 | TURSO_DATABASE_URL="..." TURSO_AUTH_TOKEN="..." swift run Remote 11 | ``` 12 | 13 | This will connect to a remote SQLite database, insert some data, and then query the results. 14 | -------------------------------------------------------------------------------- /Examples/Vector/main.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Libsql 3 | 4 | let db = try Database(":memory:") 5 | let conn = try db.connect() 6 | 7 | try conn.executeBatch(""" 8 | DROP TABLE IF EXISTS movies; 9 | CREATE TABLE movies (title TEXT, year INT, embedding F32_BLOB(3)); 10 | CREATE INDEX movies_idx ON movies (libsql_vector_idx(embedding)); 11 | INSERT INTO movies (title, year, embedding) VALUES 12 | ('Napoleon', 2023, vector32('[1,2,3]')), 13 | ('Black Hawk Down', 2001, vector32('[10,11,12]')), 14 | ('Gladiator', 2000, vector32('[7,8,9]')), 15 | ('Blade Runner', 1982, vector32('[4,5,6]')); 16 | """) 17 | 18 | let rows = try conn.query(""" 19 | SELECT title, year 20 | FROM vector_top_k('movies_idx', '[4,5,6]', 3) 21 | JOIN movies ON movies.rowid = id 22 | """) 23 | 24 | for row in rows { 25 | print(try row.get(0), try row.get(1)) 26 | } 27 | -------------------------------------------------------------------------------- /Examples/local.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tursodatabase/libsql-swift/d6c16af9a8bf5a096c5eaca1fd4dac845242ef87/Examples/local.db -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Turso (libSQL) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.9.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import Foundation 5 | import PackageDescription 6 | 7 | var package = Package( 8 | name: "Libsql", 9 | platforms: [ .iOS(.v12), .macOS(.v10_13) ], 10 | products: [ 11 | .library(name: "Libsql", targets: ["Libsql"]), 12 | 13 | // Examples 14 | .executable(name: "Query", targets: ["Query"]), 15 | .executable(name: "Transaction", targets: ["Transaction"]), 16 | .executable(name: "Batch", targets: ["Batch"]), 17 | ], 18 | targets: [ 19 | .target(name: "Libsql", dependencies: ["CLibsql"]), 20 | .binaryTarget(name: "CLibsql", path: "Sources/CLibsql/CLibsql.xcframework"), 21 | .testTarget(name: "LibsqlTests", dependencies: ["Libsql"]), 22 | 23 | // Examples 24 | .executableTarget( 25 | name: "Query", 26 | dependencies: ["Libsql"], 27 | path: "Examples/Query" 28 | ), 29 | .executableTarget( 30 | name: "Transaction", 31 | dependencies: ["Libsql"], 32 | path: "Examples/Transaction" 33 | ), 34 | .executableTarget( 35 | name: "Batch", 36 | dependencies: ["Libsql"], 37 | path: "Examples/Batch", 38 | exclude: ["README.md"] 39 | ), 40 | .executableTarget( 41 | name: "Local", 42 | dependencies: ["Libsql"], 43 | path: "Examples/Local", 44 | exclude: ["README.md", "local.db"] 45 | ), 46 | .executableTarget( 47 | name: "Memory", 48 | dependencies: ["Libsql"], 49 | path: "Examples/Memory", 50 | exclude: ["README.md"] 51 | ), 52 | .executableTarget( 53 | name: "Remote", 54 | dependencies: ["Libsql"], 55 | path: "Examples/Remote", 56 | exclude: ["README.md", "local.db", "local.db-shm", "local.db-client_wal_index", "local.db-wal"] 57 | ), 58 | .executableTarget( 59 | name: "Sync", 60 | dependencies: ["Libsql"], 61 | path: "Examples/Sync", 62 | exclude: ["README.md", "local.db", "local.db-shm", "local.db-client_wal_index", "local.db-wal"] 63 | ), 64 | .executableTarget( 65 | name: "Transactions", 66 | dependencies: ["Libsql"], 67 | path: "Examples/Transactions", 68 | exclude: ["README.md", "local.db"] 69 | ), 70 | .executableTarget( 71 | name: "Vector", 72 | dependencies: ["Libsql"], 73 | path: "Examples/Vector", 74 | exclude: ["README.md"] 75 | ), 76 | ] 77 | ) 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | libSQL Swift 5 | 6 | 7 |

libSQL Swift

8 |

9 | 10 |

11 | Databases for Swift multi-tenant AI Apps. 12 |

13 | 14 |

15 | Turso · 16 | Docs · 17 | Quickstart · 18 | SDK Reference · 19 | Blog & Tutorials 20 |

21 | 22 |

23 | 24 | 25 | MIT License 26 | 27 | 28 | 29 | 30 | Discord 31 | 32 | 33 | 34 | 35 | Contributors 36 | 37 | 38 | 39 | 40 | Examples 41 | 42 | 43 |

44 | 45 | ## Features 46 | 47 | - 🔌 Works offline with [Embedded Replicas](https://docs.turso.tech/features/embedded-replicas/introduction) 48 | - 🌎 Works with remote Turso databases 49 | - ✨ Works with Turso [AI & Vector Search](https://docs.turso.tech/features/ai-and-embeddings) 50 | - 📱 Works with macOS, iPadOS, tvOS, watchOS & iOS 51 | 52 | > [!WARNING] 53 | > This SDK is currently in technical preview. Join us in Discord to report any issues. 54 | 55 | ## Install 56 | 57 | Add `tursodatabase/libsql-swift` to your SwiftPM dependencies: 58 | 59 | ```swift 60 | import PackageDescription 61 | 62 | let package = Package( 63 | // ... 64 | dependencies: [ 65 | .package(url: "https://github.com/tursodatabase/libsql-swift", from: "0.1.1"), 66 | ], 67 | // ... 68 | ) 69 | ``` 70 | 71 | ## Quickstart 72 | 73 | The example below uses Embedded Replicas and syncs data every 1000ms from Turso. 74 | 75 | ```swift 76 | import Libsql 77 | 78 | let db = try Database( 79 | path: "./local.db", 80 | url: "TURSO_DATABASE_URL", 81 | authToken: "TURSO_AUTH_TOKEN", 82 | syncInterval: 1000 83 | ) 84 | 85 | let conn = try db.connect() 86 | 87 | try conn.execute(" 88 | CREATE TABLE IF NOT EXISTS users ( 89 | id INTEGER PRIMARY KEY AUTOINCREMENT, 90 | name TEXT 91 | ); 92 | INSERT INTO users (name) VALUES ('Iku'); 93 | ") 94 | 95 | try conn.query("SELECT * FROM users WHERE id = ?", 1) 96 | ``` 97 | 98 | ## Documentation 99 | 100 | Visit our [official documentation](https://docs.turso.tech/sdk/swift). 101 | 102 | ## Support 103 | 104 | Join us [on Discord](https://tur.so/discord-swift) to get help using this SDK. Report security issues [via email](mailto:security@turso.tech). 105 | 106 | ## Contributors 107 | 108 | See the [contributing guide](CONTRIBUTING.md) to learn how to get involved. 109 | 110 | ![Contributors](https://contrib.nn.ci/api?repo=tursodatabase/libsql-swift) 111 | 112 | 113 | 114 | good first issue 115 | 116 | 117 | -------------------------------------------------------------------------------- /Sources/CLibsql/CLibsql.xcframework/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AvailableLibraries 6 | 7 | 8 | BinaryPath 9 | liblibsql.a 10 | HeadersPath 11 | Headers 12 | LibraryIdentifier 13 | ios-arm64_x86_64-simulator 14 | LibraryPath 15 | liblibsql.a 16 | SupportedArchitectures 17 | 18 | arm64 19 | x86_64 20 | 21 | SupportedPlatform 22 | ios 23 | SupportedPlatformVariant 24 | simulator 25 | 26 | 27 | BinaryPath 28 | liblibsql.a 29 | HeadersPath 30 | Headers 31 | LibraryIdentifier 32 | ios-arm64 33 | LibraryPath 34 | liblibsql.a 35 | SupportedArchitectures 36 | 37 | arm64 38 | 39 | SupportedPlatform 40 | ios 41 | 42 | 43 | BinaryPath 44 | liblibsql.a 45 | HeadersPath 46 | Headers 47 | LibraryIdentifier 48 | macos-arm64_x86_64 49 | LibraryPath 50 | liblibsql.a 51 | SupportedArchitectures 52 | 53 | arm64 54 | x86_64 55 | 56 | SupportedPlatform 57 | macos 58 | 59 | 60 | CFBundlePackageType 61 | XFWK 62 | XCFrameworkFormatVersion 63 | 1.0 64 | 65 | 66 | -------------------------------------------------------------------------------- /Sources/CLibsql/CLibsql.xcframework/ios-arm64/Headers/libsql.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBSQL_H 2 | #define LIBSQL_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | typedef struct libsql_error_t libsql_error_t; 9 | 10 | typedef enum { 11 | LIBSQL_CYPHER_DEFAULT = 0, 12 | LIBSQL_CYPHER_AES256, 13 | } libsql_cypher_t; 14 | 15 | typedef enum { 16 | LIBSQL_TYPE_INTEGER = 1, 17 | LIBSQL_TYPE_REAL = 2, 18 | LIBSQL_TYPE_TEXT = 3, 19 | LIBSQL_TYPE_BLOB = 4, 20 | LIBSQL_TYPE_NULL = 5, 21 | } libsql_type_t; 22 | 23 | typedef enum { 24 | LIBSQL_TRACING_LEVEL_ERROR = 1, 25 | LIBSQL_TRACING_LEVEL_WARN, 26 | LIBSQL_TRACING_LEVEL_INFO, 27 | LIBSQL_TRACING_LEVEL_DEBUG, 28 | LIBSQL_TRACING_LEVEL_TRACE, 29 | } libsql_tracing_level_t; 30 | 31 | typedef struct { 32 | const char *message; 33 | const char *target; 34 | const char *file; 35 | uint64_t timestamp; 36 | size_t line; 37 | libsql_tracing_level_t level; 38 | } libsql_log_t; 39 | 40 | typedef struct { 41 | libsql_error_t *err; 42 | void *inner; 43 | } libsql_database_t; 44 | 45 | typedef struct { 46 | libsql_error_t *err; 47 | void *inner; 48 | } libsql_connection_t; 49 | 50 | typedef struct { 51 | libsql_error_t *err; 52 | void *inner; 53 | } libsql_statement_t; 54 | 55 | typedef struct { 56 | libsql_error_t *err; 57 | void *inner; 58 | } libsql_transaction_t; 59 | 60 | typedef struct { 61 | libsql_error_t *err; 62 | void *inner; 63 | } libsql_rows_t; 64 | 65 | typedef struct { 66 | libsql_error_t *err; 67 | void *inner; 68 | } libsql_row_t; 69 | 70 | typedef struct { 71 | libsql_error_t *err; 72 | } libsql_batch_t; 73 | 74 | typedef struct { 75 | const void *ptr; 76 | size_t len; 77 | } libsql_slice_t; 78 | 79 | typedef union { 80 | int64_t integer; 81 | double real; 82 | libsql_slice_t text; 83 | libsql_slice_t blob; 84 | } libsql_value_union_t; 85 | 86 | typedef struct { 87 | libsql_value_union_t value; 88 | libsql_type_t type; 89 | } libsql_value_t; 90 | 91 | typedef struct { 92 | libsql_error_t *err; 93 | libsql_value_t ok; 94 | } libsql_result_value_t; 95 | 96 | typedef struct { 97 | libsql_error_t *err; 98 | uint64_t frame_no; 99 | uint64_t frames_synced; 100 | } libsql_sync_t; 101 | 102 | typedef struct { 103 | libsql_error_t *err; 104 | } libsql_bind_t; 105 | 106 | typedef struct { 107 | libsql_error_t *err; 108 | uint64_t rows_changed; 109 | } libsql_execute_t; 110 | 111 | typedef struct { 112 | libsql_error_t *err; 113 | int64_t last_inserted_rowid; 114 | uint64_t total_changes; 115 | } libsql_connection_info_t; 116 | 117 | /** 118 | * Database description. 119 | */ 120 | typedef struct { 121 | /** The url to the primary database */ 122 | const char *url; 123 | /** Path to the database file or `:memory:` */ 124 | const char *path; 125 | /** Auth token to access the primary */ 126 | const char *auth_token; 127 | /** Encryption key to encrypt and decrypt the database in `path` */ 128 | const char *encryption_key; 129 | /** Interval to periodicaly sync with primary */ 130 | uint64_t sync_interval; 131 | /** Cypher to be used with `encryption_key` */ 132 | libsql_cypher_t cypher; 133 | /** If set, disable `read_your_writes`. To mantain consistency. */ 134 | bool disable_read_your_writes; 135 | /** Enable Webpki connector */ 136 | bool webpki; 137 | /** Offline writes */ 138 | bool synced; 139 | /** Safety assert */ 140 | bool disable_safety_assert; 141 | } libsql_database_desc_t; 142 | 143 | typedef struct { 144 | void (*logger)(libsql_log_t log); 145 | const char *version; 146 | } libsql_config_t; 147 | 148 | /** Setup some global info */ 149 | const libsql_error_t *libsql_setup(libsql_config_t config); 150 | 151 | /** Get the error message from a error */ 152 | const char *libsql_error_message(libsql_error_t *self); 153 | 154 | /** Create or open a database */ 155 | libsql_database_t libsql_database_init(libsql_database_desc_t desc); 156 | 157 | /** Sync frames with the primary */ 158 | libsql_sync_t libsql_database_sync(libsql_database_t self); 159 | 160 | /** Connect with the database */ 161 | libsql_connection_t libsql_database_connect(libsql_database_t self); 162 | 163 | /** Begin a transaction */ 164 | libsql_transaction_t libsql_connection_transaction(libsql_connection_t self); 165 | 166 | /** Send a batch statement in a connection */ 167 | libsql_batch_t 168 | libsql_connection_batch(libsql_connection_t self, const char *sql); 169 | 170 | /** Send a batch statement in a connection */ 171 | libsql_connection_info_t libsql_connection_info(libsql_connection_t self); 172 | 173 | /** Send a batch statement in a transaction */ 174 | libsql_batch_t 175 | libsql_transaction_batch(libsql_transaction_t self, const char *sql); 176 | 177 | /** Prepare a statement in a connection */ 178 | libsql_statement_t 179 | libsql_connection_prepare(libsql_connection_t self, const char *sql); 180 | /** Prepare a statement in a transaction */ 181 | libsql_statement_t 182 | libsql_transaction_prepare(libsql_transaction_t self, const char *sql); 183 | 184 | /** Execute a statement */ 185 | libsql_execute_t libsql_statement_execute(libsql_statement_t self); 186 | /** Query a statement */ 187 | libsql_rows_t libsql_statement_query(libsql_statement_t self); 188 | /** Reset a statement */ 189 | void libsql_statement_reset(libsql_statement_t self); 190 | /** Column count */ 191 | size_t libsql_statement_column_count(libsql_statement_t self); 192 | 193 | /** Get the next row from rows */ 194 | libsql_row_t libsql_rows_next(libsql_rows_t self); 195 | /** Get the column name at the index */ 196 | libsql_slice_t libsql_rows_column_name(libsql_rows_t self, int32_t index); 197 | /** Get rows column count */ 198 | int32_t libsql_rows_column_count(libsql_rows_t self); 199 | 200 | /** Get the value at the the index */ 201 | libsql_result_value_t libsql_row_value(libsql_row_t self, int32_t index); 202 | /** Get the column name at the the index */ 203 | libsql_slice_t libsql_row_name(libsql_row_t self, int32_t index); 204 | /** Get row column count */ 205 | int32_t libsql_row_length(libsql_row_t self); 206 | /** Check if the row is empty, indicating the end of `libsql_rows_next` */ 207 | bool libsql_row_empty(libsql_row_t self); 208 | 209 | /** Bind a named argument to a statement */ 210 | libsql_bind_t libsql_statement_bind_named( 211 | libsql_statement_t self, 212 | const char *name, 213 | libsql_value_t value 214 | ); 215 | /** Bind a positional argument to a statement */ 216 | libsql_bind_t 217 | libsql_statement_bind_value(libsql_statement_t self, libsql_value_t value); 218 | 219 | /** Create a libsql integer value */ 220 | libsql_value_t libsql_integer(int64_t integer); 221 | /** Create a libsql real value */ 222 | libsql_value_t libsql_real(double real); 223 | /** Create a libsql text value */ 224 | libsql_value_t libsql_text(const char *ptr, size_t len); 225 | /** Create a libsql blob value */ 226 | libsql_value_t libsql_blob(const uint8_t *ptr, size_t len); 227 | /** Create a libsql null value */ 228 | libsql_value_t libsql_null(); 229 | 230 | /** Deallocate and close a error */ 231 | void libsql_error_deinit(libsql_error_t *self); 232 | /** Deallocate and close a database */ 233 | void libsql_database_deinit(libsql_database_t self); 234 | /** Deallocate and close a connection */ 235 | void libsql_connection_deinit(libsql_connection_t self); 236 | /** Deallocate and close a statement */ 237 | void libsql_statement_deinit(libsql_statement_t self); 238 | /** Deallocate and commit a transaction (transaction becomes invalid) */ 239 | void libsql_transaction_commit(libsql_transaction_t self); 240 | /** Deallocate and rollback a transaction (transaction becomes invalid) */ 241 | void libsql_transaction_rollback(libsql_transaction_t self); 242 | /** Deallocate and close rows */ 243 | void libsql_rows_deinit(libsql_rows_t self); 244 | /** Deallocate and close a row */ 245 | void libsql_row_deinit(libsql_row_t self); 246 | /** Deallocate a slice */ 247 | void libsql_slice_deinit(libsql_slice_t value); 248 | 249 | #endif /* LIBSQL_H */ 250 | -------------------------------------------------------------------------------- /Sources/CLibsql/CLibsql.xcframework/ios-arm64/Headers/module.modulemap: -------------------------------------------------------------------------------- 1 | module CLibsql { 2 | header "libsql.h" 3 | export * 4 | } 5 | -------------------------------------------------------------------------------- /Sources/CLibsql/CLibsql.xcframework/ios-arm64/liblibsql.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tursodatabase/libsql-swift/d6c16af9a8bf5a096c5eaca1fd4dac845242ef87/Sources/CLibsql/CLibsql.xcframework/ios-arm64/liblibsql.a -------------------------------------------------------------------------------- /Sources/CLibsql/CLibsql.xcframework/ios-arm64_x86_64-simulator/Headers/libsql.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBSQL_H 2 | #define LIBSQL_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | typedef struct libsql_error_t libsql_error_t; 9 | 10 | typedef enum { 11 | LIBSQL_CYPHER_DEFAULT = 0, 12 | LIBSQL_CYPHER_AES256, 13 | } libsql_cypher_t; 14 | 15 | typedef enum { 16 | LIBSQL_TYPE_INTEGER = 1, 17 | LIBSQL_TYPE_REAL = 2, 18 | LIBSQL_TYPE_TEXT = 3, 19 | LIBSQL_TYPE_BLOB = 4, 20 | LIBSQL_TYPE_NULL = 5, 21 | } libsql_type_t; 22 | 23 | typedef enum { 24 | LIBSQL_TRACING_LEVEL_ERROR = 1, 25 | LIBSQL_TRACING_LEVEL_WARN, 26 | LIBSQL_TRACING_LEVEL_INFO, 27 | LIBSQL_TRACING_LEVEL_DEBUG, 28 | LIBSQL_TRACING_LEVEL_TRACE, 29 | } libsql_tracing_level_t; 30 | 31 | typedef struct { 32 | const char *message; 33 | const char *target; 34 | const char *file; 35 | uint64_t timestamp; 36 | size_t line; 37 | libsql_tracing_level_t level; 38 | } libsql_log_t; 39 | 40 | typedef struct { 41 | libsql_error_t *err; 42 | void *inner; 43 | } libsql_database_t; 44 | 45 | typedef struct { 46 | libsql_error_t *err; 47 | void *inner; 48 | } libsql_connection_t; 49 | 50 | typedef struct { 51 | libsql_error_t *err; 52 | void *inner; 53 | } libsql_statement_t; 54 | 55 | typedef struct { 56 | libsql_error_t *err; 57 | void *inner; 58 | } libsql_transaction_t; 59 | 60 | typedef struct { 61 | libsql_error_t *err; 62 | void *inner; 63 | } libsql_rows_t; 64 | 65 | typedef struct { 66 | libsql_error_t *err; 67 | void *inner; 68 | } libsql_row_t; 69 | 70 | typedef struct { 71 | libsql_error_t *err; 72 | } libsql_batch_t; 73 | 74 | typedef struct { 75 | const void *ptr; 76 | size_t len; 77 | } libsql_slice_t; 78 | 79 | typedef union { 80 | int64_t integer; 81 | double real; 82 | libsql_slice_t text; 83 | libsql_slice_t blob; 84 | } libsql_value_union_t; 85 | 86 | typedef struct { 87 | libsql_value_union_t value; 88 | libsql_type_t type; 89 | } libsql_value_t; 90 | 91 | typedef struct { 92 | libsql_error_t *err; 93 | libsql_value_t ok; 94 | } libsql_result_value_t; 95 | 96 | typedef struct { 97 | libsql_error_t *err; 98 | uint64_t frame_no; 99 | uint64_t frames_synced; 100 | } libsql_sync_t; 101 | 102 | typedef struct { 103 | libsql_error_t *err; 104 | } libsql_bind_t; 105 | 106 | typedef struct { 107 | libsql_error_t *err; 108 | uint64_t rows_changed; 109 | } libsql_execute_t; 110 | 111 | typedef struct { 112 | libsql_error_t *err; 113 | int64_t last_inserted_rowid; 114 | uint64_t total_changes; 115 | } libsql_connection_info_t; 116 | 117 | /** 118 | * Database description. 119 | */ 120 | typedef struct { 121 | /** The url to the primary database */ 122 | const char *url; 123 | /** Path to the database file or `:memory:` */ 124 | const char *path; 125 | /** Auth token to access the primary */ 126 | const char *auth_token; 127 | /** Encryption key to encrypt and decrypt the database in `path` */ 128 | const char *encryption_key; 129 | /** Interval to periodicaly sync with primary */ 130 | uint64_t sync_interval; 131 | /** Cypher to be used with `encryption_key` */ 132 | libsql_cypher_t cypher; 133 | /** If set, disable `read_your_writes`. To mantain consistency. */ 134 | bool disable_read_your_writes; 135 | /** Enable Webpki connector */ 136 | bool webpki; 137 | /** Offline writes */ 138 | bool synced; 139 | /** Safety assert */ 140 | bool disable_safety_assert; 141 | } libsql_database_desc_t; 142 | 143 | typedef struct { 144 | void (*logger)(libsql_log_t log); 145 | const char *version; 146 | } libsql_config_t; 147 | 148 | /** Setup some global info */ 149 | const libsql_error_t *libsql_setup(libsql_config_t config); 150 | 151 | /** Get the error message from a error */ 152 | const char *libsql_error_message(libsql_error_t *self); 153 | 154 | /** Create or open a database */ 155 | libsql_database_t libsql_database_init(libsql_database_desc_t desc); 156 | 157 | /** Sync frames with the primary */ 158 | libsql_sync_t libsql_database_sync(libsql_database_t self); 159 | 160 | /** Connect with the database */ 161 | libsql_connection_t libsql_database_connect(libsql_database_t self); 162 | 163 | /** Begin a transaction */ 164 | libsql_transaction_t libsql_connection_transaction(libsql_connection_t self); 165 | 166 | /** Send a batch statement in a connection */ 167 | libsql_batch_t 168 | libsql_connection_batch(libsql_connection_t self, const char *sql); 169 | 170 | /** Send a batch statement in a connection */ 171 | libsql_connection_info_t libsql_connection_info(libsql_connection_t self); 172 | 173 | /** Send a batch statement in a transaction */ 174 | libsql_batch_t 175 | libsql_transaction_batch(libsql_transaction_t self, const char *sql); 176 | 177 | /** Prepare a statement in a connection */ 178 | libsql_statement_t 179 | libsql_connection_prepare(libsql_connection_t self, const char *sql); 180 | /** Prepare a statement in a transaction */ 181 | libsql_statement_t 182 | libsql_transaction_prepare(libsql_transaction_t self, const char *sql); 183 | 184 | /** Execute a statement */ 185 | libsql_execute_t libsql_statement_execute(libsql_statement_t self); 186 | /** Query a statement */ 187 | libsql_rows_t libsql_statement_query(libsql_statement_t self); 188 | /** Reset a statement */ 189 | void libsql_statement_reset(libsql_statement_t self); 190 | /** Column count */ 191 | size_t libsql_statement_column_count(libsql_statement_t self); 192 | 193 | /** Get the next row from rows */ 194 | libsql_row_t libsql_rows_next(libsql_rows_t self); 195 | /** Get the column name at the index */ 196 | libsql_slice_t libsql_rows_column_name(libsql_rows_t self, int32_t index); 197 | /** Get rows column count */ 198 | int32_t libsql_rows_column_count(libsql_rows_t self); 199 | 200 | /** Get the value at the the index */ 201 | libsql_result_value_t libsql_row_value(libsql_row_t self, int32_t index); 202 | /** Get the column name at the the index */ 203 | libsql_slice_t libsql_row_name(libsql_row_t self, int32_t index); 204 | /** Get row column count */ 205 | int32_t libsql_row_length(libsql_row_t self); 206 | /** Check if the row is empty, indicating the end of `libsql_rows_next` */ 207 | bool libsql_row_empty(libsql_row_t self); 208 | 209 | /** Bind a named argument to a statement */ 210 | libsql_bind_t libsql_statement_bind_named( 211 | libsql_statement_t self, 212 | const char *name, 213 | libsql_value_t value 214 | ); 215 | /** Bind a positional argument to a statement */ 216 | libsql_bind_t 217 | libsql_statement_bind_value(libsql_statement_t self, libsql_value_t value); 218 | 219 | /** Create a libsql integer value */ 220 | libsql_value_t libsql_integer(int64_t integer); 221 | /** Create a libsql real value */ 222 | libsql_value_t libsql_real(double real); 223 | /** Create a libsql text value */ 224 | libsql_value_t libsql_text(const char *ptr, size_t len); 225 | /** Create a libsql blob value */ 226 | libsql_value_t libsql_blob(const uint8_t *ptr, size_t len); 227 | /** Create a libsql null value */ 228 | libsql_value_t libsql_null(); 229 | 230 | /** Deallocate and close a error */ 231 | void libsql_error_deinit(libsql_error_t *self); 232 | /** Deallocate and close a database */ 233 | void libsql_database_deinit(libsql_database_t self); 234 | /** Deallocate and close a connection */ 235 | void libsql_connection_deinit(libsql_connection_t self); 236 | /** Deallocate and close a statement */ 237 | void libsql_statement_deinit(libsql_statement_t self); 238 | /** Deallocate and commit a transaction (transaction becomes invalid) */ 239 | void libsql_transaction_commit(libsql_transaction_t self); 240 | /** Deallocate and rollback a transaction (transaction becomes invalid) */ 241 | void libsql_transaction_rollback(libsql_transaction_t self); 242 | /** Deallocate and close rows */ 243 | void libsql_rows_deinit(libsql_rows_t self); 244 | /** Deallocate and close a row */ 245 | void libsql_row_deinit(libsql_row_t self); 246 | /** Deallocate a slice */ 247 | void libsql_slice_deinit(libsql_slice_t value); 248 | 249 | #endif /* LIBSQL_H */ 250 | -------------------------------------------------------------------------------- /Sources/CLibsql/CLibsql.xcframework/ios-arm64_x86_64-simulator/Headers/module.modulemap: -------------------------------------------------------------------------------- 1 | module CLibsql { 2 | header "libsql.h" 3 | export * 4 | } 5 | -------------------------------------------------------------------------------- /Sources/CLibsql/CLibsql.xcframework/ios-arm64_x86_64-simulator/liblibsql.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tursodatabase/libsql-swift/d6c16af9a8bf5a096c5eaca1fd4dac845242ef87/Sources/CLibsql/CLibsql.xcframework/ios-arm64_x86_64-simulator/liblibsql.a -------------------------------------------------------------------------------- /Sources/CLibsql/CLibsql.xcframework/macos-arm64_x86_64/Headers/libsql.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBSQL_H 2 | #define LIBSQL_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | typedef struct libsql_error_t libsql_error_t; 9 | 10 | typedef enum { 11 | LIBSQL_CYPHER_DEFAULT = 0, 12 | LIBSQL_CYPHER_AES256, 13 | } libsql_cypher_t; 14 | 15 | typedef enum { 16 | LIBSQL_TYPE_INTEGER = 1, 17 | LIBSQL_TYPE_REAL = 2, 18 | LIBSQL_TYPE_TEXT = 3, 19 | LIBSQL_TYPE_BLOB = 4, 20 | LIBSQL_TYPE_NULL = 5, 21 | } libsql_type_t; 22 | 23 | typedef enum { 24 | LIBSQL_TRACING_LEVEL_ERROR = 1, 25 | LIBSQL_TRACING_LEVEL_WARN, 26 | LIBSQL_TRACING_LEVEL_INFO, 27 | LIBSQL_TRACING_LEVEL_DEBUG, 28 | LIBSQL_TRACING_LEVEL_TRACE, 29 | } libsql_tracing_level_t; 30 | 31 | typedef struct { 32 | const char *message; 33 | const char *target; 34 | const char *file; 35 | uint64_t timestamp; 36 | size_t line; 37 | libsql_tracing_level_t level; 38 | } libsql_log_t; 39 | 40 | typedef struct { 41 | libsql_error_t *err; 42 | void *inner; 43 | } libsql_database_t; 44 | 45 | typedef struct { 46 | libsql_error_t *err; 47 | void *inner; 48 | } libsql_connection_t; 49 | 50 | typedef struct { 51 | libsql_error_t *err; 52 | void *inner; 53 | } libsql_statement_t; 54 | 55 | typedef struct { 56 | libsql_error_t *err; 57 | void *inner; 58 | } libsql_transaction_t; 59 | 60 | typedef struct { 61 | libsql_error_t *err; 62 | void *inner; 63 | } libsql_rows_t; 64 | 65 | typedef struct { 66 | libsql_error_t *err; 67 | void *inner; 68 | } libsql_row_t; 69 | 70 | typedef struct { 71 | libsql_error_t *err; 72 | } libsql_batch_t; 73 | 74 | typedef struct { 75 | const void *ptr; 76 | size_t len; 77 | } libsql_slice_t; 78 | 79 | typedef union { 80 | int64_t integer; 81 | double real; 82 | libsql_slice_t text; 83 | libsql_slice_t blob; 84 | } libsql_value_union_t; 85 | 86 | typedef struct { 87 | libsql_value_union_t value; 88 | libsql_type_t type; 89 | } libsql_value_t; 90 | 91 | typedef struct { 92 | libsql_error_t *err; 93 | libsql_value_t ok; 94 | } libsql_result_value_t; 95 | 96 | typedef struct { 97 | libsql_error_t *err; 98 | uint64_t frame_no; 99 | uint64_t frames_synced; 100 | } libsql_sync_t; 101 | 102 | typedef struct { 103 | libsql_error_t *err; 104 | } libsql_bind_t; 105 | 106 | typedef struct { 107 | libsql_error_t *err; 108 | uint64_t rows_changed; 109 | } libsql_execute_t; 110 | 111 | typedef struct { 112 | libsql_error_t *err; 113 | int64_t last_inserted_rowid; 114 | uint64_t total_changes; 115 | } libsql_connection_info_t; 116 | 117 | /** 118 | * Database description. 119 | */ 120 | typedef struct { 121 | /** The url to the primary database */ 122 | const char *url; 123 | /** Path to the database file or `:memory:` */ 124 | const char *path; 125 | /** Auth token to access the primary */ 126 | const char *auth_token; 127 | /** Encryption key to encrypt and decrypt the database in `path` */ 128 | const char *encryption_key; 129 | /** Interval to periodicaly sync with primary */ 130 | uint64_t sync_interval; 131 | /** Cypher to be used with `encryption_key` */ 132 | libsql_cypher_t cypher; 133 | /** If set, disable `read_your_writes`. To mantain consistency. */ 134 | bool disable_read_your_writes; 135 | /** Enable Webpki connector */ 136 | bool webpki; 137 | /** Offline writes */ 138 | bool synced; 139 | /** Safety assert */ 140 | bool disable_safety_assert; 141 | } libsql_database_desc_t; 142 | 143 | typedef struct { 144 | void (*logger)(libsql_log_t log); 145 | const char *version; 146 | } libsql_config_t; 147 | 148 | /** Setup some global info */ 149 | const libsql_error_t *libsql_setup(libsql_config_t config); 150 | 151 | /** Get the error message from a error */ 152 | const char *libsql_error_message(libsql_error_t *self); 153 | 154 | /** Create or open a database */ 155 | libsql_database_t libsql_database_init(libsql_database_desc_t desc); 156 | 157 | /** Sync frames with the primary */ 158 | libsql_sync_t libsql_database_sync(libsql_database_t self); 159 | 160 | /** Connect with the database */ 161 | libsql_connection_t libsql_database_connect(libsql_database_t self); 162 | 163 | /** Begin a transaction */ 164 | libsql_transaction_t libsql_connection_transaction(libsql_connection_t self); 165 | 166 | /** Send a batch statement in a connection */ 167 | libsql_batch_t 168 | libsql_connection_batch(libsql_connection_t self, const char *sql); 169 | 170 | /** Send a batch statement in a connection */ 171 | libsql_connection_info_t libsql_connection_info(libsql_connection_t self); 172 | 173 | /** Send a batch statement in a transaction */ 174 | libsql_batch_t 175 | libsql_transaction_batch(libsql_transaction_t self, const char *sql); 176 | 177 | /** Prepare a statement in a connection */ 178 | libsql_statement_t 179 | libsql_connection_prepare(libsql_connection_t self, const char *sql); 180 | /** Prepare a statement in a transaction */ 181 | libsql_statement_t 182 | libsql_transaction_prepare(libsql_transaction_t self, const char *sql); 183 | 184 | /** Execute a statement */ 185 | libsql_execute_t libsql_statement_execute(libsql_statement_t self); 186 | /** Query a statement */ 187 | libsql_rows_t libsql_statement_query(libsql_statement_t self); 188 | /** Reset a statement */ 189 | void libsql_statement_reset(libsql_statement_t self); 190 | /** Column count */ 191 | size_t libsql_statement_column_count(libsql_statement_t self); 192 | 193 | /** Get the next row from rows */ 194 | libsql_row_t libsql_rows_next(libsql_rows_t self); 195 | /** Get the column name at the index */ 196 | libsql_slice_t libsql_rows_column_name(libsql_rows_t self, int32_t index); 197 | /** Get rows column count */ 198 | int32_t libsql_rows_column_count(libsql_rows_t self); 199 | 200 | /** Get the value at the the index */ 201 | libsql_result_value_t libsql_row_value(libsql_row_t self, int32_t index); 202 | /** Get the column name at the the index */ 203 | libsql_slice_t libsql_row_name(libsql_row_t self, int32_t index); 204 | /** Get row column count */ 205 | int32_t libsql_row_length(libsql_row_t self); 206 | /** Check if the row is empty, indicating the end of `libsql_rows_next` */ 207 | bool libsql_row_empty(libsql_row_t self); 208 | 209 | /** Bind a named argument to a statement */ 210 | libsql_bind_t libsql_statement_bind_named( 211 | libsql_statement_t self, 212 | const char *name, 213 | libsql_value_t value 214 | ); 215 | /** Bind a positional argument to a statement */ 216 | libsql_bind_t 217 | libsql_statement_bind_value(libsql_statement_t self, libsql_value_t value); 218 | 219 | /** Create a libsql integer value */ 220 | libsql_value_t libsql_integer(int64_t integer); 221 | /** Create a libsql real value */ 222 | libsql_value_t libsql_real(double real); 223 | /** Create a libsql text value */ 224 | libsql_value_t libsql_text(const char *ptr, size_t len); 225 | /** Create a libsql blob value */ 226 | libsql_value_t libsql_blob(const uint8_t *ptr, size_t len); 227 | /** Create a libsql null value */ 228 | libsql_value_t libsql_null(); 229 | 230 | /** Deallocate and close a error */ 231 | void libsql_error_deinit(libsql_error_t *self); 232 | /** Deallocate and close a database */ 233 | void libsql_database_deinit(libsql_database_t self); 234 | /** Deallocate and close a connection */ 235 | void libsql_connection_deinit(libsql_connection_t self); 236 | /** Deallocate and close a statement */ 237 | void libsql_statement_deinit(libsql_statement_t self); 238 | /** Deallocate and commit a transaction (transaction becomes invalid) */ 239 | void libsql_transaction_commit(libsql_transaction_t self); 240 | /** Deallocate and rollback a transaction (transaction becomes invalid) */ 241 | void libsql_transaction_rollback(libsql_transaction_t self); 242 | /** Deallocate and close rows */ 243 | void libsql_rows_deinit(libsql_rows_t self); 244 | /** Deallocate and close a row */ 245 | void libsql_row_deinit(libsql_row_t self); 246 | /** Deallocate a slice */ 247 | void libsql_slice_deinit(libsql_slice_t value); 248 | 249 | #endif /* LIBSQL_H */ 250 | -------------------------------------------------------------------------------- /Sources/CLibsql/CLibsql.xcframework/macos-arm64_x86_64/Headers/module.modulemap: -------------------------------------------------------------------------------- 1 | module CLibsql { 2 | header "libsql.h" 3 | export * 4 | } 5 | -------------------------------------------------------------------------------- /Sources/CLibsql/CLibsql.xcframework/macos-arm64_x86_64/liblibsql.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tursodatabase/libsql-swift/d6c16af9a8bf5a096c5eaca1fd4dac845242ef87/Sources/CLibsql/CLibsql.xcframework/macos-arm64_x86_64/liblibsql.a -------------------------------------------------------------------------------- /Sources/CLibsql/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -xe +f 4 | 5 | cd libsql-c 6 | 7 | function build_ios() { 8 | iphone=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk 9 | iphonesimulator=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk 10 | 11 | CARGO_PROFILE_RELEASE_BUILD_OVERRIDE_DEBUG=true 12 | RUSTFLAGS="-C link-arg=-F$SDKROOT/System/Library/Frameworks" 13 | CFLAGS="-DHAVE_GETHOSTUUID=0" 14 | 15 | SDKROOT="$iphone" 16 | cargo build --target aarch64-apple-ios --release 17 | 18 | SDKROOT="$iphonesimulator" 19 | cargo build --target x86_64-apple-ios --release 20 | 21 | SDKROOT="$iphonesimulator" 22 | cargo build --target aarch64-apple-ios-sim --release 23 | 24 | mkdir -p ./target/universal-ios-sim/release 25 | 26 | lipo \ 27 | ./target/x86_64-apple-ios/release/liblibsql.a \ 28 | ./target/aarch64-apple-ios-sim/release/liblibsql.a \ 29 | -create -output ./target/universal-ios-sim/release/liblibsql.a 30 | } 31 | 32 | build_ios 33 | 34 | nix develop .# -c "./build.sh" 35 | 36 | mkdir -p ./target/universal-macos/release 37 | 38 | lipo \ 39 | ./target/x86_64-apple-darwin/release/liblibsql.a \ 40 | ./target/aarch64-apple-darwin/release/liblibsql.a \ 41 | -create -output ./target/universal-macos/release/liblibsql.a 42 | 43 | rm -rf ../CLibsql.xcframework 44 | 45 | include_dir=`mktemp -d` 46 | 47 | cp ./libsql.h $include_dir/ 48 | cp ../module.modulemap $include_dir/ 49 | 50 | xcodebuild -create-xcframework \ 51 | -library ./target/universal-ios-sim/release/liblibsql.a -headers $include_dir \ 52 | -library ./target/aarch64-apple-ios/release/liblibsql.a -headers $include_dir \ 53 | -library ./target/universal-macos/release/liblibsql.a -headers $include_dir \ 54 | -output ../CLibsql.xcframework 55 | 56 | rm -rf $include_dir 57 | 58 | -------------------------------------------------------------------------------- /Sources/CLibsql/module.modulemap: -------------------------------------------------------------------------------- 1 | module CLibsql { 2 | header "libsql.h" 3 | export * 4 | } 5 | -------------------------------------------------------------------------------- /Sources/Libsql/Libsql.swift: -------------------------------------------------------------------------------- 1 | import CLibsql 2 | import Foundation 3 | 4 | public enum Value { 5 | case integer(Int64) 6 | case text(String) 7 | case blob(Data) 8 | case real(Double) 9 | case null 10 | } 11 | 12 | public protocol ValueRepresentable { 13 | func toValue() -> Value 14 | } 15 | 16 | extension Value: ValueRepresentable { 17 | public func toValue() -> Value { self } 18 | } 19 | 20 | extension Int: ValueRepresentable { 21 | public func toValue() -> Value { .integer(Int64(self)) } 22 | } 23 | 24 | extension Int64: ValueRepresentable { 25 | public func toValue() -> Value { .integer(self) } 26 | } 27 | 28 | extension String: ValueRepresentable { 29 | public func toValue() -> Value { .text(self) } 30 | } 31 | 32 | extension Data: ValueRepresentable { 33 | public func toValue() -> Value { .blob(self) } 34 | } 35 | 36 | extension Double: ValueRepresentable { 37 | public func toValue() -> Value { .real(self) } 38 | } 39 | 40 | public protocol Prepareable { 41 | func prepare(_ sql: String) throws -> Statement 42 | } 43 | 44 | public extension Prepareable { 45 | func execute(_ sql: String) throws -> Int { 46 | return try self.prepare(sql).execute() 47 | } 48 | 49 | func execute(_ sql: String, _ params: [String: ValueRepresentable]) throws -> Int { 50 | return try self.prepare(sql).bind(params).execute() 51 | } 52 | 53 | func execute(_ sql: String, _ params: [ValueRepresentable]) throws -> Int { 54 | return try self.prepare(sql).bind(params).execute() 55 | } 56 | 57 | func query(_ sql: String) throws -> Rows { 58 | return try self.prepare(sql).query() 59 | } 60 | 61 | func query(_ sql: String, _ params: [String: ValueRepresentable]) throws -> Rows { 62 | return try self.prepare(sql).bind(params).query() 63 | } 64 | 65 | func query(_ sql: String, _ params: [ValueRepresentable]) throws -> Rows { 66 | return try self.prepare(sql).bind(params).query() 67 | } 68 | } 69 | 70 | extension String? { 71 | func withCString(_ body: (UnsafePointer?) throws -> Result) rethrows -> Result { 72 | if self == nil { 73 | return try body(nil) 74 | } else { 75 | return try self!.withCString(body) 76 | } 77 | } 78 | } 79 | 80 | func errIf(_ err: OpaquePointer!) throws { 81 | if (err != nil) { 82 | defer { libsql_error_deinit(err) } 83 | throw LibsqlError.runtimeError(String(cString: libsql_error_message(err)!)) 84 | } 85 | } 86 | 87 | enum LibsqlError: Error { 88 | case runtimeError(String) 89 | case typeMismatch 90 | } 91 | 92 | public class Row { 93 | var inner: libsql_row_t 94 | 95 | fileprivate init?(from inner: libsql_row_t?) { 96 | guard let inner = inner else { 97 | return nil 98 | } 99 | 100 | self.inner = inner 101 | } 102 | 103 | public func get(_ index: Int32) throws -> Value { 104 | let result = libsql_row_value(self.inner, index) 105 | try errIf(result.err) 106 | 107 | switch result.ok.type { 108 | case LIBSQL_TYPE_BLOB: 109 | let slice = result.ok.value.blob 110 | defer { libsql_slice_deinit(slice) } 111 | return .blob(Data(bytes: slice.ptr, count: Int(slice.len))) 112 | case LIBSQL_TYPE_TEXT: 113 | let slice = result.ok.value.text 114 | defer { libsql_slice_deinit(slice) } 115 | return .text(String(cString: slice.ptr.assumingMemoryBound(to: UInt8.self))) 116 | case LIBSQL_TYPE_INTEGER: 117 | return .integer(result.ok.value.integer) 118 | case LIBSQL_TYPE_REAL: 119 | return .real(result.ok.value.real) 120 | case LIBSQL_TYPE_NULL: 121 | return .null 122 | default: 123 | preconditionFailure() 124 | } 125 | } 126 | 127 | public func getData(_ index: Int32) throws -> Data { 128 | guard case let .blob(data) = try self.get(index) else { 129 | throw LibsqlError.typeMismatch 130 | } 131 | return data 132 | } 133 | 134 | public func getDouble(_ index: Int32) throws -> Double { 135 | guard case let .real(double) = try self.get(index) else { 136 | throw LibsqlError.typeMismatch 137 | } 138 | return double 139 | } 140 | 141 | public func getString(_ index: Int32) throws -> String { 142 | guard case let .text(string) = try self.get(index) else { 143 | throw LibsqlError.typeMismatch 144 | } 145 | return string 146 | } 147 | 148 | public func getInt(_ index: Int32) throws -> Int { 149 | guard case let .integer(int) = try self.get(index) else { 150 | throw LibsqlError.typeMismatch 151 | } 152 | return Int(int) 153 | } 154 | } 155 | 156 | public class Rows: Sequence, IteratorProtocol { 157 | var inner: libsql_rows_t 158 | 159 | fileprivate init(from inner: libsql_rows_t) { 160 | self.inner = inner 161 | } 162 | 163 | deinit { 164 | libsql_rows_deinit(self.inner) 165 | } 166 | 167 | public func next() -> Row? { 168 | let row = libsql_rows_next(self.inner) 169 | try! errIf(row.err) 170 | 171 | if libsql_row_empty(row) { 172 | return nil 173 | } 174 | 175 | return Row(from: row) 176 | } 177 | } 178 | 179 | public class Statement { 180 | var inner: libsql_statement_t 181 | 182 | deinit { 183 | libsql_statement_deinit(self.inner) 184 | } 185 | 186 | fileprivate init(from inner: libsql_statement_t) { 187 | self.inner = inner 188 | } 189 | 190 | public func execute() throws -> Int { 191 | let exec = libsql_statement_execute(self.inner) 192 | try errIf(exec.err) 193 | 194 | return Int(exec.rows_changed) 195 | } 196 | 197 | public func query() throws -> Rows { 198 | let rows = libsql_statement_query(self.inner) 199 | try errIf(rows.err) 200 | 201 | return Rows(from: rows) 202 | } 203 | 204 | public func bind(_ params: [String: ValueRepresentable]) throws -> Self { 205 | for (name, value) in params { 206 | switch value.toValue() { 207 | case .integer(let integer): 208 | let bind = libsql_statement_bind_named( 209 | self.inner, 210 | name, 211 | libsql_integer(integer) 212 | ) 213 | try errIf(bind.err) 214 | case .text(let text): 215 | let len = text.utf8.count 216 | try text.withCString { text in 217 | let bind = libsql_statement_bind_named( 218 | self.inner, 219 | name, 220 | libsql_text(text, len) 221 | ) 222 | try errIf(bind.err) 223 | } 224 | case .blob(let slice): 225 | try slice.withUnsafeBytes { slice in 226 | let bind = libsql_statement_bind_named( 227 | self.inner, 228 | name, 229 | libsql_blob(slice.baseAddress, slice.count) 230 | ) 231 | try errIf(bind.err) 232 | } 233 | case .real(let real): 234 | let bind = libsql_statement_bind_named( 235 | self.inner, 236 | name, 237 | libsql_real(real) 238 | ) 239 | try errIf(bind.err) 240 | case .null: 241 | let bind = libsql_statement_bind_named( 242 | self.inner, 243 | name, 244 | libsql_value_t(value: .init(), type: LIBSQL_TYPE_NULL) 245 | ) 246 | try errIf(bind.err) 247 | } 248 | } 249 | 250 | return self; 251 | } 252 | 253 | public func bind(_ params: [ValueRepresentable]) throws -> Self { 254 | for value in params { 255 | switch value.toValue() { 256 | case .integer(let integer): 257 | let bind = libsql_statement_bind_value( 258 | self.inner, 259 | libsql_integer(integer) 260 | ) 261 | try errIf(bind.err) 262 | case .text(let text): 263 | 264 | let len = text.utf8.count 265 | try text.withCString { text in 266 | let bind = libsql_statement_bind_value( 267 | self.inner, 268 | libsql_text(text, len) 269 | ) 270 | try errIf(bind.err) 271 | } 272 | case .blob(let slice): 273 | try slice.withUnsafeBytes { slice in 274 | let bind = libsql_statement_bind_value( 275 | self.inner, 276 | libsql_blob(slice.baseAddress, slice.count) 277 | ) 278 | try errIf(bind.err) 279 | } 280 | case .real(let real): 281 | let bind = libsql_statement_bind_value(self.inner, libsql_real(real)) 282 | try errIf(bind.err) 283 | case .null: 284 | let bind = libsql_statement_bind_value( 285 | self.inner, 286 | libsql_value_t(value: .init(), type: LIBSQL_TYPE_NULL) 287 | ) 288 | try errIf(bind.err) 289 | } 290 | } 291 | 292 | return self; 293 | } 294 | } 295 | 296 | public class Transaction: Prepareable { 297 | var inner: libsql_transaction_t 298 | 299 | public consuming func commit() { 300 | libsql_transaction_commit(self.inner) 301 | } 302 | 303 | public consuming func rollback() { 304 | libsql_transaction_rollback(self.inner) 305 | } 306 | 307 | fileprivate init(from inner: libsql_transaction_t) { 308 | self.inner = inner 309 | } 310 | 311 | public func executeBatch(_ sql: String) throws { 312 | let batch = libsql_transaction_batch(self.inner, sql) 313 | try errIf(batch.err) 314 | } 315 | 316 | public func prepare(_ sql: String) throws -> Statement { 317 | let stmt = libsql_transaction_prepare(self.inner, sql); 318 | try errIf(stmt.err) 319 | 320 | return Statement(from: stmt) 321 | } 322 | 323 | } 324 | 325 | public class Connection: Prepareable { 326 | var inner: libsql_connection_t 327 | 328 | deinit { 329 | libsql_connection_deinit(self.inner) 330 | } 331 | 332 | fileprivate init(from inner: libsql_connection_t) { 333 | self.inner = inner 334 | } 335 | 336 | public func transaction() throws -> Transaction { 337 | let tx = libsql_connection_transaction(self.inner) 338 | try errIf(tx.err); 339 | 340 | return Transaction(from: tx) 341 | } 342 | 343 | public func executeBatch(_ sql: String) throws { 344 | let batch = libsql_connection_batch(self.inner, sql) 345 | try errIf(batch.err) 346 | } 347 | 348 | public func prepare(_ sql: String) throws -> Statement { 349 | let stmt = libsql_connection_prepare(self.inner, sql); 350 | try errIf(stmt.err) 351 | 352 | return Statement(from: stmt) 353 | } 354 | } 355 | 356 | public class Database { 357 | var inner: libsql_database_t 358 | 359 | deinit { 360 | libsql_database_deinit(self.inner) 361 | } 362 | 363 | public func sync() throws { 364 | let sync = libsql_database_sync(self.inner) 365 | try errIf(sync.err) 366 | } 367 | 368 | public func connect() throws -> Connection { 369 | let conn = libsql_database_connect(self.inner) 370 | try errIf(conn.err) 371 | 372 | return Connection(from: conn) 373 | } 374 | 375 | public init(_ path: String) throws { 376 | self.inner = try path.withCString { path in 377 | var desc = libsql_database_desc_t() 378 | desc.path = path 379 | 380 | let db = libsql_database_init(desc) 381 | try errIf(db.err) 382 | 383 | return db 384 | } 385 | } 386 | 387 | public init(url: String, authToken: String, withWebpki: Bool = false) throws { 388 | self.inner = try url.withCString { url in 389 | try authToken.withCString { authToken in 390 | var desc = libsql_database_desc_t() 391 | desc.url = url 392 | desc.auth_token = authToken 393 | desc.webpki = withWebpki 394 | 395 | let db = libsql_database_init(desc) 396 | try errIf(db.err) 397 | 398 | return db 399 | } 400 | } 401 | 402 | } 403 | 404 | public init( 405 | path: String, 406 | url: String, 407 | authToken: String, 408 | readYourWrites: Bool = true, 409 | encryptionKey: String? = nil, 410 | syncInterval: UInt64 = 0, 411 | withWebpki: Bool = false 412 | ) throws { 413 | self.inner = try path.withCString { path in 414 | try url.withCString { url in 415 | try authToken.withCString { authToken in 416 | try encryptionKey.withCString { encryptionKey in 417 | var desc = libsql_database_desc_t() 418 | desc.path = path 419 | desc.url = url 420 | desc.auth_token = authToken 421 | desc.encryption_key = encryptionKey 422 | desc.disable_read_your_writes = !readYourWrites 423 | desc.sync_interval = syncInterval 424 | desc.webpki = withWebpki 425 | 426 | let db = libsql_database_init(desc) 427 | try errIf(db.err) 428 | 429 | return db 430 | } 431 | } 432 | } 433 | } 434 | } 435 | } 436 | -------------------------------------------------------------------------------- /Tests/LibsqlTests/LibsqlTests.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XCTest 3 | 4 | @testable import Libsql 5 | 6 | final class LibsqlTests: XCTestCase { 7 | func testOpenDbMemory() throws { 8 | let db = try Database(":memory:") 9 | let _ = try db.connect() 10 | } 11 | 12 | func testOpenDbFile() throws { 13 | let db = try Database("test.db") 14 | let _ = try db.connect() 15 | } 16 | 17 | func testExecute() throws { 18 | let db = try Database(":memory:") 19 | let conn = try db.connect() 20 | _ = try conn.execute("create table test (i integer, s text)") 21 | _ = try conn.execute("insert into test values (?, ?)", [1, "lorem ipsum"]) 22 | let row = try conn.query("select * from test").next()!; 23 | 24 | XCTAssertEqual(try row.getInt(0), 1) 25 | XCTAssertEqual(try row.getString(1), "lorem ipsum") 26 | } 27 | 28 | func testExecuteBatch() throws { 29 | let db = try Database(":memory:") 30 | let conn = try db.connect() 31 | _ = try conn.executeBatch(""" 32 | create table test (i integer, s text); 33 | insert into test values (1, 'lorem ipsum'); 34 | """) 35 | let row = try conn.query("select * from test").next()!; 36 | 37 | XCTAssertEqual(try row.getInt(0), 1) 38 | XCTAssertEqual(try row.getString(1), "lorem ipsum") 39 | } 40 | 41 | func testQuerySimple() throws { 42 | let db = try Database(":memory:") 43 | let conn = try db.connect() 44 | 45 | XCTAssertEqual(try conn.query("select 1").next()!.getInt(0), 1) 46 | XCTAssertEqual(try conn.query("select :named", [":named": 1]).next()!.getInt(0), 1) 47 | XCTAssertEqual(try conn.query("select ?", [1]).next()!.getInt(0), 1) 48 | } 49 | 50 | func testStatement() throws { 51 | let db = try Database(":memory:") 52 | let conn = try db.connect() 53 | let stmt = try conn.prepare("select ?").bind([1]) 54 | XCTAssertEqual(try stmt.query().next()!.getInt(0), 1) 55 | } 56 | 57 | func testTransaction() throws { 58 | let db = try Database(":memory:") 59 | let conn = try db.connect() 60 | 61 | do { 62 | let tx = try conn.transaction() 63 | defer { tx.commit() } 64 | 65 | _ = try tx.execute("create table test (i integer)") 66 | _ = try tx.execute("insert into test values (:v)", [ ":v": 1 ]) 67 | } 68 | 69 | XCTAssertEqual(try conn.query("select * from test").next()!.getInt(0), 1) 70 | } 71 | 72 | func testTransactionRollback() throws { 73 | let db = try Database(":memory:") 74 | let conn = try db.connect() 75 | 76 | _ = try conn.execute("create table test (i integer)") 77 | 78 | do { 79 | let tx = try conn.transaction() 80 | defer { tx.rollback() } 81 | 82 | _ = try tx.execute("insert into test values (:v)", [ ":v": 1 ]) 83 | } 84 | 85 | XCTAssert(try conn.query("select * from test").next() == nil) 86 | } 87 | 88 | func testQueryMultiple() throws { 89 | let db = try Database(":memory:") 90 | let conn = try db.connect() 91 | 92 | _ = try conn.execute("create table test (i integer, t text, r real, b blob)") 93 | 94 | let range = 0...255 95 | 96 | for i in range { 97 | _ = try conn.execute( 98 | "insert into test values (?, ?, ?, ?)", 99 | [ i, "\(i)", exp(Double(i)), Data([UInt8(i)]) ] 100 | ) 101 | } 102 | 103 | for (i, row) in zip(range, try conn.query("select * from test")) { 104 | XCTAssertEqual(try row.getInt(0), i) 105 | XCTAssertEqual(try row.getString(1), "\(i)") 106 | XCTAssertEqual(try row.getDouble(2), exp(Double(i))) 107 | XCTAssertEqual(try row.getData(3), Data([UInt8(i)])) 108 | } 109 | } 110 | } 111 | --------------------------------------------------------------------------------