Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
f2a3a59
Incorporate `@Selection` logic into `@Table`
stephencelis Sep 19, 2025
4b95802
wip
stephencelis Sep 20, 2025
1be6e0b
Support tuple operations with `Table` records
stephencelis Sep 20, 2025
3e15094
Support nested tables
stephencelis Sep 20, 2025
376ee57
fixes
stephencelis Sep 21, 2025
1d4e907
wip
stephencelis Sep 21, 2025
b34df28
wip
stephencelis Sep 21, 2025
922e5b3
wip
stephencelis Sep 21, 2025
fa8b00d
wip
stephencelis Sep 21, 2025
0723ede
wip
stephencelis Sep 21, 2025
1974bb0
wip
stephencelis Sep 21, 2025
3905f40
wip
stephencelis Sep 21, 2025
7800ea8
wip
stephencelis Sep 21, 2025
21e0c9a
wip
stephencelis Sep 21, 2025
c4c2d03
wip
stephencelis Sep 21, 2025
7d434e2
wip
stephencelis Sep 22, 2025
c7cac92
wip
stephencelis Sep 22, 2025
4958464
wip
stephencelis Sep 22, 2025
e07a4eb
wip
stephencelis Sep 22, 2025
9623521
Basic docs pass
stephencelis Sep 22, 2025
642fd4e
wip
stephencelis Sep 22, 2025
a69e5fb
Fixes
stephencelis Sep 22, 2025
a0a0fca
wip
stephencelis Sep 22, 2025
3d5d296
fixes
stephencelis Sep 23, 2025
ccb5151
wip
stephencelis Sep 23, 2025
f2e4981
wip
stephencelis Sep 23, 2025
f6394a3
db function support
stephencelis Sep 23, 2025
29fcd86
wip
stephencelis Sep 23, 2025
ac6f814
wip
stephencelis Sep 23, 2025
1018903
wip
stephencelis Sep 23, 2025
65f0294
wip
stephencelis Sep 23, 2025
df4b1a7
Merge remote-tracking branch 'origin/main' into table-expression
stephencelis Sep 26, 2025
4f44594
wip
stephencelis Sep 26, 2025
c424493
wip
stephencelis Sep 27, 2025
5345f66
wip
stephencelis Sep 27, 2025
9789c32
wip
stephencelis Sep 27, 2025
cdc589b
wip
stephencelis Sep 27, 2025
661cb7d
wip
stephencelis Sep 27, 2025
137b82f
wip
stephencelis Sep 27, 2025
f18feec
wip
stephencelis Sep 27, 2025
0324749
wip
stephencelis Sep 27, 2025
c477c11
wip
stephencelis Sep 27, 2025
7ee5a6b
wip
stephencelis Sep 27, 2025
4dc29da
Infer multi-column when possible
stephencelis Sep 28, 2025
cb6e29d
wip
stephencelis Sep 28, 2025
324ab9c
wip
stephencelis Sep 28, 2025
20148be
wip
stephencelis Sep 28, 2025
0465e23
wip
stephencelis Sep 28, 2025
93e4379
wip
stephencelis Sep 29, 2025
366138a
wip
stephencelis Sep 29, 2025
9bbc844
wip
stephencelis Sep 29, 2025
70ce95f
wip
stephencelis Sep 29, 2025
cdb99e1
wip
stephencelis Sep 29, 2025
c820b2b
wip
stephencelis Sep 30, 2025
cc1c9cd
Docs and tests
mbrandonw Oct 2, 2025
5ded480
wip
stephencelis Oct 2, 2025
6d06754
wip
stephencelis Oct 2, 2025
a5b1723
wip
stephencelis Oct 2, 2025
78d5081
wip
stephencelis Oct 2, 2025
d209480
wip
stephencelis Oct 2, 2025
3a795ea
Merge remote-tracking branch 'origin/main' into table-expression
stephencelis Oct 2, 2025
b82b66d
wip
mbrandonw Oct 2, 2025
0bc5a80
wip
stephencelis Oct 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 31 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
import CompilerPluginSupport
import PackageDescription

#if canImport(FoundationEssentials)
import FoundationEssentials
#else
import Foundation
#endif

let package = Package(
name: "swift-structured-queries",
platforms: [
Expand Down Expand Up @@ -34,13 +40,17 @@ let package = Package(
),
],
traits: [
.trait(
name: "StructuredQueriesCasePaths",
description: "Introduce enum table support to StructuredQueries."
),
.trait(
name: "StructuredQueriesTagged",
description: "Introduce StructuredQueries conformances to the swift-tagged package.",
enabledTraits: []
)
description: "Introduce StructuredQueries conformances to the swift-tagged package."
),
],
dependencies: [
.package(url: "https://github.com/pointfreeco/swift-case-paths", from: "1.0.0"),
.package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.3.3"),
.package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.8.1"),
.package(url: "https://github.com/pointfreeco/swift-macro-testing", from: "0.6.3"),
Expand All @@ -61,6 +71,11 @@ let package = Package(
name: "StructuredQueriesCore",
dependencies: [
.product(name: "IssueReporting", package: "xctest-dynamic-overlay"),
.product(
name: "CasePaths",
package: "swift-case-paths",
condition: .when(traits: ["StructuredQueriesCasePaths"])
),
.product(
name: "Tagged",
package: "swift-tagged",
Expand Down Expand Up @@ -141,6 +156,19 @@ let package = Package(
swiftLanguageModes: [.v6]
)

// NB: For local testing in Xcode:
// if true {
if ProcessInfo.processInfo.environment["SPI_GENERATE_DOCS"] != nil {
package.traits.insert(
.default(
enabledTraits: [
"StructuredQueriesCasePaths",
"StructuredQueriesTagged",
]
)
)
}

let swiftSettings: [SwiftSetting] = [
.enableUpcomingFeature("MemberImportVisibility")
// .unsafeFlags([
Expand Down
31 changes: 18 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ comfortable with the library:

* [Getting started](https://swiftpackageindex.com/pointfreeco/swift-structured-queries/~/documentation/structuredqueriescore/gettingstarted)
* [Defining your schema](https://swiftpackageindex.com/pointfreeco/swift-structured-queries/~/documentation/structuredqueriescore/definingyourschema)
* [Primary keyed tables](https://swiftpackageindex.com/pointfreeco/swift-structured-queries/~/documentation/structuredqueriescore/primarykeyedtables)
* [Primary-keyed tables](https://swiftpackageindex.com/pointfreeco/swift-structured-queries/~/documentation/structuredqueriescore/primarykeyedtables)
* [Safe SQL strings](https://swiftpackageindex.com/pointfreeco/swift-structured-queries/~/documentation/structuredqueriescore/safesqlstrings)
* [Query cookbook](https://swiftpackageindex.com/pointfreeco/swift-structured-queries/~/documentation/structuredqueriescore/querycookbook)

Expand All @@ -174,19 +174,19 @@ As well as more comprehensive example usage:
## Demos

There are a number of sample applications that demonstrate how to use StructuredQueries in the
[SharingGRDB](https://github.com/pointfreeco/sharing-grdb) repo. Check out
[this](https://github.com/pointfreeco/sharing-grdb/tree/main/Examples) directory to see them all,
[SQLiteData](https://github.com/pointfreeco/sqlite-data) repo. Check out
[this](https://github.com/pointfreeco/sqlite-data/tree/main/Examples) directory to see them all,
including:

* [Case Studies](https://github.com/pointfreeco/sharing-grdb/tree/main/Examples/CaseStudies):
* [Case Studies](https://github.com/pointfreeco/sqlite-data/tree/main/Examples/CaseStudies):
A number of case studies demonstrating the built-in features of the library.

* [Reminders](https://github.com/pointfreeco/sharing-grdb/tree/main/Examples/Reminders): A rebuild
* [Reminders](https://github.com/pointfreeco/sqlite-data/tree/main/Examples/Reminders): A rebuild
of Apple's [Reminders][reminders-app-store] app that uses a SQLite database to model the
reminders, lists and tags. It features many advanced queries, such as searching, and stats
aggregation.

* [SyncUps](https://github.com/pointfreeco/sharing-grdb/tree/main/Examples/SyncUps): We also
* [SyncUps](https://github.com/pointfreeco/sqlite-data/tree/main/Examples/SyncUps): We also
rebuilt Apple's [Scrumdinger][scrumdinger] demo application using modern, best practices for
SwiftUI development, including using this library to query and persist state using SQLite.

Expand All @@ -198,8 +198,8 @@ including:
StructuredQueries is built with the goal of supporting any SQL database (SQLite, MySQL, Postgres,
_etc._), but is currently tuned to work with SQLite. It currently has one official driver:

* [SharingGRDB](https://github.com/pointfreeco/sharing-grdb): A lightweight replacement for
SwiftData and the `@Query` macro. SharingGRDB includes `StructuredQueriesGRDB`, a library that
* [SQLiteData](https://github.com/pointfreeco/sqlite-data): A lightweight replacement for
SwiftData and the `@Query` macro. SQLiteData includes `StructuredQueriesGRDB`, a library that
integrates this one with the popular [GRDB](https://github.com/groue/GRDB.swift) SQLite library.

If you are interested in building a StructuredQueries integration for another database library,
Expand All @@ -220,7 +220,7 @@ it's as simple as adding it to your `Package.swift`:

``` swift
dependencies: [
.package(url: "https://github.com/pointfreeco/swift-structured-queries", from: "0.17.0"),
.package(url: "https://github.com/pointfreeco/swift-structured-queries", from: "0.22.0"),
]
```

Expand All @@ -231,16 +231,21 @@ And then adding the product to any target that needs access to the library:
```

If you are on Swift 6.1 or greater, you can enable package traits that extend the library with
support for other libraries. For example, you can introduce type-safe identifiers to your tables
_via_ [Tagged](https://github.com/pointfreeco/swift-tagged) by enabling the
`StructuredQueriesTagged` trait:
support for other libraries:

* `StructuredQueriesCasePaths`: Adds support for single-table inheritance _via_ "enum" tables by
leveraging the [CasePaths](https://github.com/pointfreeco/swift-case-paths) library.

* `StructuredQueriesTagged`: Adds support for type-safe identifiers _via_
the [Tagged](https://github.com/pointfreeco/swift-tagged) library.

```diff
dependencies: [
.package(
url: "https://github.com/pointfreeco/swift-structured-queries",
from: "0.17.0",
from: "0.22.0",
+ traits: [
+ "StructuredQueriesCasePaths",
+ "StructuredQueriesTagged",
+ ]
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ StructuredQueries also ships SQLite-specific helpers:

- ``Table(_:)``
- ``Column(_:as:primaryKey:)``
- ``Columns(primaryKey:)``
- ``Ephemeral()``
- ``Selection()``
- ``sql(_:as:)``
Expand Down
114 changes: 72 additions & 42 deletions Sources/StructuredQueries/Macros.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,25 @@ import StructuredQueriesCore

/// Defines and implements a conformance to the ``/StructuredQueriesCore/Table`` protocol.
///
/// - Parameter name: The table's name. Defaults to a lower-camel-case pluralization of the type,
/// _e.g._ `RemindersList` becomes `"remindersLists"`.
/// - Parameters
/// - name: The table's name. Defaults to a lower-camel-case pluralization of the type,
/// _e.g._ `RemindersList` becomes `"remindersLists"`.
/// - schemaName: The table's schema name.
@attached(
extension,
conformances: Table,
PartialSelectStatement,
PrimaryKeyedTable,
names: named(From),
named(columns),
named(_columnWidth),
named(init(_:)),
named(init(decoder:)),
named(QueryValue),
named(schemaName),
named(tableName)
)
@attached(member, names: named(Draft), named(TableColumns))
@attached(member, names: named(Draft), named(Selection), named(TableColumns))
@attached(memberAttribute)
public macro Table(
_ name: String = "",
Expand All @@ -28,38 +31,7 @@ public macro Table(
type: "TableMacro"
)

/// Customizes a column generated by the ``/StructuredQueriesCore/Table`` protocol.
///
/// - Parameters:
/// - name: The column's name. Defaults to the property's name, _e.g._ 'id' becomes `"id"`.
/// - representableType: A type that represents the property type in a query expression. For types
/// that don't have a single representation in SQL, like `Date` and `UUID`.
/// - generated: Allows to declare the column as a read-only database computed column, making it
/// available for queries but not for updates.
/// - primaryKey: The column is its table's auto-incrementing primary key.
@attached(peer)
public macro Column(
_ name: String = "",
as representableType: (any QueryRepresentable.Type)? = nil,
generated: GeneratedColumnStorage? = nil,
primaryKey: Bool = false
) =
#externalMacro(
module: "StructuredQueriesMacros",
type: "ColumnMacro"
)

/// Tells StructuredQueries not to consider the annotated property a column of the table.
///
/// Like SwiftData's `@Transient` macro, but for SQL.
@attached(peer)
public macro Ephemeral() =
#externalMacro(
module: "StructuredQueriesMacros",
type: "EphemeralMacro"
)

/// Defines the ability for a type to be selected from a query.
/// Defines a "selection" of columns that can be decoded from a query.
///
/// When selecting tables and fields from a query, this data is bundled up into a tuple:
///
Expand Down Expand Up @@ -87,19 +59,77 @@ public macro Ephemeral() =
/// // [RemindersListWithReminderCount]
/// ```
///
/// The ``Table(_:)`` and `@Selection` macros can be composed together to describe a virtual table
/// or common table expression.
/// > Tip: `@Selection`s can also be used to build up common table expressions.
///
/// - Parameter name: The selection's name, _i.e._ for a common table expression. Defaults to a
/// lower-camel-case pluralization of the type, _e.g._ `RemindersList` becomes `"remindersLists"`.
@attached(
extension,
conformances: _Selection,
names: named(Columns),
named(init(decoder:))
Table,
PartialSelectStatement,
PrimaryKeyedTable,
names: named(From),
named(columns),
named(_columnWidth),
named(init(_:)),
named(init(decoder:)),
named(QueryValue),
named(schemaName),
named(tableName)
)
@attached(member, names: named(Columns))
public macro Selection() =
@attached(member, names: named(Draft), named(Selection), named(TableColumns))
@attached(memberAttribute)
public macro Selection(
_ name: String = ""
) =
#externalMacro(
module: "StructuredQueriesMacros",
type: "TableMacro"
)

/// Customizes a column generated by the ``/StructuredQueriesCore/Table`` protocol.
///
/// - Parameters:
/// - name: The column's name. Defaults to the property's name, _e.g._ 'id' becomes `"id"`.
/// - representableType: A type that represents the property type in a query expression. For types
/// that don't have a single representation in SQL, like `Date` and `UUID`.
/// - generated: Allows to declare the column as a read-only database computed column, making it
/// available for queries but not for updates.
/// - primaryKey: The column is its table's primary key.
@attached(peer)
public macro Column(
_ name: String = "",
as representableType: (any QueryRepresentable.Type)? = nil,
generated: GeneratedColumnStorage? = nil,
primaryKey: Bool = false
) =
#externalMacro(
module: "StructuredQueriesMacros",
type: "ColumnMacro"
)

/// Customizes a group of columns generated by the ``/StructuredQueriesCore/Table`` protocol.
///
/// - Parameters primaryKey: These columns are the table's composite primary key.
@attached(peer)
public macro Columns(
// as representableType: (any QueryRepresentable.Type)? = nil,
primaryKey: Bool = false
) =
#externalMacro(
module: "StructuredQueriesMacros",
type: "SelectionMacro"
type: "ColumnsMacro"
)

/// Tells StructuredQueries not to consider the annotated property a column of the table.
///
/// Like SwiftData's `@Transient` macro, but for SQL.
@attached(peer)
public macro Ephemeral() =
#externalMacro(
module: "StructuredQueriesMacros",
type: "EphemeralMacro"
)

/// Explicitly bind a value to a query.
Expand Down
3 changes: 2 additions & 1 deletion Sources/StructuredQueriesCore/Bind.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
///
/// It is not common to interact with this type directly. A value of this type is returned from the
/// `#bind` macro.
public struct BindQueryExpression<QueryValue: QueryBindable>: QueryExpression {
public struct BindQueryExpression<QueryValue: QueryRepresentable & QueryExpression>: QueryExpression
{
public let base: QueryValue

public init(
Expand Down
Loading
Loading