|
1 | 1 | # CarbunqleX - SQL Parser and Modeler |
2 | 2 |
|
3 | | -**CarbunqleX** dramatically improves the reusability and maintainability of RawSQL. |
| 3 | + |
| 4 | + |
| 5 | + |
| 6 | +[](https://www.nuget.org/packages/Carbunqlex/) |
| 7 | +[](https://www.nuget.org/packages/Carbunqlex/) |
4 | 8 |
|
5 | | -By deeply analyzing the AST of RawSQL—including subqueries and CTEs—it enables advanced query modifications while preserving semantic integrity. With CarbunqleX, you can: |
6 | | -- Overwrite selection columns |
7 | | -- Insert `JOIN` and `WHERE` conditions |
8 | | -- Transform queries into various SQL statements (`CREATE TABLE AS`, `INSERT INTO`, `UPDATE`, `DELETE`) |
| 9 | +## 🚀 Overview |
9 | 10 |
|
10 | | -## 🚀 Advanced CTE Handling |
11 | | -One of CarbunqleX's most powerful features is its **flexible handling of CTEs**. |
12 | | -Normally, CTEs are limited to the `WITH` clause and cannot be directly referenced in `WHERE` or `JOIN` conditions. |
13 | | -However, CarbunqleX detects existing CTEs and **lifts them to the top level**, allowing seamless inclusion in conditions where they would otherwise be invalid. |
14 | | -This enables highly dynamic and reusable query transformations. |
| 11 | +**CarbunqleX** enhances the reusability and maintainability of raw SQL queries by deeply analyzing their Abstract Syntax Tree (AST). This allows for powerful transformations while preserving query semantics. With CarbunqleX, you can: |
15 | 12 |
|
16 | | -## 💥 Easy Insertion of Search Conditions |
17 | | -Another standout feature of CarbunqleX is its **ease of inserting search conditions**. |
18 | | -With AST parsing, the library automatically identifies the most effective point for insertion, such as within subqueries or CTEs, ensuring that the conditions are applied optimally. |
| 13 | +- Modify selection columns |
| 14 | +- Inject `JOIN` and `WHERE` conditions dynamically |
| 15 | +- Transform queries into different SQL statements (`CREATE TABLE AS`, `INSERT INTO`, `UPDATE`, `DELETE`) |
19 | 16 |
|
20 | | -Unlike general libraries where you have to manually specify where to insert search conditions (usually at the top level of the query), CarbunqleX **automatically detects** the deepest relevant level of the query and inserts conditions at that level. |
21 | | -For instance, when dealing with a `GROUP BY` query, it's crucial to insert search conditions **before** the grouping to ensure they are effective. CarbunqleX handles this seamlessly without you needing to worry about the hierarchical structure of the query! |
| 17 | +## 💡 Key Features |
22 | 18 |
|
23 | | -## 🔥 Lightweight & Easy to Use |
24 | | -- **Minimal dependencies** – Works directly with RawSQL |
25 | | -- **No special setup or DBMS required** – Operates purely on query strings |
26 | | -- **Seamless integration** – Works alongside general ORM libraries |
| 19 | +### Advanced CTE Handling |
27 | 20 |
|
28 | | ---- |
| 21 | +CarbunqleX offers **flexible Common Table Expression (CTE) processing**. Traditionally, CTEs exist only within the `WITH` clause and cannot be referenced in `WHERE` or `JOIN` conditions. However, CarbunqleX detects existing CTEs and **lifts them to the top level**, making them accessible in places where they would otherwise be restricted. This enables highly flexible query modifications. |
29 | 22 |
|
30 | | -**Let's make RawSQL more powerful and reusable with CarbunqleX!** |
| 23 | +### Intelligent Search Condition Injection |
| 24 | + |
| 25 | +Unlike conventional SQL libraries, CarbunqleX automatically determines the most appropriate insertion point for search conditions, even within complex queries involving subqueries and CTEs. This ensures optimal filtering while preserving query integrity. For example, when dealing with `GROUP BY`, conditions are inserted **before** aggregation to ensure correctness. |
| 26 | + |
| 27 | +### Lightweight and Easy to Use |
| 28 | + |
| 29 | +- **Minimal dependencies** – Works directly with raw SQL |
| 30 | +- **No special setup or DBMS required** – Purely operates on query strings |
| 31 | +- **Seamless ORM integration** – Works alongside existing ORM frameworks |
| 32 | + |
| 33 | +## 📦 Installation |
| 34 | + |
| 35 | +To install [Carbunqlex](https://www.nuget.org/packages/Carbunqlex/), use the following command: |
| 36 | + |
| 37 | +```sh |
| 38 | +PM> NuGet\Install-Package Carbunqlex |
| 39 | +``` |
| 40 | + |
| 41 | +## 📖 Documentation |
| 42 | + |
| 43 | +### 1️⃣ Parsing a SQL Query |
| 44 | + |
| 45 | +Let's start by parsing a simple SQL query into an AST using `QueryAstParser.Parse`. We will then convert it back to SQL with `ToSql` and inspect its structure using `ToTreeString`. |
| 46 | + |
| 47 | +```csharp |
| 48 | +using Carbunqlex; |
| 49 | +using Carbunqlex.Parsing; |
| 50 | + |
| 51 | +var sql = "SELECT a.table_a_id, a.value FROM table_a AS a"; |
| 52 | +var query = QueryAstParser.Parse(sql); |
| 53 | + |
| 54 | +// Convert back to SQL |
| 55 | +Console.WriteLine("* SQL"); |
| 56 | +Console.WriteLine(query.ToSql()); |
| 57 | + |
| 58 | +// View AST structure (useful for debugging) |
| 59 | +Console.WriteLine("* AST"); |
| 60 | +Console.WriteLine(query.ToTreeString()); |
| 61 | +``` |
| 62 | + |
| 63 | +This basic example demonstrates how CarbunqleX converts SQL into a structured AST representation, making it easier to analyze and manipulate queries programmatically. |
| 64 | + |
| 65 | +### 2️⃣ Modifying the WHERE Clause |
| 66 | + |
| 67 | +Now, let's modify the query by injecting a `WHERE` condition. |
| 68 | + |
| 69 | +```csharp |
| 70 | +var query = QueryAstParser.Parse("SELECT a.table_a_id, a.value FROM table_a AS a"); |
| 71 | + |
| 72 | +// Inject a filter condition |
| 73 | +query.Where("value", w => w.Equal(1)); |
| 74 | + |
| 75 | +Console.WriteLine(query.ToSql()); |
| 76 | +``` |
| 77 | + |
| 78 | +#### 🔍 Expected SQL Output |
| 79 | + |
| 80 | +```sql |
| 81 | +SELECT a.table_a_id, a.value |
| 82 | +FROM table_a AS a |
| 83 | +WHERE a.value = 1; |
| 84 | +``` |
| 85 | + |
| 86 | +CarbunqleX automatically determines where to insert the condition while maintaining SQL integrity. |
| 87 | + |
| 88 | +### 3️⃣ Handling CTEs and Subqueries |
| 89 | + |
| 90 | +Let's take it a step further by injecting a `WHERE` condition into a query that includes **CTEs and subqueries**. |
| 91 | + |
| 92 | +```csharp |
| 93 | +var query = QueryAstParser.Parse(""" |
| 94 | + WITH regional_sales AS ( |
| 95 | + SELECT orders.region, SUM(orders.amount) AS total_sales |
| 96 | + FROM orders |
| 97 | + GROUP BY orders.region |
| 98 | + ), top_regions AS ( |
| 99 | + SELECT rs.region |
| 100 | + FROM regional_sales rs |
| 101 | + WHERE rs.total_sales > (SELECT SUM(x.total_sales)/10 FROM regional_sales x) |
| 102 | + ) |
| 103 | + SELECT orders.region, orders.product, SUM(orders.quantity) AS product_units, SUM(orders.amount) AS product_sales |
| 104 | + FROM orders |
| 105 | + WHERE orders.region IN (SELECT x.region FROM top_regions x) |
| 106 | + GROUP BY orders.region, orders.product |
| 107 | +"""); |
| 108 | + |
| 109 | +query.Where("region", w => w.Equal("'east'")); |
| 110 | +``` |
| 111 | + |
| 112 | +#### 🔍 Modified SQL Output |
| 113 | + |
| 114 | +```sql |
| 115 | +WITH regional_sales AS ( |
| 116 | + SELECT orders.region, SUM(orders.amount) AS total_sales |
| 117 | + FROM orders |
| 118 | + WHERE orders.region = 'east' |
| 119 | + GROUP BY orders.region |
| 120 | +), |
| 121 | + top_regions AS ( |
| 122 | + SELECT rs.region |
| 123 | + FROM regional_sales rs |
| 124 | + WHERE rs.total_sales > (SELECT SUM(x.total_sales)/10 FROM regional_sales x) |
| 125 | +) |
| 126 | +SELECT orders.region, orders.product, SUM(orders.quantity) AS product_units, SUM(orders.amount) AS product_sales |
| 127 | +FROM orders |
| 128 | +WHERE orders.region IN (SELECT x.region FROM top_regions x) |
| 129 | +GROUP BY orders.region, orders.product; |
| 130 | +``` |
| 131 | + |
| 132 | +CarbunqleX **intelligently places the condition in the deepest relevant query**, ensuring correctness. |
| 133 | + |
| 134 | +### 4️⃣ Advanced Filtering |
| 135 | + |
| 136 | +Now, let's introduce a **more advanced use case** where we dynamically filter data based on user permissions. We'll define a reusable function to retrieve regions a user has access to and use it in a filtering condition. |
| 137 | + |
| 138 | +#### 🔧 Define a Subquery Function |
| 139 | + |
| 140 | +```csharp |
| 141 | +private QueryNode BuildRegionScalarQueryByUser(int userId) |
| 142 | +{ |
| 143 | + return QueryAstParser.Parse(""" |
| 144 | + WITH user_permissions AS ( |
| 145 | + SELECT rrp.region |
| 146 | + FROM region_reference_permission rrp |
| 147 | + WHERE rrp.user_id = :user_id |
| 148 | + ) |
| 149 | + SELECT up.region FROM user_permissions up |
| 150 | + """) |
| 151 | + .AddParameter(":user_id", userId); |
| 152 | +} |
| 153 | +``` |
| 154 | + |
| 155 | +#### 🔍 Apply the Function in a Query |
| 156 | + |
| 157 | +```csharp |
| 158 | +var query = QueryAstParser.Parse("..."); |
| 159 | +query.Where("region", w => w.Exists(BuildRegionScalarQueryByUser(1))); |
| 160 | +``` |
| 161 | + |
| 162 | +By dynamically injecting permission-based filtering, we can ensure **secure and flexible query customization**. |
| 163 | + |
| 164 | +## 📌 Conclusion |
| 165 | + |
| 166 | +CarbunqleX makes raw SQL **more maintainable, reusable, and dynamically modifiable** without sacrificing performance. Its AST-based transformations provide a powerful way to manipulate queries at scale, making it an essential tool for advanced SQL users. |
0 commit comments