Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions packages/plpgsql-deparser/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,50 @@ interface PLpgSQLDeparserOptions {
}
```

## Return Context

For correct `RETURN` statement handling, you can pass return type information:

```typescript
import { deparseSync, ReturnInfo } from 'plpgsql-deparser';

// Without return info, the deparser uses conservative defaults
const body1 = deparseSync(parseResult);

// With return info, the deparser emits correct RETURN vs RETURN NULL
const returnInfo: ReturnInfo = { kind: 'setof' };
const body2 = deparseSync(parseResult, {}, returnInfo);
```

Supported return kinds: `'void'`, `'scalar'`, `'setof'`, `'trigger'`, `'out_params'`

## Hydration Utilities

The deparser includes utilities for working with "hydrated" PL/pgSQL ASTs, where embedded SQL expressions are parsed into SQL AST nodes for transformation.

```typescript
import {
hydratePlpgsqlAst,
dehydratePlpgsqlAst,
isHydratedExpr,
getOriginalQuery
} from 'plpgsql-deparser';

// Hydrate: convert PLpgSQL_expr.query strings into SQL AST nodes
const hydrated = hydratePlpgsqlAst(plpgsqlFunction);

// Now you can traverse and modify embedded SQL expressions as AST nodes
// (e.g., rename schemas, rewrite table references)

// Dehydrate: convert SQL AST nodes back to query strings
const dehydrated = dehydratePlpgsqlAst(hydrated);

// Then deparse to get the final function body
const body = deparseFunctionSync(dehydrated);
```

The `isHydratedExpr()` helper checks if an expression has been hydrated, and `getOriginalQuery()` retrieves the original query string from a hydrated expression.

## Note on AST Structure

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:
Expand Down
74 changes: 74 additions & 0 deletions packages/plpgsql-parser/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,78 @@ class PLpgSQLNodePath<TTag extends string = string> {
}
```

## Return Type Helpers

Extract return type information from `CreateFunctionStmt` for correct RETURN statement handling:

```typescript
import { parse, getReturnInfoFromParsedFunction, loadModule } from 'plpgsql-parser';

await loadModule();

const parsed = parse(`
CREATE FUNCTION get_users() RETURNS SETOF users LANGUAGE plpgsql AS $$
BEGIN
RETURN QUERY SELECT * FROM users;
RETURN;
END;
$$;
`);

const returnInfo = getReturnInfoFromParsedFunction(parsed.functions[0]);
console.log(returnInfo.kind); // 'setof'
```

The helper detects: `'void'`, `'scalar'`, `'setof'`, `'trigger'`, `'out_params'`

## Schema Rename Example

Transform schema names across both SQL and embedded PL/pgSQL expressions:

```typescript
import { parse, walk, walkParsedScript, deparseSync, loadModule } from 'plpgsql-parser';
import { walk as walkSql } from '@pgsql/traverse';

await loadModule();

const schemaMap = { app_public: 'myapp_v2', app_private: 'myapp_internal' };

function renameSchema(node: any) {
if (node.schemaname && schemaMap[node.schemaname]) {
node.schemaname = schemaMap[node.schemaname];
}
}

const parsed = parse(`
CREATE FUNCTION app_public.get_user(p_id int)
RETURNS app_public.users
LANGUAGE plpgsql AS $$
BEGIN
RETURN (SELECT * FROM app_public.users WHERE id = p_id);
END;
$$;
`);

// Rename schemas in outer SQL AST (function name, return type)
walkSql(parsed.sql, {
RangeVar: (path) => renameSchema(path.node),
TypeName: (path) => {
const names = path.node.names;
if (names?.[0]?.String?.sval && schemaMap[names[0].String.sval]) {
names[0].String.sval = schemaMap[names[0].String.sval];
}
},
});

// Rename schemas in PL/pgSQL embedded SQL (SELECT, INSERT, etc.)
walkParsedScript(parsed, {}, {
RangeVar: (path) => renameSchema(path.node),
});

const output = deparseSync(parsed);
// All app_public references are now myapp_v2
```

## Re-exports

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

## License

Expand Down