pg-dump-parser 1.0.3 → 1.1.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.
@@ -1,4 +1,6 @@
1
+ import { type SchemaObject } from './parsePgDump';
1
2
  import { parsePgDump } from './parsePgDump';
3
+ import { type SchemaObjectScope, scopeSchemaObject } from './scopeSchemaObject';
2
4
  import multiline from 'multiline-ts';
3
5
  import { expect, test } from 'vitest';
4
6
 
@@ -330,39 +332,66 @@ GRANT SELECT(name) ON TABLE public.foo TO postgres;
330
332
  --
331
333
  `;
332
334
 
333
- test('extracts SEQUENCE', async () => {
335
+ const omit = <T extends Record<string, unknown>>(object: T, keys: string[]) =>
336
+ Object.fromEntries(
337
+ Object.entries(object).filter(([key]) => !keys.includes(key)),
338
+ );
339
+
340
+ const expectSchemeObject = (
341
+ expectedSchemaObject: SchemaObject & { scope: SchemaObjectScope | null },
342
+ ) => {
334
343
  const schemaObjects = parsePgDump(dump);
335
344
 
336
- expect(schemaObjects).toContainEqual({
345
+ expect(schemaObjects).toContainEqual(omit(expectedSchemaObject, ['scope']));
346
+
347
+ if (typeof expectedSchemaObject.scope === 'undefined') {
348
+ return;
349
+ }
350
+
351
+ expect(scopeSchemaObject(schemaObjects, expectedSchemaObject)).toEqual(
352
+ expectedSchemaObject.scope,
353
+ );
354
+ };
355
+
356
+ test('extracts SEQUENCE', async () => {
357
+ expectSchemeObject({
337
358
  header: {
338
359
  Name: 'bar_id_seq',
339
360
  Owner: 'postgres',
340
361
  Schema: 'public',
341
362
  Type: 'SEQUENCE',
342
363
  },
364
+ scope: {
365
+ name: 'bar',
366
+ schema: 'public',
367
+ type: 'TABLE',
368
+ },
343
369
  sql: multiline`
344
- ALTER TABLE public.bar ALTER COLUMN id ADD GENERATED ALWAYS AS IDENTITY (
345
- SEQUENCE NAME public.bar_id_seq
346
- START WITH 1
347
- INCREMENT BY 1
348
- NO MINVALUE
349
- NO MAXVALUE
350
- CACHE 1
351
- );
352
- `,
370
+ ALTER TABLE public.bar ALTER COLUMN id ADD GENERATED ALWAYS AS IDENTITY (
371
+ SEQUENCE NAME public.bar_id_seq
372
+ START WITH 1
373
+ INCREMENT BY 1
374
+ NO MINVALUE
375
+ NO MAXVALUE
376
+ CACHE 1
377
+ );
378
+ `,
353
379
  });
354
380
  });
355
381
 
356
382
  test('extracts TABLE', async () => {
357
- const schemaObjects = parsePgDump(dump);
358
-
359
- expect(schemaObjects).toContainEqual({
383
+ expectSchemeObject({
360
384
  header: {
361
385
  Name: 'foo',
362
386
  Owner: 'postgres',
363
387
  Schema: 'public',
364
388
  Type: 'TABLE',
365
389
  },
390
+ scope: {
391
+ name: 'foo',
392
+ schema: 'public',
393
+ type: 'TABLE',
394
+ },
366
395
  sql: multiline`
367
396
  CREATE TABLE public.foo (
368
397
  id integer NOT NULL,
@@ -373,15 +402,18 @@ test('extracts TABLE', async () => {
373
402
  });
374
403
 
375
404
  test('extracts CONSTRAINT', async () => {
376
- const schemaObjects = parsePgDump(dump);
377
-
378
- expect(schemaObjects).toContainEqual({
405
+ expectSchemeObject({
379
406
  header: {
380
407
  Name: 'foo foo_pkey',
381
408
  Owner: 'postgres',
382
409
  Schema: 'public',
383
410
  Type: 'CONSTRAINT',
384
411
  },
412
+ scope: {
413
+ name: 'foo',
414
+ schema: 'public',
415
+ type: 'TABLE',
416
+ },
385
417
  sql: multiline`
386
418
  ALTER TABLE ONLY public.foo
387
419
  ADD CONSTRAINT foo_pkey PRIMARY KEY (id);
@@ -390,15 +422,18 @@ test('extracts CONSTRAINT', async () => {
390
422
  });
391
423
 
392
424
  test('extracts COMMENT on TABLE', async () => {
393
- const schemaObjects = parsePgDump(dump);
394
-
395
- expect(schemaObjects).toContainEqual({
425
+ expectSchemeObject({
396
426
  header: {
397
427
  Name: 'TABLE foo',
398
428
  Owner: 'postgres',
399
429
  Schema: 'public',
400
430
  Type: 'COMMENT',
401
431
  },
432
+ scope: {
433
+ name: 'foo',
434
+ schema: 'public',
435
+ type: 'TABLE',
436
+ },
402
437
  sql: multiline`
403
438
  COMMENT ON TABLE public.foo IS 'Table comment x';
404
439
  `,
@@ -406,15 +441,18 @@ test('extracts COMMENT on TABLE', async () => {
406
441
  });
407
442
 
408
443
  test('extracts COMMENT on COLUMN', async () => {
409
- const schemaObjects = parsePgDump(dump);
410
-
411
- expect(schemaObjects).toContainEqual({
444
+ expectSchemeObject({
412
445
  header: {
413
446
  Name: 'COLUMN foo.id',
414
447
  Owner: 'postgres',
415
448
  Schema: 'public',
416
449
  Type: 'COMMENT',
417
450
  },
451
+ scope: {
452
+ name: 'foo',
453
+ schema: 'public',
454
+ type: 'TABLE',
455
+ },
418
456
  sql: multiline`
419
457
  COMMENT ON COLUMN public.foo.id IS 'Column comment x';
420
458
  `,
@@ -422,15 +460,18 @@ test('extracts COMMENT on COLUMN', async () => {
422
460
  });
423
461
 
424
462
  test('extracts COMMENT on INDEX', async () => {
425
- const schemaObjects = parsePgDump(dump);
426
-
427
- expect(schemaObjects).toContainEqual({
463
+ expectSchemeObject({
428
464
  header: {
429
465
  Name: 'INDEX foo_pkey',
430
466
  Owner: 'postgres',
431
467
  Schema: 'public',
432
468
  Type: 'COMMENT',
433
469
  },
470
+ scope: {
471
+ name: 'foo',
472
+ schema: 'public',
473
+ type: 'TABLE',
474
+ },
434
475
  sql: multiline`
435
476
  COMMENT ON INDEX public.foo_pkey IS 'Index comment x';
436
477
  `,
@@ -438,15 +479,18 @@ test('extracts COMMENT on INDEX', async () => {
438
479
  });
439
480
 
440
481
  test('extracts COMMENT on SEQUENCE', async () => {
441
- const schemaObjects = parsePgDump(dump);
442
-
443
- expect(schemaObjects).toContainEqual({
482
+ expectSchemeObject({
444
483
  header: {
445
484
  Name: 'SEQUENCE foo_id_seq',
446
485
  Owner: 'postgres',
447
486
  Schema: 'public',
448
487
  Type: 'COMMENT',
449
488
  },
489
+ scope: {
490
+ name: 'foo',
491
+ schema: 'public',
492
+ type: 'TABLE',
493
+ },
450
494
  sql: multiline`
451
495
  COMMENT ON SEQUENCE public.foo_id_seq IS 'Sequence comment x';
452
496
  `,
@@ -454,15 +498,14 @@ test('extracts COMMENT on SEQUENCE', async () => {
454
498
  });
455
499
 
456
500
  test('extracts PUBLICATION', async () => {
457
- const schemaObjects = parsePgDump(dump);
458
-
459
- expect(schemaObjects).toContainEqual({
501
+ expectSchemeObject({
460
502
  header: {
461
503
  Name: 'foo_publication',
462
504
  Owner: 'postgres',
463
505
  Schema: null,
464
506
  Type: 'PUBLICATION',
465
507
  },
508
+ scope: null,
466
509
  sql: multiline`
467
510
  CREATE PUBLICATION foo_publication FOR ALL TABLES WITH (publish = 'insert, update, delete');
468
511
  `,
@@ -470,15 +513,14 @@ test('extracts PUBLICATION', async () => {
470
513
  });
471
514
 
472
515
  test('extracts SCHEMA', async () => {
473
- const schemaObjects = parsePgDump(dump);
474
-
475
- expect(schemaObjects).toContainEqual({
516
+ expectSchemeObject({
476
517
  header: {
477
518
  Name: 'quux',
478
519
  Owner: 'postgres',
479
520
  Schema: null,
480
521
  Type: 'SCHEMA',
481
522
  },
523
+ scope: null,
482
524
  sql: multiline`
483
525
  CREATE SCHEMA quux;
484
526
  `,
@@ -486,15 +528,18 @@ test('extracts SCHEMA', async () => {
486
528
  });
487
529
 
488
530
  test('extracts VIEW', async () => {
489
- const schemaObjects = parsePgDump(dump);
490
-
491
- expect(schemaObjects).toContainEqual({
531
+ expectSchemeObject({
492
532
  header: {
493
533
  Name: 'baz',
494
534
  Owner: 'postgres',
495
535
  Schema: 'public',
496
536
  Type: 'VIEW',
497
537
  },
538
+ scope: {
539
+ name: 'baz',
540
+ schema: 'public',
541
+ type: 'VIEW',
542
+ },
498
543
  sql: multiline`
499
544
  CREATE VIEW public.baz AS
500
545
  SELECT id,
@@ -505,15 +550,18 @@ test('extracts VIEW', async () => {
505
550
  });
506
551
 
507
552
  test('extracts MATERIALIZED VIEW', async () => {
508
- const schemaObjects = parsePgDump(dump);
509
-
510
- expect(schemaObjects).toContainEqual({
553
+ expectSchemeObject({
511
554
  header: {
512
555
  Name: 'qux',
513
556
  Owner: 'postgres',
514
557
  Schema: 'public',
515
558
  Type: 'MATERIALIZED VIEW',
516
559
  },
560
+ scope: {
561
+ name: 'qux',
562
+ schema: 'public',
563
+ type: 'MATERIALIZED VIEW',
564
+ },
517
565
  sql: multiline`
518
566
  CREATE MATERIALIZED VIEW public.qux AS
519
567
  SELECT id,
@@ -525,15 +573,18 @@ test('extracts MATERIALIZED VIEW', async () => {
525
573
  });
526
574
 
527
575
  test('extracts FUNCTION', async () => {
528
- const schemaObjects = parsePgDump(dump);
529
-
530
- expect(schemaObjects).toContainEqual({
576
+ expectSchemeObject({
531
577
  header: {
532
578
  Name: 'add_two_numbers(integer, integer)',
533
579
  Owner: 'postgres',
534
580
  Schema: 'public',
535
581
  Type: 'FUNCTION',
536
582
  },
583
+ scope: {
584
+ name: 'add_two_numbers',
585
+ schema: 'public',
586
+ type: 'FUNCTION',
587
+ },
537
588
  sql: multiline`
538
589
  CREATE FUNCTION public.add_two_numbers(a integer, b integer) RETURNS integer
539
590
  LANGUAGE plpgsql
@@ -547,15 +598,18 @@ test('extracts FUNCTION', async () => {
547
598
  });
548
599
 
549
600
  test('extracts PROCEDURE', async () => {
550
- const schemaObjects = parsePgDump(dump);
551
-
552
- expect(schemaObjects).toContainEqual({
601
+ expectSchemeObject({
553
602
  header: {
554
603
  Name: 'say_hello(character varying)',
555
604
  Owner: 'postgres',
556
605
  Schema: 'public',
557
606
  Type: 'PROCEDURE',
558
607
  },
608
+ scope: {
609
+ name: 'say_hello',
610
+ schema: 'public',
611
+ type: 'PROCEDURE',
612
+ },
559
613
  sql: multiline`
560
614
  CREATE PROCEDURE public.say_hello(IN name_param character varying)
561
615
  LANGUAGE plpgsql
@@ -569,15 +623,18 @@ test('extracts PROCEDURE', async () => {
569
623
  });
570
624
 
571
625
  test('extracts TRIGGER', async () => {
572
- const schemaObjects = parsePgDump(dump);
573
-
574
- expect(schemaObjects).toContainEqual({
626
+ expectSchemeObject({
575
627
  header: {
576
628
  Name: 'foo foo_insert_trigger',
577
629
  Owner: 'postgres',
578
630
  Schema: 'public',
579
631
  Type: 'TRIGGER',
580
632
  },
633
+ scope: {
634
+ name: 'foo',
635
+ schema: 'public',
636
+ type: 'TABLE',
637
+ },
581
638
  sql: multiline`
582
639
  CREATE TRIGGER foo_insert_trigger AFTER INSERT ON public.foo FOR EACH ROW EXECUTE FUNCTION public.notify_foo_insert();
583
640
  `,
@@ -585,15 +642,18 @@ test('extracts TRIGGER', async () => {
585
642
  });
586
643
 
587
644
  test('extracts TYPE', async () => {
588
- const schemaObjects = parsePgDump(dump);
589
-
590
- expect(schemaObjects).toContainEqual({
645
+ expectSchemeObject({
591
646
  header: {
592
647
  Name: 'status',
593
648
  Owner: 'postgres',
594
649
  Schema: 'public',
595
650
  Type: 'TYPE',
596
651
  },
652
+ scope: {
653
+ name: 'status',
654
+ schema: 'public',
655
+ type: 'TYPE',
656
+ },
597
657
  sql: multiline`
598
658
  CREATE TYPE public.status AS ENUM (
599
659
  'ACTIVE',
@@ -604,15 +664,18 @@ test('extracts TYPE', async () => {
604
664
  });
605
665
 
606
666
  test('extracts AGGREGATE', async () => {
607
- const schemaObjects = parsePgDump(dump);
608
-
609
- expect(schemaObjects).toContainEqual({
667
+ expectSchemeObject({
610
668
  header: {
611
669
  Name: 'my_sum(integer)',
612
670
  Owner: 'postgres',
613
671
  Schema: 'public',
614
672
  Type: 'AGGREGATE',
615
673
  },
674
+ scope: {
675
+ name: 'my_sum',
676
+ schema: 'public',
677
+ type: 'AGGREGATE',
678
+ },
616
679
  sql: multiline`
617
680
  CREATE AGGREGATE public.my_sum(integer) (
618
681
  SFUNC = public.add_two_numbers,
@@ -623,15 +686,18 @@ test('extracts AGGREGATE', async () => {
623
686
  });
624
687
 
625
688
  test('extracts FK CONSTRAINT', async () => {
626
- const schemaObjects = parsePgDump(dump);
627
-
628
- expect(schemaObjects).toContainEqual({
689
+ expectSchemeObject({
629
690
  header: {
630
691
  Name: 'bar bar_foo_id_fkey',
631
692
  Owner: 'postgres',
632
693
  Schema: 'public',
633
694
  Type: 'FK CONSTRAINT',
634
695
  },
696
+ scope: {
697
+ name: 'bar',
698
+ schema: 'public',
699
+ type: 'TABLE',
700
+ },
635
701
  sql: multiline`
636
702
  ALTER TABLE ONLY public.bar
637
703
  ADD CONSTRAINT bar_foo_id_fkey FOREIGN KEY (foo_id) REFERENCES public.foo(id) ON DELETE CASCADE;
@@ -640,15 +706,18 @@ test('extracts FK CONSTRAINT', async () => {
640
706
  });
641
707
 
642
708
  test('extracts INDEX', async () => {
643
- const schemaObjects = parsePgDump(dump);
644
-
645
- expect(schemaObjects).toContainEqual({
709
+ expectSchemeObject({
646
710
  header: {
647
711
  Name: 'bar_uid_idx',
648
712
  Owner: 'postgres',
649
713
  Schema: 'public',
650
714
  Type: 'INDEX',
651
715
  },
716
+ scope: {
717
+ name: 'bar',
718
+ schema: 'public',
719
+ type: 'TABLE',
720
+ },
652
721
  sql: multiline`
653
722
  CREATE UNIQUE INDEX bar_uid_idx ON public.bar USING btree (uid);
654
723
  `,
@@ -656,15 +725,14 @@ test('extracts INDEX', async () => {
656
725
  });
657
726
 
658
727
  test('extracts CAST', async () => {
659
- const schemaObjects = parsePgDump(dump);
660
-
661
- expect(schemaObjects).toContainEqual({
728
+ expectSchemeObject({
662
729
  header: {
663
730
  Name: 'CAST (text AS integer)',
664
731
  Owner: null,
665
732
  Schema: null,
666
733
  Type: 'CAST',
667
734
  },
735
+ scope: null,
668
736
  sql: multiline`
669
737
  CREATE CAST (text AS integer) WITH INOUT AS IMPLICIT;
670
738
  `,
@@ -672,31 +740,37 @@ test('extracts CAST', async () => {
672
740
  });
673
741
 
674
742
  test('extracts EXTENSION', async () => {
675
- const schemaObjects = parsePgDump(dump);
676
-
677
- expect(schemaObjects).toContainEqual({
743
+ expectSchemeObject({
678
744
  header: {
679
745
  Name: 'pgcrypto',
680
746
  Owner: null,
681
747
  Schema: null,
682
748
  Type: 'EXTENSION',
683
749
  },
750
+ scope: {
751
+ name: 'pgcrypto',
752
+ schema: null,
753
+ type: 'EXTENSION',
754
+ },
684
755
  sql: multiline`
685
756
  CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA public;
686
757
  `,
687
758
  });
688
759
  });
689
760
 
690
- test('extracts ACL', async () => {
691
- const schemaObjects = parsePgDump(dump);
692
-
693
- expect(schemaObjects).toContainEqual({
761
+ test('extracts ACL (ON TABLE)', async () => {
762
+ expectSchemeObject({
694
763
  header: {
695
764
  Name: 'COLUMN foo.name',
696
765
  Owner: 'postgres',
697
766
  Schema: 'public',
698
767
  Type: 'ACL',
699
768
  },
769
+ scope: {
770
+ name: 'foo',
771
+ schema: 'public',
772
+ type: 'TABLE',
773
+ },
700
774
  sql: multiline`
701
775
  GRANT SELECT(name) ON TABLE public.foo TO postgres;
702
776
  `,
@@ -704,15 +778,18 @@ test('extracts ACL', async () => {
704
778
  });
705
779
 
706
780
  test('extracts DEFAULT', async () => {
707
- const schemaObjects = parsePgDump(dump);
708
-
709
- expect(schemaObjects).toContainEqual({
781
+ expectSchemeObject({
710
782
  header: {
711
783
  Name: 'corge id',
712
784
  Owner: 'postgres',
713
785
  Schema: 'public',
714
786
  Type: 'DEFAULT',
715
787
  },
788
+ scope: {
789
+ name: 'corge',
790
+ schema: 'public',
791
+ type: 'TABLE',
792
+ },
716
793
  sql: multiline`
717
794
  ALTER TABLE ONLY public.corge ALTER COLUMN id SET DEFAULT nextval('public.corge_id_seq'::regclass);
718
795
  `,
@@ -720,15 +797,18 @@ test('extracts DEFAULT', async () => {
720
797
  });
721
798
 
722
799
  test('extracts SEQUENCE OWNED BY', async () => {
723
- const schemaObjects = parsePgDump(dump);
724
-
725
- expect(schemaObjects).toContainEqual({
800
+ expectSchemeObject({
726
801
  header: {
727
802
  Name: 'corge_id_seq',
728
803
  Owner: 'postgres',
729
804
  Schema: 'public',
730
805
  Type: 'SEQUENCE OWNED BY',
731
806
  },
807
+ scope: {
808
+ name: 'corge',
809
+ schema: 'public',
810
+ type: 'TABLE',
811
+ },
732
812
  sql: multiline`
733
813
  ALTER SEQUENCE public.corge_id_seq OWNED BY public.corge.id;
734
814
  `,
@@ -1,50 +1,56 @@
1
1
  import { z } from 'zod';
2
2
 
3
+ // These are the attribute less headers, e.g.
4
+ // --
5
+ // -- PostgreSQL database dump
6
+ // --
7
+ const TitleHeaderZodSchema = z.object({
8
+ Title: z.string(),
9
+ });
10
+
11
+ // These are the objects with attributes, e.g.
12
+ // --
13
+ // -- Name: citext; Type: EXTENSION; Schema: -; Owner: -
14
+ // --
15
+ const AttributedHeaderZodSchema = 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 ACL',
26
+ 'DEFAULT',
27
+ 'EXTENSION',
28
+ 'FK CONSTRAINT',
29
+ 'FUNCTION',
30
+ 'INDEX',
31
+ 'MATERIALIZED VIEW',
32
+ 'PROCEDURE',
33
+ 'PUBLICATION',
34
+ 'SCHEMA',
35
+ 'SEQUENCE OWNED BY',
36
+ 'SEQUENCE',
37
+ 'TABLE',
38
+ 'TEXT SEARCH CONFIGURATION',
39
+ 'TEXT SEARCH DICTIONARY',
40
+ 'TRIGGER',
41
+ 'TYPE',
42
+ 'VIEW',
43
+ ]),
44
+ });
45
+
3
46
  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 ACL',
26
- 'DEFAULT',
27
- 'EXTENSION',
28
- 'FK CONSTRAINT',
29
- 'FUNCTION',
30
- 'INDEX',
31
- 'MATERIALIZED VIEW',
32
- 'PROCEDURE',
33
- 'PUBLICATION',
34
- 'SCHEMA',
35
- 'SEQUENCE OWNED BY',
36
- 'SEQUENCE',
37
- 'TABLE',
38
- 'TEXT SEARCH CONFIGURATION',
39
- 'TEXT SEARCH DICTIONARY',
40
- 'TRIGGER',
41
- 'TYPE',
42
- 'VIEW',
43
- ]),
44
- }),
47
+ TitleHeaderZodSchema,
48
+ AttributedHeaderZodSchema,
45
49
  ]);
46
50
 
47
- type Header = z.infer<typeof HeaderZodSchema>;
51
+ export type Header = z.infer<typeof HeaderZodSchema>;
52
+ export type TitleHeader = z.infer<typeof TitleHeaderZodSchema>;
53
+ export type AttributedHeader = z.infer<typeof AttributedHeaderZodSchema>;
48
54
 
49
55
  const isHeader = (fragment: string): boolean => {
50
56
  return fragment.startsWith('--\n--');
@@ -101,7 +107,7 @@ const parseHeader = (fragment: string) => {
101
107
  return result.data;
102
108
  };
103
109
 
104
- type SchemaObject = {
110
+ export type SchemaObject = {
105
111
  header: Header;
106
112
  sql: string;
107
113
  };