{"id":19898407,"url":"https://github.com/jacentino/sqlfun","last_synced_at":"2025-10-07T20:44:40.125Z","repository":{"id":22824481,"uuid":"95048245","full_name":"jacentino/SqlFun","owner":"jacentino","description":"Idiomatic data access for F#","archived":false,"fork":false,"pushed_at":"2024-01-16T20:18:51.000Z","size":10693,"stargazers_count":78,"open_issues_count":4,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-10T01:12:05.465Z","etag":null,"topics":["async","data-access","fsharp","functional-programming","micro-orm","reader-monad","sql","sql-query"],"latest_commit_sha":null,"homepage":"","language":"TSQL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jacentino.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-06-21T21:35:30.000Z","updated_at":"2025-03-31T19:02:26.000Z","dependencies_parsed_at":"2022-08-01T07:49:35.291Z","dependency_job_id":"bbaa35b2-ddf7-459a-adcb-063b843657b4","html_url":"https://github.com/jacentino/SqlFun","commit_stats":{"total_commits":377,"total_committers":4,"mean_commits":94.25,"dds":0.07692307692307687,"last_synced_commit":"fbd7d793257375f79e197fcb08dee80e34f3c12b"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacentino%2FSqlFun","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacentino%2FSqlFun/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacentino%2FSqlFun/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacentino%2FSqlFun/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jacentino","download_url":"https://codeload.github.com/jacentino/SqlFun/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248137888,"owners_count":21053775,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","data-access","fsharp","functional-programming","micro-orm","reader-monad","sql","sql-query"],"created_at":"2024-11-12T19:04:22.523Z","updated_at":"2025-10-07T20:44:35.086Z","avatar_url":"https://github.com/jacentino.png","language":"TSQL","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SqlFun\nIdiomatic data access for F#\n\nSqlFun is a tool for writing data access code in F# functional way. \nIt's fast, type safe and gives you all powers of SQL, no custom query language constraints you.\nIt's also lightweight, you need to know a [general idea](https://github.com/jacentino/SqlFun/wiki/Basic-concepts) and few functions (and, of course SQL).\n\nIt's available as a [Nuget package](https://www.nuget.org/packages/SqlFun/)\nThere are also extensions for MS SQL ([[1]](https://www.nuget.org/packages/SqlFun.MsSql), [[2]](https://www.nuget.org/packages/SqlFun.MsDataSql)), [PostgreSQL](https://www.nuget.org/packages/SqlFun.NpgSql) and [Oracle](https://www.nuget.org/packages/SqlFun.Oracle) databases.\n\n## Features\n* Works with any ADO.NET provider\n* All SQL features available\n* Type safety\n* High performance\n* Compound, hierarchical query parameters\n* Compound, hierarchical query results\n* Support for parameter conversions\n* Support for result transformations\n* Support for enum types\n* Asynchronous queries\n* Composable, template-based queries\n* Auto-generated CRUD operations\n* Computation expressions for connection and transaction handling\n* Support for large dataset processing\n\n## Supported databases\nIn its core SqlFun does not use any features specific to some db provider, so it works with any ADO.NET provider. \nThe only limitation is possibility of execution of commands in `SchemaOnly` mode.\n\nIt was tested against MS SqlServer, PostgreSQL, Oracle, MySQL and SQLite.\n\nThere are four extensions, enabling provider-specific features:\n* the extension for MS SQL, that allows to use table valued parameters\n* the extension for PostgreSQL, making use of array parameters possible and adding more comfortable Bulk Copy mechanism\n* the extension for Oracle, adding some adaptations, like binding parameters by name, and allowing to use array parameters\n* the extension for SQLite, that allows to use date and time values\n\n## Limitations\nNot all databases manage `SchemaOnly` behavior properly:\n* MySQL, PostgreSQL and Oracle performs well only for queries that return some results - commands not returning any are executed as with `Default` behavior\n* MS SQL doesn't recognize temporary tables in `SchemaOnly` executions, although, you can use table variables instead\n\n## How it works\nMost of us think about data access code as a separate layer. We don't like to spread SQL queries across all the application.\nBetter way is to build an API exposing your database, consisting of structures representing database data, and functions responsible for processing this data (great object-oriented example is [Insight.Database](https://github.com/jonwagner/Insight.Database/wiki/Auto-Interface-Implementation) automatic interface implementation). SqlFun makes it a design requirement.\n\n### Installation\nSqlFun can be added to your solution from Package Manager Console:\n\n```PowerShell\nPM\u003e Install-Package SqlFun\n```\n\n### Configuration\nFirst step is to define function creating database connection and config record:\n```fsharp\nlet createConnection () = new SqlConnection(\u003cyour database connection string\u003e)\nlet generatorConfig = createDefaultConfig createConnection\n```\nand wire it up with functions responsible for generating queries (using partial application):\n```fsharp \nlet sql commandText = sql generatorConfig commandText\n\nlet proc name = proc generatorConfig name\n```\nand for executing them:\n```fsharp \nlet run f = DbAction.run createConnection f\n\nlet runAsync f = AsyncDb.run createConnection f\n```    \n### Data structures\nThen, data structures should be defined for results of your queries.\n```fsharp \ntype Post = {\n    id: int\n    blogId: int\n    name: string\n    title: string\n    content: string\n    author: string\n    createdAt: DateTime\n    modifiedAt: DateTime option\n    modifiedBy: string option\n    status: PostStatus\n}\n    \ntype Blog = {\n    id: int\n    name: string\n    title: string\n    description: string\n    owner: string\n    createdAt: DateTime\n    modifiedAt: DateTime option\n    modifiedBy: string option\n    posts: Post list\n}\n```    \nThe most preferrable way is to use F# record types. Record fields should reflect query result columns, because they are mapped by name.\n    \n### Defining queries\nThe best way of defining queries is to create variables for them and place in some module:\n```fsharp \nmodule Blogging =    \n \n    let getBlog: int -\u003e DbAction\u003cBlog\u003e = \n        sql \"select id, name, title, description, owner, createdAt, modifiedAt, modifiedBy \n             from Blog \n             where id = @id\"\n            \n    let getPosts: int -\u003e DbAction\u003cPost list\u003e = \n        sql \"select id, blogId, name, title, content, author, createdAt, modifiedAt, modifiedBy, status \n             from post \n             where blogId = @blogId\"\n```        \nThe functions executing queries are generated during a first access to the module contents. \n\nAt that stage, all the type checking is performed, so it's easy to make type checking part of automatic testing - one line of code for each module is needed.\n\nThe generating process uses reflection heavily, but no reflection is used while processing a query, since generated code is executed.\n\n### Executing queries\nSince your queries return `DbAction\u003c't\u003e`, they can be passed to the `run` function after applying preceding parameters.\n```fsharp \nlet blog = Blogging.getBlog 1 |\u003e run\n```\n### Async support\nThe preferrable way is to define query as asynchronous:\n```fsharp \nlet getBlog: int -\u003e AsyncDb\u003cBlog\u003e = \n    sql \"select id, name, title, description, owner, createdAt, modifiedAt, modifiedBy \n         from Blog \n         where id = @id\"\n```\nand then, execute as async:\n```fsharp \nasync {\n    let! blog = Blogging.getBlog 1 |\u003e runAsync\n    ...\n}\n```\n### Result transformations\nSince the ADO.NET allows to execute many sql commands at once, it's possible to utilize it with SqlFun. The result is a tuple:\n```fsharp \nlet getBlogWithPosts: int -\u003e AsyncDb\u003cBlog * Post list\u003e = \n    sql \"select id, name, title, description, owner, createdAt, modifiedAt, modifiedBy \n         from Blog \n         where id = @id;\n         select id, blogId, name, title, content, author, createdAt, modifiedAt, modifiedBy, status \n         from post \n         where blogId = @id\"\n ```\n The call of `sql` returns some function, thus it can be composed with another function, possibly performing result transformations.\n Let extend the blog type with a `posts: Post list` property. In this case, two results can be combined with simple function:\n ```fsharp \nlet getBlogWithPosts: int -\u003e AsyncDb\u003cBlog\u003e = \n    sql \"select id, name, title, description, owner, createdAt, modifiedAt, modifiedBy \n         from Blog \n         where id = @id;\n         select id, blogId, name, title, content, author, createdAt, modifiedAt, modifiedBy, status \n         from post \n         where blogId = @id\"\n    \u003e\u003e AsyncDb.map (fun b pl -\u003e { b with posts = pl })\n```\nIn simple cases, when code follows conventions, transormations can be specified more declarative way:\n\n```fsharp \nlet getBlogWithPosts: int -\u003e AsyncDb\u003cBlog\u003e = \n    sql \"select id, name, title, description, owner, createdAt, modifiedAt, modifiedBy \n         from Blog \n         where id = @id;\n         select id, blogId, name, title, content, author, createdAt, modifiedAt, modifiedBy, status \n         from post \n         where blogId = @id\"\n    \u003e\u003e AsyncDb.map combine\u003c_, Post\u003e\n```\nThere are also functions that allow to combine multi-row results by joining many results or grouping wide results.\n\n### Compound parameters\nRecords can be parameters as well:\n```fsharp \nlet insertPost: Post -\u003e AsyncDb\u003cint\u003e = \n    sql \"insert into post \n                (blogId, name, title, content, author, createdAt, status)\n         values (@blogId, @name, @title, @content, @author, @createdAt, @status);\n         select scope_identity()\"\n```\nThe record fields are mapped to query parameters by name.\n\n### Stored procedures\nThe result of a function calling stored procedure should be a three-element tuple (return code, output params, result):\n```fsharp \t\nlet findPosts: (PostSearchCriteria * SignatureSearchCriteria) -\u003e AsyncDb\u003cint * unit * Post list\u003e =\n    proc \"FindPosts\"\n```\nbut there are transformers, that allow to ignore parts of it:\n```fsharp \nlet findPosts: (PostSearchCriteria * SignatureSearchCriteria) -\u003e Post list AsyncDb =\n    proc \"FindPosts\"\n    \u003e\u003e AsyncDb.map resultOnly\n```\t \n### Utilizing `dbaction` and `asyncdb` computation expressions\nIt's easy to execute one query with `runAsync` or `run` function. To execute more queries in a context of one open connection, computation expression can be used:\n```fsharp \nasyncdb {\n    let! postId = Blogging.insertPost post\n    do! Blogging.insertComments postId comments\n    do! Blogging.insertTags postId tags\n} |\u003e runAsync\n```    \nThe synchronous equivalent of this expression is `dbaction`.\n\n### Transactions\nTo execute some queries in transaction, the `inTransaction` function should be used:\n```fsharp \nasyncdb {\n    let! postId = Blogging.insertPost post\n    do! Blogging.insertComments postId comments\n    do! Blogging.insertTags postId tags\n} \n|\u003e AsyncDb.inTransaction\n|\u003e runAsync\n```\nIts synchronous equivalent is `DbAction.inTransaction`.\n\n## Documentation \u0026 examples\n\nFor more comprehensive documentation refer project [github pages](https://jacentino.github.io/SqlFun).\n\nFor more examples refer [test project](https://github.com/jacentino/SqlFun/tree/master/SqlFun.Tests).\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjacentino%2Fsqlfun","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjacentino%2Fsqlfun","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjacentino%2Fsqlfun/lists"}