├── .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 |
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 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
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 | 
111 |
112 |
113 |
114 |
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 |
--------------------------------------------------------------------------------