b93c0920897bf0ab8f466ec57066c9a1c9bea572
[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         'character'                      => { data_type => 'char', size => 1 },
104         'char'                           => { data_type => 'char', size => 1 },
105         text                             => { data_type => 'text' },
106         # varchar with no size has unlimited size, we rewrite to 'text'
107         varchar                          => { data_type => 'text',
108                                               original => { data_type => 'varchar' } },
109         # check that default NULL is correctly rewritten
110         'varchar(3) default NULL'        => { data_type => 'varchar', size => 3,
111                                               default_value => \'null' },
112
113         # Datetime Types
114         date                             => { data_type => 'date' },
115         interval                         => { data_type => 'interval' },
116         'interval(2)'                    => { data_type => 'interval', size => 2 },
117         time                             => { data_type => 'time' },
118         'time(2)'                        => { data_type => 'time', size => 2 },
119         'time without time zone'         => { data_type => 'time' },
120         'time(2) without time zone'      => { data_type => 'time', size => 2 },
121         'time with time zone'            => { data_type => 'time with time zone' },
122         'time(2) with time zone'         => { data_type => 'time with time zone', size => 2 },
123         timestamp                        => { data_type => 'timestamp' },
124         'timestamp default now()'
125                                          => { data_type => 'timestamp', default_value => \'current_timestamp',
126                                               original => { default_value => \'now()' } },
127         'timestamp(2)'                   => { data_type => 'timestamp', size => 2 },
128         'timestamp without time zone'    => { data_type => 'timestamp' },
129         'timestamp(2) without time zone' => { data_type => 'timestamp', size => 2 },
130
131         'timestamp with time zone'       => { data_type => 'timestamp with time zone' },
132         'timestamp(2) with time zone'    => { data_type => 'timestamp with time zone', size => 2 },
133
134         # Blob Types
135         bytea => { data_type => 'bytea' },
136
137         # Enum Types
138         pg_loader_test_enum => { data_type => 'enum', extra => { custom_type_name => 'pg_loader_test_enum',
139                                                                  list => [ qw/foo bar baz/] } },
140     },
141     pre_create => [
142         q{
143             CREATE TYPE pg_loader_test_enum AS ENUM (
144                 'foo', 'bar', 'baz'
145             )
146         },
147     ],
148     extra       => {
149         create => [
150             q{
151                 CREATE SCHEMA dbicsl_test
152             },
153             q{
154                 CREATE SEQUENCE dbicsl_test.myseq
155             },
156             q{
157                 CREATE TABLE pg_loader_test1 (
158                     id INTEGER NOT NULL DEFAULT nextval('dbicsl_test.myseq') PRIMARY KEY,
159                     value VARCHAR(100)
160                 )
161             },
162             qq{
163                 COMMENT ON TABLE pg_loader_test1 IS 'The\15\12Table ∑'
164             },
165             qq{
166                 COMMENT ON COLUMN pg_loader_test1.value IS 'The\15\12Column'
167             },
168             q{
169                 CREATE TABLE pg_loader_test2 (
170                     id SERIAL PRIMARY KEY,
171                     value VARCHAR(100)
172                 )
173             },
174             q{
175                 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'
176             },
177             q{
178                 CREATE SCHEMA "dbicsl-test"
179             },
180             q{
181                 CREATE TABLE "dbicsl-test".pg_loader_test4 (
182                     id SERIAL PRIMARY KEY,
183                     value VARCHAR(100)
184                 )
185             },
186             q{
187                 CREATE TABLE "dbicsl-test".pg_loader_test5 (
188                     id SERIAL PRIMARY KEY,
189                     value VARCHAR(100),
190                     four_id INTEGER UNIQUE REFERENCES "dbicsl-test".pg_loader_test4 (id)
191                 )
192             },
193             q{
194                 CREATE SCHEMA "dbicsl.test"
195             },
196             q{
197                 CREATE TABLE "dbicsl.test".pg_loader_test6 (
198                     id SERIAL PRIMARY KEY,
199                     value VARCHAR(100),
200                     pg_loader_test4_id INTEGER REFERENCES "dbicsl-test".pg_loader_test4 (id)
201                 )
202             },
203             q{
204                 CREATE TABLE "dbicsl.test".pg_loader_test7 (
205                     id SERIAL PRIMARY KEY,
206                     value VARCHAR(100),
207                     six_id INTEGER UNIQUE REFERENCES "dbicsl.test".pg_loader_test6 (id)
208                 )
209             },
210             q{
211                 CREATE TABLE "dbicsl-test".pg_loader_test8 (
212                     id SERIAL PRIMARY KEY,
213                     value VARCHAR(100),
214                     pg_loader_test7_id INTEGER REFERENCES "dbicsl.test".pg_loader_test7 (id)
215                 )
216             },
217         ],
218         pre_drop_ddl => [
219             'DROP SCHEMA dbicsl_test CASCADE',
220             'DROP SCHEMA "dbicsl-test" CASCADE',
221             'DROP SCHEMA "dbicsl.test" CASCADE',
222             'DROP TYPE pg_loader_test_enum',
223         ],
224         drop  => [ qw/ pg_loader_test1 pg_loader_test2 / ],
225         count => 4 + 28 * 2,
226         run   => sub {
227             my ($schema, $monikers, $classes) = @_;
228
229             is $schema->source($monikers->{pg_loader_test1})->column_info('id')->{sequence},
230                 'dbicsl_test.myseq',
231                 'qualified sequence detected';
232
233             my $class    = $classes->{pg_loader_test1};
234             my $filename = $schema->loader->get_dump_filename($class);
235
236             my $code = slurp_file $filename;
237
238             like $code, qr/^=head1 NAME\n\n^$class - The\nTable ∑\n\n^=cut\n/m,
239                 'table comment';
240
241             like $code, qr/^=head2 value\n\n(.+:.+\n)+\nThe\nColumn\n\n/m,
242                 'column comment and attrs';
243
244             $class    = $classes->{pg_loader_test2};
245             $filename = $schema->loader->get_dump_filename($class);
246
247             $code = slurp_file $filename;
248
249             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,
250                 'long table comment is in DESCRIPTION';
251
252             foreach my $db_schema (['dbicsl-test', 'dbicsl.test'], '%') {
253                 lives_and {
254                     rmtree EXTRA_DUMP_DIR;
255
256                     my @warns;
257                     local $SIG{__WARN__} = sub {
258                         push @warns, $_[0] unless $_[0] =~ /\bcollides\b/;
259                     };
260
261                     make_schema_at(
262                         'PGMultiSchema',
263                         {
264                             naming => 'current',
265                             db_schema => $db_schema,
266                             preserve_case => 1,
267                             dump_directory => EXTRA_DUMP_DIR,
268                             quiet => 1,
269                         },
270                         [ $dsn, $user, $password, {
271                             on_connect_do  => [ 'SET client_min_messages=WARNING' ],
272                         } ],
273                     );
274
275                     diag join "\n", @warns if @warns;
276
277                     is @warns, 0;
278                 } 'dumped schema for "dbicsl-test" and "dbicsl.test" schemas with no warnings';
279
280                 my ($test_schema, $rsrc, $rs, $row, %uniqs, $rel_info);
281
282                 lives_and {
283                     ok $test_schema = PGMultiSchema->connect($dsn, $user, $password, {
284                         on_connect_do  => [ 'SET client_min_messages=WARNING' ],
285                     });
286                 } 'connected test schema';
287
288                 lives_and {
289                     ok $rsrc = $test_schema->source('PgLoaderTest4');
290                 } 'got source for table in schema name with dash';
291
292                 is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
293                     'column in schema name with dash';
294
295                 is try { $rsrc->column_info('value')->{data_type} }, 'varchar',
296                     'column in schema name with dash';
297
298                 is try { $rsrc->column_info('value')->{size} }, 100,
299                     'column in schema name with dash';
300
301                 lives_and {
302                     ok $rs = $test_schema->resultset('PgLoaderTest4');
303                 } 'got resultset for table in schema name with dash';
304
305                 lives_and {
306                     ok $row = $rs->create({ value => 'foo' });
307                 } 'executed SQL on table in schema name with dash';
308
309                 $rel_info = try { $rsrc->relationship_info('pg_loader_test5') };
310
311                 is_deeply $rel_info->{cond}, {
312                     'foreign.four_id' => 'self.id'
313                 }, 'relationship in schema name with dash';
314
315                 is $rel_info->{attrs}{accessor}, 'single',
316                     'relationship in schema name with dash';
317
318                 is $rel_info->{attrs}{join_type}, 'LEFT',
319                     'relationship in schema name with dash';
320
321                 lives_and {
322                     ok $rsrc = $test_schema->source('PgLoaderTest5');
323                 } 'got source for table in schema name with dash';
324
325                 %uniqs = try { $rsrc->unique_constraints };
326
327                 is keys %uniqs, 2,
328                     'got unique and primary constraint in schema name with dash';
329
330                 lives_and {
331                     ok $rsrc = $test_schema->source('PgLoaderTest6');
332                 } 'got source for table in schema name with dot';
333
334                 is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
335                     'column in schema name with dot introspected correctly';
336
337                 is try { $rsrc->column_info('value')->{data_type} }, 'varchar',
338                     'column in schema name with dot introspected correctly';
339
340                 is try { $rsrc->column_info('value')->{size} }, 100,
341                     'column in schema name with dot introspected correctly';
342
343                 lives_and {
344                     ok $rs = $test_schema->resultset('PgLoaderTest6');
345                 } 'got resultset for table in schema name with dot';
346
347                 lives_and {
348                     ok $row = $rs->create({ value => 'foo' });
349                 } 'executed SQL on table in schema name with dot';
350
351                 $rel_info = try { $rsrc->relationship_info('pg_loader_test7') };
352
353                 is_deeply $rel_info->{cond}, {
354                     'foreign.six_id' => 'self.id'
355                 }, 'relationship in schema name with dot';
356
357                 is $rel_info->{attrs}{accessor}, 'single',
358                     'relationship in schema name with dot';
359
360                 is $rel_info->{attrs}{join_type}, 'LEFT',
361                     'relationship in schema name with dot';
362
363                 lives_and {
364                     ok $rsrc = $test_schema->source('PgLoaderTest7');
365                 } 'got source for table in schema name with dot';
366
367                 %uniqs = try { $rsrc->unique_constraints };
368
369                 is keys %uniqs, 2,
370                     'got unique and primary constraint in schema name with dot';
371
372                 lives_and {
373                     ok $test_schema->source('PgLoaderTest6')
374                         ->has_relationship('pg_loader_test4');
375                 } 'cross-schema relationship in multi-db_schema';
376
377                 lives_and {
378                     ok $test_schema->source('PgLoaderTest4')
379                         ->has_relationship('pg_loader_test6s');
380                 } 'cross-schema relationship in multi-db_schema';
381
382                 lives_and {
383                     ok $test_schema->source('PgLoaderTest8')
384                         ->has_relationship('pg_loader_test7');
385                 } 'cross-schema relationship in multi-db_schema';
386
387                 lives_and {
388                     ok $test_schema->source('PgLoaderTest7')
389                         ->has_relationship('pg_loader_test8s');
390                 } 'cross-schema relationship in multi-db_schema';
391             }
392         },
393     },
394 );
395
396 if( !$dsn || !$user ) {
397     $tester->skip_tests('You need to set the DBICTEST_PG_DSN, _USER, and _PASS environment variables');
398 }
399 else {
400     $tester->run_tests();
401 }
402
403 END {
404     rmtree EXTRA_DUMP_DIR unless $ENV{SCHEMA_LOADER_TESTS_NOCLEANUP};
405 }
406 # vim:et sw=4 sts=4 tw=0: