pg-dump-parser 1.6.0 → 1.8.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.
@@ -0,0 +1,343 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const parsePgDump_1 = require("./parsePgDump");
7
+ const sortSchemaObjects_1 = require("./sortSchemaObjects");
8
+ const multiline_ts_1 = __importDefault(require("multiline-ts"));
9
+ const vitest_1 = require("vitest");
10
+ (0, vitest_1.describe)('sortSchemaObjects', () => {
11
+ (0, vitest_1.test)('sorts schema objects by type order', () => {
12
+ const dump = (0, multiline_ts_1.default) `
13
+ --
14
+ -- Name: foo; Type: TABLE; Schema: public; Owner: postgres
15
+ --
16
+
17
+ CREATE TABLE public.foo (
18
+ id integer NOT NULL,
19
+ name text NOT NULL
20
+ );
21
+
22
+
23
+ --
24
+ -- Name: bar; Type: INDEX; Schema: public; Owner: postgres
25
+ --
26
+
27
+ CREATE INDEX bar ON public.foo USING btree (name);
28
+
29
+
30
+ --
31
+ -- Name: baz; Type: EXTENSION; Schema: -; Owner: -
32
+ --
33
+
34
+ CREATE EXTENSION IF NOT EXISTS baz;
35
+
36
+
37
+ --
38
+ -- Name: qux; Type: CONSTRAINT; Schema: public; Owner: postgres
39
+ --
40
+
41
+ ALTER TABLE ONLY public.foo
42
+ ADD CONSTRAINT qux PRIMARY KEY (id);
43
+ `;
44
+ const schemaObjects = (0, parsePgDump_1.parsePgDump)(dump);
45
+ const sorted = (0, sortSchemaObjects_1.sortSchemaObjects)(schemaObjects);
46
+ // Extension should come first
47
+ (0, vitest_1.expect)(sorted[0].header).toMatchObject({ Name: 'baz', Type: 'EXTENSION' });
48
+ // Then table
49
+ (0, vitest_1.expect)(sorted[1].header).toMatchObject({ Name: 'foo', Type: 'TABLE' });
50
+ // Then constraint
51
+ (0, vitest_1.expect)(sorted[2].header).toMatchObject({ Name: 'qux', Type: 'CONSTRAINT' });
52
+ // Finally index
53
+ (0, vitest_1.expect)(sorted[3].header).toMatchObject({ Name: 'bar', Type: 'INDEX' });
54
+ });
55
+ (0, vitest_1.test)('sorts constraints by type (PRIMARY, UNIQUE, FOREIGN, CHECK)', () => {
56
+ const dump = (0, multiline_ts_1.default) `
57
+ --
58
+ -- Name: foo foo_check; Type: CONSTRAINT; Schema: public; Owner: postgres
59
+ --
60
+
61
+ ALTER TABLE ONLY public.foo
62
+ ADD CONSTRAINT foo_check CHECK (id > 0);
63
+
64
+
65
+ --
66
+ -- Name: foo foo_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
67
+ --
68
+
69
+ ALTER TABLE ONLY public.foo
70
+ ADD CONSTRAINT foo_fkey FOREIGN KEY (bar_id) REFERENCES public.bar(id);
71
+
72
+
73
+ --
74
+ -- Name: foo foo_unique; Type: CONSTRAINT; Schema: public; Owner: postgres
75
+ --
76
+
77
+ ALTER TABLE ONLY public.foo
78
+ ADD CONSTRAINT foo_unique UNIQUE (name);
79
+
80
+
81
+ --
82
+ -- Name: foo foo_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres
83
+ --
84
+
85
+ ALTER TABLE ONLY public.foo
86
+ ADD CONSTRAINT foo_pkey PRIMARY KEY (id);
87
+ `;
88
+ const schemaObjects = (0, parsePgDump_1.parsePgDump)(dump);
89
+ const sorted = (0, sortSchemaObjects_1.sortSchemaObjects)(schemaObjects);
90
+ // Primary key should come first
91
+ (0, vitest_1.expect)(sorted[0].sql).toContain('PRIMARY KEY');
92
+ // Then unique
93
+ (0, vitest_1.expect)(sorted[1].sql).toContain('UNIQUE');
94
+ // Then check
95
+ (0, vitest_1.expect)(sorted[2].sql).toContain('CHECK');
96
+ // Finally foreign key
97
+ (0, vitest_1.expect)(sorted[3].sql).toContain('FOREIGN KEY');
98
+ });
99
+ (0, vitest_1.test)('sorts indexes alphabetically within the same table', () => {
100
+ const dump = (0, multiline_ts_1.default) `
101
+ --
102
+ -- Name: foo; Type: TABLE; Schema: public; Owner: postgres
103
+ --
104
+
105
+ CREATE TABLE public.foo (
106
+ id integer NOT NULL,
107
+ name text NOT NULL,
108
+ email text
109
+ );
110
+
111
+
112
+ --
113
+ -- Name: foo_name_idx; Type: INDEX; Schema: public; Owner: postgres
114
+ --
115
+
116
+ CREATE INDEX foo_name_idx ON public.foo USING btree (name);
117
+
118
+
119
+ --
120
+ -- Name: foo_email_idx; Type: INDEX; Schema: public; Owner: postgres
121
+ --
122
+
123
+ CREATE INDEX foo_email_idx ON public.foo USING btree (email);
124
+
125
+
126
+ --
127
+ -- Name: foo_id_idx; Type: INDEX; Schema: public; Owner: postgres
128
+ --
129
+
130
+ CREATE INDEX foo_id_idx ON public.foo USING btree (id);
131
+ `;
132
+ const schemaObjects = (0, parsePgDump_1.parsePgDump)(dump);
133
+ const sorted = (0, sortSchemaObjects_1.sortSchemaObjects)(schemaObjects);
134
+ const indexes = sorted.filter((object) => 'Type' in object.header && object.header.Type === 'INDEX');
135
+ (0, vitest_1.expect)(indexes[0].header).toMatchObject({ Name: 'foo_email_idx' });
136
+ (0, vitest_1.expect)(indexes[1].header).toMatchObject({ Name: 'foo_id_idx' });
137
+ (0, vitest_1.expect)(indexes[2].header).toMatchObject({ Name: 'foo_name_idx' });
138
+ });
139
+ (0, vitest_1.test)('sorts comments by type and target', () => {
140
+ const dump = (0, multiline_ts_1.default) `
141
+ --
142
+ -- Name: COLUMN foo.name; Type: COMMENT; Schema: public; Owner: postgres
143
+ --
144
+
145
+ COMMENT ON COLUMN public.foo.name IS 'Name column';
146
+
147
+
148
+ --
149
+ -- Name: TABLE foo; Type: COMMENT; Schema: public; Owner: postgres
150
+ --
151
+
152
+ COMMENT ON TABLE public.foo IS 'Foo table';
153
+
154
+
155
+ --
156
+ -- Name: COLUMN foo.id; Type: COMMENT; Schema: public; Owner: postgres
157
+ --
158
+
159
+ COMMENT ON COLUMN public.foo.id IS 'ID column';
160
+
161
+
162
+ --
163
+ -- Name: EXTENSION pgcrypto; Type: COMMENT; Schema: -; Owner: -
164
+ --
165
+
166
+ COMMENT ON EXTENSION pgcrypto IS 'Crypto extension';
167
+ `;
168
+ const schemaObjects = (0, parsePgDump_1.parsePgDump)(dump);
169
+ const sorted = (0, sortSchemaObjects_1.sortSchemaObjects)(schemaObjects);
170
+ const comments = sorted.filter((object) => 'Type' in object.header && object.header.Type === 'COMMENT');
171
+ // Extension comment first
172
+ (0, vitest_1.expect)(comments[0].header).toMatchObject({ Name: 'EXTENSION pgcrypto' });
173
+ // Then table comment
174
+ (0, vitest_1.expect)(comments[1].header).toMatchObject({ Name: 'TABLE foo' });
175
+ // Then column comments sorted by name
176
+ (0, vitest_1.expect)(comments[2].header).toMatchObject({ Name: 'COLUMN foo.id' });
177
+ (0, vitest_1.expect)(comments[3].header).toMatchObject({ Name: 'COLUMN foo.name' });
178
+ });
179
+ (0, vitest_1.test)('groups and sorts schema objects by scope', () => {
180
+ const dump = (0, multiline_ts_1.default) `
181
+ --
182
+ -- Name: foo; Type: TABLE; Schema: public; Owner: postgres
183
+ --
184
+
185
+ CREATE TABLE public.foo (
186
+ id integer NOT NULL,
187
+ name text NOT NULL
188
+ );
189
+
190
+
191
+ --
192
+ -- Name: foo_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres
193
+ --
194
+
195
+ ALTER TABLE ONLY public.foo
196
+ ADD CONSTRAINT foo_pkey PRIMARY KEY (id);
197
+
198
+
199
+ --
200
+ -- Name: foo_name_idx; Type: INDEX; Schema: public; Owner: postgres
201
+ --
202
+
203
+ CREATE INDEX foo_name_idx ON public.foo USING btree (name);
204
+
205
+
206
+ --
207
+ -- Name: bar; Type: TABLE; Schema: public; Owner: postgres
208
+ --
209
+
210
+ CREATE TABLE public.bar (
211
+ id integer NOT NULL,
212
+ foo_id integer
213
+ );
214
+
215
+
216
+ --
217
+ -- Name: bar_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres
218
+ --
219
+
220
+ ALTER TABLE ONLY public.bar
221
+ ADD CONSTRAINT bar_pkey PRIMARY KEY (id);
222
+
223
+
224
+ --
225
+ -- Name: bar_foo_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
226
+ --
227
+
228
+ ALTER TABLE ONLY public.bar
229
+ ADD CONSTRAINT bar_foo_id_fkey FOREIGN KEY (foo_id) REFERENCES public.foo(id);
230
+ `;
231
+ const schemaObjects = (0, parsePgDump_1.parsePgDump)(dump);
232
+ const grouped = (0, sortSchemaObjects_1.groupAndSortSchemaObjects)(schemaObjects);
233
+ // Should have two groups for the two tables
234
+ const tableGroups = Array.from(grouped.keys()).filter((key) => key.includes('TABLE:'));
235
+ (0, vitest_1.expect)(tableGroups).toHaveLength(2);
236
+ // Each group should be sorted internally
237
+ for (const [key, objects] of grouped.entries()) {
238
+ if (key.includes('TABLE:public:foo')) {
239
+ // foo table group should have table, constraint, and index
240
+ (0, vitest_1.expect)(objects).toHaveLength(3);
241
+ (0, vitest_1.expect)(objects[0].header).toMatchObject({ Type: 'TABLE' });
242
+ (0, vitest_1.expect)(objects[1].header).toMatchObject({ Type: 'CONSTRAINT' });
243
+ (0, vitest_1.expect)(objects[2].header).toMatchObject({ Type: 'INDEX' });
244
+ }
245
+ else if (key.includes('TABLE:public:bar')) {
246
+ // bar table group should have table and two constraints
247
+ (0, vitest_1.expect)(objects).toHaveLength(3);
248
+ (0, vitest_1.expect)(objects[0].header).toMatchObject({ Type: 'TABLE' });
249
+ (0, vitest_1.expect)(objects[1].header).toMatchObject({ Type: 'CONSTRAINT' });
250
+ (0, vitest_1.expect)(objects[2].header).toMatchObject({ Type: 'FK CONSTRAINT' });
251
+ }
252
+ }
253
+ });
254
+ (0, vitest_1.test)('sortSchemaObjectsByScope returns flat sorted array', () => {
255
+ const dump = (0, multiline_ts_1.default) `
256
+ --
257
+ -- Name: foo; Type: TABLE; Schema: public; Owner: postgres
258
+ --
259
+
260
+ CREATE TABLE public.foo (
261
+ id integer NOT NULL
262
+ );
263
+
264
+
265
+ --
266
+ -- Name: foo_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres
267
+ --
268
+
269
+ ALTER TABLE ONLY public.foo
270
+ ADD CONSTRAINT foo_pkey PRIMARY KEY (id);
271
+
272
+
273
+ --
274
+ -- Name: bar; Type: TABLE; Schema: public; Owner: postgres
275
+ --
276
+
277
+ CREATE TABLE public.bar (
278
+ id integer NOT NULL
279
+ );
280
+
281
+
282
+ --
283
+ -- Name: bar_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres
284
+ --
285
+
286
+ ALTER TABLE ONLY public.bar
287
+ ADD CONSTRAINT bar_pkey PRIMARY KEY (id);
288
+ `;
289
+ const schemaObjects = (0, parsePgDump_1.parsePgDump)(dump);
290
+ const sorted = (0, sortSchemaObjects_1.sortSchemaObjectsByScope)(schemaObjects);
291
+ (0, vitest_1.expect)(sorted).toHaveLength(4);
292
+ // Objects should be grouped by table
293
+ // First bar table and its constraint
294
+ (0, vitest_1.expect)(sorted[0].header).toMatchObject({ Name: 'bar', Type: 'TABLE' });
295
+ (0, vitest_1.expect)(sorted[1].header).toMatchObject({
296
+ Name: 'bar_pkey',
297
+ Type: 'CONSTRAINT',
298
+ });
299
+ // Then foo table and its constraint
300
+ (0, vitest_1.expect)(sorted[2].header).toMatchObject({ Name: 'foo', Type: 'TABLE' });
301
+ (0, vitest_1.expect)(sorted[3].header).toMatchObject({
302
+ Name: 'foo_pkey',
303
+ Type: 'CONSTRAINT',
304
+ });
305
+ });
306
+ (0, vitest_1.test)('handles schemas correctly in sorting', () => {
307
+ const dump = (0, multiline_ts_1.default) `
308
+ --
309
+ -- Name: foo; Type: TABLE; Schema: custom; Owner: postgres
310
+ --
311
+
312
+ CREATE TABLE custom.foo (
313
+ id integer NOT NULL
314
+ );
315
+
316
+
317
+ --
318
+ -- Name: foo; Type: TABLE; Schema: public; Owner: postgres
319
+ --
320
+
321
+ CREATE TABLE public.foo (
322
+ id integer NOT NULL
323
+ );
324
+
325
+
326
+ --
327
+ -- Name: bar; Type: TABLE; Schema: -; Owner: postgres
328
+ --
329
+
330
+ CREATE TABLE bar (
331
+ id integer NOT NULL
332
+ );
333
+ `;
334
+ const schemaObjects = (0, parsePgDump_1.parsePgDump)(dump);
335
+ const sorted = (0, sortSchemaObjects_1.sortSchemaObjects)(schemaObjects);
336
+ // Tables with null schema should come first
337
+ (0, vitest_1.expect)(sorted[0].header).toMatchObject({ Name: 'bar', Schema: null });
338
+ // Then sorted by schema name
339
+ (0, vitest_1.expect)(sorted[1].header).toMatchObject({ Name: 'foo', Schema: 'custom' });
340
+ (0, vitest_1.expect)(sorted[2].header).toMatchObject({ Name: 'foo', Schema: 'public' });
341
+ });
342
+ });
343
+ //# sourceMappingURL=sortSchemaObjects.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sortSchemaObjects.test.js","sourceRoot":"","sources":["../src/sortSchemaObjects.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -2,24 +2,24 @@
2
2
  "author": {
3
3
  "email": "gajus@gajus.com",
4
4
  "name": "Gajus Kuizinas",
5
- "url": "http://gajus.com"
5
+ "url": "https://gajus.com"
6
6
  },
7
7
  "dependencies": {
8
8
  "multiline-ts": "^4.0.1",
9
- "zod": "^3.23.8"
9
+ "zod": "^4.3.5"
10
10
  },
11
11
  "description": "Parses PostgreSQL dump files into an array of schema objects.",
12
12
  "devDependencies": {
13
- "@semantic-release/commit-analyzer": "^9.0.2",
14
- "@semantic-release/github": "^8.0.7",
15
- "@semantic-release/npm": "^9.0.2",
16
- "@types/node": "^18.15.3",
17
- "cspell": "^6.30.2",
18
- "eslint": "^8.36.0",
19
- "eslint-config-canonical": "^41.0.1",
20
- "semantic-release": "^20.1.3",
21
- "typescript": "^5.0.2",
22
- "vitest": "^0.29.7"
13
+ "@semantic-release/commit-analyzer": "^13.0.1",
14
+ "@semantic-release/github": "^12.0.2",
15
+ "@semantic-release/npm": "^13.1.3",
16
+ "@types/node": "^25.0.8",
17
+ "cspell": "^9.6.0",
18
+ "eslint": "^9.39.2",
19
+ "eslint-config-canonical": "^47.3.7",
20
+ "semantic-release": "^25.0.2",
21
+ "typescript": "^5.9.3",
22
+ "vitest": "^4.0.17"
23
23
  },
24
24
  "engines": {
25
25
  "node": ">=22"
@@ -56,5 +56,5 @@
56
56
  "test:vitest": "vitest --run --passWithNoTests"
57
57
  },
58
58
  "types": "./dist/index.d.ts",
59
- "version": "1.6.0"
59
+ "version": "1.8.0"
60
60
  }
package/src/index.ts CHANGED
@@ -6,3 +6,8 @@ export {
6
6
  type TitleHeader,
7
7
  } from './parsePgDump';
8
8
  export { type SchemaObjectScope, scopeSchemaObject } from './scopeSchemaObject';
9
+ export {
10
+ groupAndSortSchemaObjects,
11
+ sortSchemaObjects,
12
+ sortSchemaObjectsByScope,
13
+ } from './sortSchemaObjects';
@@ -1,5 +1,4 @@
1
- import { type SchemaObject } from './parsePgDump';
2
- import { parsePgDump } from './parsePgDump';
1
+ import { parsePgDump, type SchemaObject } from './parsePgDump';
3
2
  import { type SchemaObjectScope, scopeSchemaObject } from './scopeSchemaObject';
4
3
  import multiline from 'multiline-ts';
5
4
  import { expect, test } from 'vitest';
@@ -315,6 +314,12 @@ COMMENT ON MATERIALIZED VIEW public.qux IS 'Materialized view comment x';
315
314
  COMMENT ON COLUMN public.qux.id IS 'Column comment x';
316
315
 
317
316
 
317
+ --
318
+ -- Name: portfolio_project_search_hit_default; Type: TABLE ATTACH; Schema: public; Owner: flyway
319
+ --
320
+
321
+ ALTER TABLE ONLY public.portfolio_project_search_hit ATTACH PARTITION public.portfolio_project_search_hit_default DEFAULT;
322
+
318
323
  --
319
324
  -- Name: corge id; Type: DEFAULT; Schema: public; Owner: postgres
320
325
  --
@@ -412,7 +417,7 @@ const omit = <T extends Record<string, unknown>>(
412
417
  );
413
418
 
414
419
  const expectSchemeObject = (
415
- expectedSchemaObject: SchemaObject & { scope: SchemaObjectScope | null },
420
+ expectedSchemaObject: SchemaObject & { scope: null | SchemaObjectScope },
416
421
  ) => {
417
422
  const schemaObjects = parsePgDump(dump);
418
423
 
@@ -1067,6 +1072,25 @@ test('extracts OWNER TO (FUNCTION)', async () => {
1067
1072
  });
1068
1073
  });
1069
1074
 
1075
+ test('extracts OWNER TO (TABLE)', async () => {
1076
+ expectSchemeObject({
1077
+ header: {
1078
+ Name: 'foo',
1079
+ Owner: 'postgres',
1080
+ Schema: 'public',
1081
+ Type: 'TABLE',
1082
+ },
1083
+ scope: {
1084
+ name: 'foo',
1085
+ schema: 'public',
1086
+ type: 'TABLE',
1087
+ },
1088
+ sql: multiline`
1089
+ ALTER TABLE public.foo OWNER TO postgres;
1090
+ `,
1091
+ });
1092
+ });
1093
+
1070
1094
  test('extracts OWNER TO (TYPE)', async () => {
1071
1095
  expectSchemeObject({
1072
1096
  header: {
@@ -1,3 +1,4 @@
1
+ /* eslint-disable no-console */
1
2
  import { z } from 'zod';
2
3
 
3
4
  // These are the attribute less headers, e.g.
@@ -34,6 +35,7 @@ const AttributedHeaderZodSchema = z.object({
34
35
  'SCHEMA',
35
36
  'SEQUENCE OWNED BY',
36
37
  'SEQUENCE',
38
+ 'TABLE ATTACH',
37
39
  'TABLE',
38
40
  'TEXT SEARCH CONFIGURATION',
39
41
  'TEXT SEARCH DICTIONARY',
@@ -48,9 +50,9 @@ const HeaderZodSchema = z.union([
48
50
  AttributedHeaderZodSchema,
49
51
  ]);
50
52
 
53
+ export type AttributedHeader = z.infer<typeof AttributedHeaderZodSchema>;
51
54
  export type Header = z.infer<typeof HeaderZodSchema>;
52
55
  export type TitleHeader = z.infer<typeof TitleHeaderZodSchema>;
53
- export type AttributedHeader = z.infer<typeof AttributedHeaderZodSchema>;
54
56
 
55
57
  const isHeader = (fragment: string): boolean => {
56
58
  return fragment.startsWith('--\n--');
@@ -64,7 +66,7 @@ const parseValue = (value: string) => {
64
66
  return value;
65
67
  };
66
68
 
67
- const parseAttribute = (attribute: string): [string, string | null] => {
69
+ const parseAttribute = (attribute: string): [string, null | string] => {
68
70
  const [name, value] = attribute.split(':');
69
71
 
70
72
  return [name, parseValue(value.trim())];
@@ -75,36 +77,43 @@ const parseAttribute = (attribute: string): [string, string | null] => {
75
77
  // --
76
78
 
77
79
  const parseHeader = (fragment: string) => {
78
- const lines = fragment.split('\n');
80
+ try {
81
+ const lines = fragment.split('\n');
79
82
 
80
- if (lines.length !== 3) {
81
- throw new Error('Invalid header');
82
- }
83
+ if (lines.length !== 3) {
84
+ throw new Error('Invalid header');
85
+ }
83
86
 
84
- const contentLine = lines[1].slice(3);
87
+ const contentLine = lines[1].slice(3);
85
88
 
86
- if (
87
- contentLine === 'PostgreSQL database dump' ||
88
- contentLine === 'PostgreSQL database dump complete'
89
- ) {
90
- return HeaderZodSchema.parse({
91
- Title: contentLine,
92
- });
93
- }
89
+ if (
90
+ contentLine === 'PostgreSQL database dump' ||
91
+ contentLine === 'PostgreSQL database dump complete'
92
+ ) {
93
+ return HeaderZodSchema.parse({
94
+ Title: contentLine,
95
+ });
96
+ }
94
97
 
95
- const content = Object.fromEntries(
96
- contentLine.split('; ').map((attribute) => {
97
- return parseAttribute(attribute);
98
- }),
99
- );
98
+ const content = Object.fromEntries(
99
+ contentLine.split('; ').map((attribute) => {
100
+ return parseAttribute(attribute);
101
+ }),
102
+ );
100
103
 
101
- const result = HeaderZodSchema.safeParse(content);
104
+ const result = HeaderZodSchema.safeParse(content);
102
105
 
103
- if (!result.success) {
104
- throw new Error('Invalid header');
105
- }
106
+ if (!result.success) {
107
+ throw new Error('Invalid header');
108
+ }
106
109
 
107
- return result.data;
110
+ return result.data;
111
+ } catch (error) {
112
+ console.warn('[pg-dump-parser] failing fragment');
113
+ console.warn(fragment);
114
+
115
+ throw error;
116
+ }
108
117
  };
109
118
 
110
119
  export type SchemaObject = {
@@ -7,7 +7,7 @@ type TableTarget = {
7
7
  };
8
8
 
9
9
  const extractOwnedByTarget = (fragment: string): TableTarget => {
10
- const { schema, name } =
10
+ const { name, schema } =
11
11
  fragment.match(/OWNED BY\s(?<schema>[^.]+)\.(?<name>[^.]+)/u)?.groups ?? {};
12
12
 
13
13
  if (!schema) {
@@ -25,7 +25,7 @@ const extractOwnedByTarget = (fragment: string): TableTarget => {
25
25
  };
26
26
 
27
27
  const extractOnTableTarget = (fragment: string): TableTarget => {
28
- const { schema, name } =
28
+ const { name, schema } =
29
29
  fragment.match(/ON TABLE\s(?<schema>[^.]+)\.(?<name>\S+)/u)?.groups ?? {};
30
30
 
31
31
  if (!schema) {
@@ -43,7 +43,7 @@ const extractOnTableTarget = (fragment: string): TableTarget => {
43
43
  };
44
44
 
45
45
  const extractCreateIndexTarget = (fragment: string): TableTarget => {
46
- const { schema, name } =
46
+ const { name, schema } =
47
47
  fragment.match(/ON\s(?<schema>[^.]+)\.(?<name>\S+)/u)?.groups ?? {};
48
48
 
49
49
  if (!schema) {
@@ -61,7 +61,7 @@ const extractCreateIndexTarget = (fragment: string): TableTarget => {
61
61
  };
62
62
 
63
63
  const extractAlterTableTarget = (fragment: string): TableTarget => {
64
- const { schema, name } =
64
+ const { name, schema } =
65
65
  fragment.match(/ALTER TABLE (?:ONLY\s)?(?<schema>[^.]+)\.(?<name>\S+)/u)
66
66
  ?.groups ?? {};
67
67
 
@@ -82,7 +82,6 @@ const extractAlterTableTarget = (fragment: string): TableTarget => {
82
82
  const extractFunctionLikeName = (fragment: string): string => {
83
83
  const { name } =
84
84
  fragment.match(
85
- // eslint-disable-next-line unicorn/no-unsafe-regex
86
85
  /(?:AGGREGATE|FUNCTION|PROCEDURE)\s+(?:(?<schema>\S+)\.)?(?<name>\w+)\s*\(/u,
87
86
  )?.groups ?? {};
88
87
 
@@ -158,10 +157,10 @@ const findTableLikeOwner = (
158
157
  schemaObjects: AttributedSchemaObject[],
159
158
  name: string,
160
159
  schema: string,
161
- ): SchemaObjectScope | null => {
160
+ ): null | SchemaObjectScope => {
162
161
  const targetSchemaObject = schemaObjects.find((schemaObject) => {
163
162
  return (
164
- ['TABLE', 'VIEW', 'MATERIALIZED VIEW'].includes(
163
+ ['MATERIALIZED VIEW', 'TABLE', 'VIEW'].includes(
165
164
  schemaObject.header.Type,
166
165
  ) &&
167
166
  schemaObject.header.Name === name &&
@@ -183,11 +182,10 @@ const findTableLikeOwner = (
183
182
  };
184
183
  };
185
184
 
186
- // eslint-disable-next-line complexity
187
185
  const scopeComment = (
188
186
  schemaObjects: AttributedSchemaObject[],
189
187
  subject: AttributedSchemaObject,
190
- ): SchemaObjectScope | null => {
188
+ ): null | SchemaObjectScope => {
191
189
  const target = extractCommentOnTarget(subject.sql);
192
190
 
193
191
  if (target.type === 'AGGREGATE') {
@@ -344,11 +342,10 @@ const scopeComment = (
344
342
  return null;
345
343
  };
346
344
 
347
- // eslint-disable-next-line complexity
348
345
  const scopeAttributedSchemaObject = (
349
346
  schemaObjects: AttributedSchemaObject[],
350
347
  subject: AttributedSchemaObject,
351
- ): SchemaObjectScope | null => {
348
+ ): null | SchemaObjectScope => {
352
349
  if (subject.header.Type === 'AGGREGATE') {
353
350
  return {
354
351
  name: subject.header.Name.split('(')[0],
@@ -475,7 +472,7 @@ const scopeAttributedSchemaObject = (
475
472
  export const scopeSchemaObject = (
476
473
  schemaObjects: SchemaObject[],
477
474
  subject: SchemaObject,
478
- ): SchemaObjectScope | null => {
475
+ ): null | SchemaObjectScope => {
479
476
  if (!('Type' in subject.header)) {
480
477
  return null;
481
478
  }