rawsql-ts 0.11.6-beta → 0.11.7-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/README.md +735 -735
  2. package/dist/esm/index.min.js.map +1 -1
  3. package/dist/esm/tsconfig.browser.tsbuildinfo +1 -1
  4. package/dist/index.min.js.map +1 -1
  5. package/dist/tsconfig.tsbuildinfo +1 -1
  6. package/package.json +1 -1
  7. /package/dist/esm/{types/src → src}/index.d.ts +0 -0
  8. /package/dist/esm/{types/src → src}/models/BinarySelectQuery.d.ts +0 -0
  9. /package/dist/esm/{types/src → src}/models/CTEError.d.ts +0 -0
  10. /package/dist/esm/{types/src → src}/models/Clause.d.ts +0 -0
  11. /package/dist/esm/{types/src → src}/models/CreateTableQuery.d.ts +0 -0
  12. /package/dist/esm/{types/src → src}/models/HintClause.d.ts +0 -0
  13. /package/dist/esm/{types/src → src}/models/InsertQuery.d.ts +0 -0
  14. /package/dist/esm/{types/src → src}/models/KeywordTrie.d.ts +0 -0
  15. /package/dist/esm/{types/src → src}/models/Lexeme.d.ts +0 -0
  16. /package/dist/esm/{types/src → src}/models/SelectQuery.d.ts +0 -0
  17. /package/dist/esm/{types/src → src}/models/SimpleSelectQuery.d.ts +0 -0
  18. /package/dist/esm/{types/src → src}/models/SqlComponent.d.ts +0 -0
  19. /package/dist/esm/{types/src → src}/models/SqlPrintToken.d.ts +0 -0
  20. /package/dist/esm/{types/src → src}/models/UpdateQuery.d.ts +0 -0
  21. /package/dist/esm/{types/src → src}/models/ValueComponent.d.ts +0 -0
  22. /package/dist/esm/{types/src → src}/models/ValuesQuery.d.ts +0 -0
  23. /package/dist/esm/{types/src → src}/parsers/CommandExpressionParser.d.ts +0 -0
  24. /package/dist/esm/{types/src → src}/parsers/CommonTableParser.d.ts +0 -0
  25. /package/dist/esm/{types/src → src}/parsers/FetchClauseParser.d.ts +0 -0
  26. /package/dist/esm/{types/src → src}/parsers/ForClauseParser.d.ts +0 -0
  27. /package/dist/esm/{types/src → src}/parsers/FromClauseParser.d.ts +0 -0
  28. /package/dist/esm/{types/src → src}/parsers/FullNameParser.d.ts +0 -0
  29. /package/dist/esm/{types/src → src}/parsers/FunctionExpressionParser.d.ts +0 -0
  30. /package/dist/esm/{types/src → src}/parsers/GroupByParser.d.ts +0 -0
  31. /package/dist/esm/{types/src → src}/parsers/HavingParser.d.ts +0 -0
  32. /package/dist/esm/{types/src → src}/parsers/IdentifierDecorator.d.ts +0 -0
  33. /package/dist/esm/{types/src → src}/parsers/IdentifierParser.d.ts +0 -0
  34. /package/dist/esm/{types/src → src}/parsers/InsertQueryParser.d.ts +0 -0
  35. /package/dist/esm/{types/src → src}/parsers/JoinClauseParser.d.ts +0 -0
  36. /package/dist/esm/{types/src → src}/parsers/JoinOnClauseParser.d.ts +0 -0
  37. /package/dist/esm/{types/src → src}/parsers/JoinUsingClauseParser.d.ts +0 -0
  38. /package/dist/esm/{types/src → src}/parsers/KeywordParser.d.ts +0 -0
  39. /package/dist/esm/{types/src → src}/parsers/LimitClauseParser.d.ts +0 -0
  40. /package/dist/esm/{types/src → src}/parsers/LiteralParser.d.ts +0 -0
  41. /package/dist/esm/{types/src → src}/parsers/OffsetClauseParser.d.ts +0 -0
  42. /package/dist/esm/{types/src → src}/parsers/OrderByClauseParser.d.ts +0 -0
  43. /package/dist/esm/{types/src → src}/parsers/OverExpressionParser.d.ts +0 -0
  44. /package/dist/esm/{types/src → src}/parsers/ParameterDecorator.d.ts +0 -0
  45. /package/dist/esm/{types/src → src}/parsers/ParameterExpressionParser.d.ts +0 -0
  46. /package/dist/esm/{types/src → src}/parsers/ParenExpressionParser.d.ts +0 -0
  47. /package/dist/esm/{types/src → src}/parsers/ParseError.d.ts +0 -0
  48. /package/dist/esm/{types/src → src}/parsers/PartitionByParser.d.ts +0 -0
  49. /package/dist/esm/{types/src → src}/parsers/ReturningClauseParser.d.ts +0 -0
  50. /package/dist/esm/{types/src → src}/parsers/SelectClauseParser.d.ts +0 -0
  51. /package/dist/esm/{types/src → src}/parsers/SelectQueryParser.d.ts +0 -0
  52. /package/dist/esm/{types/src → src}/parsers/SetClauseParser.d.ts +0 -0
  53. /package/dist/esm/{types/src → src}/parsers/SourceAliasExpressionParser.d.ts +0 -0
  54. /package/dist/esm/{types/src → src}/parsers/SourceExpressionParser.d.ts +0 -0
  55. /package/dist/esm/{types/src → src}/parsers/SourceParser.d.ts +0 -0
  56. /package/dist/esm/{types/src → src}/parsers/SqlPrintTokenParser.d.ts +0 -0
  57. /package/dist/esm/{types/src → src}/parsers/SqlTokenizer.d.ts +0 -0
  58. /package/dist/esm/{types/src → src}/parsers/StringSpecifierExpressionParser.d.ts +0 -0
  59. /package/dist/esm/{types/src → src}/parsers/UnaryExpressionParser.d.ts +0 -0
  60. /package/dist/esm/{types/src → src}/parsers/UpdateClauseParser.d.ts +0 -0
  61. /package/dist/esm/{types/src → src}/parsers/UpdateQueryParser.d.ts +0 -0
  62. /package/dist/esm/{types/src → src}/parsers/ValueParser.d.ts +0 -0
  63. /package/dist/esm/{types/src → src}/parsers/ValuesQueryParser.d.ts +0 -0
  64. /package/dist/esm/{types/src → src}/parsers/WhereClauseParser.d.ts +0 -0
  65. /package/dist/esm/{types/src → src}/parsers/WindowClauseParser.d.ts +0 -0
  66. /package/dist/esm/{types/src → src}/parsers/WindowExpressionParser.d.ts +0 -0
  67. /package/dist/esm/{types/src → src}/parsers/WithClauseParser.d.ts +0 -0
  68. /package/dist/esm/{types/src → src}/tokenReaders/BaseTokenReader.d.ts +0 -0
  69. /package/dist/esm/{types/src → src}/tokenReaders/CommandTokenReader.d.ts +0 -0
  70. /package/dist/esm/{types/src → src}/tokenReaders/EscapedIdentifierTokenReader.d.ts +0 -0
  71. /package/dist/esm/{types/src → src}/tokenReaders/FunctionTokenReader.d.ts +0 -0
  72. /package/dist/esm/{types/src → src}/tokenReaders/IdentifierTokenReader.d.ts +0 -0
  73. /package/dist/esm/{types/src → src}/tokenReaders/LiteralTokenReader.d.ts +0 -0
  74. /package/dist/esm/{types/src → src}/tokenReaders/OperatorTokenReader.d.ts +0 -0
  75. /package/dist/esm/{types/src → src}/tokenReaders/ParameterTokenReader.d.ts +0 -0
  76. /package/dist/esm/{types/src → src}/tokenReaders/StringSpecifierTokenReader.d.ts +0 -0
  77. /package/dist/esm/{types/src → src}/tokenReaders/SymbolTokenReader.d.ts +0 -0
  78. /package/dist/esm/{types/src → src}/tokenReaders/TokenReaderManager.d.ts +0 -0
  79. /package/dist/esm/{types/src → src}/tokenReaders/TypeTokenReader.d.ts +0 -0
  80. /package/dist/esm/{types/src → src}/transformers/CTEBuilder.d.ts +0 -0
  81. /package/dist/esm/{types/src → src}/transformers/CTECollector.d.ts +0 -0
  82. /package/dist/esm/{types/src → src}/transformers/CTEDependencyTracer.d.ts +0 -0
  83. /package/dist/esm/{types/src → src}/transformers/CTEDisabler.d.ts +0 -0
  84. /package/dist/esm/{types/src → src}/transformers/CTEInjector.d.ts +0 -0
  85. /package/dist/esm/{types/src → src}/transformers/CTENormalizer.d.ts +0 -0
  86. /package/dist/esm/{types/src → src}/transformers/DynamicQueryBuilder.d.ts +0 -0
  87. /package/dist/esm/{types/src → src}/transformers/EnhancedJsonMapping.d.ts +0 -0
  88. /package/dist/esm/{types/src → src}/transformers/Formatter.d.ts +0 -0
  89. /package/dist/esm/{types/src → src}/transformers/JsonMappingConverter.d.ts +0 -0
  90. /package/dist/esm/{types/src → src}/transformers/JsonMappingUnifier.d.ts +0 -0
  91. /package/dist/esm/{types/src → src}/transformers/LinePrinter.d.ts +0 -0
  92. /package/dist/esm/{types/src → src}/transformers/ModelDrivenJsonMapping.d.ts +0 -0
  93. /package/dist/esm/{types/src → src}/transformers/ParameterCollector.d.ts +0 -0
  94. /package/dist/esm/{types/src → src}/transformers/PostgresArrayEntityCteBuilder.d.ts +0 -0
  95. /package/dist/esm/{types/src → src}/transformers/PostgresJsonQueryBuilder.d.ts +0 -0
  96. /package/dist/esm/{types/src → src}/transformers/PostgresObjectEntityCteBuilder.d.ts +0 -0
  97. /package/dist/esm/{types/src → src}/transformers/QueryBuilder.d.ts +0 -0
  98. /package/dist/esm/{types/src → src}/transformers/SchemaCollector.d.ts +0 -0
  99. /package/dist/esm/{types/src → src}/transformers/SelectValueCollector.d.ts +0 -0
  100. /package/dist/esm/{types/src → src}/transformers/SelectableColumnCollector.d.ts +0 -0
  101. /package/dist/esm/{types/src → src}/transformers/SqlFormatter.d.ts +0 -0
  102. /package/dist/esm/{types/src → src}/transformers/SqlOutputToken.d.ts +0 -0
  103. /package/dist/esm/{types/src → src}/transformers/SqlPaginationInjector.d.ts +0 -0
  104. /package/dist/esm/{types/src → src}/transformers/SqlParamInjector.d.ts +0 -0
  105. /package/dist/esm/{types/src → src}/transformers/SqlParameterBinder.d.ts +0 -0
  106. /package/dist/esm/{types/src → src}/transformers/SqlPrinter.d.ts +0 -0
  107. /package/dist/esm/{types/src → src}/transformers/SqlSortInjector.d.ts +0 -0
  108. /package/dist/esm/{types/src → src}/transformers/TableColumnResolver.d.ts +0 -0
  109. /package/dist/esm/{types/src → src}/transformers/TableSourceCollector.d.ts +0 -0
  110. /package/dist/esm/{types/src → src}/transformers/TypeTransformationPostProcessor.d.ts +0 -0
  111. /package/dist/esm/{types/src → src}/transformers/UpstreamSelectQueryFinder.d.ts +0 -0
  112. /package/dist/esm/{types/src → src}/utils/CommentEditor.d.ts +0 -0
  113. /package/dist/esm/{types/src → src}/utils/CommentUtils.d.ts +0 -0
  114. /package/dist/esm/{types/src → src}/utils/JsonSchemaValidator.d.ts +0 -0
  115. /package/dist/esm/{types/src → src}/utils/OperatorPrecedence.d.ts +0 -0
  116. /package/dist/esm/{types/src → src}/utils/ParameterDetector.d.ts +0 -0
  117. /package/dist/esm/{types/src → src}/utils/ParameterHelper.d.ts +0 -0
  118. /package/dist/esm/{types/src → src}/utils/ParameterRemover.d.ts +0 -0
  119. /package/dist/esm/{types/src → src}/utils/SchemaManager.d.ts +0 -0
  120. /package/dist/esm/{types/src → src}/utils/SqlSchemaValidator.d.ts +0 -0
  121. /package/dist/esm/{types/src → src}/utils/charLookupTable.d.ts +0 -0
  122. /package/dist/esm/{types/src → src}/utils/stringUtils.d.ts +0 -0
package/README.md CHANGED
@@ -1,735 +1,735 @@
1
- # rawsql-ts
2
-
3
- ![No external dependencies](https://img.shields.io/badge/dependencies-none-brightgreen)
4
- ![Browser Support](https://img.shields.io/badge/browser-%F0%9F%9A%80-brightgreen)
5
- ![npm version](https://img.shields.io/npm/v/rawsql-ts)
6
- ![npm downloads](https://img.shields.io/npm/dm/rawsql-ts)
7
- ![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)
8
-
9
- 🌐 [Online Demo (GitHub Pages)](https://mk3008.github.io/rawsql-ts/)
10
-
11
- rawsql-ts is a high-performance SQL parser and AST transformer library written in TypeScript. It empowers you to represent raw SQL as objects, enabling flexible manipulation of SQL statements directly within your program. This object-oriented approach allows for partial transformation, decomposition into manageable components, and recombination as needed, dramatically improving the maintainability and reusability of complex SQL.
12
-
13
- It is designed for extensibility and advanced SQL analysis, with initial focus on PostgreSQL syntax but not limited to it. The library enables easy SQL parsing, transformation, and analysis for a wide range of SQL dialects.
14
-
15
- > [!Note]
16
- > This library is currently in beta. The API may change until the v1.0 release.
17
-
18
- ---
19
-
20
- ## Key Features
21
-
22
- - Zero dependencies: fully self-contained and lightweight
23
- - High-speed SQL parsing and AST analysis (over 3x faster than major libraries)
24
- - Rich utilities for SQL structure transformation and analysis
25
- - Advanced SQL formatting capabilities, including multi-line formatting and customizable styles
26
- - **Programmatic CTE Management** - Add, remove, and manipulate Common Table Expressions (CTEs) programmatically with support for PostgreSQL MATERIALIZED/NOT MATERIALIZED hints
27
- - **JSON-to-TypeScript type transformation** - Automatically convert JSON-ified SQL results (dates as strings, BigInts) back to proper TypeScript types with configurable transformation rules
28
- - **All-in-one dynamic query building with `DynamicQueryBuilder`** - combines filtering, sorting, pagination, and JSON serialization in a single, type-safe interface
29
- - Dynamic SQL parameter injection for building flexible search queries with `SqlParamInjector` (supports like, ilike, in, any, range queries, OR/AND conditions and more)
30
- - Dynamic ORDER BY clause injection with `SqlSortInjector` for flexible sorting with support for ASC/DESC, NULLS positioning, and append mode
31
- - Dynamic LIMIT/OFFSET pagination injection with `SqlPaginationInjector` for efficient data pagination with page-based and offset-based support
32
- - Type-safe schema management and JSON mapping conversion with full TypeScript support
33
- - Static query validation and regression testing against your database schema with `SqlSchemaValidator`, enabling early error detection and robust unit tests for schema changes.
34
-
35
- ![Benchmark Results](https://quickchart.io/chart?c={type:'bar',data:{labels:['Tokens20','Tokens70','Tokens140','Tokens230'],datasets:[{label:'rawsql-ts',data:[0.029,0.075,0.137,0.239],backgroundColor:'rgba(54,162,235,0.8)',borderColor:'rgba(54,162,235,1)',borderWidth:1},{label:'node-sql-parser',data:[0.210,0.223,0.420,0.871],backgroundColor:'rgba(255,206,86,0.8)',borderColor:'rgba(255,206,86,1)',borderWidth:1},{label:'sql-formatter',data:[0.228,0.547,1.057,1.906],backgroundColor:'rgba(255,99,132,0.8)',borderColor:'rgba(255,99,132,1)',borderWidth:1}]},options:{plugins:{legend:{labels:{color:'black'}}},scales:{x:{ticks:{color:'black'}},y:{ticks:{color:'black'}}},backgroundColor:'white'}}&width=700&height=450)
36
-
37
- > [!Note]
38
- > The "Mean" column represents the average time taken to process a query. Lower values indicate faster performance. For more details, see the [Benchmark](#benchmarks).
39
-
40
- ## Browser & CDN Ready
41
-
42
- You can use rawsql-ts directly in modern browsers via CDN (unpkg/jsdelivr)!
43
- No Node.js dependencies, no build tools required.
44
- Just import it like this:
45
-
46
- ```html
47
- <!-- Always get the latest version -->
48
- <script type="module">
49
- import { parse } from "https://unpkg.com/rawsql-ts/dist/esm/index.min.js";
50
- </script>
51
- ```
52
-
53
- ```html
54
- <!-- Pin a specific version for stability -->
55
- <script type="module">
56
- import { parse } from "https://unpkg.com/rawsql-ts@0.10.9-beta/dist/esm/index.min.js";
57
- </script>
58
- ```
59
-
60
- ---
61
-
62
- ## Installation
63
-
64
- ```bash
65
- npm install rawsql-ts
66
- ```
67
-
68
- ---
69
-
70
- ## Quick Start
71
-
72
- Experience the power of rawsql-ts with `DynamicQueryBuilder` - build complex queries with filtering, sorting, pagination, and JSON serialization in one go!
73
-
74
- ```typescript
75
- import { DynamicQueryBuilder, SqlFormatter } from 'rawsql-ts';
76
-
77
- // Start with a simple base SQL
78
- const baseSql = 'SELECT id, name, email, created_at FROM users WHERE active = true';
79
-
80
- // Build a complete dynamic query with all features
81
- const builder = new DynamicQueryBuilder();
82
- const query = builder.buildQuery(baseSql, {
83
- // Dynamic filtering
84
- filter: {
85
- status: 'premium',
86
- created_at: { '>': '2024-01-01' }
87
- },
88
- // Dynamic sorting
89
- sort: {
90
- created_at: { desc: true },
91
- name: { asc: true }
92
- },
93
- // Dynamic pagination
94
- paging: {
95
- page: 2,
96
- pageSize: 10
97
- },
98
- // JSON serialization (optional)
99
- serialize: {
100
- rootName: 'user',
101
- rootEntity: {
102
- id: 'user',
103
- name: 'User',
104
- columns: { id: 'id', name: 'name', email: 'email', createdAt: 'created_at' }
105
- },
106
- nestedEntities: []
107
- }
108
- });
109
-
110
- // Format and execute
111
- const formatter = new SqlFormatter();
112
- const { formattedSql, params } = formatter.format(query);
113
-
114
- console.log('Generated SQL:');
115
- console.log(formattedSql);
116
- // Output: Optimized PostgreSQL JSON query with filtering, sorting, and pagination
117
-
118
- console.log('Parameters:');
119
- console.log(params);
120
- // Output: { status: 'premium', created_at_gt: '2024-01-01' }
121
- ```
122
-
123
- ---
124
-
125
- ## CTE Management API
126
-
127
- The CTE Management API provides programmatic control over Common Table Expressions (CTEs), allowing you to build and manipulate WITH clauses dynamically. This is particularly useful for building complex analytical queries, data transformation pipelines, and hierarchical data structures.
128
-
129
- ```typescript
130
- import { SelectQueryParser, SqlFormatter } from 'rawsql-ts';
131
-
132
- // Build a multi-step data pipeline with CTEs
133
- const pipeline = SelectQueryParser.parse('SELECT * FROM final_results').toSimpleQuery();
134
-
135
- // Step 1: Add raw data CTE with PostgreSQL MATERIALIZED hint
136
- const salesData = SelectQueryParser.parse(`
137
- SELECT customer_id, order_date, amount, product_category
138
- FROM sales WHERE order_date >= '2024-01-01'
139
- `);
140
- pipeline.addCTE('raw_sales', salesData.toSimpleQuery(), { materialized: true });
141
-
142
- // Step 2: Add aggregation CTE
143
- const monthlyStats = SelectQueryParser.parse(`
144
- SELECT customer_id, DATE_TRUNC('month', order_date) as month,
145
- SUM(amount) as total, COUNT(*) as orders
146
- FROM raw_sales
147
- GROUP BY customer_id, DATE_TRUNC('month', order_date)
148
- `);
149
- pipeline.addCTE('monthly_stats', monthlyStats.toSimpleQuery());
150
-
151
- // Manage CTEs programmatically
152
- console.log(pipeline.getCTENames()); // ['raw_sales', 'monthly_stats']
153
- console.log(pipeline.hasCTE('raw_sales')); // true
154
-
155
- // Replace the final query to use the CTEs
156
- const finalQuery = SelectQueryParser.parse(`
157
- SELECT * FROM monthly_stats WHERE total > 10000
158
- `);
159
- pipeline.replaceCTE('final_results', finalQuery.toSimpleQuery());
160
-
161
- // Format and execute
162
- const formatter = new SqlFormatter();
163
- const { formattedSql } = formatter.format(pipeline);
164
- // Output: WITH "raw_sales" AS MATERIALIZED (...), "monthly_stats" AS (...) SELECT * FROM monthly_stats WHERE total > 10000
165
- ```
166
-
167
- Key features include:
168
- - **Dynamic CTE Management**: Add, remove, replace, and query CTEs programmatically
169
- - **PostgreSQL MATERIALIZED Support**: Control query optimization with MATERIALIZED/NOT MATERIALIZED hints
170
- - **Type Safety**: Full TypeScript support with error handling for duplicate names and invalid operations
171
- - **Performance Optimized**: O(1) CTE name lookups for efficient operations
172
-
173
- For comprehensive examples and advanced usage patterns, see the [CTE Management API Usage Guide](../../docs/usage-guides/cte-management-api-usage-guide.md).
174
-
175
- ---
176
-
177
- ## SelectQueryParser & Query Types
178
-
179
- rawsql-ts provides robust parsers for `SELECT`, `INSERT`, and `UPDATE` statements, automatically handling SQL comments and providing detailed error messages. By converting SQL into a generic Abstract Syntax Tree (AST), it enables a wide variety of transformation processes.
180
-
181
- ### Query Type Overview
182
-
183
- The parser returns different query types based on SQL structure:
184
-
185
- - **`SimpleSelectQuery`**: Single SELECT statement with comprehensive manipulation API
186
- - **`BinarySelectQuery`**: Combined queries using UNION, INTERSECT, EXCEPT operators
187
- - **`ValuesQuery`**: VALUES clause queries for data insertion or testing
188
- - **`SelectQuery`**: Base interface implemented by all SELECT query classes
189
-
190
- ```typescript
191
- import { SelectQueryParser } from 'rawsql-ts';
192
-
193
- // Returns SimpleSelectQuery
194
- const simpleQuery = SelectQueryParser.parse('SELECT id, name FROM products WHERE category = \'electronics\'');
195
-
196
- // Returns BinarySelectQuery
197
- const unionQuery = SelectQueryParser.parse('SELECT id, name FROM products UNION SELECT id, name FROM archived_products');
198
-
199
- // Returns ValuesQuery
200
- const valuesQuery = SelectQueryParser.parse('VALUES (1, \'Alice\'), (2, \'Bob\')');
201
- ```
202
-
203
- ### SimpleSelectQuery - Rich Programmatic API
204
-
205
- `SimpleSelectQuery` provides extensive methods for programmatic query building and manipulation:
206
-
207
- **Dynamic Condition Building:**
208
- - `appendWhereExpr(columnName, exprBuilder)` - Add conditions by column name with upstream injection support
209
- - `appendWhereRaw()`, `appendHavingRaw()` - Append raw SQL conditions
210
- - `setParameter(name, value)` - Manage named parameters directly on the query object
211
-
212
- **Query Composition:**
213
- - `toUnion()`, `toIntersect()`, `toExcept()` - Combine with other queries
214
- - `innerJoin()`, `leftJoin()`, `rightJoin()` - Add JOIN clauses programmatically
215
- - `appendWith()` - Add Common Table Expressions (CTEs)
216
-
217
- **Advanced Features:**
218
- - Column-aware condition injection that resolves aliases and expressions
219
- - Parameter management with validation and type safety
220
- - Subquery wrapping with `toSource(alias)` for complex compositions
221
-
222
- ```typescript
223
- import { SelectQueryParser } from 'rawsql-ts';
224
-
225
- const query = SelectQueryParser.parse('SELECT id, salary * 1.1 AS adjusted_salary FROM employees');
226
-
227
- // Add condition targeting the calculated column
228
- query.appendWhereExpr('adjusted_salary', expr => `${expr} > 50000`);
229
-
230
- // Set parameters directly on the query
231
- query.setParameter('dept_id', 123);
232
-
233
- // Add JOINs programmatically
234
- query.leftJoinRaw('departments', 'd', 'department_id');
235
-
236
- // Combine with another query
237
- const adminQuery = SelectQueryParser.parse('SELECT id, salary FROM admins');
238
- const combinedQuery = query.toUnion(adminQuery);
239
- ```
240
-
241
- For comprehensive API documentation and advanced examples, see the [SimpleSelectQuery Usage Guide](../../docs/usage-guides/class-SimpleSelectQuery-usage-guide.md).
242
-
243
- For SelectQueryParser details, see the [SelectQueryParser Usage Guide](../../docs/usage-guides/class-SelectQueryParser-usage-guide.md).
244
-
245
- ---
246
-
247
- ## SqlFormatter Features
248
-
249
- The `SqlFormatter` class is the recommended way to format SQL queries, offering advanced capabilities like indentation, keyword casing, and multi-line formatting.
250
- It also allows for detailed style customization. For example, you can define your own formatting rules:
251
-
252
- ```typescript
253
- import { SelectQueryParser, SqlFormatter } from 'rawsql-ts';
254
-
255
- const customStyle = {
256
- identifierEscape: {
257
- start: "",
258
- end: ""
259
- },
260
- parameterSymbol: ":",
261
- parameterStyle: "named",
262
- indentSize: 4,
263
- indentChar: " ",
264
- newline: "\n",
265
- keywordCase: "lower",
266
- commaBreak: "before",
267
- andBreak: "before"
268
- };
269
-
270
- const sqlToFormat = `SELECT u.user_id, u.user_name FROM users as u WHERE status = :active ORDER BY created_at DESC;`;
271
- const queryToFormat = SelectQueryParser.parse(sqlToFormat);
272
- const customFormatter = new SqlFormatter(customStyle);
273
- const { formattedSql: customFormattedSql } = customFormatter.format(queryToFormat);
274
-
275
- console.log(customFormattedSql);
276
- /*
277
- select
278
- u.user_id
279
- , u.user_name
280
- from
281
- users as u
282
- where
283
- status = :active
284
- order by
285
- created_at desc;
286
- */
287
- ```
288
-
289
- For more details, see the [SqlFormatter Usage Guide](../../docs/usage-guides/class-SqlFormatter-usage-guide.md).
290
-
291
- ---
292
-
293
- ## SqlParamInjector Features
294
-
295
- The `SqlParamInjector` class revolutionizes how you build dynamic search queries. Instead of manually constructing different SQL statements for various search conditions, you simply provide a fixed base SQL and a state object. `SqlParamInjector` then dynamically injects parameters and automatically generates the optimal WHERE conditions.
296
-
297
- Key benefits include:
298
- - **Simplified Query Management**: Prepare a single base SQL; `SqlParamInjector` handles the variations.
299
- - **Effortless Optimal Queries**: Just pass a state object, and it generates a highly efficient query.
300
- - **Performance-Oriented**: Conditions are intelligently inserted as close to the data source as possible, significantly improving query performance by filtering data early.
301
- - **Zero Conditional Logic in Code**: Forget writing complex IF statements in your application code to handle different filters.
302
- - **Enhanced SQL Reusability**: Your base SQL remains clean and can be reused across different scenarios with varying search criteria.
303
- - **Rich Operator Support**: Supports various SQL operators including equality, comparison, range (min/max), pattern matching (like/ilike), IN clauses, and PostgreSQL array operators.
304
- - **Advanced Condition Logic**: Supports OR/AND conditions, automatic parentheses grouping, and explicit column mapping for flexible query construction.
305
-
306
- ```typescript
307
- import { SqlParamInjector, SqlFormatter } from 'rawsql-ts';
308
-
309
- const sql = `SELECT u.user_id, u.user_name FROM users as u WHERE u.active = TRUE`;
310
- const injector = new SqlParamInjector();
311
- // Inject parameters and generate WHERE conditions
312
- const injectedQuery = injector.inject(sql, { user_id: 42, user_name: 'Alice' });
313
-
314
- const formatter = new SqlFormatter();
315
- const { formattedSql, params } = formatter.format(injectedQuery);
316
-
317
- console.log(formattedSql);
318
- // Output: select "u"."user_id", "u"."user_name" from "users" as "u" where "u"."active" = true and "u"."user_id" = :user_id and "u"."user_name" = :user_name
319
- console.log(params);
320
- // Output: { user_id: 42, user_name: 'Alice' }
321
- ```
322
-
323
- For more details, see the [SqlParamInjector Usage Guide](../../docs/usage-guides/class-SqlParamInjector-usage-guide.md).
324
-
325
- ---
326
-
327
- ## SqlSortInjector Features
328
-
329
- The `SqlSortInjector` class enables dynamic ORDER BY clause injection into SQL queries, providing a clean and flexible way to handle sorting requirements. Instead of manually constructing different SQL statements for various sorting scenarios, you can inject sort conditions into your base SQL query, making your code more maintainable and reusable.
330
-
331
- Key benefits include:
332
- - **Dynamic Sort Injection**: Add ORDER BY conditions to existing queries without modifying the base SQL
333
- - **Flexible Sort Options**: Support for ASC/DESC directions with NULLS FIRST/LAST positioning
334
- - **Append Mode**: Preserves existing ORDER BY clauses and appends new conditions
335
- - **Column Alias Support**: Works seamlessly with column aliases and calculated expressions
336
- - **TableColumnResolver Integration**: Supports wildcard SELECT queries through schema resolution
337
- - **Clean Separation**: Remove existing ORDER BY clauses when needed with `removeOrderBy()`
338
-
339
- ```typescript
340
- import { SqlSortInjector, SqlFormatter } from 'rawsql-ts';
341
-
342
- // Basic sort injection
343
- const baseSql = 'SELECT id, name, created_at FROM users WHERE active = true';
344
- const sortConditions = {
345
- created_at: { desc: true, nullsLast: true },
346
- name: { asc: true }
347
- };
348
-
349
- const injector = new SqlSortInjector();
350
- const sortedQuery = injector.inject(baseSql, sortConditions);
351
-
352
- const formatter = new SqlFormatter();
353
- const { formattedSql } = formatter.format(sortedQuery);
354
-
355
- console.log(formattedSql);
356
- // Output: select "id", "name", "created_at" from "users" where "active" = true order by "created_at" desc nulls last, "name"
357
-
358
- // Append to existing ORDER BY
359
- const existingSql = 'SELECT id, name FROM users ORDER BY id ASC';
360
- const additionalSort = { name: { desc: true } };
361
- const appendedQuery = injector.inject(existingSql, additionalSort);
362
- // Result: SELECT id, name FROM users ORDER BY id ASC, name DESC
363
-
364
- // Remove existing ORDER BY when needed
365
- const cleanQuery = SqlSortInjector.removeOrderBy(existingSql);
366
- const newSortedQuery = injector.inject(cleanQuery, { name: { desc: true } });
367
- // Result: SELECT id, name FROM users ORDER BY name DESC
368
- ```
369
-
370
- For more details, see the [SqlSortInjector Usage Guide](../../docs/usage-guides/class-SqlSortInjector-usage-guide.md).
371
-
372
- ---
373
-
374
- ## SqlPaginationInjector Features
375
-
376
- The `SqlPaginationInjector` class enables dynamic LIMIT/OFFSET clause injection into SQL queries, providing a clean and efficient way to handle data pagination. Instead of manually constructing different SQL statements for various page sizes and offsets, you can inject pagination into your base SQL query, making your code more maintainable and performance-optimized.
377
-
378
- Key benefits include:
379
- - **Page-Based Pagination**: Simple page number and page size configuration with automatic offset calculation
380
- - **Automatic OFFSET Optimization**: Smart handling where page 1 omits unnecessary OFFSET 0 clauses
381
- - **Existing Clause Detection**: Prevents conflicts by detecting and reporting existing LIMIT/OFFSET clauses
382
- - **Validation & Safety**: Built-in validation for page numbers, page sizes, and configurable maximum limits
383
- - **Clean Removal**: Remove existing pagination clauses when needed with `removePagination()`
384
- - **Performance Conscious**: Generates standard LIMIT/OFFSET patterns optimized for database engines
385
-
386
- ```typescript
387
- import { SqlPaginationInjector, SqlFormatter } from 'rawsql-ts';
388
-
389
- // Basic pagination injection
390
- const baseSql = 'SELECT id, name, email FROM users WHERE active = true ORDER BY created_at DESC';
391
- const pagination = {
392
- page: 2, // Page number (1-based)
393
- pageSize: 20 // Items per page
394
- };
395
-
396
- const injector = new SqlPaginationInjector();
397
- const paginatedQuery = injector.inject(baseSql, pagination);
398
-
399
- const formatter = new SqlFormatter();
400
- const { formattedSql } = formatter.format(paginatedQuery);
401
-
402
- console.log(formattedSql);
403
- // Output: select "id", "name", "email" from "users" where "active" = true order by "created_at" desc limit 20 offset 20
404
-
405
- // First page optimization (no OFFSET needed)
406
- const firstPagePagination = { page: 1, pageSize: 10 };
407
- const firstPageQuery = injector.inject(baseSql, firstPagePagination);
408
- const { formattedSql: firstPageSql } = formatter.format(firstPageQuery);
409
- console.log(firstPageSql);
410
- // Output: select "id", "name", "email" from "users" where "active" = true order by "created_at" desc limit 10
411
-
412
- // Remove existing pagination when needed
413
- const existingPaginatedSql = 'SELECT id, name FROM users LIMIT 50 OFFSET 100';
414
- const cleanQuery = SqlPaginationInjector.removePagination(existingPaginatedSql);
415
- const newPagination = { page: 1, pageSize: 25 };
416
- const repaginatedQuery = injector.inject(cleanQuery, newPagination);
417
- // Result: Clean query with new pagination applied
418
-
419
- // Combine with other injectors for complete query building
420
- let query = new SqlParamInjector().inject(baseSql, { status: 'active' });
421
- query = new SqlSortInjector().inject(query, { created_at: { desc: true } });
422
- query = new SqlPaginationInjector().inject(query, { page: 3, pageSize: 15 });
423
- // Result: Filtered, sorted, and paginated query ready for execution
424
- ```
425
-
426
- For more details, see the [SqlPaginationInjector Usage Guide](../../docs/usage-guides/class-SqlPaginationInjector-usage-guide.md).
427
-
428
- ---
429
-
430
- ## DynamicQueryBuilder Features
431
-
432
- The `DynamicQueryBuilder` class is a powerful, all-in-one solution that combines SQL parsing with dynamic condition injection (filtering, sorting, pagination, and JSON serialization). It provides a unified interface for building complex queries without the need to manually chain multiple injectors, making it ideal for modern web applications that require flexible, dynamic query generation.
433
-
434
- Key benefits include:
435
- - **Unified Interface**: Single class that combines filtering, sorting, pagination, and JSON serialization
436
- - **Framework-Agnostic**: Pure JavaScript/TypeScript with no file system dependencies
437
- - **Composable Architecture**: Internally uses specialized injectors in optimal order for performance
438
- - **Type-Safe**: Full TypeScript support with strongly typed options and return values
439
- - **Performance Optimized**: Applies conditions in the most efficient order (filter → sort → paginate → serialize)
440
- - **Easy Testing**: No external dependencies make unit testing straightforward
441
-
442
- ```typescript
443
- import { DynamicQueryBuilder, SqlFormatter } from 'rawsql-ts';
444
-
445
- // Create a builder instance
446
- const builder = new DynamicQueryBuilder();
447
-
448
- // Build a complete dynamic query with all features
449
- const baseQuery = 'SELECT id, name, email, created_at FROM users WHERE active = true';
450
- const options = {
451
- filter: {
452
- status: 'premium',
453
- created_at: { min: '2024-01-01' } // Range filter
454
- },
455
- sort: {
456
- created_at: { desc: true, nullsLast: true },
457
- name: { asc: true }
458
- },
459
- paging: { page: 2, pageSize: 20 },
460
- serialize: {
461
- rootName: 'users',
462
- rootEntity: {
463
- id: 'user',
464
- name: 'User',
465
- columns: { id: 'id', name: 'name', email: 'email', created: 'created_at' }
466
- },
467
- nestedEntities: []
468
- }
469
- };
470
-
471
- const dynamicQuery = builder.buildQuery(baseQuery, options);
472
-
473
- const formatter = new SqlFormatter();
474
- const { formattedSql, params } = formatter.format(dynamicQuery);
475
-
476
- console.log(formattedSql);
477
- // Output: Complex JSON query with all conditions applied
478
- console.log(params);
479
- // Output: { status: 'premium', created_at_min: '2024-01-01', paging_limit: 20, paging_offset: 20 }
480
-
481
- // Convenience methods for specific use cases
482
- const filteredOnly = builder.buildFilteredQuery(baseQuery, { name: 'Alice' });
483
- const sortedOnly = builder.buildSortedQuery(baseQuery, { created_at: { desc: true } });
484
- const paginatedOnly = builder.buildPaginatedQuery(baseQuery, { page: 1, pageSize: 10 });
485
-
486
- // Validate SQL without applying modifications
487
- const isValid = builder.validateSql('SELECT id FROM users');
488
- console.log(isValid); // true
489
- ```
490
-
491
- For more details, see the [DynamicQueryBuilder Usage Guide](../../docs/usage-guides/class-DynamicQueryBuilder-usage-guide.md).
492
-
493
- ---
494
-
495
- ## PostgresJsonQueryBuilder Features
496
-
497
- The `PostgresJsonQueryBuilder` class transforms relational SQL queries into PostgreSQL JSON queries that return hierarchical JSON structures. It automatically handles complex relationships between entities and generates optimized Common Table Expressions (CTEs) for efficient JSON aggregation, making it perfect for building APIs, reports, and data exports.
498
-
499
- Key benefits include:
500
- - **Hierarchical JSON Generation**: Transforms flat relational data into nested JSON objects and arrays
501
- - **Automatic CTE Management**: Generates optimized CTEs with proper dependency ordering
502
- - **Flexible Relationship Types**: Supports both object (0..1) and array (1..N) relationships
503
- - **NULL Handling**: Properly represents missing relationships as NULL instead of empty objects
504
- - **Performance Optimized**: Uses depth-based processing and JSONB for optimal PostgreSQL performance
505
- - **Zero Manual Serialization**: Eliminates the need for manual object mapping and JSON construction
506
-
507
- ```typescript
508
- import { SelectQueryParser, PostgresJsonQueryBuilder } from 'rawsql-ts';
509
-
510
- // Parse your base SQL query
511
- const baseQuery = SelectQueryParser.parse(`
512
- SELECT o.order_id, o.order_date, c.customer_name, i.product_name, i.quantity
513
- FROM orders o
514
- LEFT JOIN customers c ON o.customer_id = c.customer_id
515
- LEFT JOIN order_items i ON o.order_id = i.order_id
516
- `) as SimpleSelectQuery;
517
-
518
- // Define JSON mapping configuration
519
- const mapping = {
520
- rootName: "order",
521
- rootEntity: { id: "order", name: "Order", columns: { "id": "order_id", "date": "order_date" }},
522
- nestedEntities: [
523
- { id: "customer", parentId: "order", propertyName: "customer", relationshipType: "object",
524
- columns: { "name": "customer_name" }},
525
- { id: "items", parentId: "order", propertyName: "items", relationshipType: "array",
526
- columns: { "product": "product_name", "qty": "quantity" }} ]
527
- };
528
-
529
- // Transform to JSON query
530
- const builder = new PostgresJsonQueryBuilder();
531
- const jsonQuery = builder.buildJson(baseQuery, mapping);
532
- // Returns optimized PostgreSQL query with CTEs that produces:
533
- // [{ "id": 1, "date": "2024-01-15", "customer": {"name": "John"}, "items": [{"product": "Widget", "qty": 2}] }]
534
- ```
535
-
536
- For more details, see the [PostgresJsonQueryBuilder Usage Guide](../../docs/usage-guides/class-PostgresJsonQueryBuilder-usage-guide.md).
537
-
538
- ---
539
-
540
- ## SqlSchemaValidator Features
541
-
542
- The `SqlSchemaValidator` class helps ensure your SQL queries are valid against a predefined database schema. It can extract schema information about the physical tables your SQL query depends on. By comparing this extracted information with your defined schema (e.g., a schema definition class), you can statically verify the query's behavior. This enables you to perform regression testing as part of your unit tests when schema definitions change, ensuring that your queries remain compatible.
543
-
544
- It checks if the tables and columns referenced in your query actually exist in your schema, and it understands table aliases. If there's a problem, it gives you a clear error message telling you what's wrong and where.
545
-
546
- Key benefits include:
547
- - **Schema Validation**: Verifies SQL queries against your database schema.
548
- - **Table and Column Verification**: Confirms the existence of tables and columns used in the query.
549
- - **Alias Aware**: Correctly resolves table aliases.
550
- - **Clear Error Reporting**: Provides descriptive error messages for easy debugging.
551
- - **Static Analysis**: Allows comparison of SQL-derived schema information with predefined schema definitions.
552
- - **Automated Regression Testing**: Facilitates unit testing for schema change impacts on queries.
553
-
554
- ```typescript
555
- import { SelectQueryParser, SqlSchemaValidator } from 'rawsql-ts';
556
-
557
- // Define your database schema
558
- const schema = {
559
- users: ['user_id', 'user_name', 'email', 'status'],
560
- orders: ['order_id', 'user_id', 'order_date', 'total_amount']
561
- };
562
-
563
- const validator = new SqlSchemaValidator(schema);
564
-
565
- // Example: Validate a SELECT query
566
- const validSql = 'SELECT u.user_id, u.user_name FROM users as u WHERE u.status = \'active\'';
567
- const queryToValidate = SelectQueryParser.parse(validSql);
568
-
569
- try {
570
- validator.validate(queryToValidate);
571
- console.log('Query is valid against the schema.');
572
- } catch (error) {
573
- console.error('Schema validation failed:', error.message);
574
- }
575
-
576
- // Example: Validate a query with a non-existent column
577
- const invalidSql = 'SELECT user_id, non_existent_column FROM users';
578
- const invalidQuery = SelectQueryParser.parse(invalidSql);
579
-
580
- try {
581
- validator.validate(invalidQuery);
582
- } catch (error) {
583
- console.error('Schema validation error for non-existent column:', error.message);
584
- // Expected output: Validation failed: Column 'non_existent_column' not found in table 'users'.
585
- }
586
- ```
587
-
588
- For more details on `SqlSchemaValidator`, see the [SqlSchemaValidator Usage Guide](../../docs/usage-guides/class-SqlSchemaValidator-usage-guide.md).
589
-
590
- ---
591
-
592
- ## QueryBuilder Features
593
-
594
- `QueryBuilder` is a powerful utility that enhances the management and generation of SQL modification queries (such as `INSERT` or `UPDATE`) by leveraging select queries. This approach significantly improves the maintainability of complex data manipulation logic. It allows for the conversion of select queries into corresponding update-type queries, streamlining development and ensuring consistency.
595
-
596
- ```typescript
597
- import { SelectQueryParser, QueryBuilder, SqlFormatter, QueryNormalizer } from 'rawsql-ts';
598
-
599
- // Example: Convert a SELECT query to an UPDATE query
600
- const selectSourceSql = 'SELECT id, new_email AS email, last_login FROM user_updates_source WHERE needs_update = TRUE';
601
- // QueryBuilder.buildUpdateQuery expects a SimpleSelectQuery as input.
602
- // If your source is a complex query (e.g. with UNIONs or CTEs), normalize it first.
603
- const normalizedSelectQuery = QueryNormalizer.normalize(SelectQueryParser.parse(selectSourceSql));
604
-
605
- // Define the target table for the UPDATE and the primary key(s) for joining
606
- const targetTable = 'users';
607
- const primaryKeys = ['id']; // Column(s) to match records between source and target
608
-
609
- const updateQuery = QueryBuilder.buildUpdateQuery(
610
- normalizedSelectQuery,
611
- 'd', // Alias of the source query in the FROM clause
612
- targetTable,
613
- primaryKeys
614
- );
615
-
616
- const formatter = new SqlFormatter({ preset: 'postgres' }); // Using postgres preset for clarity
617
- const { formattedSql: updateSql } = formatter.format(updateQuery);
618
-
619
- console.log(updateSql);
620
- // Example output (actual output depends on the SQL dialect and specific query structure):
621
- // update "users" set "email" = "d"."email", "last_login" = "d"."last_login" from (SELECT id, new_email AS email, last_login FROM user_updates_source WHERE needs_update = TRUE) as "d" where "users"."id" = "d"."id"
622
- ```
623
-
624
- For more details on `QueryBuilder`, see the [QueryBuilder Usage Guide](../../docs/usage-guides/class-QueryBuilder-usage-guide.md).
625
-
626
- ---
627
-
628
- ## SchemaManager Features
629
-
630
- The `SchemaManager` class provides unified schema definition and type-safe conversion to various formats, eliminating code duplication for rawsql-ts. It serves as a central hub for managing database schema definitions and converting them to formats required by different components like `SqlParamInjector` and `PostgresJsonQueryBuilder`.
631
-
632
- Key benefits include:
633
- - **Unified Schema Definition**: Define your database schema once and generate JSON mappings, table column resolvers, and other formats for multiple rawsql-ts components
634
- - **Type-Safe JSON Mapping**: Fully typed conversion without using `any` types
635
- - **Schema Validation**: Built-in validation ensures schema consistency and integrity
636
- - **Relationship Management**: Supports complex table relationships with object and array outputs
637
-
638
- ```typescript
639
- import { SchemaManager, createSchemaManager } from 'rawsql-ts';
640
-
641
- // Define your database schema once
642
- const schemas = {
643
- users: {
644
- name: 'users',
645
- columns: {
646
- user_id: { name: 'user_id', isPrimaryKey: true },
647
- user_name: { name: 'user_name' },
648
- email: { name: 'email' }
649
- }
650
- }
651
- };
652
-
653
- // Create SchemaManager and use with other components
654
- const schemaManager = createSchemaManager(schemas);
655
- const tableColumnResolver = schemaManager.createTableColumnResolver();
656
- const injector = new SqlParamInjector({ tableColumnResolver });
657
- ```
658
-
659
- For more details on `SchemaManager`, see the [SchemaManager Usage Guide](../../docs/usage-guides/class-SchemaManager-usage-guide.md).
660
-
661
- ---
662
-
663
- ## Benchmarks
664
-
665
- This project includes a comprehensive benchmark suite to evaluate the performance of `rawsql-ts` in comparison with other popular libraries such as `node-sql-parser` and `sql-formatter`.
666
-
667
- ### How to Run
668
-
669
- ```bash
670
- npm run benchmark
671
- ```
672
-
673
- ### Benchmark Details
674
-
675
- The benchmark suite measures SQL parsing and formatting speed across queries of varying complexity:
676
-
677
- - **Tokens20**: Simple SELECT with a basic WHERE clause (~20 tokens)
678
- - **Tokens70**: Medium complexity query with JOINs and multiple conditions (~70 tokens)
679
- - **Tokens140**: Complex query with CTEs and aggregations (~140 tokens)
680
- - **Tokens230**: Highly complex query with multiple CTEs, subqueries, and window functions (~230 tokens)
681
-
682
- ### Benchmark Environment
683
-
684
- ```
685
- benchmark.js v2.1.4
686
- Windows 10.0.26100
687
- AMD Ryzen 7 7800X3D (8C/16T)
688
- Node.js v22.14.0
689
- ```
690
-
691
- ### Results
692
-
693
- #### Tokens20
694
- | Method | Mean (ms) | Error (ms) | StdDev (ms) | Times slower vs rawsql-ts |
695
- |---------------------------------- |-----------:|----------:|----------:|--------------------------:|
696
- | rawsql-ts | 0.029 | 0.0087 | 0.0044 | - |
697
- | node-sql-parser | 0.210 | 0.4505 | 0.2298 | 7.3x |
698
- | sql-formatter | 0.228 | 0.1598 | 0.0815 | 8.0x |
699
-
700
- > [!Note] When the token count is extremely low, `rawsql-ts` becomes disproportionately fast. However, such small queries are rare in real-world scenarios, so this result is excluded from the overall performance summary.
701
-
702
- #### Tokens70
703
- | Method | Mean (ms) | Error (ms) | StdDev (ms) | Times slower vs rawsql-ts |
704
- |---------------------------------- |-----------:|----------:|----------:|--------------------------:|
705
- | rawsql-ts | 0.075 | 0.0541 | 0.0276 | - |
706
- | node-sql-parser | 0.223 | 0.0848 | 0.0432 | 3.0x |
707
- | sql-formatter | 0.547 | 0.1432 | 0.0731 | 7.3x |
708
-
709
- #### Tokens140
710
- | Method | Mean (ms) | Error (ms) | StdDev (ms) | Times slower vs rawsql-ts |
711
- |---------------------------------- |-----------:|----------:|----------:|--------------------------:|
712
- | rawsql-ts | 0.137 | 0.0175 | 0.0089 | - |
713
- | node-sql-parser | 0.420 | 0.1030 | 0.0526 | 3.1x |
714
- | sql-formatter | 1.057 | 0.2390 | 0.1220 | 7.7x |
715
-
716
- #### Tokens230
717
- | Method | Mean (ms) | Error (ms) | StdDev (ms) | Times slower vs rawsql-ts |
718
- |---------------------------------- |-----------:|----------:|----------:|--------------------------:|
719
- | rawsql-ts | 0.239 | 0.0577 | 0.0294 | - |
720
- | node-sql-parser | 0.871 | 0.2042 | 0.1042 | 3.6x |
721
- | sql-formatter | 1.906 | 1.4631 | 0.7465 | 8.0x |
722
-
723
- ### Performance Summary
724
-
725
- - `rawsql-ts` remains one of the fastest parsers, though it is approximately 10% slower in version 0.7 compared to previous versions. This is due to the addition of enhanced parameterized query parsing and SQL formatting capabilities.
726
- - About 3–4x faster than `node-sql-parser`.
727
- - About 4–5x faster than `sql-parser-cst`.
728
- - About 7–8x faster than `sql-formatter`.
729
- - Maintains high performance even for complex SQL, while providing comprehensive features.
730
-
731
- > **Note:** These benchmarks are based on a specific hardware and software environment. Actual performance may vary depending on system configuration and query complexity.
732
-
733
- ---
734
-
735
- Feel free to try rawsql-ts! Questions, requests, and bug reports are always welcome.
1
+ # rawsql-ts
2
+
3
+ ![No external dependencies](https://img.shields.io/badge/dependencies-none-brightgreen)
4
+ ![Browser Support](https://img.shields.io/badge/browser-%F0%9F%9A%80-brightgreen)
5
+ ![npm version](https://img.shields.io/npm/v/rawsql-ts)
6
+ ![npm downloads](https://img.shields.io/npm/dm/rawsql-ts)
7
+ ![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)
8
+
9
+ 🌐 [Online Demo (GitHub Pages)](https://mk3008.github.io/rawsql-ts/)
10
+
11
+ rawsql-ts is a high-performance SQL parser and AST transformer library written in TypeScript. It empowers you to represent raw SQL as objects, enabling flexible manipulation of SQL statements directly within your program. This object-oriented approach allows for partial transformation, decomposition into manageable components, and recombination as needed, dramatically improving the maintainability and reusability of complex SQL.
12
+
13
+ It is designed for extensibility and advanced SQL analysis, with initial focus on PostgreSQL syntax but not limited to it. The library enables easy SQL parsing, transformation, and analysis for a wide range of SQL dialects.
14
+
15
+ > [!Note]
16
+ > This library is currently in beta. The API may change until the v1.0 release.
17
+
18
+ ---
19
+
20
+ ## Key Features
21
+
22
+ - Zero dependencies: fully self-contained and lightweight
23
+ - High-speed SQL parsing and AST analysis (over 3x faster than major libraries)
24
+ - Rich utilities for SQL structure transformation and analysis
25
+ - Advanced SQL formatting capabilities, including multi-line formatting and customizable styles
26
+ - **Programmatic CTE Management** - Add, remove, and manipulate Common Table Expressions (CTEs) programmatically with support for PostgreSQL MATERIALIZED/NOT MATERIALIZED hints
27
+ - **JSON-to-TypeScript type transformation** - Automatically convert JSON-ified SQL results (dates as strings, BigInts) back to proper TypeScript types with configurable transformation rules
28
+ - **All-in-one dynamic query building with `DynamicQueryBuilder`** - combines filtering, sorting, pagination, and JSON serialization in a single, type-safe interface
29
+ - Dynamic SQL parameter injection for building flexible search queries with `SqlParamInjector` (supports like, ilike, in, any, range queries, OR/AND conditions and more)
30
+ - Dynamic ORDER BY clause injection with `SqlSortInjector` for flexible sorting with support for ASC/DESC, NULLS positioning, and append mode
31
+ - Dynamic LIMIT/OFFSET pagination injection with `SqlPaginationInjector` for efficient data pagination with page-based and offset-based support
32
+ - Type-safe schema management and JSON mapping conversion with full TypeScript support
33
+ - Static query validation and regression testing against your database schema with `SqlSchemaValidator`, enabling early error detection and robust unit tests for schema changes.
34
+
35
+ ![Benchmark Results](https://quickchart.io/chart?c={type:'bar',data:{labels:['Tokens20','Tokens70','Tokens140','Tokens230'],datasets:[{label:'rawsql-ts',data:[0.029,0.075,0.137,0.239],backgroundColor:'rgba(54,162,235,0.8)',borderColor:'rgba(54,162,235,1)',borderWidth:1},{label:'node-sql-parser',data:[0.210,0.223,0.420,0.871],backgroundColor:'rgba(255,206,86,0.8)',borderColor:'rgba(255,206,86,1)',borderWidth:1},{label:'sql-formatter',data:[0.228,0.547,1.057,1.906],backgroundColor:'rgba(255,99,132,0.8)',borderColor:'rgba(255,99,132,1)',borderWidth:1}]},options:{plugins:{legend:{labels:{color:'black'}}},scales:{x:{ticks:{color:'black'}},y:{ticks:{color:'black'}}},backgroundColor:'white'}}&width=700&height=450)
36
+
37
+ > [!Note]
38
+ > The "Mean" column represents the average time taken to process a query. Lower values indicate faster performance. For more details, see the [Benchmark](#benchmarks).
39
+
40
+ ## Browser & CDN Ready
41
+
42
+ You can use rawsql-ts directly in modern browsers via CDN (unpkg/jsdelivr)!
43
+ No Node.js dependencies, no build tools required.
44
+ Just import it like this:
45
+
46
+ ```html
47
+ <!-- Always get the latest version -->
48
+ <script type="module">
49
+ import { parse } from "https://unpkg.com/rawsql-ts/dist/esm/index.min.js";
50
+ </script>
51
+ ```
52
+
53
+ ```html
54
+ <!-- Pin a specific version for stability -->
55
+ <script type="module">
56
+ import { parse } from "https://unpkg.com/rawsql-ts@0.10.9-beta/dist/esm/index.min.js";
57
+ </script>
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Installation
63
+
64
+ ```bash
65
+ npm install rawsql-ts
66
+ ```
67
+
68
+ ---
69
+
70
+ ## Quick Start
71
+
72
+ Experience the power of rawsql-ts with `DynamicQueryBuilder` - build complex queries with filtering, sorting, pagination, and JSON serialization in one go!
73
+
74
+ ```typescript
75
+ import { DynamicQueryBuilder, SqlFormatter } from 'rawsql-ts';
76
+
77
+ // Start with a simple base SQL
78
+ const baseSql = 'SELECT id, name, email, created_at FROM users WHERE active = true';
79
+
80
+ // Build a complete dynamic query with all features
81
+ const builder = new DynamicQueryBuilder();
82
+ const query = builder.buildQuery(baseSql, {
83
+ // Dynamic filtering
84
+ filter: {
85
+ status: 'premium',
86
+ created_at: { '>': '2024-01-01' }
87
+ },
88
+ // Dynamic sorting
89
+ sort: {
90
+ created_at: { desc: true },
91
+ name: { asc: true }
92
+ },
93
+ // Dynamic pagination
94
+ paging: {
95
+ page: 2,
96
+ pageSize: 10
97
+ },
98
+ // JSON serialization (optional)
99
+ serialize: {
100
+ rootName: 'user',
101
+ rootEntity: {
102
+ id: 'user',
103
+ name: 'User',
104
+ columns: { id: 'id', name: 'name', email: 'email', createdAt: 'created_at' }
105
+ },
106
+ nestedEntities: []
107
+ }
108
+ });
109
+
110
+ // Format and execute
111
+ const formatter = new SqlFormatter();
112
+ const { formattedSql, params } = formatter.format(query);
113
+
114
+ console.log('Generated SQL:');
115
+ console.log(formattedSql);
116
+ // Output: Optimized PostgreSQL JSON query with filtering, sorting, and pagination
117
+
118
+ console.log('Parameters:');
119
+ console.log(params);
120
+ // Output: { status: 'premium', created_at_gt: '2024-01-01' }
121
+ ```
122
+
123
+ ---
124
+
125
+ ## CTE Management API
126
+
127
+ The CTE Management API provides programmatic control over Common Table Expressions (CTEs), allowing you to build and manipulate WITH clauses dynamically. This is particularly useful for building complex analytical queries, data transformation pipelines, and hierarchical data structures.
128
+
129
+ ```typescript
130
+ import { SelectQueryParser, SqlFormatter } from 'rawsql-ts';
131
+
132
+ // Build a multi-step data pipeline with CTEs
133
+ const pipeline = SelectQueryParser.parse('SELECT * FROM final_results').toSimpleQuery();
134
+
135
+ // Step 1: Add raw data CTE with PostgreSQL MATERIALIZED hint
136
+ const salesData = SelectQueryParser.parse(`
137
+ SELECT customer_id, order_date, amount, product_category
138
+ FROM sales WHERE order_date >= '2024-01-01'
139
+ `);
140
+ pipeline.addCTE('raw_sales', salesData.toSimpleQuery(), { materialized: true });
141
+
142
+ // Step 2: Add aggregation CTE
143
+ const monthlyStats = SelectQueryParser.parse(`
144
+ SELECT customer_id, DATE_TRUNC('month', order_date) as month,
145
+ SUM(amount) as total, COUNT(*) as orders
146
+ FROM raw_sales
147
+ GROUP BY customer_id, DATE_TRUNC('month', order_date)
148
+ `);
149
+ pipeline.addCTE('monthly_stats', monthlyStats.toSimpleQuery());
150
+
151
+ // Manage CTEs programmatically
152
+ console.log(pipeline.getCTENames()); // ['raw_sales', 'monthly_stats']
153
+ console.log(pipeline.hasCTE('raw_sales')); // true
154
+
155
+ // Replace the final query to use the CTEs
156
+ const finalQuery = SelectQueryParser.parse(`
157
+ SELECT * FROM monthly_stats WHERE total > 10000
158
+ `);
159
+ pipeline.replaceCTE('final_results', finalQuery.toSimpleQuery());
160
+
161
+ // Format and execute
162
+ const formatter = new SqlFormatter();
163
+ const { formattedSql } = formatter.format(pipeline);
164
+ // Output: WITH "raw_sales" AS MATERIALIZED (...), "monthly_stats" AS (...) SELECT * FROM monthly_stats WHERE total > 10000
165
+ ```
166
+
167
+ Key features include:
168
+ - **Dynamic CTE Management**: Add, remove, replace, and query CTEs programmatically
169
+ - **PostgreSQL MATERIALIZED Support**: Control query optimization with MATERIALIZED/NOT MATERIALIZED hints
170
+ - **Type Safety**: Full TypeScript support with error handling for duplicate names and invalid operations
171
+ - **Performance Optimized**: O(1) CTE name lookups for efficient operations
172
+
173
+ For comprehensive examples and advanced usage patterns, see the [CTE Management API Usage Guide](../../docs/usage-guides/cte-management-api-usage-guide.md).
174
+
175
+ ---
176
+
177
+ ## SelectQueryParser & Query Types
178
+
179
+ rawsql-ts provides robust parsers for `SELECT`, `INSERT`, and `UPDATE` statements, automatically handling SQL comments and providing detailed error messages. By converting SQL into a generic Abstract Syntax Tree (AST), it enables a wide variety of transformation processes.
180
+
181
+ ### Query Type Overview
182
+
183
+ The parser returns different query types based on SQL structure:
184
+
185
+ - **`SimpleSelectQuery`**: Single SELECT statement with comprehensive manipulation API
186
+ - **`BinarySelectQuery`**: Combined queries using UNION, INTERSECT, EXCEPT operators
187
+ - **`ValuesQuery`**: VALUES clause queries for data insertion or testing
188
+ - **`SelectQuery`**: Base interface implemented by all SELECT query classes
189
+
190
+ ```typescript
191
+ import { SelectQueryParser } from 'rawsql-ts';
192
+
193
+ // Returns SimpleSelectQuery
194
+ const simpleQuery = SelectQueryParser.parse('SELECT id, name FROM products WHERE category = \'electronics\'');
195
+
196
+ // Returns BinarySelectQuery
197
+ const unionQuery = SelectQueryParser.parse('SELECT id, name FROM products UNION SELECT id, name FROM archived_products');
198
+
199
+ // Returns ValuesQuery
200
+ const valuesQuery = SelectQueryParser.parse('VALUES (1, \'Alice\'), (2, \'Bob\')');
201
+ ```
202
+
203
+ ### SimpleSelectQuery - Rich Programmatic API
204
+
205
+ `SimpleSelectQuery` provides extensive methods for programmatic query building and manipulation:
206
+
207
+ **Dynamic Condition Building:**
208
+ - `appendWhereExpr(columnName, exprBuilder)` - Add conditions by column name with upstream injection support
209
+ - `appendWhereRaw()`, `appendHavingRaw()` - Append raw SQL conditions
210
+ - `setParameter(name, value)` - Manage named parameters directly on the query object
211
+
212
+ **Query Composition:**
213
+ - `toUnion()`, `toIntersect()`, `toExcept()` - Combine with other queries
214
+ - `innerJoin()`, `leftJoin()`, `rightJoin()` - Add JOIN clauses programmatically
215
+ - `appendWith()` - Add Common Table Expressions (CTEs)
216
+
217
+ **Advanced Features:**
218
+ - Column-aware condition injection that resolves aliases and expressions
219
+ - Parameter management with validation and type safety
220
+ - Subquery wrapping with `toSource(alias)` for complex compositions
221
+
222
+ ```typescript
223
+ import { SelectQueryParser } from 'rawsql-ts';
224
+
225
+ const query = SelectQueryParser.parse('SELECT id, salary * 1.1 AS adjusted_salary FROM employees');
226
+
227
+ // Add condition targeting the calculated column
228
+ query.appendWhereExpr('adjusted_salary', expr => `${expr} > 50000`);
229
+
230
+ // Set parameters directly on the query
231
+ query.setParameter('dept_id', 123);
232
+
233
+ // Add JOINs programmatically
234
+ query.leftJoinRaw('departments', 'd', 'department_id');
235
+
236
+ // Combine with another query
237
+ const adminQuery = SelectQueryParser.parse('SELECT id, salary FROM admins');
238
+ const combinedQuery = query.toUnion(adminQuery);
239
+ ```
240
+
241
+ For comprehensive API documentation and advanced examples, see the [SimpleSelectQuery Usage Guide](../../docs/usage-guides/class-SimpleSelectQuery-usage-guide.md).
242
+
243
+ For SelectQueryParser details, see the [SelectQueryParser Usage Guide](../../docs/usage-guides/class-SelectQueryParser-usage-guide.md).
244
+
245
+ ---
246
+
247
+ ## SqlFormatter Features
248
+
249
+ The `SqlFormatter` class is the recommended way to format SQL queries, offering advanced capabilities like indentation, keyword casing, and multi-line formatting.
250
+ It also allows for detailed style customization. For example, you can define your own formatting rules:
251
+
252
+ ```typescript
253
+ import { SelectQueryParser, SqlFormatter } from 'rawsql-ts';
254
+
255
+ const customStyle = {
256
+ identifierEscape: {
257
+ start: "",
258
+ end: ""
259
+ },
260
+ parameterSymbol: ":",
261
+ parameterStyle: "named",
262
+ indentSize: 4,
263
+ indentChar: " ",
264
+ newline: "\n",
265
+ keywordCase: "lower",
266
+ commaBreak: "before",
267
+ andBreak: "before"
268
+ };
269
+
270
+ const sqlToFormat = `SELECT u.user_id, u.user_name FROM users as u WHERE status = :active ORDER BY created_at DESC;`;
271
+ const queryToFormat = SelectQueryParser.parse(sqlToFormat);
272
+ const customFormatter = new SqlFormatter(customStyle);
273
+ const { formattedSql: customFormattedSql } = customFormatter.format(queryToFormat);
274
+
275
+ console.log(customFormattedSql);
276
+ /*
277
+ select
278
+ u.user_id
279
+ , u.user_name
280
+ from
281
+ users as u
282
+ where
283
+ status = :active
284
+ order by
285
+ created_at desc;
286
+ */
287
+ ```
288
+
289
+ For more details, see the [SqlFormatter Usage Guide](../../docs/usage-guides/class-SqlFormatter-usage-guide.md).
290
+
291
+ ---
292
+
293
+ ## SqlParamInjector Features
294
+
295
+ The `SqlParamInjector` class revolutionizes how you build dynamic search queries. Instead of manually constructing different SQL statements for various search conditions, you simply provide a fixed base SQL and a state object. `SqlParamInjector` then dynamically injects parameters and automatically generates the optimal WHERE conditions.
296
+
297
+ Key benefits include:
298
+ - **Simplified Query Management**: Prepare a single base SQL; `SqlParamInjector` handles the variations.
299
+ - **Effortless Optimal Queries**: Just pass a state object, and it generates a highly efficient query.
300
+ - **Performance-Oriented**: Conditions are intelligently inserted as close to the data source as possible, significantly improving query performance by filtering data early.
301
+ - **Zero Conditional Logic in Code**: Forget writing complex IF statements in your application code to handle different filters.
302
+ - **Enhanced SQL Reusability**: Your base SQL remains clean and can be reused across different scenarios with varying search criteria.
303
+ - **Rich Operator Support**: Supports various SQL operators including equality, comparison, range (min/max), pattern matching (like/ilike), IN clauses, and PostgreSQL array operators.
304
+ - **Advanced Condition Logic**: Supports OR/AND conditions, automatic parentheses grouping, and explicit column mapping for flexible query construction.
305
+
306
+ ```typescript
307
+ import { SqlParamInjector, SqlFormatter } from 'rawsql-ts';
308
+
309
+ const sql = `SELECT u.user_id, u.user_name FROM users as u WHERE u.active = TRUE`;
310
+ const injector = new SqlParamInjector();
311
+ // Inject parameters and generate WHERE conditions
312
+ const injectedQuery = injector.inject(sql, { user_id: 42, user_name: 'Alice' });
313
+
314
+ const formatter = new SqlFormatter();
315
+ const { formattedSql, params } = formatter.format(injectedQuery);
316
+
317
+ console.log(formattedSql);
318
+ // Output: select "u"."user_id", "u"."user_name" from "users" as "u" where "u"."active" = true and "u"."user_id" = :user_id and "u"."user_name" = :user_name
319
+ console.log(params);
320
+ // Output: { user_id: 42, user_name: 'Alice' }
321
+ ```
322
+
323
+ For more details, see the [SqlParamInjector Usage Guide](../../docs/usage-guides/class-SqlParamInjector-usage-guide.md).
324
+
325
+ ---
326
+
327
+ ## SqlSortInjector Features
328
+
329
+ The `SqlSortInjector` class enables dynamic ORDER BY clause injection into SQL queries, providing a clean and flexible way to handle sorting requirements. Instead of manually constructing different SQL statements for various sorting scenarios, you can inject sort conditions into your base SQL query, making your code more maintainable and reusable.
330
+
331
+ Key benefits include:
332
+ - **Dynamic Sort Injection**: Add ORDER BY conditions to existing queries without modifying the base SQL
333
+ - **Flexible Sort Options**: Support for ASC/DESC directions with NULLS FIRST/LAST positioning
334
+ - **Append Mode**: Preserves existing ORDER BY clauses and appends new conditions
335
+ - **Column Alias Support**: Works seamlessly with column aliases and calculated expressions
336
+ - **TableColumnResolver Integration**: Supports wildcard SELECT queries through schema resolution
337
+ - **Clean Separation**: Remove existing ORDER BY clauses when needed with `removeOrderBy()`
338
+
339
+ ```typescript
340
+ import { SqlSortInjector, SqlFormatter } from 'rawsql-ts';
341
+
342
+ // Basic sort injection
343
+ const baseSql = 'SELECT id, name, created_at FROM users WHERE active = true';
344
+ const sortConditions = {
345
+ created_at: { desc: true, nullsLast: true },
346
+ name: { asc: true }
347
+ };
348
+
349
+ const injector = new SqlSortInjector();
350
+ const sortedQuery = injector.inject(baseSql, sortConditions);
351
+
352
+ const formatter = new SqlFormatter();
353
+ const { formattedSql } = formatter.format(sortedQuery);
354
+
355
+ console.log(formattedSql);
356
+ // Output: select "id", "name", "created_at" from "users" where "active" = true order by "created_at" desc nulls last, "name"
357
+
358
+ // Append to existing ORDER BY
359
+ const existingSql = 'SELECT id, name FROM users ORDER BY id ASC';
360
+ const additionalSort = { name: { desc: true } };
361
+ const appendedQuery = injector.inject(existingSql, additionalSort);
362
+ // Result: SELECT id, name FROM users ORDER BY id ASC, name DESC
363
+
364
+ // Remove existing ORDER BY when needed
365
+ const cleanQuery = SqlSortInjector.removeOrderBy(existingSql);
366
+ const newSortedQuery = injector.inject(cleanQuery, { name: { desc: true } });
367
+ // Result: SELECT id, name FROM users ORDER BY name DESC
368
+ ```
369
+
370
+ For more details, see the [SqlSortInjector Usage Guide](../../docs/usage-guides/class-SqlSortInjector-usage-guide.md).
371
+
372
+ ---
373
+
374
+ ## SqlPaginationInjector Features
375
+
376
+ The `SqlPaginationInjector` class enables dynamic LIMIT/OFFSET clause injection into SQL queries, providing a clean and efficient way to handle data pagination. Instead of manually constructing different SQL statements for various page sizes and offsets, you can inject pagination into your base SQL query, making your code more maintainable and performance-optimized.
377
+
378
+ Key benefits include:
379
+ - **Page-Based Pagination**: Simple page number and page size configuration with automatic offset calculation
380
+ - **Automatic OFFSET Optimization**: Smart handling where page 1 omits unnecessary OFFSET 0 clauses
381
+ - **Existing Clause Detection**: Prevents conflicts by detecting and reporting existing LIMIT/OFFSET clauses
382
+ - **Validation & Safety**: Built-in validation for page numbers, page sizes, and configurable maximum limits
383
+ - **Clean Removal**: Remove existing pagination clauses when needed with `removePagination()`
384
+ - **Performance Conscious**: Generates standard LIMIT/OFFSET patterns optimized for database engines
385
+
386
+ ```typescript
387
+ import { SqlPaginationInjector, SqlFormatter } from 'rawsql-ts';
388
+
389
+ // Basic pagination injection
390
+ const baseSql = 'SELECT id, name, email FROM users WHERE active = true ORDER BY created_at DESC';
391
+ const pagination = {
392
+ page: 2, // Page number (1-based)
393
+ pageSize: 20 // Items per page
394
+ };
395
+
396
+ const injector = new SqlPaginationInjector();
397
+ const paginatedQuery = injector.inject(baseSql, pagination);
398
+
399
+ const formatter = new SqlFormatter();
400
+ const { formattedSql } = formatter.format(paginatedQuery);
401
+
402
+ console.log(formattedSql);
403
+ // Output: select "id", "name", "email" from "users" where "active" = true order by "created_at" desc limit 20 offset 20
404
+
405
+ // First page optimization (no OFFSET needed)
406
+ const firstPagePagination = { page: 1, pageSize: 10 };
407
+ const firstPageQuery = injector.inject(baseSql, firstPagePagination);
408
+ const { formattedSql: firstPageSql } = formatter.format(firstPageQuery);
409
+ console.log(firstPageSql);
410
+ // Output: select "id", "name", "email" from "users" where "active" = true order by "created_at" desc limit 10
411
+
412
+ // Remove existing pagination when needed
413
+ const existingPaginatedSql = 'SELECT id, name FROM users LIMIT 50 OFFSET 100';
414
+ const cleanQuery = SqlPaginationInjector.removePagination(existingPaginatedSql);
415
+ const newPagination = { page: 1, pageSize: 25 };
416
+ const repaginatedQuery = injector.inject(cleanQuery, newPagination);
417
+ // Result: Clean query with new pagination applied
418
+
419
+ // Combine with other injectors for complete query building
420
+ let query = new SqlParamInjector().inject(baseSql, { status: 'active' });
421
+ query = new SqlSortInjector().inject(query, { created_at: { desc: true } });
422
+ query = new SqlPaginationInjector().inject(query, { page: 3, pageSize: 15 });
423
+ // Result: Filtered, sorted, and paginated query ready for execution
424
+ ```
425
+
426
+ For more details, see the [SqlPaginationInjector Usage Guide](../../docs/usage-guides/class-SqlPaginationInjector-usage-guide.md).
427
+
428
+ ---
429
+
430
+ ## DynamicQueryBuilder Features
431
+
432
+ The `DynamicQueryBuilder` class is a powerful, all-in-one solution that combines SQL parsing with dynamic condition injection (filtering, sorting, pagination, and JSON serialization). It provides a unified interface for building complex queries without the need to manually chain multiple injectors, making it ideal for modern web applications that require flexible, dynamic query generation.
433
+
434
+ Key benefits include:
435
+ - **Unified Interface**: Single class that combines filtering, sorting, pagination, and JSON serialization
436
+ - **Framework-Agnostic**: Pure JavaScript/TypeScript with no file system dependencies
437
+ - **Composable Architecture**: Internally uses specialized injectors in optimal order for performance
438
+ - **Type-Safe**: Full TypeScript support with strongly typed options and return values
439
+ - **Performance Optimized**: Applies conditions in the most efficient order (filter → sort → paginate → serialize)
440
+ - **Easy Testing**: No external dependencies make unit testing straightforward
441
+
442
+ ```typescript
443
+ import { DynamicQueryBuilder, SqlFormatter } from 'rawsql-ts';
444
+
445
+ // Create a builder instance
446
+ const builder = new DynamicQueryBuilder();
447
+
448
+ // Build a complete dynamic query with all features
449
+ const baseQuery = 'SELECT id, name, email, created_at FROM users WHERE active = true';
450
+ const options = {
451
+ filter: {
452
+ status: 'premium',
453
+ created_at: { min: '2024-01-01' } // Range filter
454
+ },
455
+ sort: {
456
+ created_at: { desc: true, nullsLast: true },
457
+ name: { asc: true }
458
+ },
459
+ paging: { page: 2, pageSize: 20 },
460
+ serialize: {
461
+ rootName: 'users',
462
+ rootEntity: {
463
+ id: 'user',
464
+ name: 'User',
465
+ columns: { id: 'id', name: 'name', email: 'email', created: 'created_at' }
466
+ },
467
+ nestedEntities: []
468
+ }
469
+ };
470
+
471
+ const dynamicQuery = builder.buildQuery(baseQuery, options);
472
+
473
+ const formatter = new SqlFormatter();
474
+ const { formattedSql, params } = formatter.format(dynamicQuery);
475
+
476
+ console.log(formattedSql);
477
+ // Output: Complex JSON query with all conditions applied
478
+ console.log(params);
479
+ // Output: { status: 'premium', created_at_min: '2024-01-01', paging_limit: 20, paging_offset: 20 }
480
+
481
+ // Convenience methods for specific use cases
482
+ const filteredOnly = builder.buildFilteredQuery(baseQuery, { name: 'Alice' });
483
+ const sortedOnly = builder.buildSortedQuery(baseQuery, { created_at: { desc: true } });
484
+ const paginatedOnly = builder.buildPaginatedQuery(baseQuery, { page: 1, pageSize: 10 });
485
+
486
+ // Validate SQL without applying modifications
487
+ const isValid = builder.validateSql('SELECT id FROM users');
488
+ console.log(isValid); // true
489
+ ```
490
+
491
+ For more details, see the [DynamicQueryBuilder Usage Guide](../../docs/usage-guides/class-DynamicQueryBuilder-usage-guide.md).
492
+
493
+ ---
494
+
495
+ ## PostgresJsonQueryBuilder Features
496
+
497
+ The `PostgresJsonQueryBuilder` class transforms relational SQL queries into PostgreSQL JSON queries that return hierarchical JSON structures. It automatically handles complex relationships between entities and generates optimized Common Table Expressions (CTEs) for efficient JSON aggregation, making it perfect for building APIs, reports, and data exports.
498
+
499
+ Key benefits include:
500
+ - **Hierarchical JSON Generation**: Transforms flat relational data into nested JSON objects and arrays
501
+ - **Automatic CTE Management**: Generates optimized CTEs with proper dependency ordering
502
+ - **Flexible Relationship Types**: Supports both object (0..1) and array (1..N) relationships
503
+ - **NULL Handling**: Properly represents missing relationships as NULL instead of empty objects
504
+ - **Performance Optimized**: Uses depth-based processing and JSONB for optimal PostgreSQL performance
505
+ - **Zero Manual Serialization**: Eliminates the need for manual object mapping and JSON construction
506
+
507
+ ```typescript
508
+ import { SelectQueryParser, PostgresJsonQueryBuilder } from 'rawsql-ts';
509
+
510
+ // Parse your base SQL query
511
+ const baseQuery = SelectQueryParser.parse(`
512
+ SELECT o.order_id, o.order_date, c.customer_name, i.product_name, i.quantity
513
+ FROM orders o
514
+ LEFT JOIN customers c ON o.customer_id = c.customer_id
515
+ LEFT JOIN order_items i ON o.order_id = i.order_id
516
+ `) as SimpleSelectQuery;
517
+
518
+ // Define JSON mapping configuration
519
+ const mapping = {
520
+ rootName: "order",
521
+ rootEntity: { id: "order", name: "Order", columns: { "id": "order_id", "date": "order_date" }},
522
+ nestedEntities: [
523
+ { id: "customer", parentId: "order", propertyName: "customer", relationshipType: "object",
524
+ columns: { "name": "customer_name" }},
525
+ { id: "items", parentId: "order", propertyName: "items", relationshipType: "array",
526
+ columns: { "product": "product_name", "qty": "quantity" }} ]
527
+ };
528
+
529
+ // Transform to JSON query
530
+ const builder = new PostgresJsonQueryBuilder();
531
+ const jsonQuery = builder.buildJson(baseQuery, mapping);
532
+ // Returns optimized PostgreSQL query with CTEs that produces:
533
+ // [{ "id": 1, "date": "2024-01-15", "customer": {"name": "John"}, "items": [{"product": "Widget", "qty": 2}] }]
534
+ ```
535
+
536
+ For more details, see the [PostgresJsonQueryBuilder Usage Guide](../../docs/usage-guides/class-PostgresJsonQueryBuilder-usage-guide.md).
537
+
538
+ ---
539
+
540
+ ## SqlSchemaValidator Features
541
+
542
+ The `SqlSchemaValidator` class helps ensure your SQL queries are valid against a predefined database schema. It can extract schema information about the physical tables your SQL query depends on. By comparing this extracted information with your defined schema (e.g., a schema definition class), you can statically verify the query's behavior. This enables you to perform regression testing as part of your unit tests when schema definitions change, ensuring that your queries remain compatible.
543
+
544
+ It checks if the tables and columns referenced in your query actually exist in your schema, and it understands table aliases. If there's a problem, it gives you a clear error message telling you what's wrong and where.
545
+
546
+ Key benefits include:
547
+ - **Schema Validation**: Verifies SQL queries against your database schema.
548
+ - **Table and Column Verification**: Confirms the existence of tables and columns used in the query.
549
+ - **Alias Aware**: Correctly resolves table aliases.
550
+ - **Clear Error Reporting**: Provides descriptive error messages for easy debugging.
551
+ - **Static Analysis**: Allows comparison of SQL-derived schema information with predefined schema definitions.
552
+ - **Automated Regression Testing**: Facilitates unit testing for schema change impacts on queries.
553
+
554
+ ```typescript
555
+ import { SelectQueryParser, SqlSchemaValidator } from 'rawsql-ts';
556
+
557
+ // Define your database schema
558
+ const schema = {
559
+ users: ['user_id', 'user_name', 'email', 'status'],
560
+ orders: ['order_id', 'user_id', 'order_date', 'total_amount']
561
+ };
562
+
563
+ const validator = new SqlSchemaValidator(schema);
564
+
565
+ // Example: Validate a SELECT query
566
+ const validSql = 'SELECT u.user_id, u.user_name FROM users as u WHERE u.status = \'active\'';
567
+ const queryToValidate = SelectQueryParser.parse(validSql);
568
+
569
+ try {
570
+ validator.validate(queryToValidate);
571
+ console.log('Query is valid against the schema.');
572
+ } catch (error) {
573
+ console.error('Schema validation failed:', error.message);
574
+ }
575
+
576
+ // Example: Validate a query with a non-existent column
577
+ const invalidSql = 'SELECT user_id, non_existent_column FROM users';
578
+ const invalidQuery = SelectQueryParser.parse(invalidSql);
579
+
580
+ try {
581
+ validator.validate(invalidQuery);
582
+ } catch (error) {
583
+ console.error('Schema validation error for non-existent column:', error.message);
584
+ // Expected output: Validation failed: Column 'non_existent_column' not found in table 'users'.
585
+ }
586
+ ```
587
+
588
+ For more details on `SqlSchemaValidator`, see the [SqlSchemaValidator Usage Guide](../../docs/usage-guides/class-SqlSchemaValidator-usage-guide.md).
589
+
590
+ ---
591
+
592
+ ## QueryBuilder Features
593
+
594
+ `QueryBuilder` is a powerful utility that enhances the management and generation of SQL modification queries (such as `INSERT` or `UPDATE`) by leveraging select queries. This approach significantly improves the maintainability of complex data manipulation logic. It allows for the conversion of select queries into corresponding update-type queries, streamlining development and ensuring consistency.
595
+
596
+ ```typescript
597
+ import { SelectQueryParser, QueryBuilder, SqlFormatter, QueryNormalizer } from 'rawsql-ts';
598
+
599
+ // Example: Convert a SELECT query to an UPDATE query
600
+ const selectSourceSql = 'SELECT id, new_email AS email, last_login FROM user_updates_source WHERE needs_update = TRUE';
601
+ // QueryBuilder.buildUpdateQuery expects a SimpleSelectQuery as input.
602
+ // If your source is a complex query (e.g. with UNIONs or CTEs), normalize it first.
603
+ const normalizedSelectQuery = QueryNormalizer.normalize(SelectQueryParser.parse(selectSourceSql));
604
+
605
+ // Define the target table for the UPDATE and the primary key(s) for joining
606
+ const targetTable = 'users';
607
+ const primaryKeys = ['id']; // Column(s) to match records between source and target
608
+
609
+ const updateQuery = QueryBuilder.buildUpdateQuery(
610
+ normalizedSelectQuery,
611
+ 'd', // Alias of the source query in the FROM clause
612
+ targetTable,
613
+ primaryKeys
614
+ );
615
+
616
+ const formatter = new SqlFormatter({ preset: 'postgres' }); // Using postgres preset for clarity
617
+ const { formattedSql: updateSql } = formatter.format(updateQuery);
618
+
619
+ console.log(updateSql);
620
+ // Example output (actual output depends on the SQL dialect and specific query structure):
621
+ // update "users" set "email" = "d"."email", "last_login" = "d"."last_login" from (SELECT id, new_email AS email, last_login FROM user_updates_source WHERE needs_update = TRUE) as "d" where "users"."id" = "d"."id"
622
+ ```
623
+
624
+ For more details on `QueryBuilder`, see the [QueryBuilder Usage Guide](../../docs/usage-guides/class-QueryBuilder-usage-guide.md).
625
+
626
+ ---
627
+
628
+ ## SchemaManager Features
629
+
630
+ The `SchemaManager` class provides unified schema definition and type-safe conversion to various formats, eliminating code duplication for rawsql-ts. It serves as a central hub for managing database schema definitions and converting them to formats required by different components like `SqlParamInjector` and `PostgresJsonQueryBuilder`.
631
+
632
+ Key benefits include:
633
+ - **Unified Schema Definition**: Define your database schema once and generate JSON mappings, table column resolvers, and other formats for multiple rawsql-ts components
634
+ - **Type-Safe JSON Mapping**: Fully typed conversion without using `any` types
635
+ - **Schema Validation**: Built-in validation ensures schema consistency and integrity
636
+ - **Relationship Management**: Supports complex table relationships with object and array outputs
637
+
638
+ ```typescript
639
+ import { SchemaManager, createSchemaManager } from 'rawsql-ts';
640
+
641
+ // Define your database schema once
642
+ const schemas = {
643
+ users: {
644
+ name: 'users',
645
+ columns: {
646
+ user_id: { name: 'user_id', isPrimaryKey: true },
647
+ user_name: { name: 'user_name' },
648
+ email: { name: 'email' }
649
+ }
650
+ }
651
+ };
652
+
653
+ // Create SchemaManager and use with other components
654
+ const schemaManager = createSchemaManager(schemas);
655
+ const tableColumnResolver = schemaManager.createTableColumnResolver();
656
+ const injector = new SqlParamInjector({ tableColumnResolver });
657
+ ```
658
+
659
+ For more details on `SchemaManager`, see the [SchemaManager Usage Guide](../../docs/usage-guides/class-SchemaManager-usage-guide.md).
660
+
661
+ ---
662
+
663
+ ## Benchmarks
664
+
665
+ This project includes a comprehensive benchmark suite to evaluate the performance of `rawsql-ts` in comparison with other popular libraries such as `node-sql-parser` and `sql-formatter`.
666
+
667
+ ### How to Run
668
+
669
+ ```bash
670
+ npm run benchmark
671
+ ```
672
+
673
+ ### Benchmark Details
674
+
675
+ The benchmark suite measures SQL parsing and formatting speed across queries of varying complexity:
676
+
677
+ - **Tokens20**: Simple SELECT with a basic WHERE clause (~20 tokens)
678
+ - **Tokens70**: Medium complexity query with JOINs and multiple conditions (~70 tokens)
679
+ - **Tokens140**: Complex query with CTEs and aggregations (~140 tokens)
680
+ - **Tokens230**: Highly complex query with multiple CTEs, subqueries, and window functions (~230 tokens)
681
+
682
+ ### Benchmark Environment
683
+
684
+ ```
685
+ benchmark.js v2.1.4
686
+ Windows 10.0.26100
687
+ AMD Ryzen 7 7800X3D (8C/16T)
688
+ Node.js v22.14.0
689
+ ```
690
+
691
+ ### Results
692
+
693
+ #### Tokens20
694
+ | Method | Mean (ms) | Error (ms) | StdDev (ms) | Times slower vs rawsql-ts |
695
+ |---------------------------------- |-----------:|----------:|----------:|--------------------------:|
696
+ | rawsql-ts | 0.029 | 0.0087 | 0.0044 | - |
697
+ | node-sql-parser | 0.210 | 0.4505 | 0.2298 | 7.3x |
698
+ | sql-formatter | 0.228 | 0.1598 | 0.0815 | 8.0x |
699
+
700
+ > [!Note] When the token count is extremely low, `rawsql-ts` becomes disproportionately fast. However, such small queries are rare in real-world scenarios, so this result is excluded from the overall performance summary.
701
+
702
+ #### Tokens70
703
+ | Method | Mean (ms) | Error (ms) | StdDev (ms) | Times slower vs rawsql-ts |
704
+ |---------------------------------- |-----------:|----------:|----------:|--------------------------:|
705
+ | rawsql-ts | 0.075 | 0.0541 | 0.0276 | - |
706
+ | node-sql-parser | 0.223 | 0.0848 | 0.0432 | 3.0x |
707
+ | sql-formatter | 0.547 | 0.1432 | 0.0731 | 7.3x |
708
+
709
+ #### Tokens140
710
+ | Method | Mean (ms) | Error (ms) | StdDev (ms) | Times slower vs rawsql-ts |
711
+ |---------------------------------- |-----------:|----------:|----------:|--------------------------:|
712
+ | rawsql-ts | 0.137 | 0.0175 | 0.0089 | - |
713
+ | node-sql-parser | 0.420 | 0.1030 | 0.0526 | 3.1x |
714
+ | sql-formatter | 1.057 | 0.2390 | 0.1220 | 7.7x |
715
+
716
+ #### Tokens230
717
+ | Method | Mean (ms) | Error (ms) | StdDev (ms) | Times slower vs rawsql-ts |
718
+ |---------------------------------- |-----------:|----------:|----------:|--------------------------:|
719
+ | rawsql-ts | 0.239 | 0.0577 | 0.0294 | - |
720
+ | node-sql-parser | 0.871 | 0.2042 | 0.1042 | 3.6x |
721
+ | sql-formatter | 1.906 | 1.4631 | 0.7465 | 8.0x |
722
+
723
+ ### Performance Summary
724
+
725
+ - `rawsql-ts` remains one of the fastest parsers, though it is approximately 10% slower in version 0.7 compared to previous versions. This is due to the addition of enhanced parameterized query parsing and SQL formatting capabilities.
726
+ - About 3–4x faster than `node-sql-parser`.
727
+ - About 4–5x faster than `sql-parser-cst`.
728
+ - About 7–8x faster than `sql-formatter`.
729
+ - Maintains high performance even for complex SQL, while providing comprehensive features.
730
+
731
+ > **Note:** These benchmarks are based on a specific hardware and software environment. Actual performance may vary depending on system configuration and query complexity.
732
+
733
+ ---
734
+
735
+ Feel free to try rawsql-ts! Questions, requests, and bug reports are always welcome.