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