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