Test 'bit varying' without size for Pg
[dbsrgits/DBIx-Class-Schema-Loader.git] / t / 10_03pg_common.t
1 use DBIx::Class::Schema::Loader::Optional::Dependencies
2     -skip_all_without => 'test_rdbms_pg';
3
4 use strict;
5 use warnings;
6 use utf8;
7 use DBIx::Class::Schema::Loader 'make_schema_at';
8 use DBIx::Class::Schema::Loader::Utils qw/no_warnings slurp_file/;
9 use Test::More;
10 use Test::Exception;
11 use Try::Tiny;
12 use File::Path 'rmtree';
13 use namespace::clean;
14
15 use lib qw(t/lib);
16 use dbixcsl_common_tests ();
17 use dbixcsl_test_dir '$tdir';
18
19 use constant EXTRA_DUMP_DIR => "$tdir/pg_extra_dump";
20
21 my $dsn      = $ENV{DBICTEST_PG_DSN} || '';
22 my $user     = $ENV{DBICTEST_PG_USER} || '';
23 my $password = $ENV{DBICTEST_PG_PASS} || '';
24
25 dbixcsl_common_tests->new(
26     vendor      => 'Pg',
27     auto_inc_pk => 'SERIAL NOT NULL PRIMARY KEY',
28     dsn         => $dsn,
29     user        => $user,
30     password    => $password,
31     loader_options  => { preserve_case => 1 },
32     connect_info_opts => {
33         pg_enable_utf8 => 1,
34         on_connect_do  => [ 'SET client_min_messages=WARNING' ],
35     },
36     quote_char  => '"',
37     default_is_deferrable => 0,
38     default_on_clause => 'NO ACTION',
39     data_types  => {
40         # http://www.postgresql.org/docs/7.4/interactive/datatype.html
41         #
42         # Numeric Types
43         boolean     => { data_type => 'boolean' },
44         bool        => { data_type => 'boolean' },
45         'bool default false'
46                     => { data_type => 'boolean', default_value => \'false' },
47         'bool default true'
48                     => { data_type => 'boolean', default_value => \'true' },
49         'bool default 0::bool'
50                     => { data_type => 'boolean', default_value => \'false' },
51         'bool default 1::bool'
52                     => { data_type => 'boolean', default_value => \'true' },
53
54         bigint      => { data_type => 'bigint' },
55         int8        => { data_type => 'bigint' },
56         bigserial   => { data_type => 'bigint', is_auto_increment => 1 },
57         serial8     => { data_type => 'bigint', is_auto_increment => 1 },
58         integer     => { data_type => 'integer' },
59         int         => { data_type => 'integer' },
60         int4        => { data_type => 'integer' },
61         serial      => { data_type => 'integer', is_auto_increment => 1 },
62         serial4     => { data_type => 'integer', is_auto_increment => 1 },
63         smallint    => { data_type => 'smallint' },
64         int2        => { data_type => 'smallint' },
65
66         money       => { data_type => 'money' },
67
68         'double precision' => { data_type => 'double precision' },
69         float8             => { data_type => 'double precision' },
70         real               => { data_type => 'real' },
71         float4             => { data_type => 'real' },
72         'float(24)'        => { data_type => 'real' },
73         'float(25)'        => { data_type => 'double precision' },
74         'float(53)'        => { data_type => 'double precision' },
75         float              => { data_type => 'double precision' },
76
77         numeric            => { data_type => 'numeric' },
78         decimal            => { data_type => 'numeric' },
79         'numeric(6,3)'     => { data_type => 'numeric', size => [6,3] },
80         'decimal(6,3)'     => { data_type => 'numeric', size => [6,3] },
81
82         # Bit String Types
83         'bit varying(2)' => { data_type => 'varbit', size => 2 },
84         'varbit(2)'      => { data_type => 'varbit', size => 2 },
85         'bit varying'    => { data_type => 'varbit' },
86         'varbit'         => { data_type => 'varbit' },
87         bit              => { data_type => 'bit', size => 1 },
88         'bit(3)'         => { data_type => 'bit', size => 3 },
89
90         # Network Types
91         inet    => { data_type => 'inet' },
92         cidr    => { data_type => 'cidr' },
93         macaddr => { data_type => 'macaddr' },
94
95         # Geometric Types
96         point   => { data_type => 'point' },
97         line    => { data_type => 'line' },
98         lseg    => { data_type => 'lseg' },
99         box     => { data_type => 'box' },
100         path    => { data_type => 'path' },
101         polygon => { data_type => 'polygon' },
102         circle  => { data_type => 'circle' },
103
104         # Character Types
105         'character varying(2)'           => { data_type => 'varchar', size => 2 },
106         'varchar(2)'                     => { data_type => 'varchar', size => 2 },
107         'character(2)'                   => { data_type => 'char', size => 2 },
108         'char(2)'                        => { data_type => 'char', size => 2 },
109         # check that default null is correctly rewritten
110         'char(3) default null'           => { data_type => 'char', size => 3,
111                                               default_value => \'null' },
112         'character'                      => { data_type => 'char', size => 1 },
113         'char'                           => { data_type => 'char', size => 1 },
114         text                             => { data_type => 'text' },
115         # varchar with no size has unlimited size, we rewrite to 'text'
116         varchar                          => { data_type => 'text',
117                                               original => { data_type => 'varchar' } },
118         # check default null again (to make sure ref is safe)
119         'varchar(3) default null'        => { data_type => 'varchar', size => 3,
120                                               default_value => \'null' },
121
122         # Datetime Types
123         date                             => { data_type => 'date' },
124         interval                         => { data_type => 'interval' },
125         'interval(0)'                    => { data_type => 'interval', size => 0 },
126         'interval(2)'                    => { data_type => 'interval', size => 2 },
127         time                             => { data_type => 'time' },
128         'time(0)'                        => { data_type => 'time', size => 0 },
129         'time(2)'                        => { data_type => 'time', size => 2 },
130         'time without time zone'         => { data_type => 'time' },
131         'time(0) without time zone'      => { data_type => 'time', size => 0 },
132         'time with time zone'            => { data_type => 'time with time zone' },
133         'time(0) with time zone'         => { data_type => 'time with time zone', size => 0 },
134         'time(2) with time zone'         => { data_type => 'time with time zone', size => 2 },
135         timestamp                        => { data_type => 'timestamp' },
136         'timestamp default now()'        => { data_type => 'timestamp',
137                                               default_value => \'current_timestamp',
138                                               original => { default_value => \'now()' } },
139         'timestamp(0)'                   => { data_type => 'timestamp', size => 0 },
140         'timestamp(2)'                   => { data_type => 'timestamp', size => 2 },
141         'timestamp without time zone'    => { data_type => 'timestamp' },
142         'timestamp(0) without time zone' => { data_type => 'timestamp', size => 0 },
143         'timestamp(2) without time zone' => { data_type => 'timestamp', size => 2 },
144
145         'timestamp with time zone'       => { data_type => 'timestamp with time zone' },
146         'timestamp(0) with time zone'    => { data_type => 'timestamp with time zone', size => 0 },
147         'timestamp(2) with time zone'    => { data_type => 'timestamp with time zone', size => 2 },
148
149         # Blob Types
150         bytea => { data_type => 'bytea' },
151
152         # Enum Types
153         pg_loader_test_enum => { data_type => 'enum',
154                                  extra => { custom_type_name => 'pg_loader_test_enum',
155                                             list => [ qw/foo bar baz/] } },
156     },
157     pre_create => [
158         q{
159             CREATE TYPE pg_loader_test_enum AS ENUM (
160                 'foo', 'bar', 'baz'
161             )
162         },
163     ],
164     extra       => {
165         create => [
166             q{
167                 CREATE SCHEMA dbicsl_test
168             },
169             q{
170                 CREATE SEQUENCE dbicsl_test.myseq
171             },
172             q{
173                 CREATE TABLE pg_loader_test1 (
174                     id INTEGER NOT NULL DEFAULT nextval('dbicsl_test.myseq') PRIMARY KEY,
175                     value VARCHAR(100)
176                 )
177             },
178             qq{
179                 COMMENT ON TABLE pg_loader_test1 IS 'The\15\12Table ∑'
180             },
181             qq{
182                 COMMENT ON COLUMN pg_loader_test1.value IS 'The\15\12Column'
183             },
184             q{
185                 CREATE TABLE pg_loader_test2 (
186                     id SERIAL PRIMARY KEY,
187                     value VARCHAR(100)
188                 )
189             },
190             q{
191                 COMMENT ON TABLE pg_loader_test2 IS 'very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long comment'
192             },
193             q{
194                 CREATE SCHEMA "dbicsl-test"
195             },
196             q{
197                 CREATE TABLE "dbicsl-test".pg_loader_test4 (
198                     id SERIAL PRIMARY KEY,
199                     value VARCHAR(100)
200                 )
201             },
202             q{
203                 CREATE TABLE "dbicsl-test".pg_loader_test5 (
204                     id SERIAL PRIMARY KEY,
205                     value VARCHAR(100),
206                     four_id INTEGER REFERENCES "dbicsl-test".pg_loader_test4 (id),
207                     CONSTRAINT loader_test5_uniq UNIQUE (four_id)
208                 )
209             },
210             q{
211                 CREATE SCHEMA "dbicsl.test"
212             },
213             q{
214                 CREATE TABLE "dbicsl.test".pg_loader_test5 (
215                     pk SERIAL PRIMARY KEY,
216                     value VARCHAR(100),
217                     four_id INTEGER REFERENCES "dbicsl-test".pg_loader_test4 (id),
218                     CONSTRAINT loader_test5_uniq UNIQUE (four_id)
219                 )
220             },
221             q{
222                 CREATE TABLE "dbicsl.test".pg_loader_test6 (
223                     id SERIAL PRIMARY KEY,
224                     value VARCHAR(100),
225                     pg_loader_test4_id INTEGER REFERENCES "dbicsl-test".pg_loader_test4 (id)
226                 )
227             },
228             q{
229                 CREATE TABLE "dbicsl.test".pg_loader_test7 (
230                     id SERIAL PRIMARY KEY,
231                     value VARCHAR(100),
232                     six_id INTEGER UNIQUE REFERENCES "dbicsl.test".pg_loader_test6 (id)
233                 )
234             },
235             q{
236                 CREATE TABLE "dbicsl-test".pg_loader_test8 (
237                     id SERIAL PRIMARY KEY,
238                     value VARCHAR(100),
239                     pg_loader_test7_id INTEGER REFERENCES "dbicsl.test".pg_loader_test7 (id)
240                 )
241             },
242             # 4 through 8 are used for the multi-schema tests
243             q{
244                 create table pg_loader_test9 (
245                     id bigserial primary key
246                 )
247             },
248             q{
249                 create table pg_loader_test10 (
250                     id bigserial primary key,
251                     nine_id int,
252                     foreign key (nine_id) references pg_loader_test9(id)
253                         on delete restrict on update set null deferrable
254                 )
255             },
256             q{
257                 create view pg_loader_test11 as
258                     select * from pg_loader_test1
259             },
260             q{
261                 create table pg_loader_test12 (
262                     id integer not null,
263                     value integer,
264                     active boolean,
265                     name text
266                 )
267             },
268             q{
269                 create unique index uniq_id_lc_name on pg_loader_test12 (
270                     id, lower(name)
271                 )
272             },
273             q{
274                 create unique index uniq_uc_name_id on pg_loader_test12 (
275                     upper(name), id
276                 )
277             },
278             q{
279                 create unique index pg_loader_test12_value on pg_loader_test12 (
280                     value
281                 )
282             },
283             q{
284                 create unique index pg_loader_test12_name_active on pg_loader_test12 (
285                     name
286                 ) where active
287             },
288         ],
289         pre_drop_ddl => [
290             'DROP SCHEMA dbicsl_test CASCADE',
291             'DROP SCHEMA "dbicsl-test" CASCADE',
292             'DROP SCHEMA "dbicsl.test" CASCADE',
293             'DROP TYPE pg_loader_test_enum',
294             'DROP VIEW pg_loader_test11',
295         ],
296         drop  => [ qw/pg_loader_test1 pg_loader_test2 pg_loader_test9 pg_loader_test10 pg_loader_test12/ ],
297         count => 10 + 30 * 2,
298         run   => sub {
299             my ($schema, $monikers, $classes) = @_;
300
301             is $schema->source($monikers->{pg_loader_test1})->column_info('id')->{sequence},
302                 'dbicsl_test.myseq',
303                 'qualified sequence detected';
304
305             my $class    = $classes->{pg_loader_test1};
306             my $filename = $schema->loader->get_dump_filename($class);
307
308             my $code = slurp_file $filename;
309
310             like $code, qr/^=head1 NAME\n\n^$class - The\nTable ∑\n\n^=cut\n/m,
311                 'table comment';
312
313             like $code, qr/^=head2 value\n\n(.+:.+\n)+\nThe\nColumn\n\n/m,
314                 'column comment and attrs';
315
316             $class    = $classes->{pg_loader_test2};
317             $filename = $schema->loader->get_dump_filename($class);
318
319             $code = slurp_file $filename;
320
321             like $code, qr/^=head1 NAME\n\n^$class\n\n=head1 DESCRIPTION\n\n^very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long comment\n\n^=cut\n/m,
322                 'long table comment is in DESCRIPTION';
323
324             # test on delete/update fk clause introspection
325             ok ((my $rel_info = $schema->source('PgLoaderTest10')->relationship_info('nine')),
326                 'got rel info');
327
328             is $rel_info->{attrs}{on_delete}, 'RESTRICT',
329                 'ON DELETE clause introspected correctly';
330
331             is $rel_info->{attrs}{on_update}, 'SET NULL',
332                 'ON UPDATE clause introspected correctly';
333
334             is $rel_info->{attrs}{is_deferrable}, 1,
335                 'DEFERRABLE clause introspected correctly';
336
337             foreach my $db_schema (['dbicsl-test', 'dbicsl.test'], '%') {
338                 lives_and {
339                     rmtree EXTRA_DUMP_DIR;
340
341                     my @warns;
342                     local $SIG{__WARN__} = sub {
343                         push @warns, $_[0] unless $_[0] =~ /\bcollides\b/;
344                     };
345
346                     make_schema_at(
347                         'PGMultiSchema',
348                         {
349                             naming => 'current',
350                             db_schema => $db_schema,
351                             preserve_case => 1,
352                             dump_directory => EXTRA_DUMP_DIR,
353                             quiet => 1,
354                         },
355                         [ $dsn, $user, $password, {
356                             on_connect_do  => [ 'SET client_min_messages=WARNING' ],
357                         } ],
358                     );
359
360                     diag join "\n", @warns if @warns;
361
362                     is @warns, 0;
363                 } 'dumped schema for "dbicsl-test" and "dbicsl.test" schemas with no warnings';
364
365                 my ($test_schema, $rsrc, $rs, $row, %uniqs, $rel_info);
366
367                 lives_and {
368                     ok $test_schema = PGMultiSchema->connect($dsn, $user, $password, {
369                         on_connect_do  => [ 'SET client_min_messages=WARNING' ],
370                     });
371                 } 'connected test schema';
372
373                 lives_and {
374                     ok $rsrc = $test_schema->source('PgLoaderTest4');
375                 } 'got source for table in schema name with dash';
376
377                 is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
378                     'column in schema name with dash';
379
380                 is try { $rsrc->column_info('value')->{data_type} }, 'varchar',
381                     'column in schema name with dash';
382
383                 is try { $rsrc->column_info('value')->{size} }, 100,
384                     'column in schema name with dash';
385
386                 lives_and {
387                     ok $rs = $test_schema->resultset('PgLoaderTest4');
388                 } 'got resultset for table in schema name with dash';
389
390                 lives_and {
391                     ok $row = $rs->create({ value => 'foo' });
392                 } 'executed SQL on table in schema name with dash';
393
394                 $rel_info = try { $rsrc->relationship_info('dbicsl_dash_test_pg_loader_test5') };
395
396                 is_deeply $rel_info->{cond}, {
397                     'foreign.four_id' => 'self.id'
398                 }, 'relationship in schema name with dash';
399
400                 is $rel_info->{attrs}{accessor}, 'single',
401                     'relationship in schema name with dash';
402
403                 is $rel_info->{attrs}{join_type}, 'LEFT',
404                     'relationship in schema name with dash';
405
406                 lives_and {
407                     ok $rsrc = $test_schema->source('DbicslDashTestPgLoaderTest5');
408                 } 'got source for table in schema name with dash';
409
410                 %uniqs = try { $rsrc->unique_constraints };
411
412                 is keys %uniqs, 2,
413                     'got unique and primary constraint in schema name with dash';
414
415                 delete $uniqs{primary};
416
417                 is_deeply(
418                     (values %uniqs)[0], ['four_id'],
419                     'unique constraint is correct in schema name with dash'
420                 );
421
422                 lives_and {
423                     ok $rsrc = $test_schema->source('PgLoaderTest6');
424                 } 'got source for table in schema name with dot';
425
426                 is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
427                     'column in schema name with dot introspected correctly';
428
429                 is try { $rsrc->column_info('value')->{data_type} }, 'varchar',
430                     'column in schema name with dot introspected correctly';
431
432                 is try { $rsrc->column_info('value')->{size} }, 100,
433                     'column in schema name with dot introspected correctly';
434
435                 lives_and {
436                     ok $rs = $test_schema->resultset('PgLoaderTest6');
437                 } 'got resultset for table in schema name with dot';
438
439                 lives_and {
440                     ok $row = $rs->create({ value => 'foo' });
441                 } 'executed SQL on table in schema name with dot';
442
443                 $rel_info = try { $rsrc->relationship_info('pg_loader_test7') };
444
445                 is_deeply $rel_info->{cond}, {
446                     'foreign.six_id' => 'self.id'
447                 }, 'relationship in schema name with dot';
448
449                 is $rel_info->{attrs}{accessor}, 'single',
450                     'relationship in schema name with dot';
451
452                 is $rel_info->{attrs}{join_type}, 'LEFT',
453                     'relationship in schema name with dot';
454
455                 lives_and {
456                     ok $rsrc = $test_schema->source('PgLoaderTest7');
457                 } 'got source for table in schema name with dot';
458
459                 %uniqs = try { $rsrc->unique_constraints };
460
461                 is keys %uniqs, 2,
462                     'got unique and primary constraint in schema name with dot';
463
464                 delete $uniqs{primary};
465
466                 is_deeply(
467                     (values %uniqs)[0], ['six_id'],
468                     'unique constraint is correct in schema name with dot'
469                 );
470
471                 lives_and {
472                     ok $test_schema->source('PgLoaderTest6')
473                         ->has_relationship('pg_loader_test4');
474                 } 'cross-schema relationship in multi-db_schema';
475
476                 lives_and {
477                     ok $test_schema->source('PgLoaderTest4')
478                         ->has_relationship('pg_loader_test6s');
479                 } 'cross-schema relationship in multi-db_schema';
480
481                 lives_and {
482                     ok $test_schema->source('PgLoaderTest8')
483                         ->has_relationship('pg_loader_test7');
484                 } 'cross-schema relationship in multi-db_schema';
485
486                 lives_and {
487                     ok $test_schema->source('PgLoaderTest7')
488                         ->has_relationship('pg_loader_test8s');
489                 } 'cross-schema relationship in multi-db_schema';
490             }
491
492             # test that views are marked as such
493             isa_ok $schema->resultset($monikers->{pg_loader_test11})->result_source, 'DBIx::Class::ResultSource::View',
494                 'views have table_class set correctly';
495
496             is_deeply
497                 { $schema->source($monikers->{pg_loader_test12})->unique_constraints },
498                 { pg_loader_test12_value => ['value'] },
499                 'unique indexes are dumped correctly';
500         },
501     },
502 )->run_tests();
503
504 END {
505     rmtree EXTRA_DUMP_DIR unless $ENV{SCHEMA_LOADER_TESTS_NOCLEANUP};
506 }
507 # vim:et sw=4 sts=4 tw=0: