Code Monkey home page Code Monkey logo

sqlfrags's Introduction

SqlFrags - a SQL generator for F#

Build Status

The problem:

Tools like Dapper make it easy to materialize objects, but you need to write SQL strings by hand.

SQL generation is painful and requires careful typing. It can be made easier by adding a static component. SqlFrags is that static component.

F# gives you a nice list syntax, so you provide a list of "Frags" (a distriminated union of SQL fragments you want to have in your query), like so:

open SqlFrags.SqlGen

// declaring some table names for later reference...

let Emp = Table "employee"
let Org = Table "organization"

let upd = [
    Emp.Update
        [
            "salary", "10"
            "name", "'heimo'"
            "address", "@addressparam"

        ]
    WhereS "foo > bar"
]


let rendered = upd |> Frags.Emit SqlSyntax.Any

This renders to string:

update employee
set salary = 10, name = 'heimo', address = @addressparam
where foo > bar

That should make it a bit harder to screw up.

Simple "select ... from ..." is simple (with Table extension methods):

let fields = [ Emp.Select [ "Salary"; "Name" ] ]
let all = [ Emp.SelectAll ]

You can do nested subqueries:

let nested =
    [ Raw "select * from"
        NestAs("root", [ User.SelectAll ]) ]

This gives you indented, parenthesized SQL (aliased as you specified):

select *
from
(
    select *
    from USER_DATA
) root

Simple updates are easy enough to do with existing micro-orms like Dapper.Contrib or PetaPoco. However, you often need to produce complex queries, so you can crank up the difficulty with nesting, aliases etc. Emitting completely illegal SQL is fine, SqlFrags is not one to second guess you - it diligently renders the garbage you feed it:

let query =
    [ Emp.SelectC <| Emp.Cols [ "id"; "name"; "salary"; "team" ]
        SelectAs [ Emp?Foo, "testalias" ]
        From Emp
        WhereS "salary > 1000"
        Many [ Skip
                WhereS "foo > bar"
                Skip ]
        JoinOn(Org.Col "ID", Emp.Col "OrgID", Table "OrgAlias", "")
        Where [ Emp?Company == Org?Id ]
        GroupBy [ "team"; "foo" ]
        OrderBy [ "salary asc"; "hello desc" ] ]

Did you you see that JoinOn? It does:

inner join organization OrgAlias on employee.OrgID=OrgAlias.ID

If you wanted "outer", just pass "outer" as the last argument to JoinOn (empty string defaults to "inner join").

And what are those "Many" and Skip parts? They are provided for convenience, when splicing sublists in programmatically generated queries.

There are some overloaded operators and extension methods to simplify select and where clauses:

        // select stuff with --> and --->
[ Emp --> [ "Salary"; "Name" ] ]
|> rendersTo "select Salary, Name\nfrom employee"

// same with extension method - please use extension methods instead of operators!
[ Emp.Select [ "Salary"; "Name" ] ]
|> rendersTo "select Salary, Name\nfrom employee"

[ Emp ---> [ Emp?Salary; Emp?Name ] ]
|> rendersTo "select employee.Salary, employee.Name\nfrom employee"
// ===^ (where condition without quoting)
[
    Emp --> ["*"]
    Where [Emp?ID ===^ "@ID"]
] |> rendersTo "select *\nfrom employee\nwhere employee.ID=@ID"

// === (where condition with quoting)
[
    Emp --> ["*"]
    Where [Emp?ID === "jorma"]
] |> rendersTo "select *\nfrom employee\nwhere employee.ID='jorma'"

[ Emp.Delete ]
|> rendersTo "delete from employee"

[
    Where [Emp?ID.Equals "@bar"]
    Where [Emp?ID.EqualsQ "bar"]
    Where [Emp?ID.EqualsCol Org?ID]
    Where [Emp?ID.In "[1,2]"]
    Where [Emp?ID.Op "<" "12"]

] |> rendersTo "where employee.ID=@bar\nwhere employee.ID='bar'\nwhere employee.ID=organization.ID\nwhere employee.ID in [1,2]\nwhere employee.ID < 12"

FAQ

This README is out of date

Maybe. See the test case for up to date syntax.

Why SqlFrags when there are millions of other SQL generators on the web?

There aren't for .NET. Search for yourself.

Can I use this on C#?

Nope, too tied to F# data structures. Similar "mechanical SQL emission" philosophy for C# is implemented e.g. in https://github.com/sqlkata/querybuilder

What's up with the name?

Yes, this project was earlier known as "Fapper", which turned out to be rather problematic. Enjoy the new name.

What databases does it support?

All of them, but depends. E.g. "Page" fragment won't work in old Oracle versions. If your SQL contains @ like query parameters they won't work with oracle (and I didn't yet do a helper for that). You get the idea. The API has support to branch the rendering based on Sql syntax, but currently only SqlSyntax.Any is used.

Why not use XXX or YYY instead?

SqlFrags allows you to compose queries from fragments. You can create the fragments (or lists of fragments) in functions, assign repeated fragments to variables, etc. This is like creating HTML with Suave.Html, Giraffe ViewModel or Elm.

You don't need to have access to database schema (yet alone live database, like with SqlProvider) to create queries. This helps if you are building software against arbitrary databases (think tools like Django Admin), or where schema is configurable.

You don't need to learn to "trick" the ORM to emit the SQL you want. What you write is what you get.

The codebase is trivial mapping of DU cases to emitted strings:

let rec serializeFrag (syntax: SqlSyntax) frag =
    match frag with
    | SelectS els -> "select " + colonList els
    | Select cols ->
        "select " +
            (cols |> Seq.map (fun c -> c.Str) |> colonList)
    | SelectAs cols ->
        "select " +
            (cols |> Seq.map (fun (c,alias) -> sprintf "%s as %s" c.Str alias) |> colonList)
    | FromS els -> "from " + colonList els
    | From (Table t) -> "from " + t
...

So, if you want to add something you need, you just do it. Copy the SqlGen.fs to your project, or make a PR and join the SqlFrags family.

Installation

https://www.nuget.org/packages/SqlFrags

License

MIT

sqlfrags's People

Contributors

vivainio avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

sqlfrags's Issues

Like sqlkata?

Why Fapper when there are millions of other SQL generators on the web?
There aren't for .NET. Search for yourself.

Are you aware of sqlkata?
Perhaps its worth mentioning as a C# alternative?

https://sqlkata.com/docs

The value of constructor `SelectS` is not defined

Documentation shows an example with

SelectS ["*"]

but I cannot find it in the source code and my demo does not compile.

How about following function to fix it?

let SelectS (columns: seq<string>) : Frag =
    String.concat ", " columns
    |> (+) "select "
    |> Raw

Recommend name change

Fapper is NSFW, and this will definitely either lead to adoption issues or a fork over the name. Some American will otherwise get sued for a hostile work environment, or worse actually empower a hostile work environment.

My personal recommendations are Faddish or FDapper.

Sorry to be no fun, but F# actually gets used at work in some places.

Library Name Change

Love the idea! This is definitely something community needs. The name, though is unfortunate because despite best intentions, it may not pass the HR "sniff test", which will undoubtedly hurt adoption.

Looking forward to giving this a test drive!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.