Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
35 changes: 35 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,41 @@ jobs:
name: test-results-lql-${{ strategy.job-index }}
path: '**/TestResults/*.trx'

# LQL F# Type Provider tests
lql-fsharp-typeprovider-tests:
name: LQL F# Type Provider Tests
runs-on: ubuntu-latest
needs: [build, changes]
if: needs.changes.outputs.lql == 'true'
steps:
- uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}

- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.fsproj') }}
restore-keys: |
${{ runner.os }}-nuget-

- name: Restore
run: dotnet restore Lql/Lql.TypeProvider.FSharp.Tests

- name: Test
run: dotnet test Lql/Lql.TypeProvider.FSharp.Tests --no-restore --verbosity normal --logger "trx;LogFileName=test-results.trx"

- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-lql-fsharp-typeprovider
path: '**/TestResults/*.trx'

# Migration tests
migration-tests:
name: Migration Tests
Expand Down
11 changes: 11 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": "Run DataProvider.Example.FSharp",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/DataProvider/DataProvider.Example.FSharp/bin/Debug/net9.0/DataProvider.Example.FSharp.dll",
"args": [],
"cwd": "${workspaceFolder}/DataProvider/DataProvider.Example.FSharp",
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": "DB Browser",
"type": "coreclr",
Expand Down
34 changes: 22 additions & 12 deletions Agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

## Coding Rules

- **NEVER THROW** - Return `Result<T>`. Wrap failures in try/catch
- **NEVER THROW** - Return `Result<T,E>`. Wrap failures in try/catch
- **No casting/!** - Pattern match on type only
- **NO GIT** - Source control is illegal
- **No suppressing warnings** - Illegal
Expand All @@ -18,22 +18,32 @@
- **Copious ILogger** - Especially sync projects
- **NO INTERFACES** - Use `Action<T>`/`Func<T>`
- **Expressions over assignments**
- **Routinely format with csharpier** - `dotnet csharpier .` <- In root folder
- **Named parameters** - No ordinal calls
- **Close type hierarchies** - Private constructors:
```csharp
public abstract partial record Result<TSuccess, TFailure> { private Result() { } }
```
- **Extension methods on IDbConnection/IDbTransaction only**
- **Pattern match, don't if** - Switch expressions on type
- **No skipping tests** - Failing = OK, Skip = illegal
- **E2E tests only** - No mocks, integration testing
- **Type aliases for Results** - `using XResult = Result<X, XError>`
- **Immutable** - Records, `ImmutableList`, `FrozenSet`, `ImmutableArray`
- **NO REGEX** - ANTLR or SqlParserCS
- **XMLDOC on public members** - Except tests
- **< 450 LOC per file**
- **No commented code** - Delete it
- **No placeholders** - Leave compile errors with TODO
- **Skipping tests = ⛔️ ILLEGAL** - Failing tests = OK. Aggressively unskip tests
- **Test at the highest level** - Avoid mocks. Only full integration testing
- **Keep files under 450 LOC and functions under 20 LOC**
- **Always use type aliases (using) for result types** - Don't write like this: `new Result<string, SqlError>.Ok`
- **All tables must have a SINGLE primary key**
- **Primary keys MUST be UUIDs**
- **No singletons** - Inject `Func` into static methods
- **Immutable types!** - Use records. Don't use `List<T>`. Use `ImmutableList` `FrozenSet` or `ImmutableArray`
- **No in-memory dbs** - Real dbs all the way
- **NO REGEX** - Parse SQL with ANTLR .g4 grammars or SqlParserCS library
- **All public members require XMLDOC** - Except in test projects
- **One type per file** (except small records)
- **No commented-out code** - Delete it
- **No consecutive Console.WriteLine** - Use single string interpolation
- **No placeholders** - If incomplete, leave LOUD compilation error with TODO
- **Never use Fluent Assertions**

## CSS
- **MINIMAL CSS** - Do not duplicate CSS clases
- **Name classes after component, NOT section** - Sections should not have their own CSS classes

## Testing
- E2E with zero mocking
Expand Down
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

## Coding Rules

- **NEVER THROW** - Return `Result<T>`. Wrap failures in try/catch
- **NEVER THROW** - Return `Result<T,E>``. Wrap failures in try/catch
- **No casting/!** - Pattern match on type only
- **NO GIT** - Source control is illegal
- **No suppressing warnings** - Illegal
Expand All @@ -32,6 +32,7 @@ public abstract partial record Result<TSuccess, TFailure> { private Result() { }
- **Primary keys MUST be UUIDs**
- **No singletons** - Inject `Func` into static methods
- **Immutable types!** - Use records. Don't use `List<T>`. Use `ImmutableList` `FrozenSet` or `ImmutableArray`
- **No in-memory dbs** - Real dbs all the way
- **NO REGEX** - Parse SQL with ANTLR .g4 grammars or SqlParserCS library
- **All public members require XMLDOC** - Except in test projects
- **One type per file** (except small records)
Expand Down
78 changes: 64 additions & 14 deletions DataProvider.sln
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataProvider.Example.Tests"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataProvider.Example", "DataProvider\DataProvider.Example\DataProvider.Example.csproj", "{EA9A0385-249F-4141-AD03-D67649110A84}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lql.Browser", "Lql\Lql.Browser\Lql.Browser.csproj", "{1B5BAB33-4256-400B-A4F8-F318418A3548}"
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Lql.TypeProvider.FSharp", "Lql\Lql.TypeProvider.FSharp\Lql.TypeProvider.FSharp.fsproj", "{B1234567-89AB-CDEF-0123-456789ABCDEF}"
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "DataProvider.SQLite.FSharp", "DataProvider\DataProvider.SQLite.FSharp\DataProvider.SQLite.FSharp.fsproj", "{D1234567-89AB-CDEF-0123-456789ABCDEF}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "DataProvider.Example.FSharp", "DataProvider\DataProvider.Example.FSharp\DataProvider.Example.FSharp.fsproj", "{5C11B1F1-F6FF-45B9-B037-EDD054EED3F3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lql.Browser", "Lql\Lql.Browser\Lql.Browser.csproj", "{0D96933C-DE5D-472B-9E9F-68DD15B85CF7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sync", "Sync", "{5E63119C-E70B-5D45-ECC9-8CBACC584223}"
EndProject
Expand Down Expand Up @@ -107,6 +113,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.Web", "Samples\Da
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.Web.Tests", "Samples\Dashboard\Dashboard.Web.Tests\Dashboard.Web.Tests.csproj", "{25C125F3-B766-4DCD-8032-DB89818FFBC3}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Lql.TypeProvider.FSharp.Tests", "Lql\Lql.TypeProvider.FSharp.Tests\Lql.TypeProvider.FSharp.Tests.fsproj", "{B0104C42-1B46-4CA5-9E91-A5F09D7E5B92}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lql.TypeProvider.FSharp.Tests.Data", "Lql\Lql.TypeProvider.FSharp.Tests.Data\Lql.TypeProvider.FSharp.Tests.Data.csproj", "{0D6A831B-4759-46F2-8527-51C8A9CB6F6F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -261,18 +271,24 @@ Global
{EA9A0385-249F-4141-AD03-D67649110A84}.Release|x64.Build.0 = Release|Any CPU
{EA9A0385-249F-4141-AD03-D67649110A84}.Release|x86.ActiveCfg = Release|Any CPU
{EA9A0385-249F-4141-AD03-D67649110A84}.Release|x86.Build.0 = Release|Any CPU
{1B5BAB33-4256-400B-A4F8-F318418A3548}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1B5BAB33-4256-400B-A4F8-F318418A3548}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1B5BAB33-4256-400B-A4F8-F318418A3548}.Debug|x64.ActiveCfg = Debug|Any CPU
{1B5BAB33-4256-400B-A4F8-F318418A3548}.Debug|x64.Build.0 = Debug|Any CPU
{1B5BAB33-4256-400B-A4F8-F318418A3548}.Debug|x86.ActiveCfg = Debug|Any CPU
{1B5BAB33-4256-400B-A4F8-F318418A3548}.Debug|x86.Build.0 = Debug|Any CPU
{1B5BAB33-4256-400B-A4F8-F318418A3548}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1B5BAB33-4256-400B-A4F8-F318418A3548}.Release|Any CPU.Build.0 = Release|Any CPU
{1B5BAB33-4256-400B-A4F8-F318418A3548}.Release|x64.ActiveCfg = Release|Any CPU
{1B5BAB33-4256-400B-A4F8-F318418A3548}.Release|x64.Build.0 = Release|Any CPU
{1B5BAB33-4256-400B-A4F8-F318418A3548}.Release|x86.ActiveCfg = Release|Any CPU
{1B5BAB33-4256-400B-A4F8-F318418A3548}.Release|x86.Build.0 = Release|Any CPU
{B1234567-89AB-CDEF-0123-456789ABCDEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B1234567-89AB-CDEF-0123-456789ABCDEF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D1234567-89AB-CDEF-0123-456789ABCDEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D1234567-89AB-CDEF-0123-456789ABCDEF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5C11B1F1-F6FF-45B9-B037-EDD054EED3F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5C11B1F1-F6FF-45B9-B037-EDD054EED3F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0D96933C-DE5D-472B-9E9F-68DD15B85CF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0D96933C-DE5D-472B-9E9F-68DD15B85CF7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0D96933C-DE5D-472B-9E9F-68DD15B85CF7}.Debug|x64.ActiveCfg = Debug|Any CPU
{0D96933C-DE5D-472B-9E9F-68DD15B85CF7}.Debug|x64.Build.0 = Debug|Any CPU
{0D96933C-DE5D-472B-9E9F-68DD15B85CF7}.Debug|x86.ActiveCfg = Debug|Any CPU
{0D96933C-DE5D-472B-9E9F-68DD15B85CF7}.Debug|x86.Build.0 = Debug|Any CPU
{0D96933C-DE5D-472B-9E9F-68DD15B85CF7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0D96933C-DE5D-472B-9E9F-68DD15B85CF7}.Release|Any CPU.Build.0 = Release|Any CPU
{0D96933C-DE5D-472B-9E9F-68DD15B85CF7}.Release|x64.ActiveCfg = Release|Any CPU
{0D96933C-DE5D-472B-9E9F-68DD15B85CF7}.Release|x64.Build.0 = Release|Any CPU
{0D96933C-DE5D-472B-9E9F-68DD15B85CF7}.Release|x86.ActiveCfg = Release|Any CPU
{0D96933C-DE5D-472B-9E9F-68DD15B85CF7}.Release|x86.Build.0 = Release|Any CPU
{C0B4116E-0635-4597-971D-6B70229FA30A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C0B4116E-0635-4597-971D-6B70229FA30A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C0B4116E-0635-4597-971D-6B70229FA30A}.Debug|x64.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -645,6 +661,30 @@ Global
{25C125F3-B766-4DCD-8032-DB89818FFBC3}.Release|x64.Build.0 = Release|Any CPU
{25C125F3-B766-4DCD-8032-DB89818FFBC3}.Release|x86.ActiveCfg = Release|Any CPU
{25C125F3-B766-4DCD-8032-DB89818FFBC3}.Release|x86.Build.0 = Release|Any CPU
{B0104C42-1B46-4CA5-9E91-A5F09D7E5B92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B0104C42-1B46-4CA5-9E91-A5F09D7E5B92}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B0104C42-1B46-4CA5-9E91-A5F09D7E5B92}.Debug|x64.ActiveCfg = Debug|Any CPU
{B0104C42-1B46-4CA5-9E91-A5F09D7E5B92}.Debug|x64.Build.0 = Debug|Any CPU
{B0104C42-1B46-4CA5-9E91-A5F09D7E5B92}.Debug|x86.ActiveCfg = Debug|Any CPU
{B0104C42-1B46-4CA5-9E91-A5F09D7E5B92}.Debug|x86.Build.0 = Debug|Any CPU
{B0104C42-1B46-4CA5-9E91-A5F09D7E5B92}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B0104C42-1B46-4CA5-9E91-A5F09D7E5B92}.Release|Any CPU.Build.0 = Release|Any CPU
{B0104C42-1B46-4CA5-9E91-A5F09D7E5B92}.Release|x64.ActiveCfg = Release|Any CPU
{B0104C42-1B46-4CA5-9E91-A5F09D7E5B92}.Release|x64.Build.0 = Release|Any CPU
{B0104C42-1B46-4CA5-9E91-A5F09D7E5B92}.Release|x86.ActiveCfg = Release|Any CPU
{B0104C42-1B46-4CA5-9E91-A5F09D7E5B92}.Release|x86.Build.0 = Release|Any CPU
{0D6A831B-4759-46F2-8527-51C8A9CB6F6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0D6A831B-4759-46F2-8527-51C8A9CB6F6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0D6A831B-4759-46F2-8527-51C8A9CB6F6F}.Debug|x64.ActiveCfg = Debug|Any CPU
{0D6A831B-4759-46F2-8527-51C8A9CB6F6F}.Debug|x64.Build.0 = Debug|Any CPU
{0D6A831B-4759-46F2-8527-51C8A9CB6F6F}.Debug|x86.ActiveCfg = Debug|Any CPU
{0D6A831B-4759-46F2-8527-51C8A9CB6F6F}.Debug|x86.Build.0 = Debug|Any CPU
{0D6A831B-4759-46F2-8527-51C8A9CB6F6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0D6A831B-4759-46F2-8527-51C8A9CB6F6F}.Release|Any CPU.Build.0 = Release|Any CPU
{0D6A831B-4759-46F2-8527-51C8A9CB6F6F}.Release|x64.ActiveCfg = Release|Any CPU
{0D6A831B-4759-46F2-8527-51C8A9CB6F6F}.Release|x64.Build.0 = Release|Any CPU
{0D6A831B-4759-46F2-8527-51C8A9CB6F6F}.Release|x86.ActiveCfg = Release|Any CPU
{0D6A831B-4759-46F2-8527-51C8A9CB6F6F}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -662,7 +702,10 @@ Global
{A7EC2050-FE5E-4BBD-AF5F-7F07D3688118} = {43BAF0A3-C050-BE83-B489-7FC6F9FDE235}
{16FA9B36-CB2A-4B79-A3BE-937C94BF03F8} = {43BAF0A3-C050-BE83-B489-7FC6F9FDE235}
{EA9A0385-249F-4141-AD03-D67649110A84} = {43BAF0A3-C050-BE83-B489-7FC6F9FDE235}
{1B5BAB33-4256-400B-A4F8-F318418A3548} = {54B846BA-A27D-B76F-8730-402A5742FF43}
{B1234567-89AB-CDEF-0123-456789ABCDEF} = {54B846BA-A27D-B76F-8730-402A5742FF43}
{D1234567-89AB-CDEF-0123-456789ABCDEF} = {43BAF0A3-C050-BE83-B489-7FC6F9FDE235}
{5C11B1F1-F6FF-45B9-B037-EDD054EED3F3} = {43BAF0A3-C050-BE83-B489-7FC6F9FDE235}
{0D96933C-DE5D-472B-9E9F-68DD15B85CF7} = {54B846BA-A27D-B76F-8730-402A5742FF43}
{C0B4116E-0635-4597-971D-6B70229FA30A} = {5E63119C-E70B-5D45-ECC9-8CBACC584223}
{9B303409-0052-45B9-8616-CC1ED80A5595} = {5E63119C-E70B-5D45-ECC9-8CBACC584223}
{50CFDEC4-66C8-4330-8D5F-9D96A764378B} = {5E63119C-E70B-5D45-ECC9-8CBACC584223}
Expand All @@ -688,8 +731,15 @@ Global
{4EB6CC28-7D1B-4E39-80F2-84CA4494AF23} = {048F5F03-6DDC-C04F-70D5-B8139DC8E373}
{2FD305AC-927E-4D24-9FA6-923C30E4E4A8} = {048F5F03-6DDC-C04F-70D5-B8139DC8E373}
{57572A45-33CD-4928-9C30-13480AEDB313} = {C7F49633-8D5E-7E19-1580-A6459B2EAE66}
{A8A70E6D-1D43-437F-9971-44A4FA1BDD74} = {43BAF0A3-C050-BE83-B489-7FC6F9FDE235}
{0858FE19-C59B-4A77-B76E-7053E8AFCC8D} = {C7F49633-8D5E-7E19-1580-A6459B2EAE66}
{CA395494-F072-4A5B-9DD4-950530A69E0E} = {5D20AA90-6969-D8BD-9DCD-8634F4692FDA}
{1AE87774-E914-40BC-95BA-56FB45D78C0D} = {54B846BA-A27D-B76F-8730-402A5742FF43}
{6AB2EA96-4A75-49DB-AC65-B247BBFAE9A3} = {54B846BA-A27D-B76F-8730-402A5742FF43}
{A82453CD-8E3C-44B7-A78F-97F392016385} = {B03CA193-C175-FB88-B41C-CBBC0E037C7E}
{25C125F3-B766-4DCD-8032-DB89818FFBC3} = {B03CA193-C175-FB88-B41C-CBBC0E037C7E}
{B0104C42-1B46-4CA5-9E91-A5F09D7E5B92} = {54B846BA-A27D-B76F-8730-402A5742FF43}
{0D6A831B-4759-46F2-8527-51C8A9CB6F6F} = {54B846BA-A27D-B76F-8730-402A5742FF43}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {53128A75-E7B6-4B83-B079-A309FCC2AD9C}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<LangVersion>preview</LangVersion>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningLevel>3</WarningLevel>
</PropertyGroup>

<ItemGroup>
<Compile Include="LqlValidator.fs" />
<Compile Include="Program.fs" />
</ItemGroup>

<ItemGroup>
<Content Include="GetCustomers.lql">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="GetInvoices.lql">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
<PackageReference Include="FSharp.TypeProviders.SDK" Version="6.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="../../Lql/Lql.SQLite/Lql.SQLite.csproj" />
<ProjectReference Include="../../Lql/Lql/Lql.csproj" />
<ProjectReference Include="../../Lql/Lql.TypeProvider.FSharp/Lql.TypeProvider.FSharp.fsproj" />
<ProjectReference Include="../../Other/Selecta/Selecta.csproj" />
</ItemGroup>

</Project>
4 changes: 4 additions & 0 deletions DataProvider/DataProvider.Example.FSharp/GetCustomers.lql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Customer
|> join(Address, on = Customer.Id = Address.CustomerId)
|> select(Customer.Id, Customer.CustomerName, Customer.Email, Customer.Phone, Customer.CreatedDate, Address.Id AS AddressId, Address.CustomerId, Address.Street, Address.City, Address.State, Address.ZipCode, Address.Country)
|> order_by(Customer.CustomerName)
4 changes: 4 additions & 0 deletions DataProvider/DataProvider.Example.FSharp/GetInvoices.lql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Invoice
|> join(InvoiceLine, on = Invoice.Id = InvoiceLine.InvoiceId)
|> select(Invoice.Id, Invoice.InvoiceNumber, Invoice.InvoiceDate, Invoice.CustomerName, Invoice.CustomerEmail, Invoice.TotalAmount, InvoiceLine.Description, InvoiceLine.Quantity, InvoiceLine.UnitPrice, InvoiceLine.Amount)
|> order_by(Invoice.InvoiceDate)
66 changes: 66 additions & 0 deletions DataProvider/DataProvider.Example.FSharp/LqlValidator.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
module LqlValidator

open System
open Microsoft.Data.Sqlite
open Lql
open Lql.SQLite
open Outcome
open Selecta

//TODO: this does not belong here. Move to core code

/// Validates LQL at compile time and provides execution methods
type LqlQuery private() =

/// Validates and executes an LQL query
static member inline Execute(connectionString: string, [<ReflectedDefinition>] lqlQuery: string) =
// Validate at compile time
let statementResult = LqlStatementConverter.ToStatement(lqlQuery)
match statementResult with
| :? Outcome.Result<LqlStatement, SqlError>.Ok<LqlStatement, SqlError> as success ->
let lqlStatement = success.Value
match lqlStatement.AstNode with
| :? Pipeline as pipeline ->
let sqliteContext = SQLiteContext()
let sql = PipelineProcessor.ConvertPipelineToSql(pipeline, sqliteContext)

// Execute the query
use conn = new SqliteConnection(connectionString)
conn.Open()
use cmd = new SqliteCommand(sql, conn)
use reader = cmd.ExecuteReader()

let results = ResizeArray<Map<string, obj>>()
while reader.Read() do
let row =
[| for i in 0 .. reader.FieldCount - 1 ->
let name = reader.GetName(i)
let value = if reader.IsDBNull(i) then box DBNull.Value else reader.GetValue(i)
(name, value) |]
|> Map.ofArray
results.Add(row)

Ok(results |> List.ofSeq)
| _ ->
Error "Invalid LQL statement type"
| :? Outcome.Result<LqlStatement, SqlError>.Error<LqlStatement, SqlError> as failure ->
Error(sprintf "Invalid LQL syntax: %s" failure.Value.Message)
| _ ->
Error "Unknown result type from LQL parser"

/// Gets the SQL for an LQL query (for debugging)
static member inline ToSql([<ReflectedDefinition>] lqlQuery: string) =
let statementResult = LqlStatementConverter.ToStatement(lqlQuery)
match statementResult with
| :? Outcome.Result<LqlStatement, SqlError>.Ok<LqlStatement, SqlError> as success ->
let lqlStatement = success.Value
match lqlStatement.AstNode with
| :? Pipeline as pipeline ->
let sqliteContext = SQLiteContext()
Ok(PipelineProcessor.ConvertPipelineToSql(pipeline, sqliteContext))
| _ ->
Error "Invalid LQL statement type"
| :? Outcome.Result<LqlStatement, SqlError>.Error<LqlStatement, SqlError> as failure ->
Error(sprintf "Invalid LQL syntax: %s" failure.Value.Message)
| _ ->
Error "Unknown result type from LQL parser"
Loading
Loading