Skip to content

Commit 0aef8e2

Browse files
committed
utils readme
1 parent 112e541 commit 0aef8e2

File tree

3 files changed

+182
-12
lines changed

3 files changed

+182
-12
lines changed

packages/utils/README.md

Lines changed: 95 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,21 @@
1616

1717
`@pgsql/utils` is a companion utility library for `@pgsql/types`, offering convenient functions to work with PostgreSQL Abstract Syntax Tree (AST) nodes and enums in a type-safe manner. This library facilitates the creation of AST nodes and simplifies the process of converting between enum names and their respective integer values, as defined in the PostgreSQL parser output.
1818

19+
# Table of Contents
20+
21+
1. [@pgsql/utils](#pgsql-utils)
22+
- [Key Features](#key-features)
23+
2. [Installation](#installation)
24+
3. [Usage](#usage)
25+
- [AST Node Creation](#ast-node-creation)
26+
- [Select Statement](#select-statement)
27+
- [Creating Table Schemas Dynamically](#creating-table-schemas-dynamically)
28+
- [Enum Value Conversion](#enum-value-conversion)
29+
- [Example 1: Converting Enum Name to Integer](#example-1-converting-enum-name-to-integer)
30+
- [Example 2: Converting Integer to Enum Name](#example-2-converting-integer-to-enum-name)
31+
4. [Related Projects](#related)
32+
5. [Disclaimer](#disclaimer)
33+
1934
## Features
2035

2136
- **AST Node Creation**: Simplifies the process of constructing PostgreSQL AST nodes, allowing for easy assembly of SQL queries or statements programmatically.
@@ -38,25 +53,96 @@ npm install @pgsql/utils
3853

3954
With the AST helper methods, creating complex SQL ASTs becomes straightforward and intuitive.
4055

56+
#### Select Statement
57+
4158
```ts
4259
import ast, { CreateStmt, ColumnDef } from '@pgsql/utils';
4360
import { deparse } from 'pgsql-deparser';
4461

45-
const newColumn: ColumnDef = ast.columnDef({
46-
colname: 'id',
47-
typeName: ast.typeName({
48-
names: [ast.string({ str: 'int4' })]
62+
const selectStmt: SelectStmt = ast.selectStmt({
63+
targetList: [
64+
ast.resTarget({
65+
val: ast.columnRef({
66+
fields: [ast.aStar()]
67+
})
68+
})
69+
],
70+
fromClause: [
71+
ast.rangeVar({
72+
schemaname: 'myschema',
73+
relname: 'mytable',
74+
inh: true,
75+
relpersistence: 'p'
76+
})
77+
],
78+
whereClause: ast.aExpr({
79+
kind: 'AEXPR_OP',
80+
name: [ast.string({ str: '=' })],
81+
lexpr: ast.columnRef({
82+
fields: [ast.string({ str: 'a' })]
83+
}),
84+
rexpr: ast.typeCast({
85+
arg: ast.aConst({
86+
val: ast.string({ str: 't' })
87+
}),
88+
typeName: ast.typeName({
89+
names: [
90+
ast.string({ str: 'pg_catalog' }),
91+
ast.string({ str: 'bool' })
92+
],
93+
typemod: -1
94+
})
4995
})
96+
}),
97+
limitOption: 'LIMIT_OPTION_DEFAULT',
98+
op: 'SETOP_NONE'
5099
});
51100

52-
const createStmt: CreateStmt = ast.createStmt({
53-
relation: ast.rangeVar({
54-
relname: 'new_table'
101+
deparse(createStmt, {});
102+
// SELECT * FROM myschema.mytable WHERE a = TRUE
103+
```
104+
105+
#### Creating Table Schemas Dynamically
106+
107+
```ts
108+
// Example JSON with schema
109+
const schema = {
110+
"tableName": "users",
111+
"columns": [
112+
{ "name": "id", "type": "int", "constraints": ["PRIMARY KEY"] },
113+
{ "name": "username", "type": "string" },
114+
{ "name": "email", "type": "string", "constraints": ["UNIQUE"] },
115+
{ "name": "created_at", "type": "timestamp", "constraints": ["NOT NULL"] }
116+
]
117+
};
118+
119+
// Construct the CREATE TABLE statement
120+
const createStmt = ast.createStmt({
121+
relation: ast.rangeVar({ relname: schema.tableName }),
122+
tableElts: schema.columns.map(column => ast.columnDef({
123+
colname: column.name,
124+
typeName: ast.typeName({
125+
names: [ast.string({ str: column.type })]
55126
}),
56-
tableElts: [newColumn]
127+
constraints: column.constraints?.map(constraint =>
128+
ast.constraint({
129+
contype: constraint === "PRIMARY KEY" ? "CONSTR_PRIMARY" : constraint === "UNIQUE" ? "CONSTR_UNIQUE" : "CONSTR_NOTNULL",
130+
keys: [ast.string({ str: column.name })]
131+
})
132+
)
133+
}))
57134
});
58135

59-
deparse(createStmt, {}) // SQL!
136+
// Assuming `deparse` function converts AST to SQL string
137+
const sql = deparse(createStmt, {});
138+
console.log(sql);
139+
// CREATE TABLE (
140+
// id int PRIMARY KEY ( id ),
141+
// username string,
142+
// email string UNIQUE ( email ),
143+
// created_at timestamp NOT NULL ( created_at )
144+
// )
145+
60146
```
61147

62148
### Enum Value Conversion

packages/utils/__test__/__snapshots__/utils.test.ts.snap

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,15 @@ exports[`asts 2`] = `
3434
)"
3535
`;
3636

37+
exports[`dynamic creation of tables 1`] = `
38+
"CREATE TABLE (
39+
id int PRIMARY KEY ( id ),
40+
username string,
41+
email string UNIQUE ( email ),
42+
created_at timestamp NOT NULL ( created_at )
43+
)"
44+
`;
45+
3746
exports[`getEnumValue 1`] = `"AEXPR_OP"`;
3847

3948
exports[`getEnumValue 2`] = `"AEXPR_OP_ANY"`;

packages/utils/__test__/utils.test.ts

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as u from '../src';
2-
import ast from '../src/asts';
3-
import { CreateStmt, ColumnDef, Node } from '../src/wrapped';
2+
import ast, { CreateStmt, ColumnDef, SelectStmt } from '../src';
43
import { deparse } from 'pgsql-deparser';
54

65
it('getEnumValue', () => {
@@ -28,4 +27,80 @@ it('asts', () => {
2827
})
2928
expect(createStmt).toMatchSnapshot();
3029
expect(deparse(createStmt, {})).toMatchSnapshot();
31-
});
30+
});
31+
32+
it('SelectStmt', () => {
33+
const selectStmt: SelectStmt = ast.selectStmt({
34+
targetList: [
35+
ast.resTarget({
36+
val: ast.columnRef({
37+
fields: [ast.aStar()]
38+
})
39+
})
40+
],
41+
fromClause: [
42+
ast.rangeVar({
43+
schemaname: 'myschema',
44+
relname: 'mytable',
45+
inh: true,
46+
relpersistence: 'p'
47+
})
48+
],
49+
whereClause: ast.aExpr({
50+
kind: 'AEXPR_OP',
51+
name: [ast.string({ str: '=' })],
52+
lexpr: ast.columnRef({
53+
fields: [ast.string({ str: 'a' })]
54+
}),
55+
rexpr: ast.typeCast({
56+
arg: ast.aConst({
57+
val: ast.string({ str: 't' })
58+
}),
59+
typeName: ast.typeName({
60+
names: [
61+
ast.string({ str: 'pg_catalog' }),
62+
ast.string({ str: 'bool' })
63+
],
64+
typemod: -1
65+
})
66+
})
67+
}),
68+
limitOption: 'LIMIT_OPTION_DEFAULT',
69+
op: 'SETOP_NONE'
70+
});
71+
expect(deparse(selectStmt, {})).toEqual('SELECT * FROM myschema.mytable WHERE a = TRUE');
72+
});
73+
74+
it('dynamic creation of tables', () => {
75+
// Example JSON schema
76+
const schema = {
77+
"tableName": "users",
78+
"columns": [
79+
{ "name": "id", "type": "int", "constraints": ["PRIMARY KEY"] },
80+
{ "name": "username", "type": "string" },
81+
{ "name": "email", "type": "string", "constraints": ["UNIQUE"] },
82+
{ "name": "created_at", "type": "timestamp", "constraints": ["NOT NULL"] }
83+
]
84+
};
85+
86+
// Construct the CREATE TABLE statement
87+
const createStmt = ast.createStmt({
88+
relation: ast.rangeVar({ relname: schema.tableName }),
89+
tableElts: schema.columns.map(column => ast.columnDef({
90+
colname: column.name,
91+
typeName: ast.typeName({
92+
names: [ast.string({ str: column.type })]
93+
}),
94+
constraints: column.constraints?.map(constraint =>
95+
ast.constraint({
96+
contype: constraint === "PRIMARY KEY" ? "CONSTR_PRIMARY" : constraint === "UNIQUE" ? "CONSTR_UNIQUE" : "CONSTR_NOTNULL",
97+
keys: [ast.string({ str: column.name })]
98+
})
99+
)
100+
}))
101+
});
102+
103+
// Assuming `deparse` function converts AST to SQL string
104+
const sql = deparse(createStmt, {});
105+
expect(sql).toMatchSnapshot();
106+
})

0 commit comments

Comments
 (0)