|
2 | 2 |
|
3 | 3 | Typed SQL for F#, from one source, on .NET **and** through Fable to **Rust**, |
4 | 4 | **JavaScript NodeJs** and **Erlang/BEAM**. |
5 | | - |
6 | | -NOTE: Rust is "comming soon" as it needs some Fable side PRs merged first. |
| 5 | + |
| 6 | +NOTE: Rust is "coming soon" as it needs some Fable side PRs merged first. |
7 | 7 |
|
8 | 8 | Point the generator at your database, get a module per table with typed columns |
9 | 9 | and row mappers, and write queries against them: |
@@ -86,37 +86,62 @@ BEAM runs the whole portable half (query building, SQL generation, value |
86 | 86 | encoding, row readers, the async layer) but has no connector yet. `epgsql` and |
87 | 87 | `mysql-otp` are pure Erlang and so the easiest ones to add. |
88 | 88 |
|
89 | | -Not on NuGet yet: reference the projects directly. |
| 89 | +Packages: `SQLProvider.Fable.Core`, `.Ado`, `.Rust`, `.Js`, and the generator |
| 90 | +as the dotnet tool `SQLProvider.Fable.Generator`. CI packs them on every build |
| 91 | +and publishes to nuget.org on a `v*` tag. |
90 | 92 |
|
91 | 93 | --- |
92 | 94 |
|
93 | 95 | ## Getting started |
94 | 96 |
|
95 | | -Reference the runtime, plus the backend for your target: |
| 97 | +Reference `SQLProvider.Fable.Core` plus the connector for your target — only |
| 98 | +one connector ends up in a given build: |
96 | 99 |
|
97 | 100 | ```xml |
98 | | -<ProjectReference Include="path/to/src/SQLProvider.Fable.Core/SQLProvider.Fable.Core.fsproj" /> |
99 | | -<ProjectReference Include="path/to/src/SQLProvider.Fable.Ado/SQLProvider.Fable.Ado.fsproj" /> |
| 101 | +<PackageReference Include="SQLProvider.Fable.Core" Version="0.1.0" /> |
| 102 | +<PackageReference Include="SQLProvider.Fable.Ado" Version="0.1.0" /> <!-- .NET --> |
| 103 | +<!-- or SQLProvider.Fable.Rust / SQLProvider.Fable.Js for a Fable target --> |
100 | 104 | ``` |
101 | 105 |
|
102 | | -`Core` is where everything portable lives. Add `SQLProvider.Fable.Ado` for .NET, |
103 | | -`SQLProvider.Fable.Rust` for Fable/Rust, or `SQLProvider.Fable.Js` for |
104 | | -Fable/JavaScript — only one of them ends up in a given build. |
| 106 | +(Working from a clone instead, `ProjectReference` the same projects under |
| 107 | +`src/`.) |
| 108 | + |
| 109 | +The packages carry their F# sources under `fable/`, the way Fable libraries do, |
| 110 | +so a Fable project compiles the library to its own target while .NET uses the |
| 111 | +compiled assembly from the same package. |
105 | 112 |
|
106 | 113 | JavaScript and BEAM need nothing beyond released Fable. Rust additionally needs |
107 | | -the unmerged fix described under [Status](#status). |
| 114 | +the unmerged fix described under [Status](#status), and the consuming crate's |
| 115 | +`Cargo.toml` needs the driver stack the connector calls into: |
| 116 | + |
| 117 | +```toml |
| 118 | +[dependencies] |
| 119 | +sqlx = { version = "0.8", default-features = false, features = [ |
| 120 | + "runtime-tokio", "any", "sqlite", "postgres", "mysql", |
| 121 | +] } |
| 122 | +tokio = { version = "1", features = ["rt-multi-thread"] } |
| 123 | +futures = "0.3" |
| 124 | +``` |
108 | 125 |
|
109 | 126 | --- |
110 | 127 |
|
111 | 128 | ## Generating from your schema |
112 | 129 |
|
113 | 130 | Hand-writing `Col.text "Customer" "Name"` per column is the work a provider is |
114 | | -supposed to remove, so generate it: |
| 131 | +supposed to remove, so generate it. The generator is a dotnet tool: |
115 | 132 |
|
116 | 133 | ```bash |
117 | | -dotnet run --project tools/SQLProvider.Fable.Generator -- --sqlite ./app.db --module Northwind --out ./src/Schema.fs |
| 134 | +dotnet tool install --global SQLProvider.Fable.Generator |
118 | 135 | ``` |
119 | 136 |
|
| 137 | +```bash |
| 138 | +sqlprovider-fable-gen --sqlite ./app.db --module Northwind --out ./src/Schema.fs |
| 139 | +``` |
| 140 | + |
| 141 | +`--postgres "Host=...;Username=...;Password=...;Database=..."` reads a live |
| 142 | +PostgreSQL schema the same way. (From a clone, |
| 143 | +`dotnet run --project tools/SQLProvider.Fable.Generator -- ...` does the same.) |
| 144 | + |
120 | 145 | Add `Schema.fs` to your project and check it in. Regenerate when the schema |
121 | 146 | changes rather than editing it — the names in it have to keep matching the |
122 | 147 | database. |
@@ -318,6 +343,12 @@ germanCustomers |
318 | 343 |
|
319 | 344 | MySQL accepts `INTERSECT` and `EXCEPT` from 8.0.31. |
320 | 345 |
|
| 346 | +Only ordering and paging apply to the combined result. Every other clause — |
| 347 | +`where`, `groupBy`, `select` — still describes the **first** SELECT, whether |
| 348 | +written before or after the `union`: SQL cannot filter a combined result |
| 349 | +without nesting it in a FROM, which this library does not do. Filter the arms, |
| 350 | +not the union. |
| 351 | + |
321 | 352 | ```fsharp |
322 | 353 | |> Query.where (Expr.contains Customer.Name.E searchBox) |
323 | 354 | |> Query.selectExpr "band" (Expr.ifThenElse (Customer.Balance >. 100.0) high low) |
|
0 commit comments