@@ -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
16491Convert 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
369338Create custom modifiers by implementing the ` NodeModifier ` interface:
@@ -374,7 +343,7 @@ Create custom modifiers by implementing the `NodeModifier` interface:
374343use Flow\PgQuery\AST\{ModificationContext, NodeModifier};
375344use 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
379348final 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
460430try {
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
466438try {
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
0 commit comments