Lambda Query Language (LQL)
A functional pipeline-style DSL that transpiles to SQL. Write database logic once, run it anywhere.
The Problem
SQL dialects differ. PostgreSQL, SQLite, and SQL Server each have their own quirks. This creates problems:
- Migrations - Schema changes need different SQL for each database
- Business Logic - Triggers, stored procedures, and constraints vary by vendor
- Sync Logic - Offline-first apps need identical logic on client (SQLite) and server (Postgres)
- Testing - Running tests against SQLite while production uses Postgres
The Solution
LQL is a single query language that transpiles to any SQL dialect. Write once, deploy everywhere.
Users
|> filter(fn(row) => row.Age > 18 and row.Status = 'active')
|> join(Orders, on = Users.Id = Orders.UserId)
|> group_by(Users.Id, Users.Name)
|> select(Users.Name, sum(Orders.Total) as TotalSpent)
|> order_by(TotalSpent desc)
|> limit(10)
This transpiles to correct SQL for PostgreSQL, SQLite, or SQL Server.
Use Cases
Cross-Database Migrations
Define schema changes in LQL. Migration.CLI generates the right SQL for your target database.
Cross DB Platform Business Logic With Triggers
Write triggers and constraints in LQL. Deploy the same logic to any database.
Offline-First Sync
Sync framework uses LQL for conflict resolution. Same logic runs on mobile (SQLite) and server (Postgres).
Integration Testing
Test against SQLite locally, deploy to Postgres in production. Same queries, same results.
Quick Start
CLI Tool
dotnet tool install -g LqlCli.SQLite
lql --input query.lql --output query.sql
NuGet Packages
<PackageReference Include="Lql.SQLite" Version="*" />
<PackageReference Include="Lql.Postgres" Version="*" />
<PackageReference Include="Lql.SqlServer" Version="*" />
Programmatic Usage
using Lql;
using Lql.SQLite;
var lql = "Users |> filter(fn(row) => row.Age > 21) |> select(Name, Email)";
var sql = LqlCodeParser.Parse(lql).ToSql(new SQLiteContext());
F# Type Provider
Validate LQL queries at compile time. Invalid queries cause compilation errors, not runtime errors.
Installation
<PackageReference Include="Lql.TypeProvider.FSharp" Version="*" />
Basic Usage
open Lql
// Define types with validated LQL - errors caught at COMPILE TIME
type GetUsers = LqlCommand<"Users |> select(Users.Id, Users.Name, Users.Email)">
type ActiveUsers = LqlCommand<"Users |> filter(fn(row) => row.Status = 'active') |> select(*)">
// Access generated SQL and original query
let sql = GetUsers.Sql // Generated SQL string
let query = GetUsers.Query // Original LQL string
What Gets Validated
The type provider validates your LQL at compile time and generates two properties:
Query- The original LQL query stringSql- The generated SQL (SQLite dialect)
Query Examples
// Select with columns
type SelectColumns = LqlCommand<"Users |> select(Users.Id, Users.Name, Users.Email)">
// Filtering with AND/OR
type FilterComplex = LqlCommand<"Users |> filter(fn(row) => row.Users.Age > 18 and row.Users.Status = 'active') |> select(*)">
// Joins
type JoinQuery = LqlCommand<"Users |> join(Orders, on = Users.Id = Orders.UserId) |> select(Users.Name, Orders.Total)">
type LeftJoin = LqlCommand<"Users |> left_join(Orders, on = Users.Id = Orders.UserId) |> select(*)">
// Aggregations with GROUP BY and HAVING
type GroupBy = LqlCommand<"Orders |> group_by(Orders.UserId) |> select(Orders.UserId, count(*) as order_count)">
type Having = LqlCommand<"Orders |> group_by(Orders.UserId) |> having(fn(g) => count(*) > 5) |> select(Orders.UserId, count(*) as cnt)">
// Order, limit, offset
type Pagination = LqlCommand<"Users |> order_by(Users.Name asc) |> limit(10) |> offset(20) |> select(*)">
// Arithmetic expressions
type Calculated = LqlCommand<"Products |> select(Products.Price * Products.Quantity as total)">
Compile-Time Error Example
Invalid LQL causes a build error with line/column position:
// This FAILS to compile with: "Invalid LQL syntax at line 1, column 15"
type BadQuery = LqlCommand<"Users |> selectt(*)"> // typo: 'selectt'
Executing Queries
open Microsoft.Data.Sqlite
let executeQuery() =
use conn = new SqliteConnection("Data Source=mydb.db")
conn.Open()
// SQL is validated at compile time, safe to execute
use cmd = new SqliteCommand(GetUsers.Sql, conn)
use reader = cmd.ExecuteReader()
// ... process results
Pipeline Operations
| Operation | Description |
|---|---|
select(cols...) |
Choose columns |
filter(fn(row) => ...) |
Filter rows |
join(table, on = ...) |
Join tables |
left_join(table, on = ...) |
Left join |
group_by(cols...) |
Group rows |
having(fn(row) => ...) |
Filter groups |
order_by(col [asc/desc]) |
Sort results |
limit(n) / offset(n) |
Pagination |
distinct() |
Unique rows |
union(query) |
Combine queries |
VS Code Extension
Search for "LQL" in VS Code Extensions for syntax highlighting and IntelliSense.
Website
Visit lql.dev for interactive playground.
License
MIT License