-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathschema_test.go
More file actions
1614 lines (1558 loc) · 61.2 KB
/
Copy pathschema_test.go
File metadata and controls
1614 lines (1558 loc) · 61.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
package schema
import (
"context"
"database/sql"
"io"
"testing"
_ "github.com/jackc/pgx/v4/stdlib"
"github.com/kr/pretty"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stripe/pg-schema-diff/internal/pgengine"
"github.com/stripe/pg-schema-diff/internal/queries"
)
type testCase struct {
name string
opts []GetSchemaOpt
ddl []string
expectedSchema Schema
// expectedHash is the expected hash of the schema. If it is not provided, the test will not validate the hash.
expectedHash string
expectedErrIs error
expectedErrContains string
}
var (
defaultCollation = SchemaQualifiedName{
EscapedName: `"default"`,
SchemaName: "pg_catalog",
}
cCollation = SchemaQualifiedName{
EscapedName: `"C"`,
SchemaName: "pg_catalog",
}
testCases = []*testCase{
// Exclude materialized views from the test for now because Postgres 14-15 fully qualify column names while Postgres
// 16+ simplify fully qualified names to unqualified. The easiest option will be to create a version specific test
// that is skipped for Postgres 14-15.
{
name: "Simple schema (validate all schema objects and schema name filters)",
opts: []GetSchemaOpt{
WithIncludeSchemas("public", "schema_1", "schema_2"),
},
ddl: []string{`
CREATE SCHEMA schema_1;
CREATE SCHEMA schema_2;
-- Validate schemas are filtered out
CREATE SCHEMA schema_filtered_1;
CREATE EXTENSION pg_trgm WITH SCHEMA schema_1 VERSION '1.6';
CREATE EXTENSION pg_visibility WITH SCHEMA schema_filtered_1 VERSION '1.2';
CREATE EXTENSION pg_stat_statements VERSION '1.9';
CREATE TYPE foobar_enum AS ENUM ('foobar_1', 'foobar_2', 'foobar_3');
CREATE TYPE schema_1.foobar_enum AS ENUM ('foobar_1', 'foobar_2');
-- Validate types are filtered out
CREATE TYPE schema_filtered_1.foobar_enum AS ENUM ('foobar_1', 'foobar_2');
CREATE SEQUENCE schema_1.foobar_sequence
AS BIGINT
INCREMENT BY 2
MINVALUE 5 MAXVALUE 100
START WITH 10 CACHE 5 CYCLE
OWNED BY NONE;
-- Validate sequences are filtered out
CREATE SEQUENCE schema_filtered_1.foobar_sequence
AS BIGINT
INCREMENT BY 2
MINVALUE 5 MAXVALUE 100
START WITH 10 CACHE 5 CYCLE
OWNED BY NONE;
CREATE FUNCTION schema_1.increment(i integer) RETURNS integer AS $$
BEGIN
RETURN i + 1;
END;
$$ LANGUAGE plpgsql;
-- Validate functions are filtered out
CREATE FUNCTION schema_filtered_1.add(a integer, b integer) RETURNS integer
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT
RETURN a + b;
CREATE FUNCTION function_with_dependencies(a integer, b integer) RETURNS integer
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT
RETURN schema_filtered_1.add(a, b) + schema_1.increment(a);
CREATE TABLE schema_2.foo (
id SERIAL,
author TEXT COLLATE "C",
content TEXT NOT NULL DEFAULT '',
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP CHECK (created_at > CURRENT_TIMESTAMP - interval '1 month') NO INHERIT,
version INT NOT NULL DEFAULT 0,
PRIMARY KEY(id, version),
CHECK ( function_with_dependencies(id, id) > 0)
);
ALTER TABLE schema_2.foo ENABLE ROW LEVEL SECURITY;
ALTER TABLE schema_2.foo ADD CONSTRAINT author_content_check CHECK ( LENGTH(content) > 0 AND LENGTH(author) > 0 ) NO INHERIT NOT VALID;
CREATE INDEX some_idx ON schema_2.foo (created_at DESC, author ASC);
CREATE UNIQUE INDEX some_unique_idx ON schema_2.foo (content);
CREATE INDEX some_gin_idx ON schema_2.foo USING GIN (author schema_1.gin_trgm_ops);
ALTER TABLE schema_2.foo REPLICA IDENTITY USING INDEX some_unique_idx;
CREATE POLICY foo_policy_1 ON schema_2.foo
AS PERMISSIVE
FOR ALL
TO PUBLIC
USING (author = current_user)
WITH CHECK (version > 0);
CREATE ROLE some_role_1;
CREATE ROLE some_role_2;
CREATE POLICY foo_policy_2 ON schema_2.foo
AS RESTRICTIVE
FOR INSERT
TO some_role_2, some_role_1
WITH CHECK (version > 0);
CREATE FUNCTION increment_version() RETURNS TRIGGER AS $$
BEGIN
NEW.version = OLD.version + 1;
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE FUNCTION schema_filtered_1.increment_version() RETURNS TRIGGER AS $$
BEGIN
NEW.version = OLD.version + 1;
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE TRIGGER some_trigger
BEFORE UPDATE ON schema_2.foo
FOR EACH ROW
WHEN (OLD.* IS DISTINCT FROM NEW.*)
-- Reference a function in a filtered out schema. The trigger should still be included.
EXECUTE PROCEDURE schema_filtered_1.increment_version();
CREATE CONSTRAINT TRIGGER some_constraint_trigger
AFTER UPDATE ON schema_2.foo
DEFERRABLE INITIALLY DEFERRED
FOR EACH ROW
WHEN (OLD.* IS DISTINCT FROM NEW.*)
-- Reference a function in a filtered out schema. The trigger should still be included.
EXECUTE PROCEDURE schema_filtered_1.increment_version();
CREATE PROCEDURE schema_2.some_insert_procedure(a INTEGER, b INTEGER)
LANGUAGE SQL
BEGIN ATOMIC
INSERT INTO schema_2.foo DEFAULT VALUES;
END;
CREATE PROCEDURE some_plpgsql_procedure(foobar NUMERIC)
LANGUAGE plpgsql
AS $$
BEGIN
RAISE NOTICE 'some notice';
END
$$;
-- Create table with conflicting name that has check constraints
CREATE TABLE schema_1.foo(
id INT NOT NULL,
CHECK (id > 0)
);
ALTER TABLE schema_1.foo ENABLE ROW LEVEL SECURITY;
ALTER TABLE schema_1.foo FORCE ROW LEVEL SECURITY;
CREATE POLICY foo_policy_1 ON schema_1.foo
AS RESTRICTIVE
FOR UPDATE
TO PUBLIC
WITH CHECK (id > 0);
CREATE TABLE schema_1.foo_fk(
id INT,
version INT
);
CREATE INDEX some_idx on schema_1.foo_fk (id, version);
ALTER TABLE schema_1.foo_fk ADD CONSTRAINT foo_fk_fk FOREIGN KEY (id, version) REFERENCES schema_2.foo (id, version)
ON UPDATE CASCADE
ON DELETE CASCADE
NOT VALID;
CREATE VIEW schema_2.foo_view
WITH (security_barrier=true) AS
SELECT foo.id, foo.author
FROM schema_2.foo
JOIN schema_1.foo_fk ON foo.id = foo_fk.id;
-- Validate tables are filtered out
CREATE TABLE schema_filtered_1.foo_fk(
id INT,
version INT CHECK (version > 0) -- Validate check constraints are filtered out
);
-- Validate indexes are filtered out
CREATE INDEX some_idx on schema_filtered_1.foo_fk (id, version);
-- Validate FK are filtered out
ALTER TABLE schema_filtered_1.foo_fk ADD CONSTRAINT foo_fk_fk FOREIGN KEY (id, version) REFERENCES schema_2.foo (id, version)
ON UPDATE CASCADE
ON DELETE CASCADE
NOT VALID;
-- Validate procedures are filtered out
CREATE PROCEDURE schema_filtered_1.some_filtered_procedure(a INTEGER)
LANGUAGE SQL
BEGIN ATOMIC
INSERT INTO schema_2.foo DEFAULT VALUES;
END;
-- Validate triggers are filtered out
CREATE TRIGGER some_trigger
BEFORE UPDATE ON schema_filtered_1.foo_fk
FOR EACH ROW
WHEN (OLD.* IS DISTINCT FROM NEW.*)
-- Reference a function in a filtered out schema. The trigger should still be included.
EXECUTE PROCEDURE public.increment_version();
-- Validate policies are filtered out
CREATE POLICY foo_policy_1 ON schema_filtered_1.foo_fk
AS PERMISSIVE
FOR SELECT
TO PUBLIC
USING (version > 0);
-- Validate views are filtered out.
CREATE VIEW schema_filtered_1.foo_view AS
SELECT id, author
FROM schema_2.foo;
-- Add a column with a default to test HasMissingValOptimization
ALTER TABLE schema_2.foo ADD COLUMN added_col TEXT DEFAULT 'some_default';
-- Add table privileges to test they are fetched correctly
GRANT SELECT ON schema_2.foo TO some_role_1;
GRANT INSERT ON schema_2.foo TO some_role_2 WITH GRANT OPTION;
`},
expectedHash: "69342de97351df15",
expectedSchema: Schema{
NamedSchemas: []NamedSchema{
{Name: "public"},
{Name: "schema_1"},
{Name: "schema_2"},
},
Extensions: []Extension{
{
SchemaQualifiedName: SchemaQualifiedName{
SchemaName: "schema_1",
EscapedName: EscapeIdentifier("pg_trgm"),
},
Version: "1.6",
},
{
SchemaQualifiedName: SchemaQualifiedName{
SchemaName: "public",
EscapedName: EscapeIdentifier("pg_stat_statements"),
},
Version: "1.9",
},
},
Enums: []Enum{
{
SchemaQualifiedName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foobar_enum\""},
Labels: []string{"foobar_1", "foobar_2", "foobar_3"},
},
{
SchemaQualifiedName: SchemaQualifiedName{SchemaName: "schema_1", EscapedName: "\"foobar_enum\""},
Labels: []string{"foobar_1", "foobar_2"},
},
},
Tables: []Table{
{
SchemaQualifiedName: SchemaQualifiedName{SchemaName: "schema_2", EscapedName: "\"foo\""},
Columns: []Column{
{Name: "id", Type: "integer", Size: 4, Default: "nextval('schema_2.foo_id_seq'::regclass)"},
{Name: "author", Type: "text", IsNullable: true, Size: -1, Collation: cCollation},
{Name: "content", Type: "text", Default: "''::text", Size: -1, Collation: defaultCollation},
{Name: "created_at", Type: "timestamp without time zone", Default: "CURRENT_TIMESTAMP", Size: 8},
{Name: "version", Type: "integer", Default: "0", Size: 4},
{Name: "added_col", Type: "text", Default: "'some_default'::text", IsNullable: true, Size: -1, Collation: defaultCollation, HasMissingValOptimization: true},
},
CheckConstraints: []CheckConstraint{
{Name: "author_content_check", Expression: "((length(content) > 0) AND (length(author) > 0))", KeyColumns: []string{"author", "content"}},
{Name: "foo_created_at_check", Expression: "(created_at > (CURRENT_TIMESTAMP - '1 mon'::interval))", IsValid: true, KeyColumns: []string{"created_at"}},
{
Name: "foo_id_check",
Expression: "(function_with_dependencies(id, id) > 0)",
IsValid: true,
IsInheritable: true,
DependsOnFunctions: []SchemaQualifiedName{
{EscapedName: "\"function_with_dependencies\"(a integer, b integer)", SchemaName: "public"},
},
KeyColumns: []string{"id"},
},
},
Policies: []Policy{
{
EscapedName: "\"foo_policy_1\"",
IsPermissive: true,
AppliesTo: []string{"PUBLIC"},
Cmd: AllPolicyCmd,
UsingExpression: "(author = CURRENT_USER)",
CheckExpression: "(version > 0)",
Columns: []string{"author", "version"},
},
{
EscapedName: "\"foo_policy_2\"",
AppliesTo: []string{"some_role_1", "some_role_2"},
Cmd: InsertPolicyCmd,
CheckExpression: "(version > 0)",
Columns: []string{"version"},
},
},
Privileges: []TablePrivilege{
{Grantee: "some_role_2", Privilege: "INSERT", IsGrantable: true},
{Grantee: "some_role_1", Privilege: "SELECT", IsGrantable: false},
},
ReplicaIdentity: ReplicaIdentityIndex,
RLSEnabled: true,
},
{
SchemaQualifiedName: SchemaQualifiedName{SchemaName: "schema_1", EscapedName: "\"foo\""},
Columns: []Column{
{Name: "id", Type: "integer", Size: 4, Default: ""},
},
CheckConstraints: []CheckConstraint{
{Name: "foo_id_check", Expression: "(id > 0)", IsValid: true, IsInheritable: true, KeyColumns: []string{"id"}},
},
Policies: []Policy{
{
EscapedName: "\"foo_policy_1\"",
IsPermissive: false,
AppliesTo: []string{"PUBLIC"},
Cmd: UpdatePolicyCmd,
CheckExpression: "(id > 0)",
Columns: []string{"id"},
},
},
ReplicaIdentity: ReplicaIdentityDefault,
RLSEnabled: true,
RLSForced: true,
},
{
SchemaQualifiedName: SchemaQualifiedName{SchemaName: "schema_1", EscapedName: "\"foo_fk\""},
Columns: []Column{
{
Name: "id",
Type: "integer",
Collation: SchemaQualifiedName{},
Default: "",
IsNullable: true,
Size: 4,
},
{
Name: "version",
Type: "integer",
Collation: SchemaQualifiedName{},
Default: "",
IsNullable: true,
Size: 4,
},
},
CheckConstraints: nil,
ReplicaIdentity: ReplicaIdentityDefault,
PartitionKeyDef: "",
ForValues: "",
},
},
ForeignKeyConstraints: []ForeignKeyConstraint{
{
EscapedName: "\"foo_fk_fk\"",
OwningTable: SchemaQualifiedName{
SchemaName: "schema_1",
EscapedName: "\"foo_fk\"",
},
ForeignTable: SchemaQualifiedName{
SchemaName: "schema_2",
EscapedName: "\"foo\"",
},
ConstraintDef: "FOREIGN KEY (id, version) REFERENCES schema_2.foo(id, version) ON UPDATE CASCADE ON DELETE CASCADE NOT VALID",
IsValid: false,
},
},
Sequences: []Sequence{
{
SchemaQualifiedName: SchemaQualifiedName{SchemaName: "schema_2", EscapedName: "\"foo_id_seq\""},
Owner: &SequenceOwner{
TableName: SchemaQualifiedName{SchemaName: "schema_2", EscapedName: "\"foo\""},
ColumnName: "id",
},
Type: "integer",
StartValue: 1,
Increment: 1,
MaxValue: 2147483647,
MinValue: 1,
CacheSize: 1,
Cycle: false,
},
{
SchemaQualifiedName: SchemaQualifiedName{SchemaName: "schema_1", EscapedName: "\"foobar_sequence\""},
Type: "bigint",
StartValue: 10,
Increment: 2,
MaxValue: 100,
MinValue: 5,
CacheSize: 5,
Cycle: true,
},
},
Indexes: []Index{
{
OwningRelName: SchemaQualifiedName{SchemaName: "schema_2", EscapedName: "\"foo\""},
OwningRelKind: RelKindOrdinaryTable,
Name: "foo_pkey",
Columns: []string{"id", "version"},
IsUnique: true,
Constraint: &IndexConstraint{Type: PkIndexConstraintType, EscapedConstraintName: "\"foo_pkey\"", ConstraintDef: "PRIMARY KEY (id, version)", IsLocal: true},
GetIndexDefStmt: "CREATE UNIQUE INDEX foo_pkey ON schema_2.foo USING btree (id, version)",
},
{
OwningRelName: SchemaQualifiedName{SchemaName: "schema_2", EscapedName: "\"foo\""},
OwningRelKind: RelKindOrdinaryTable,
Name: "some_idx",
Columns: []string{"created_at", "author"},
GetIndexDefStmt: "CREATE INDEX some_idx ON schema_2.foo USING btree (created_at DESC, author)",
},
{
OwningRelName: SchemaQualifiedName{SchemaName: "schema_2", EscapedName: "\"foo\""},
OwningRelKind: RelKindOrdinaryTable,
Name: "some_unique_idx",
Columns: []string{"content"},
IsUnique: true,
GetIndexDefStmt: "CREATE UNIQUE INDEX some_unique_idx ON schema_2.foo USING btree (content)",
},
{
OwningRelName: SchemaQualifiedName{SchemaName: "schema_2", EscapedName: "\"foo\""},
OwningRelKind: RelKindOrdinaryTable,
Name: "some_gin_idx",
Columns: []string{"author"},
GetIndexDefStmt: "CREATE INDEX some_gin_idx ON schema_2.foo USING gin (author schema_1.gin_trgm_ops)",
},
{
Name: "some_idx",
OwningRelName: SchemaQualifiedName{
SchemaName: "schema_1",
EscapedName: "\"foo_fk\"",
},
OwningRelKind: RelKindOrdinaryTable,
Columns: []string{
"id",
"version",
},
GetIndexDefStmt: "CREATE INDEX some_idx ON schema_1.foo_fk USING btree (id, version)",
},
},
Functions: []Function{
{
SchemaQualifiedName: SchemaQualifiedName{EscapedName: "\"function_with_dependencies\"(a integer, b integer)", SchemaName: "public"},
FunctionDef: "CREATE OR REPLACE FUNCTION public.function_with_dependencies(a integer, b integer)\n RETURNS integer\n LANGUAGE sql\n IMMUTABLE STRICT\nRETURN (schema_filtered_1.add(a, b) + schema_1.increment(a))\n",
Language: "sql",
DependsOnFunctions: []SchemaQualifiedName{
{EscapedName: "\"add\"(a integer, b integer)", SchemaName: "schema_filtered_1"},
{EscapedName: "\"increment\"(i integer)", SchemaName: "schema_1"},
},
},
{
SchemaQualifiedName: SchemaQualifiedName{EscapedName: "\"increment\"(i integer)", SchemaName: "schema_1"},
FunctionDef: "CREATE OR REPLACE FUNCTION schema_1.increment(i integer)\n RETURNS integer\n LANGUAGE plpgsql\nAS $function$\n\t\t\t\t\tBEGIN\n\t\t\t\t\t\t\tRETURN i + 1;\n\t\t\t\t\tEND;\n\t\t\t$function$\n",
Language: "plpgsql",
},
{
SchemaQualifiedName: SchemaQualifiedName{EscapedName: "\"increment_version\"()", SchemaName: "public"},
FunctionDef: "CREATE OR REPLACE FUNCTION public.increment_version()\n RETURNS trigger\n LANGUAGE plpgsql\nAS $function$\n\t\t\t\tBEGIN\n\t\t\t\t\tNEW.version = OLD.version + 1;\n\t\t\t\t\tRETURN NEW;\n\t\t\t\tEND;\n\t\t\t$function$\n",
Language: "plpgsql",
},
},
Procedures: []Procedure{
{
SchemaQualifiedName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"some_plpgsql_procedure\"(IN foobar numeric)"},
Def: "CREATE OR REPLACE PROCEDURE public.some_plpgsql_procedure(IN foobar numeric)\n LANGUAGE plpgsql\nAS $procedure$\n\t\t\t\tBEGIN\n\t\t\t\t\tRAISE NOTICE 'some notice';\n\t\t\t\tEND\n\t\t\t\t$procedure$\n",
},
{
SchemaQualifiedName: SchemaQualifiedName{SchemaName: "schema_2", EscapedName: "\"some_insert_procedure\"(IN a integer, IN b integer)"},
Def: "CREATE OR REPLACE PROCEDURE schema_2.some_insert_procedure(IN a integer, IN b integer)\n LANGUAGE sql\nBEGIN ATOMIC\n INSERT INTO schema_2.foo DEFAULT VALUES;\nEND\n",
},
},
Triggers: []Trigger{
{
EscapedName: "\"some_trigger\"",
OwningTable: SchemaQualifiedName{EscapedName: "\"foo\"", SchemaName: "schema_2"},
Function: SchemaQualifiedName{EscapedName: "\"increment_version\"()", SchemaName: "schema_filtered_1"},
GetTriggerDefStmt: "CREATE TRIGGER some_trigger BEFORE UPDATE ON schema_2.foo FOR EACH ROW WHEN ((old.* IS DISTINCT FROM new.*)) EXECUTE FUNCTION schema_filtered_1.increment_version()",
},
{
EscapedName: "\"some_constraint_trigger\"",
OwningTable: SchemaQualifiedName{EscapedName: "\"foo\"", SchemaName: "schema_2"},
Function: SchemaQualifiedName{EscapedName: "\"increment_version\"()", SchemaName: "schema_filtered_1"},
GetTriggerDefStmt: "CREATE CONSTRAINT TRIGGER some_constraint_trigger AFTER UPDATE ON schema_2.foo DEFERRABLE INITIALLY DEFERRED FOR EACH ROW WHEN ((old.* IS DISTINCT FROM new.*)) EXECUTE FUNCTION schema_filtered_1.increment_version()",
IsConstraint: true,
},
},
Views: []View{
{
SchemaQualifiedName: SchemaQualifiedName{
SchemaName: "schema_2",
EscapedName: "\"foo_view\"",
},
ViewDefinition: " SELECT foo.id,\n foo.author\n FROM schema_2.foo\n JOIN schema_1.foo_fk ON foo.id = foo_fk.id;",
Options: map[string]string{
"security_barrier": "true",
},
TableDependencies: []TableDependency{
{
SchemaQualifiedName: SchemaQualifiedName{SchemaName: "schema_1", EscapedName: `"foo_fk"`},
Columns: []string{"id"},
},
{
SchemaQualifiedName: SchemaQualifiedName{SchemaName: "schema_2", EscapedName: `"foo"`},
Columns: []string{"author", "id"},
},
},
},
},
},
},
{
name: "Partition test",
ddl: []string{`
CREATE TABLE foo (
id SERIAL CHECK (id > 0),
author TEXT COLLATE "C",
content TEXT DEFAULT '',
genre VARCHAR(256) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP CHECK (created_at > CURRENT_TIMESTAMP - interval '1 month'),
version INT GENERATED ALWAYS AS IDENTITY,
PRIMARY KEY (author, id)
) PARTITION BY LIST (author);
ALTER TABLE foo ADD CONSTRAINT author_check CHECK (author IS NOT NULL AND LENGTH(author) > 0) NOT VALID;
ALTER TABLE foo REPLICA IDENTITY FULL;
CREATE FUNCTION increment_version() RETURNS TRIGGER AS $$
BEGIN
NEW.version = OLD.version + 1;
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE TRIGGER some_trigger
BEFORE UPDATE ON foo
FOR EACH ROW
WHEN (OLD.* IS DISTINCT FROM NEW.*)
EXECUTE PROCEDURE increment_version();
CREATE TABLE foo_1 PARTITION OF foo(
content NOT NULL
) FOR VALUES IN ('some author 1');
ALTER TABLE foo_1 REPLICA IDENTITY NOTHING;
CREATE TABLE foo_2 PARTITION OF foo FOR VALUES IN ('some author 2');
CREATE TABLE foo_3 PARTITION OF foo FOR VALUES IN ('some author 3');
-- partitioned indexes
CREATE INDEX some_partitioned_idx ON foo USING hash(author);
CREATE UNIQUE INDEX some_unique_partitioned_idx ON foo(author, created_at DESC);
CREATE INDEX some_invalid_idx ON ONLY foo(author, genre);
-- local indexes
CREATE UNIQUE INDEX foo_1_local_idx ON foo_1(author, content);
CREATE UNIQUE INDEX foo_2_local_idx ON foo_2(author DESC, id);
CREATE UNIQUE INDEX foo_3_local_idx ON foo_3(author, created_at);
CREATE TRIGGER some_partition_trigger
BEFORE UPDATE ON foo_1
FOR EACH ROW
WHEN (OLD.* IS DISTINCT FROM NEW.*)
EXECUTE PROCEDURE increment_version();
CREATE TABLE foo_fk(
author TEXT,
id INT,
content TEXT DEFAULT ''
) PARTITION BY LIST (author);
CREATE TABLE foo_fk_1 PARTITION OF foo_fk FOR VALUES IN ('some author 1');
ALTER TABLE foo_fk ADD CONSTRAINT foo_fk_fk FOREIGN KEY (author, id) REFERENCES foo (author, id)
ON UPDATE CASCADE
ON DELETE CASCADE;
ALTER TABLE foo_fk_1 ADD CONSTRAINT foo_fk_1_fk FOREIGN KEY (author, content) REFERENCES foo_1 (author, content)
NOT VALID;
`},
expectedHash: "32c5a9c52dcfb15e",
expectedSchema: Schema{
NamedSchemas: []NamedSchema{
{Name: "public"},
},
Tables: []Table{
{
SchemaQualifiedName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo\""},
Columns: []Column{
{Name: "id", Type: "integer", Size: 4, Default: "nextval('foo_id_seq'::regclass)"},
{Name: "author", Type: "text", Size: -1, Collation: cCollation},
{Name: "content", Type: "text", Default: "''::text", IsNullable: true, Size: -1, Collation: defaultCollation},
{Name: "genre", Type: "character varying(256)", Size: -1, Collation: defaultCollation},
{Name: "created_at", Type: "timestamp without time zone", Default: "CURRENT_TIMESTAMP", Size: 8},
{Name: "version", Type: "integer", Size: 4,
Identity: &ColumnIdentity{Type: "a", MinValue: 1, MaxValue: 2147483647, StartValue: 1, Increment: 1, CacheSize: 1, Cycle: false},
},
},
CheckConstraints: []CheckConstraint{
{Name: "author_check", Expression: "((author IS NOT NULL) AND (length(author) > 0))", IsInheritable: true, KeyColumns: []string{"author"}},
{Name: "foo_created_at_check", Expression: "(created_at > (CURRENT_TIMESTAMP - '1 mon'::interval))", IsValid: true, IsInheritable: true, KeyColumns: []string{"created_at"}},
{Name: "foo_id_check", Expression: "(id > 0)", IsValid: true, IsInheritable: true, KeyColumns: []string{"id"}},
},
ReplicaIdentity: ReplicaIdentityFull,
PartitionKeyDef: "LIST (author)",
},
{
ParentTable: &SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo\""},
SchemaQualifiedName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_1\""},
Columns: []Column{
{Name: "id", Type: "integer", Size: 4, Default: "nextval('foo_id_seq'::regclass)"},
{Name: "author", Type: "text", Size: -1, Collation: cCollation},
{Name: "content", Type: "text", Default: "''::text", Size: -1, Collation: defaultCollation},
{Name: "genre", Type: "character varying(256)", Size: -1, Collation: defaultCollation},
{Name: "created_at", Type: "timestamp without time zone", Default: "CURRENT_TIMESTAMP", Size: 8},
{Name: "version", Type: "integer", Size: 4},
},
CheckConstraints: nil,
ReplicaIdentity: ReplicaIdentityNothing,
ForValues: "FOR VALUES IN ('some author 1')",
},
{
ParentTable: &SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo\""},
SchemaQualifiedName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_2\""},
Columns: []Column{
{Name: "id", Type: "integer", Size: 4, Default: "nextval('foo_id_seq'::regclass)"},
{Name: "author", Type: "text", Size: -1, Collation: cCollation},
{Name: "content", Type: "text", Default: "''::text", IsNullable: true, Size: -1, Collation: defaultCollation},
{Name: "genre", Type: "character varying(256)", Size: -1, Collation: defaultCollation},
{Name: "created_at", Type: "timestamp without time zone", Default: "CURRENT_TIMESTAMP", Size: 8},
{Name: "version", Type: "integer", IsNullable: false, Size: 4},
},
CheckConstraints: nil,
ReplicaIdentity: ReplicaIdentityDefault,
ForValues: "FOR VALUES IN ('some author 2')",
},
{
ParentTable: &SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo\""},
SchemaQualifiedName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_3\""},
Columns: []Column{
{Name: "id", Type: "integer", Size: 4, Default: "nextval('foo_id_seq'::regclass)"},
{Name: "author", Type: "text", Size: -1, Collation: cCollation},
{Name: "content", Type: "text", Default: "''::text", IsNullable: true, Size: -1, Collation: defaultCollation},
{Name: "genre", Type: "character varying(256)", Size: -1, Collation: defaultCollation},
{Name: "created_at", Type: "timestamp without time zone", Default: "CURRENT_TIMESTAMP", Size: 8},
{Name: "version", Type: "integer", IsNullable: false, Size: 4},
},
CheckConstraints: nil,
ReplicaIdentity: ReplicaIdentityDefault,
ForValues: "FOR VALUES IN ('some author 3')",
},
{
SchemaQualifiedName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_fk\""},
Columns: []Column{
{
Name: "author",
Type: "text",
Collation: SchemaQualifiedName{SchemaName: "pg_catalog", EscapedName: "\"default\""},
Default: "",
IsNullable: true,
Size: -1,
},
{
Name: "id",
Type: "integer",
Collation: SchemaQualifiedName{},
Default: "",
IsNullable: true,
Size: 4,
},
{
Name: "content",
Type: "text",
Collation: SchemaQualifiedName{SchemaName: "pg_catalog", EscapedName: "\"default\""},
Default: "''::text",
IsNullable: true,
Size: -1,
},
},
CheckConstraints: nil,
ReplicaIdentity: ReplicaIdentityDefault,
PartitionKeyDef: "LIST (author)",
ForValues: "",
},
{
SchemaQualifiedName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_fk_1\""},
ParentTable: &SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_fk\""},
Columns: []Column{
{
Name: "author",
Type: "text",
Collation: SchemaQualifiedName{SchemaName: "pg_catalog", EscapedName: "\"default\""},
Default: "",
IsNullable: true,
Size: -1,
},
{
Name: "id",
Type: "integer",
Collation: SchemaQualifiedName{},
Default: "",
IsNullable: true,
Size: 4,
},
{
Name: "content",
Type: "text",
Collation: SchemaQualifiedName{SchemaName: "pg_catalog", EscapedName: "\"default\""},
Default: "''::text",
IsNullable: true,
Size: -1,
},
},
CheckConstraints: nil,
ReplicaIdentity: ReplicaIdentityDefault,
PartitionKeyDef: "",
ForValues: "FOR VALUES IN ('some author 1')",
},
},
Indexes: []Index{
{
OwningRelName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo\""},
OwningRelKind: RelKindPartitionedTable,
Name: "foo_pkey", Columns: []string{"author", "id"}, IsUnique: true,
Constraint: &IndexConstraint{Type: PkIndexConstraintType, EscapedConstraintName: "\"foo_pkey\"", ConstraintDef: "PRIMARY KEY (author, id)", IsLocal: true},
GetIndexDefStmt: "CREATE UNIQUE INDEX foo_pkey ON ONLY public.foo USING btree (author, id)",
},
{
OwningRelName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo\""},
OwningRelKind: RelKindPartitionedTable,
Name: "some_partitioned_idx", Columns: []string{"author"},
GetIndexDefStmt: "CREATE INDEX some_partitioned_idx ON ONLY public.foo USING hash (author)",
},
{
OwningRelName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo\""},
OwningRelKind: RelKindPartitionedTable,
Name: "some_unique_partitioned_idx", Columns: []string{"author", "created_at"}, IsUnique: true,
GetIndexDefStmt: "CREATE UNIQUE INDEX some_unique_partitioned_idx ON ONLY public.foo USING btree (author, created_at DESC)",
},
{
OwningRelName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo\""},
OwningRelKind: RelKindPartitionedTable,
Name: "some_invalid_idx", Columns: []string{"author", "genre"}, IsInvalid: true, IsUnique: false,
GetIndexDefStmt: "CREATE INDEX some_invalid_idx ON ONLY public.foo USING btree (author, genre)",
},
// foo_1 indexes
{
OwningRelName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_1\""},
OwningRelKind: RelKindOrdinaryTable,
Name: "foo_1_author_idx", Columns: []string{"author"},
ParentIdx: &SchemaQualifiedName{SchemaName: "public", EscapedName: "\"some_partitioned_idx\""},
GetIndexDefStmt: "CREATE INDEX foo_1_author_idx ON public.foo_1 USING hash (author)",
},
{
OwningRelName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_1\""},
OwningRelKind: RelKindOrdinaryTable,
Name: "foo_1_author_created_at_idx", Columns: []string{"author", "created_at"}, IsUnique: true,
ParentIdx: &SchemaQualifiedName{SchemaName: "public", EscapedName: "\"some_unique_partitioned_idx\""},
GetIndexDefStmt: "CREATE UNIQUE INDEX foo_1_author_created_at_idx ON public.foo_1 USING btree (author, created_at DESC)",
},
{
OwningRelName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_1\""},
OwningRelKind: RelKindOrdinaryTable,
Name: "foo_1_local_idx", Columns: []string{"author", "content"}, IsUnique: true,
GetIndexDefStmt: "CREATE UNIQUE INDEX foo_1_local_idx ON public.foo_1 USING btree (author, content)",
},
{
OwningRelName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_1\""},
OwningRelKind: RelKindOrdinaryTable,
Name: "foo_1_pkey", Columns: []string{"author", "id"}, IsUnique: true,
ParentIdx: &SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_pkey\""},
Constraint: &IndexConstraint{Type: PkIndexConstraintType, EscapedConstraintName: "\"foo_1_pkey\"", ConstraintDef: "PRIMARY KEY (author, id)"},
GetIndexDefStmt: "CREATE UNIQUE INDEX foo_1_pkey ON public.foo_1 USING btree (author, id)",
},
// foo_2 indexes
{
OwningRelName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_2\""},
OwningRelKind: RelKindOrdinaryTable,
Name: "foo_2_author_idx", Columns: []string{"author"},
ParentIdx: &SchemaQualifiedName{SchemaName: "public", EscapedName: "\"some_partitioned_idx\""},
GetIndexDefStmt: "CREATE INDEX foo_2_author_idx ON public.foo_2 USING hash (author)",
},
{
OwningRelName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_2\""},
OwningRelKind: RelKindOrdinaryTable,
Name: "foo_2_author_created_at_idx", Columns: []string{"author", "created_at"}, IsUnique: true,
ParentIdx: &SchemaQualifiedName{SchemaName: "public", EscapedName: "\"some_unique_partitioned_idx\""},
GetIndexDefStmt: "CREATE UNIQUE INDEX foo_2_author_created_at_idx ON public.foo_2 USING btree (author, created_at DESC)",
},
{
OwningRelName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_2\""},
OwningRelKind: RelKindOrdinaryTable,
Name: "foo_2_local_idx", Columns: []string{"author", "id"}, IsUnique: true,
GetIndexDefStmt: "CREATE UNIQUE INDEX foo_2_local_idx ON public.foo_2 USING btree (author DESC, id)",
},
{
OwningRelName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_2\""},
OwningRelKind: RelKindOrdinaryTable,
Name: "foo_2_pkey", Columns: []string{"author", "id"}, IsUnique: true,
ParentIdx: &SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_pkey\""},
Constraint: &IndexConstraint{Type: PkIndexConstraintType, EscapedConstraintName: "\"foo_2_pkey\"", ConstraintDef: "PRIMARY KEY (author, id)"},
GetIndexDefStmt: "CREATE UNIQUE INDEX foo_2_pkey ON public.foo_2 USING btree (author, id)",
},
// foo_3 indexes
{
OwningRelName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_3\""},
OwningRelKind: RelKindOrdinaryTable,
Name: "foo_3_author_idx", Columns: []string{"author"},
ParentIdx: &SchemaQualifiedName{SchemaName: "public", EscapedName: "\"some_partitioned_idx\""},
GetIndexDefStmt: "CREATE INDEX foo_3_author_idx ON public.foo_3 USING hash (author)",
},
{
OwningRelName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_3\""},
OwningRelKind: RelKindOrdinaryTable,
Name: "foo_3_author_created_at_idx", Columns: []string{"author", "created_at"}, IsUnique: true,
ParentIdx: &SchemaQualifiedName{SchemaName: "public", EscapedName: "\"some_unique_partitioned_idx\""},
GetIndexDefStmt: "CREATE UNIQUE INDEX foo_3_author_created_at_idx ON public.foo_3 USING btree (author, created_at DESC)",
},
{
OwningRelName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_3\""},
OwningRelKind: RelKindOrdinaryTable,
Name: "foo_3_local_idx", Columns: []string{"author", "created_at"}, IsUnique: true,
GetIndexDefStmt: "CREATE UNIQUE INDEX foo_3_local_idx ON public.foo_3 USING btree (author, created_at)",
},
{
OwningRelName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_3\""},
OwningRelKind: RelKindOrdinaryTable,
Name: "foo_3_pkey", Columns: []string{"author", "id"}, IsUnique: true,
ParentIdx: &SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_pkey\""},
Constraint: &IndexConstraint{Type: PkIndexConstraintType, EscapedConstraintName: "\"foo_3_pkey\"", ConstraintDef: "PRIMARY KEY (author, id)"},
GetIndexDefStmt: "CREATE UNIQUE INDEX foo_3_pkey ON public.foo_3 USING btree (author, id)",
},
},
ForeignKeyConstraints: []ForeignKeyConstraint{
{
EscapedName: "\"foo_fk_fk\"",
OwningTable: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_fk\""},
ForeignTable: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo\""},
ConstraintDef: "FOREIGN KEY (author, id) REFERENCES foo(author, id) ON UPDATE CASCADE ON DELETE CASCADE",
IsValid: true,
},
{
EscapedName: "\"foo_fk_1_fk\"",
OwningTable: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_fk_1\""},
ForeignTable: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_1\""},
ConstraintDef: "FOREIGN KEY (author, content) REFERENCES foo_1(author, content) NOT VALID",
IsValid: false,
},
},
Sequences: []Sequence{
{
SchemaQualifiedName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_id_seq\""},
Owner: &SequenceOwner{
TableName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo\""},
ColumnName: "id",
},
Type: "integer",
StartValue: 1,
Increment: 1,
MaxValue: 2147483647,
MinValue: 1,
CacheSize: 1,
Cycle: false,
},
},
Functions: []Function{
{
SchemaQualifiedName: SchemaQualifiedName{EscapedName: "\"increment_version\"()", SchemaName: "public"},
FunctionDef: "CREATE OR REPLACE FUNCTION public.increment_version()\n RETURNS trigger\n LANGUAGE plpgsql\nAS $function$\n\t\t\t\tBEGIN\n\t\t\t\t\tNEW.version = OLD.version + 1;\n\t\t\t\t\tRETURN NEW;\n\t\t\t\tEND;\n\t\t\t$function$\n",
Language: "plpgsql",
},
},
Triggers: []Trigger{
{
EscapedName: "\"some_trigger\"",
OwningTable: SchemaQualifiedName{EscapedName: "\"foo\"", SchemaName: "public"},
Function: SchemaQualifiedName{EscapedName: "\"increment_version\"()", SchemaName: "public"},
GetTriggerDefStmt: "CREATE TRIGGER some_trigger BEFORE UPDATE ON public.foo FOR EACH ROW WHEN ((old.* IS DISTINCT FROM new.*)) EXECUTE FUNCTION increment_version()",
},
{
EscapedName: "\"some_partition_trigger\"",
OwningTable: SchemaQualifiedName{EscapedName: "\"foo_1\"", SchemaName: "public"},
Function: SchemaQualifiedName{EscapedName: "\"increment_version\"()", SchemaName: "public"},
GetTriggerDefStmt: "CREATE TRIGGER some_partition_trigger BEFORE UPDATE ON public.foo_1 FOR EACH ROW WHEN ((old.* IS DISTINCT FROM new.*)) EXECUTE FUNCTION increment_version()",
},
},
},
},
{
name: "Partition test local primary key",
ddl: []string{`
CREATE TABLE foo (
id INTEGER,
author TEXT
) PARTITION BY LIST (author);
CREATE TABLE foo_1 PARTITION OF foo(
PRIMARY KEY (author, id)
) FOR VALUES IN ('some author 1');
`},
expectedSchema: Schema{
NamedSchemas: []NamedSchema{
{Name: "public"},
},
Tables: []Table{
{
SchemaQualifiedName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo\""},
Columns: []Column{
{Name: "id", Type: "integer", IsNullable: true, Size: 4},
{Name: "author", Type: "text", IsNullable: true, Size: -1, Collation: defaultCollation},
},
CheckConstraints: nil,
ReplicaIdentity: ReplicaIdentityDefault,
PartitionKeyDef: "LIST (author)",
},
{
ParentTable: &SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo\""},
SchemaQualifiedName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_1\""},
Columns: []Column{
{Name: "id", Type: "integer", Size: 4},
{Name: "author", Type: "text", Size: -1, Collation: defaultCollation},
},
CheckConstraints: nil,
ReplicaIdentity: ReplicaIdentityDefault,
ForValues: "FOR VALUES IN ('some author 1')",
},
},
Indexes: []Index{
{
OwningRelName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo_1\""},
OwningRelKind: RelKindOrdinaryTable,
Name: "foo_1_pkey", Columns: []string{"author", "id"}, IsUnique: true,
Constraint: &IndexConstraint{Type: PkIndexConstraintType, EscapedConstraintName: "\"foo_1_pkey\"", ConstraintDef: "PRIMARY KEY (author, id)", IsLocal: true},
GetIndexDefStmt: "CREATE UNIQUE INDEX foo_1_pkey ON public.foo_1 USING btree (author, id)",
},
},
},
},
{
name: "Common columns",
ddl: []string{`
CREATE TABLE foo (
varchar VARCHAR(128) NOT NULL DEFAULT '',
text TEXT NOT NULL DEFAULT '',
bool BOOLEAN NOT NULL DEFAULT False,
blob BYTEA NOT NULL DEFAULT '',
smallint SMALLINT NOT NULL DEFAULT 0,
real REAL NOT NULL DEFAULT 0.0,
double_precision DOUBLE PRECISION NOT NULL DEFAULT 0.0,
integer INTEGER NOT NULL DEFAULT 0,
big_integer BIGINT NOT NULL DEFAULT 0,
decimal DECIMAL(65, 10) NOT NULL DEFAULT 0.0,
serial SERIAL NOT NULL,
identity_always BIGINT GENERATED ALWAYS AS IDENTITY ( MINVALUE 2 MAXVALUE 9 START 3 INCREMENT 4 CACHE 5 NO CYCLE ),
identity_default BIGINT GENERATED BY DEFAULT AS IDENTITY ( MINVALUE 20 MAXVALUE 90 START 30 INCREMENT 40 CACHE 50 CYCLE )
);
`},
expectedSchema: Schema{
NamedSchemas: []NamedSchema{
{Name: "public"},
},
Tables: []Table{
{
SchemaQualifiedName: SchemaQualifiedName{SchemaName: "public", EscapedName: "\"foo\""},
Columns: []Column{
{Name: "varchar", Type: "character varying(128)", Default: "''::character varying", Size: -1, Collation: defaultCollation},
{Name: "text", Type: "text", Default: "''::text", Size: -1, Collation: defaultCollation},
{Name: "bool", Type: "boolean", Default: "false", Size: 1},
{Name: "blob", Type: "bytea", Default: `'\x'::bytea`, Size: -1},
{Name: "smallint", Type: "smallint", Default: "0", Size: 2},
{Name: "real", Type: "real", Default: "0.0", Size: 4},
{Name: "double_precision", Type: "double precision", Default: "0.0", Size: 8},
{Name: "integer", Type: "integer", Default: "0", Size: 4},
{Name: "big_integer", Type: "bigint", Default: "0", Size: 8},
{Name: "decimal", Type: "numeric(65,10)", Default: "0.0", Size: -1},
{Name: "serial", Type: "integer", Collation: SchemaQualifiedName{}, Default: "nextval('foo_serial_seq'::regclass)", IsNullable: false, Size: 4},
{Name: "identity_always", Type: "bigint", Size: 8,
Identity: &ColumnIdentity{
Type: ColumnIdentityTypeAlways,
MinValue: 2,
MaxValue: 9,
StartValue: 3,
Increment: 4,
CacheSize: 5,
Cycle: false,
},
},