{"id":26084201,"url":"https://github.com/mk3008/carbunql","last_synced_at":"2025-04-12T00:50:37.870Z","repository":{"id":65237811,"uuid":"581167836","full_name":"mk3008/Carbunql","owner":"mk3008","description":"Carbunql is an advanced Raw SQL editing library.","archived":false,"fork":false,"pushed_at":"2025-01-28T12:57:27.000Z","size":17210,"stargazers_count":41,"open_issues_count":20,"forks_count":4,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-10T03:08:44.148Z","etag":null,"topics":["dynamic-programming","raw-sql","sql","sql-builders","sql-parsing"],"latest_commit_sha":null,"homepage":"https://mk3008.github.io/Carbunql/","language":"C#","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/mk3008.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":"2022-12-22T13:01:33.000Z","updated_at":"2025-02-14T13:37:19.000Z","dependencies_parsed_at":"2023-09-21T18:07:19.883Z","dependency_job_id":"cdaa1ec7-665a-4748-956b-fb4975ee479b","html_url":"https://github.com/mk3008/Carbunql","commit_stats":{"total_commits":159,"total_committers":1,"mean_commits":159.0,"dds":0.0,"last_synced_commit":"485bdbc9c056914dd24cf2cd05a7a2dd25f5e311"},"previous_names":[],"tags_count":56,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mk3008%2FCarbunql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mk3008%2FCarbunql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mk3008%2FCarbunql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mk3008%2FCarbunql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mk3008","download_url":"https://codeload.github.com/mk3008/Carbunql/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248501901,"owners_count":21114681,"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":["dynamic-programming","raw-sql","sql","sql-builders","sql-parsing"],"created_at":"2025-03-09T04:51:02.689Z","updated_at":"2025-04-12T00:50:37.847Z","avatar_url":"https://github.com/mk3008.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Carbunql\n\n![image](https://github.com/user-attachments/assets/3c364944-8de3-4200-8293-3492f7680e06)\n\n![GitHub](https://img.shields.io/github/license/mk3008/Carbunql)\n![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/mk3008/Carbunql)\n![Github Last commit](https://img.shields.io/github/last-commit/mk3008/Carbunql)  \n[![SqModel](https://img.shields.io/nuget/v/Carbunql.svg)](https://www.nuget.org/packages/Carbunql/) \n[![SqModel](https://img.shields.io/nuget/dt/Carbunql.svg)](https://www.nuget.org/packages/Carbunql/) \n\nThis C# library allows you to flexibly edit Raw SQL as object models. It supports easy addition and modification of columns and search conditions, as well as transformations to CTEs (Common Table Expressions), subqueries, and even insert and update queries.\n\nBy using Carbunql, you can maximize the reusability of Raw SQL and efficiently manage your queries.\n\n## Demo\n\nThis is a sample that accepts any search conditions and adds them as search conditions. If no conditions are entered, they will be excluded from the search conditions.\n\n### Source code\n\nI will only extract the parts that process SQL. Please refer to the link below for the complete source.\nhttps://github.com/mk3008/Carbunql/blob/main/demo/DynamicFiltering/Program.cs\n\n```cs\nprivate static string GenerateProductQuery(decimal? minPrice, decimal? maxPrice, string? category, bool? inStock)\n{\n    var sql = \"\"\"\nSELECT\n    p.product_id,\n    p.product_name,\n    p.price,\n    p.category,\n    p.in_stock\nFROM\n    product as p\n\"\"\";\n\n    var pname = \":category\";\n\n    // Convert the selection query to an object\n    var sq = SelectQuery.Parse(sql)\n        .GreaterThanOrEqualIfNotNullOrEmpty(\"price\", minPrice)\n        .LessThanOrEqualIfNotNullOrEmpty(\"price\", maxPrice)\n        .AddParameter(pname, category)\n        .EqualIfNotNullOrEmpty(\"category\", pname)\n        .EqualIfNotNullOrEmpty(\"in_stock\", inStock);\n\n    return sq.ToText();\n}\n```\n\n### Parameter\n\n```\nEnter minimum price (or leave blank to omit):\n\nEnter maximum price (or leave blank to omit):\n100\nEnter category (or leave blank to omit):\ntea\nEnter in-stock status (true/false) (or leave blank to omit):\ntrue\n```\n\n### Generated SQL\n\nYou can see that only the specified search criteria has been inserted.\n\n```sql\n/*\n  :category = 'tea'\n*/\nSELECT\n    p.product_id,\n    p.product_name,\n    p.price,\n    p.category,\n    p.in_stock\nFROM\n    product AS p\nWHERE\n    p.price \u003c= 100\n    AND p.category = :category\n    AND p.in_stock = True\n```\n\n## Advanced Demo\n\nThis is a sample that displays an aggregate report for a specified year and month.\n\nYou can see that even complex processing using CTE and UNION can be written simply.\n\n### Source code\n\nI will only extract the parts that process SQL. Please refer to the link below for the complete source.\nhttps://github.com/mk3008/Carbunql/blob/main/demo/DynamicCTE/Program.cs\n\n```cs\npublic static string GenerateReportQuery(bool includeSummary, DateTime summaryMonth)\n{\n    string dailySummaryQuery = \"\"\"\n        SELECT\n            sale_date\n            , sum(amount) AS amount_total\n            , '' as caption \n            , 1 as sort_number\n        FROM\n            salse\n        GROUP BY\n            sale_date\n        \"\"\";\n\n    var dailySummary = FluentTable.Create(dailySummaryQuery, \"daily_summary\", \"d\");\n\n    string monthlySummaryQuery = \"\"\"\n        SELECT\n            date_trunc('month', sale_date) + '1 month -1 day' as sale_date\n            , sum(amount) AS amount_total\n            , 'monthly total' as caption \n            , 2 as sort_number\n        FROM\n            salse\n        GROUP BY\n            date_trunc('month', sale_date) + '1 month -1 day'\n        \"\"\";\n\n    var monthlySummary = FluentTable.Create(monthlySummaryQuery, \"monthly_summary\", \"m\");\n\n    // Create daily summary query\n    var sq = new SelectQuery()\n        .From(dailySummary)\n        .SelectAll(dailySummary);\n\n    if (includeSummary)\n    {\n        // Add monthly summary query with UNION ALL\n        sq.UnionAll(() =\u003e\n        {\n            var xsq = new SelectQuery()\n                .From(monthlySummary)\n                .SelectAll(monthlySummary);\n            return xsq;\n        });\n    }\n\n    // Add date filter condition\n    var saleDate = \":sale_date\";\n    sq.AddParameter(saleDate, summaryMonth)\n        .BetweenInclusiveStart(\"sale_date\", saleDate, $\"{saleDate}::timestamp + '1 month'\");\n\n    // Convert the entire query to a CTE\n    sq = sq.ToCTEQuery(\"final\", \"f\");\n\n    // Add sorting conditions\n    sq.RemoveSelect(\"sort_number\")\n        .OrderBy(\"sale_date\")\n        .OrderBy(\"sort_number\");\n\n    return sq.ToText();\n}\n```\n\n### Parameter\n\n```\nWhich month to summarize? (yyyy-mm-dd)\n2024-08-01\nInclude monthly summary rows? (true/false)\ntrue\n```\n\n### Generated SQL\n\nA query that combines a daily aggregation query and a monthly aggregation query will be output.\n\nPlease also note that the search conditions are inserted in two places. Carbunql will insert the search conditions in the appropriate positions without you having to specify them in detail.\n\n```sql\n/*\n  :sale_date = '2024/08/01 0:00:00'\n*/\nWITH\n    daily_summary AS (\n        SELECT\n            sale_date,\n            SUM(amount) AS amount_total,\n            '' AS caption,\n            1 AS sort_number\n        FROM\n            salse\n        WHERE\n            :sale_date \u003c= salse.sale_date\n            AND salse.sale_date \u003c :sale_date::timestamp + '1 month'\n        GROUP BY\n            sale_date\n    ),\n    monthly_summary AS (\n        SELECT\n            DATE_TRUNC('month', sale_date) + '1 month -1 day' AS sale_date,\n            SUM(amount) AS amount_total,\n            'monthly total' AS caption,\n            2 AS sort_number\n        FROM\n            salse\n        WHERE\n            :sale_date \u003c= salse.sale_date\n            AND salse.sale_date \u003c :sale_date::timestamp + '1 month'\n        GROUP BY\n            DATE_TRUNC('month', sale_date) + '1 month -1 day'\n    ),\n    final AS (\n        SELECT\n            d.sale_date,\n            d.amount_total,\n            d.caption,\n            d.sort_number\n        FROM\n            daily_summary AS d\n        UNION ALL\n        SELECT\n            m.sale_date,\n            m.amount_total,\n            m.caption,\n            m.sort_number\n        FROM\n            monthly_summary AS m\n    )\nSELECT\n    f.sale_date,\n    f.amount_total,\n    f.caption\nFROM\n    final AS f\nORDER BY\n    f.sale_date,\n    f.sort_number\n```\n\n## Features\n\n- Convert Raw SQL to object models.\n- Convert object models back to Raw SQL.\n- Format Raw SQL.\n- Convert select queries to various query types:\n  - Insert queries\n  - Update queries\n  - Delete queries\n  - Merge queries\n  - Table creation queries\n- Edit columns and search conditions in select queries.\n- No DBMS environment or entity classes required.\n- Perform basic syntax checks.\n- The [Carbunql.Dapper](https://www.nuget.org/packages/Carbunql.Dapper) library allows you to run queries with Dapper.\n\nYou can try out some of the processing on the online demo site.\n\nhttps://mk3008.github.io/Carbunql/\n\n![image](https://github.com/user-attachments/assets/21ddb1af-ae13-4405-a2c9-40b15f372e84)\n\n## Constraints\n\n- Comments are removed when modeling.\n\n## Getting started\nInstall the package from NuGet.\n\n```\nPM\u003e Install-Package Carbunql\n```\n\nhttps://www.nuget.org/packages/Carbunql/\n\n## Model a select query\n\nJust pass the select query string to the constructor of the SelectQuery class.\n\n```cs\nusing Carbunql;\n\nvar text = \"select s.sale_id, s.store_id, date_trunc('month', s.sale_date) as allocate_ym, s.sale_price from sales as s\";\nvar sq = SelectQuery.Parse(text);\n```\n\n## Return the model to a select query\n\nUse the `ToText` or `ToOneLineText` method.\n\nThe `ToText` method will return a formatted select query. Parameter information will also be added as a comment.\n\nThe `ToOneLineText` method will output a single line without formatting. Use the ToOneLineText method if performance is important.\n\n```cs\nusing Carbunql;\n\nvar text = \"select s.sale_id, s.store_id, date_trunc('month', s.sale_date) as allocate_ym, s.sale_price from sales as s\";\nvar sq = SelectQuery.Parse(text);\nvar query = sq.ToOneLineText();\n```\n\n## Execute the query\n\nIf you use [Carbunql.Dapper](https://www.nuget.org/packages/Carbunql.Dapper), you can execute it directly without writing it back to RawSQL.\n\nThis is the recommended method because parameter information is also passed.\n\n```\nPM\u003e Install-Package Carbunql.Dapper\n```\n\nThe full source code for the demo can be found below.\nhttps://github.com/mk3008/Carbunql/blob/main/test/Carbunql.Dapper.Test/DapperTest.cs\n\n```cs\nusing var cn = PostgresDB.ConnectionOpenAsNew(Logger);\n\nvar sql = @\"with\ndata_ds(c1, c2) as (\n    values\n    (1,1)\n    , (1,2)\n    , (2,1)\n    , (2,2)\n)\nselect\n    *\nfrom\n    data_ds\nwhere\n    c1 = :val\n\";\n\nvar sq = SelectQuery.Parse(sql);\nsq.AddParameter(\":val\", 1);\n\nusing var r = cn.ExecuteReader(sq);\nvar cnt = 0;\nwhile (r.Read())\n{\n    cnt++;\n}\nAssert.Equal(2, cnt);\n```\n\n## Create an empty select query\n\nIf you do not specify arguments in the constructor, a model without SELECT and FROM clauses will be created. Please add SELECT and FROM clauses manually.\n\n```cs\nusing Carbunql;\n\nvar sq = new SelectQuery();\n```\n\n## Add a FROM clause\n\nIf you added an empty select query, use the `From` function to manually add a FROM clause. The first argument is the table name, and the second argument is the alias name.\n\n\u003e [!NOTE]\n\u003e Don't forget to import the namespace Carbunql.Fluent.\n\n```cs\nusing Carbunql;\nusing Carbunql.Fluent;\n\nvar sq = new SelectQuery()\n  .From(\"customer\", \"c\");\n```\n\n## Add a column to select\n\nYou can add a column to select by using the `Select` function. The first argument is the column name, and the second argument is the column alias name. The column alias name is optional.\n\n```cs\nusing Carbunql;\nusing Carbunql.Fluent;\n\nvar sq = new SelectQuery()\n  .From(\"customer\", \"c\")\n  .Select(\"c.customer_id\")\n  .Select(\"c.first_name || c.last_name\", \"customer_name\");\n```\n\n## Add search conditions\n\nYou can add search conditions by using the `AddWhere` method.\n\nThe first argument is the name of the column to which you want to add a condition.\n\nThe second argument is a delegate (or lambda expression) that takes the column source and column name as input and generates the condition part of the SQL.\n\n```cs\nusing Carbunql;\n\nvar text = \"select s.sale_id, s.store_id, date_trunc('month', s.sale_date) as allocate_ym, s.sale_price from sales as s\";\nvar sq = SelectQuery.Parse(text)\n  .AddWhere(\"sale_id\", (source, column) =\u003e $\"{source.Alias}.{column} = 1\");\n```\n\nThe above code can be written even more succinctly if we import the namespace Carbunql.Fluent:\n\n```cs\nusing Carbunql;\nusing Carbunql.Fluent;\n\nvar text = \"select s.sale_id, s.store_id, date_trunc('month', s.sale_date) as allocate_ym, s.sale_price from sales as s\";\nvar sq = SelectQuery.Parse(text)\n  .Equal(\"sale_id\", 1);\n```\n\n## About the AddWhere function\n\nPlease note the following specifications.\n\n- It is added with AND conditions.\n- If the column name to be searched does not exist, an error will occur.\n- The search target is the entire query. The query reference order is analyzed, and the conditions are inserted only for the query source at the deepest position.\n\nFor example, the search condition for the column `sale_id` is inserted only for `s` in the subquery.\nIt is not inserted for the query source `subq`. This is because it references a query source to which the search condition has already been applied.\n\n```sql\nselect\n    subq.sale_id\nfrom\n    (\n        select\n            s.sale_id\n        from\n            sale as s --the deepest query source to which sale_id belongs\n    ) subq\n```\n\nIn this way, since the reference order of the query source is analyzed, the search condition is inserted in the optimal position without any consideration of the insertion location.\n\nHowever, since this library does not reference the DBMS (table definition), it cannot detect column names that are not explicitly stated in the select query.\n\nIf you try to insert a search condition for the column `price` into the select query above, an error will occur even if the column is defined in the DBMS.\n\nIn this case, make it clear that there is a column name in the select query. If you write it like this, the column `price` will be detected.\n\n```sql\nselect\n    subq.sale_id\nfrom\n    (\n        select\n            s.sale_id\n            , s.price\n        from\n            sale as s\n    ) subq\n```\n\nAlso, when searching for a column, it checks which query source it is attributed to. For this reason, if the alias name of the query source is omitted, it may not be detected correctly.\n\nFor example, in the case of a single query source like the one below, the query source is only `s`, so it is possible to identify the column `price`.\n\n```\nselect\n    sale_id\n    , price\nfrom\n    sale as s\n```\n\nHowever, if a table is joined, parsing will fail even if the SQL is executable because it does not refer to the DBMS (table definition). In the following query, it is not possible to determine whether the column \"price\" belongs to `s` or `c` based on the information in the select query alone.\n\n```\nselect\n    sale_id\n    , price\nfrom\n    sale as s\n    inner join customer as c on s.customer_id = c.customer_id\n```\n\n## Referenced Libraries\n### ZString / MIT License\nhttps://github.com/Cysharp/ZString\n\nhttps://github.com/Cysharp/ZString/blob/master/LICENSE\n\nCopyright (c) 2020 Cysharp, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n### Dapper / Apache License 2.0\nhttps://github.com/DapperLib/Dapper\n\nhttps://github.com/DapperLib/Dapper/blob/main/License.txt\n\nThe Dapper library and tools are licenced under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0\n\nThe Dapper logo is copyright Marc Gravell 2021 onwards; it is fine to use the Dapper logo when referencing the Dapper library and utilities, but\nthe Dapper logo (including derivatives) must not be used in a way that misrepresents an external product or library as being affiliated or endorsed\nwith Dapper. For example, you must not use the Dapper logo as the package icon on your own external tool (even if it uses Dapper internally),\nwithout written permission. If in doubt: ask.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmk3008%2Fcarbunql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmk3008%2Fcarbunql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmk3008%2Fcarbunql/lists"}