pg-dump-parser 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +24 -0
- package/README.md +63 -0
- package/dist/parsePgDump.d.ts +36 -0
- package/dist/parsePgDump.d.ts.map +1 -0
- package/dist/parsePgDump.js +111 -0
- package/dist/parsePgDump.js.map +1 -0
- package/dist/parsePgDump.test.d.ts +2 -0
- package/dist/parsePgDump.test.d.ts.map +1 -0
- package/dist/parsePgDump.test.js +695 -0
- package/dist/parsePgDump.test.js.map +1 -0
- package/package.json +60 -0
- package/src/parsePgDump.test.ts +736 -0
- package/src/parsePgDump.ts +143 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
const HeaderZodSchema = z.union([
|
|
4
|
+
// These are the attribute less headers, e.g.
|
|
5
|
+
// --
|
|
6
|
+
// -- PostgreSQL database dump
|
|
7
|
+
// --
|
|
8
|
+
z.object({
|
|
9
|
+
Title: z.string(),
|
|
10
|
+
}),
|
|
11
|
+
// These are the objects with attributes, e.g.
|
|
12
|
+
// --
|
|
13
|
+
// -- Name: citext; Type: EXTENSION; Schema: -; Owner: -
|
|
14
|
+
// --
|
|
15
|
+
z.object({
|
|
16
|
+
Name: z.string(),
|
|
17
|
+
Owner: z.string().nullable(),
|
|
18
|
+
Schema: z.string().nullable(),
|
|
19
|
+
Type: z.enum([
|
|
20
|
+
'ACL',
|
|
21
|
+
'AGGREGATE',
|
|
22
|
+
'CAST',
|
|
23
|
+
'COMMENT',
|
|
24
|
+
'CONSTRAINT',
|
|
25
|
+
'DEFAULT',
|
|
26
|
+
'EXTENSION',
|
|
27
|
+
'FK CONSTRAINT',
|
|
28
|
+
'FUNCTION',
|
|
29
|
+
'INDEX',
|
|
30
|
+
'MATERIALIZED VIEW',
|
|
31
|
+
'PROCEDURE',
|
|
32
|
+
'PUBLICATION',
|
|
33
|
+
'SCHEMA',
|
|
34
|
+
'SEQUENCE OWNED BY',
|
|
35
|
+
'SEQUENCE',
|
|
36
|
+
'TABLE',
|
|
37
|
+
'TEXT SEARCH CONFIGURATION',
|
|
38
|
+
'TEXT SEARCH DICTIONARY',
|
|
39
|
+
'TRIGGER',
|
|
40
|
+
'TYPE',
|
|
41
|
+
'VIEW',
|
|
42
|
+
]),
|
|
43
|
+
}),
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
type Header = z.infer<typeof HeaderZodSchema>;
|
|
47
|
+
|
|
48
|
+
const isHeader = (fragment: string): boolean => {
|
|
49
|
+
return fragment.startsWith('--\n--');
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const parseValue = (value: string) => {
|
|
53
|
+
if (value === '-' || value === '' || value === undefined) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return value;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const parseAttribute = (attribute: string): [string, string | null] => {
|
|
61
|
+
const [name, value] = attribute.split(':');
|
|
62
|
+
|
|
63
|
+
return [name, parseValue(value.trim())];
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// --
|
|
67
|
+
// -- Name: TABLE user_survey; Type: ACL; Schema: public; Owner: postgres
|
|
68
|
+
// --
|
|
69
|
+
|
|
70
|
+
const parseHeader = (fragment: string) => {
|
|
71
|
+
const lines = fragment.split('\n');
|
|
72
|
+
|
|
73
|
+
if (lines.length !== 3) {
|
|
74
|
+
throw new Error('Invalid header');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const contentLine = lines[1].slice(3);
|
|
78
|
+
|
|
79
|
+
if (
|
|
80
|
+
contentLine === 'PostgreSQL database dump' ||
|
|
81
|
+
contentLine === 'PostgreSQL database dump complete'
|
|
82
|
+
) {
|
|
83
|
+
return HeaderZodSchema.parse({
|
|
84
|
+
Title: contentLine,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const content = Object.fromEntries(
|
|
89
|
+
contentLine.split('; ').map((attribute) => {
|
|
90
|
+
return parseAttribute(attribute);
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const result = HeaderZodSchema.safeParse(content);
|
|
95
|
+
|
|
96
|
+
if (!result.success) {
|
|
97
|
+
throw new Error('Invalid header');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return result.data;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
type Table = {
|
|
104
|
+
name: string;
|
|
105
|
+
schema: string;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
type SchemaObject = {
|
|
109
|
+
header: Header;
|
|
110
|
+
sql: string;
|
|
111
|
+
table?: Table;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export const parsePgDump = (dump: string) => {
|
|
115
|
+
const schemaObjects: SchemaObject[] = [];
|
|
116
|
+
|
|
117
|
+
const fragments = dump.trim().split(/(--\n-- .*\n--)/u);
|
|
118
|
+
|
|
119
|
+
let lastHeader: Header | null = null;
|
|
120
|
+
|
|
121
|
+
for (const fragment of fragments.map((chunk) => chunk.trim())) {
|
|
122
|
+
if (fragment === '') {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (isHeader(fragment)) {
|
|
127
|
+
lastHeader = parseHeader(fragment);
|
|
128
|
+
} else if (lastHeader) {
|
|
129
|
+
const subFragments = fragment.split('\n\n\n');
|
|
130
|
+
|
|
131
|
+
for (const subFragment of subFragments) {
|
|
132
|
+
schemaObjects.push({
|
|
133
|
+
header: lastHeader,
|
|
134
|
+
sql: subFragment,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
throw new Error('No header');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return schemaObjects;
|
|
143
|
+
};
|