Skip to content

Commit f703e01

Browse files
committed
chore: unify DSL
1 parent 8115d07 commit f703e01

File tree

11 files changed

+705
-490
lines changed

11 files changed

+705
-490
lines changed

documentation/components/libs/pg-query.md

Lines changed: 83 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -47,136 +47,62 @@ foreach (pg_query_functions($query)->all() as $func) {
4747
}
4848
```
4949

50-
## Parser Class
50+
## Parsing and Utilities
5151

5252
```php
5353
<?php
5454

55-
use Flow\PgQuery\Parser;
56-
57-
$parser = new Parser();
55+
use function Flow\PgQuery\DSL\{
56+
pg_parse,
57+
pg_fingerprint,
58+
pg_normalize,
59+
pg_normalize_utility,
60+
pg_split,
61+
pg_summary
62+
};
5863

5964
// Parse SQL into ParsedQuery
60-
$query = $parser->parse('SELECT * FROM users WHERE id = 1');
65+
$query = pg_parse('SELECT * FROM users WHERE id = 1');
6166

6267
// Generate fingerprint (same for structurally equivalent queries)
63-
$fingerprint = $parser->fingerprint('SELECT * FROM users WHERE id = 1');
68+
$fingerprint = pg_fingerprint('SELECT * FROM users WHERE id = 1');
6469

6570
// Normalize query (replace literals with positional parameters)
66-
$normalized = $parser->normalize("SELECT * FROM users WHERE name = 'John'");
71+
$normalized = pg_normalize("SELECT * FROM users WHERE name = 'John'");
6772
// Returns: SELECT * FROM users WHERE name = $1
6873

6974
// Normalize also handles Doctrine-style named parameters
70-
$normalized = $parser->normalize('SELECT * FROM users WHERE id = :id');
75+
$normalized = pg_normalize('SELECT * FROM users WHERE id = :id');
7176
// Returns: SELECT * FROM users WHERE id = $1
7277

7378
// Normalize utility/DDL statements
74-
$normalized = $parser->normalizeUtility('CREATE TABLE users (id INT, name VARCHAR(255))');
79+
$normalized = pg_normalize_utility('CREATE TABLE users (id INT, name VARCHAR(255))');
7580

7681
// Split multiple statements
77-
$statements = $parser->split('SELECT 1; SELECT 2;');
82+
$statements = pg_split('SELECT 1; SELECT 2;');
7883
// Returns: ['SELECT 1', ' SELECT 2']
7984

8085
// Generate query summary (protobuf format, useful for logging)
81-
$summary = $parser->summary('SELECT * FROM users WHERE id = 1');
82-
```
83-
84-
## DSL Functions
85-
86-
```php
87-
<?php
88-
89-
use function Flow\PgQuery\DSL\{
90-
pg_parse,
91-
pg_parser,
92-
pg_fingerprint,
93-
pg_normalize,
94-
pg_normalize_utility,
95-
pg_split,
96-
pg_deparse,
97-
pg_deparse_options,
98-
pg_format,
99-
pg_summary,
100-
pg_query_columns,
101-
pg_query_tables,
102-
pg_query_functions,
103-
pg_to_paginated_query,
104-
pg_to_count_query,
105-
pg_to_keyset_query,
106-
pg_keyset_column
107-
};
108-
109-
$query = pg_parse('SELECT * FROM users');
110-
$parser = pg_parser();
111-
$fingerprint = pg_fingerprint('SELECT * FROM users WHERE id = 1');
112-
$normalized = pg_normalize('SELECT * FROM users WHERE id = 1');
113-
$normalizedDdl = pg_normalize_utility('CREATE TABLE users (id INT)');
114-
$statements = pg_split('SELECT 1; SELECT 2;');
115-
$summary = pg_summary('SELECT * FROM users');
116-
117-
// Extract columns, tables, and functions
118-
$columns = pg_query_columns($query)->all();
119-
$tables = pg_query_tables($query)->all();
120-
$functions = pg_query_functions(pg_parse('SELECT COUNT(*) FROM users'))->all();
121-
122-
// Deparse (convert AST back to SQL)
123-
$sql = pg_deparse($query); // Simple output
124-
$sql = pg_deparse($query, pg_deparse_options()->indentSize(2)); // Pretty printed
125-
126-
// Format SQL (parse + deparse with pretty printing)
127-
$formatted = pg_format('SELECT id,name FROM users WHERE active=true');
128-
// Returns:
129-
// SELECT id, name
130-
// FROM users
131-
// WHERE active = true
86+
$summary = pg_summary('SELECT * FROM users WHERE id = 1');
13287
```
13388

134-
## ParsedQuery Methods
135-
136-
| Method | Description | Returns |
137-
|--------|-------------|---------|
138-
| `deparse(?DeparseOptions $options)` | Convert AST back to SQL string | `string` |
139-
| `traverse(NodeVisitor|NodeModifier ...)` | Traverse AST with visitors/modifiers | `$this` |
140-
| `raw()` | Access underlying protobuf ParseResult | `ParseResult` |
141-
142-
## Extractor Functions
143-
144-
| Function | Description | Returns |
145-
|----------|-------------|---------|
146-
| `pg_query_tables($query)` | Extract tables from the query | `Tables` |
147-
| `pg_query_columns($query)` | Extract columns from the query | `Columns` |
148-
| `pg_query_functions($query)` | Extract function calls from the query | `Functions` |
149-
150-
### Extractor Methods
151-
152-
**Columns** extractor:
153-
- `all()` - Get all columns
154-
- `forTable(string $tableName)` - Get columns filtered by table/alias
155-
156-
**Tables** extractor:
157-
- `all()` - Get all tables
158-
159-
**Functions** extractor:
160-
- `all()` - Get all function calls
161-
16289
## Deparsing (AST to SQL)
16390

16491
Convert a parsed query back to SQL, optionally with pretty-printing:
16592

16693
```php
16794
<?php
16895

169-
use Flow\PgQuery\{DeparseOptions, Parser};
96+
use function Flow\PgQuery\DSL\{pg_parse, pg_deparse, pg_deparse_options, pg_format};
17097

171-
$parser = new Parser();
172-
$query = $parser->parse('SELECT u.id, u.name FROM users u JOIN orders o ON u.id = o.user_id WHERE u.active = true');
98+
$query = pg_parse('SELECT u.id, u.name FROM users u JOIN orders o ON u.id = o.user_id WHERE u.active = true');
17399

174100
// Simple deparse (compact output)
175-
$sql = $query->deparse();
101+
$sql = pg_deparse($query);
176102
// Returns: SELECT u.id, u.name FROM users u JOIN orders o ON u.id = o.user_id WHERE u.active = true
177103

178104
// Pretty-printed output
179-
$sql = $query->deparse(DeparseOptions::new());
105+
$sql = pg_deparse($query, pg_deparse_options());
180106
// Returns:
181107
// SELECT u.id, u.name
182108
// FROM
@@ -185,13 +111,15 @@ $sql = $query->deparse(DeparseOptions::new());
185111
// WHERE u.active = true
186112

187113
// Custom formatting options
188-
$sql = $query->deparse(
189-
DeparseOptions::new()
190-
->indentSize(2) // 2 spaces per indent level
191-
->maxLineLength(60) // Wrap at 60 characters
192-
->trailingNewline() // Add newline at end
193-
->commasStartOfLine() // Place commas at line start
114+
$sql = pg_deparse($query, pg_deparse_options()
115+
->indentSize(2) // 2 spaces per indent level
116+
->maxLineLength(60) // Wrap at 60 characters
117+
->trailingNewline() // Add newline at end
118+
->commasStartOfLine() // Place commas at line start
194119
);
120+
121+
// Shorthand: parse and format in one step
122+
$formatted = pg_format('SELECT id,name FROM users WHERE active=true');
195123
```
196124

197125
### DeparseOptions
@@ -364,6 +292,47 @@ The cursor values come from the last row of the previous page. Keyset pagination
364292
- Handles mixed ASC/DESC sort orders correctly
365293
- Works with existing WHERE conditions (combined with AND)
366294

295+
### Using Modifiers Directly
296+
297+
For more control, you can use modifier objects directly with `traverse()`:
298+
299+
```php
300+
<?php
301+
302+
use Flow\PgQuery\AST\Transformers\SortOrder;
303+
304+
use function Flow\PgQuery\DSL\{
305+
pg_parse,
306+
pg_pagination,
307+
pg_count_modifier,
308+
pg_keyset_pagination,
309+
pg_keyset_column
310+
};
311+
312+
// Offset pagination modifier
313+
$query = pg_parse('SELECT * FROM users ORDER BY id');
314+
$query->traverse(pg_pagination(limit: 10, offset: 20));
315+
echo $query->deparse(); // SELECT * FROM users ORDER BY id LIMIT 10 OFFSET 20
316+
317+
// Count modifier
318+
$query = pg_parse('SELECT * FROM users WHERE active = true ORDER BY name');
319+
$query->traverse(pg_count_modifier());
320+
echo $query->deparse(); // SELECT count(*) FROM (SELECT * FROM users WHERE active = true) _count_subq
321+
322+
// Keyset pagination modifier
323+
$query = pg_parse('SELECT * FROM users ORDER BY created_at, id');
324+
$query->traverse(pg_keyset_pagination(
325+
limit: 10,
326+
columns: [
327+
pg_keyset_column('created_at', SortOrder::ASC),
328+
pg_keyset_column('id', SortOrder::ASC),
329+
],
330+
cursor: ['2025-01-15', 42]
331+
));
332+
echo $query->deparse();
333+
// SELECT * FROM users WHERE created_at > $1 OR (created_at = $1 AND id > $2) ORDER BY created_at, id LIMIT 10
334+
```
335+
367336
### Custom Modifiers
368337

369338
Create custom modifiers by implementing the `NodeModifier` interface:
@@ -374,7 +343,7 @@ Create custom modifiers by implementing the `NodeModifier` interface:
374343
use Flow\PgQuery\AST\{ModificationContext, NodeModifier};
375344
use Flow\PgQuery\Protobuf\AST\SelectStmt;
376345

377-
use function Flow\PgQuery\DSL\pg_parse;
346+
use function Flow\PgQuery\DSL\{pg_parse, pg_deparse};
378347

379348
final readonly class AddDistinctModifier implements NodeModifier
380349
{
@@ -397,7 +366,7 @@ final readonly class AddDistinctModifier implements NodeModifier
397366

398367
$query = pg_parse('SELECT id, name FROM users');
399368
$query->traverse(new AddDistinctModifier());
400-
echo $query->deparse(); // SELECT DISTINCT id, name FROM users
369+
echo pg_deparse($query); // SELECT DISTINCT id, name FROM users
401370
```
402371

403372
### NodeModifier Interface
@@ -454,19 +423,25 @@ foreach ($query->raw()->getStmts() as $stmt) {
454423
```php
455424
<?php
456425

457-
use Flow\PgQuery\Parser;
458-
use Flow\PgQuery\Exception\{ParserException, ExtensionNotLoadedException};
426+
use Flow\PgQuery\Exception\{ParserException, ExtensionNotLoadedException, PaginationException};
427+
428+
use function Flow\PgQuery\DSL\{pg_parse, pg_pagination};
459429

460430
try {
461-
$parser = new Parser();
431+
$query = pg_parse('INVALID SQL');
462432
} catch (ExtensionNotLoadedException $e) {
463433
// pg_query extension is not loaded
434+
} catch (ParserException $e) {
435+
echo "Parse error: " . $e->getMessage();
464436
}
465437

466438
try {
467-
$parser->parse('INVALID SQL');
468-
} catch (ParserException $e) {
469-
echo "Parse error: " . $e->getMessage();
439+
// OFFSET without ORDER BY throws exception
440+
$query = pg_parse('SELECT * FROM users');
441+
$query->traverse(pg_pagination(10, 5));
442+
} catch (PaginationException $e) {
443+
echo "Pagination error: " . $e->getMessage();
444+
// "OFFSET without ORDER BY produces non-deterministic results"
470445
}
471446
```
472447

src/core/etl/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"flow-php/types": "self.version",
1818
"flow-php/array-dot": "self.version",
1919
"flow-php/filesystem": "self.version",
20+
"flow-php/pg-query": "self.version",
2021
"psr/simple-cache": "^1.0 || ^2.0 || ^3.0",
2122
"webmozart/glob": "^3.0 || ^4.0",
2223
"symfony/string": "^6.4 || ^7.3 || ^8.0"

0 commit comments

Comments
 (0)