Skip to content

Commit 0fb6131

Browse files
committed
docs: update READMEs with new APIs and usage examples
plpgsql-parser: - Add Return Type Helpers section (getReturnInfoFromParsedFunction) - Add Schema Rename Example showing heterogeneous AST transformation - Update Re-exports section with new exports plpgsql-deparser: - Add Return Context section for correct RETURN statement handling - Add Hydration Utilities section (hydrate/dehydrate/isHydratedExpr/getOriginalQuery)
1 parent a5944ab commit 0fb6131

File tree

2 files changed

+118
-0
lines changed

2 files changed

+118
-0
lines changed

packages/plpgsql-deparser/README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,50 @@ interface PLpgSQLDeparserOptions {
175175
}
176176
```
177177

178+
## Return Context
179+
180+
For correct `RETURN` statement handling, you can pass return type information:
181+
182+
```typescript
183+
import { deparseSync, ReturnInfo } from 'plpgsql-deparser';
184+
185+
// Without return info, the deparser uses conservative defaults
186+
const body1 = deparseSync(parseResult);
187+
188+
// With return info, the deparser emits correct RETURN vs RETURN NULL
189+
const returnInfo: ReturnInfo = { kind: 'setof' };
190+
const body2 = deparseSync(parseResult, {}, returnInfo);
191+
```
192+
193+
Supported return kinds: `'void'`, `'scalar'`, `'setof'`, `'trigger'`, `'out_params'`
194+
195+
## Hydration Utilities
196+
197+
The deparser includes utilities for working with "hydrated" PL/pgSQL ASTs, where embedded SQL expressions are parsed into SQL AST nodes for transformation.
198+
199+
```typescript
200+
import {
201+
hydratePlpgsqlAst,
202+
dehydratePlpgsqlAst,
203+
isHydratedExpr,
204+
getOriginalQuery
205+
} from 'plpgsql-deparser';
206+
207+
// Hydrate: convert PLpgSQL_expr.query strings into SQL AST nodes
208+
const hydrated = hydratePlpgsqlAst(plpgsqlFunction);
209+
210+
// Now you can traverse and modify embedded SQL expressions as AST nodes
211+
// (e.g., rename schemas, rewrite table references)
212+
213+
// Dehydrate: convert SQL AST nodes back to query strings
214+
const dehydrated = dehydratePlpgsqlAst(hydrated);
215+
216+
// Then deparse to get the final function body
217+
const body = deparseFunctionSync(dehydrated);
218+
```
219+
220+
The `isHydratedExpr()` helper checks if an expression has been hydrated, and `getOriginalQuery()` retrieves the original query string from a hydrated expression.
221+
178222
## Note on AST Structure
179223

180224
The PL/pgSQL AST returned by `parsePlPgSQL` represents the internal structure of function bodies, not the `CREATE FUNCTION` statement itself. To get a complete function definition, you would need to:

packages/plpgsql-parser/README.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,78 @@ class PLpgSQLNodePath<TTag extends string = string> {
191191
}
192192
```
193193

194+
## Return Type Helpers
195+
196+
Extract return type information from `CreateFunctionStmt` for correct RETURN statement handling:
197+
198+
```typescript
199+
import { parse, getReturnInfoFromParsedFunction, loadModule } from 'plpgsql-parser';
200+
201+
await loadModule();
202+
203+
const parsed = parse(`
204+
CREATE FUNCTION get_users() RETURNS SETOF users LANGUAGE plpgsql AS $$
205+
BEGIN
206+
RETURN QUERY SELECT * FROM users;
207+
RETURN;
208+
END;
209+
$$;
210+
`);
211+
212+
const returnInfo = getReturnInfoFromParsedFunction(parsed.functions[0]);
213+
console.log(returnInfo.kind); // 'setof'
214+
```
215+
216+
The helper detects: `'void'`, `'scalar'`, `'setof'`, `'trigger'`, `'out_params'`
217+
218+
## Schema Rename Example
219+
220+
Transform schema names across both SQL and embedded PL/pgSQL expressions:
221+
222+
```typescript
223+
import { parse, walk, walkParsedScript, deparseSync, loadModule } from 'plpgsql-parser';
224+
import { walk as walkSql } from '@pgsql/traverse';
225+
226+
await loadModule();
227+
228+
const schemaMap = { app_public: 'myapp_v2', app_private: 'myapp_internal' };
229+
230+
function renameSchema(node: any) {
231+
if (node.schemaname && schemaMap[node.schemaname]) {
232+
node.schemaname = schemaMap[node.schemaname];
233+
}
234+
}
235+
236+
const parsed = parse(`
237+
CREATE FUNCTION app_public.get_user(p_id int)
238+
RETURNS app_public.users
239+
LANGUAGE plpgsql AS $$
240+
BEGIN
241+
RETURN (SELECT * FROM app_public.users WHERE id = p_id);
242+
END;
243+
$$;
244+
`);
245+
246+
// Rename schemas in outer SQL AST (function name, return type)
247+
walkSql(parsed.sql, {
248+
RangeVar: (path) => renameSchema(path.node),
249+
TypeName: (path) => {
250+
const names = path.node.names;
251+
if (names?.[0]?.String?.sval && schemaMap[names[0].String.sval]) {
252+
names[0].String.sval = schemaMap[names[0].String.sval];
253+
}
254+
},
255+
});
256+
257+
// Rename schemas in PL/pgSQL embedded SQL (SELECT, INSERT, etc.)
258+
walkParsedScript(parsed, {}, {
259+
RangeVar: (path) => renameSchema(path.node),
260+
});
261+
262+
const output = deparseSync(parsed);
263+
// All app_public references are now myapp_v2
264+
```
265+
194266
## Re-exports
195267

196268
For power users, the package re-exports underlying primitives:
@@ -201,6 +273,8 @@ For power users, the package re-exports underlying primitives:
201273
- `deparsePlpgsqlBody` - PL/pgSQL deparser from `plpgsql-deparser`
202274
- `hydratePlpgsqlAst` - Hydration utility from `plpgsql-deparser`
203275
- `dehydratePlpgsqlAst` - Dehydration utility from `plpgsql-deparser`
276+
- `getReturnInfo` - Extract return type from `CreateFunctionStmt`
277+
- `ReturnInfo`, `ReturnInfoKind` - Return type info types
204278

205279
## License
206280

0 commit comments

Comments
 (0)