19ff4dd3ce10d2b6d34620e0249cf0432b843258
[dbsrgits/DBIx-Class-Schema-Loader.git] / t / 10_03pg_common.t
1 use strict;
2 use lib qw(t/lib);
3 use DBIx::Class::Schema::Loader 'make_schema_at';
4 use DBIx::Class::Schema::Loader::Utils 'no_warnings';
5 use dbixcsl_common_tests;
6 use Test::More;
7 use Test::Exception;
8 use File::Slurp 'slurp';
9 use utf8;
10 use Encode 'decode';
11 use Try::Tiny;
12
13 my $dsn      = $ENV{DBICTEST_PG_DSN} || '';
14 my $user     = $ENV{DBICTEST_PG_USER} || '';
15 my $password = $ENV{DBICTEST_PG_PASS} || '';
16
17 my $tester = dbixcsl_common_tests->new(
18     vendor      => 'Pg',
19     auto_inc_pk => 'SERIAL NOT NULL PRIMARY KEY',
20     dsn         => $dsn,
21     user        => $user,
22     password    => $password,
23     loader_options  => { preserve_case => 1 },
24     connect_info_opts => {
25         pg_enable_utf8 => 1,
26         on_connect_do  => [ 'SET client_min_messages=WARNING' ],
27     },
28     quote_char  => '"',
29     data_types  => {
30         # http://www.postgresql.org/docs/7.4/interactive/datatype.html
31         #
32         # Numeric Types
33         boolean     => { data_type => 'boolean' },
34         bool        => { data_type => 'boolean' },
35         'bool default false'
36                     => { data_type => 'boolean', default_value => \'false' },
37
38         bigint      => { data_type => 'bigint' },
39         int8        => { data_type => 'bigint' },
40         bigserial   => { data_type => 'bigint', is_auto_increment => 1 },
41         serial8     => { data_type => 'bigint', is_auto_increment => 1 },
42         integer     => { data_type => 'integer' },
43         int         => { data_type => 'integer' },
44         int4        => { data_type => 'integer' },
45         serial      => { data_type => 'integer', is_auto_increment => 1 },
46         serial4     => { data_type => 'integer', is_auto_increment => 1 },
47         smallint    => { data_type => 'smallint' },
48         int2        => { data_type => 'smallint' },
49
50         money       => { data_type => 'money' },
51
52         'double precision' => { data_type => 'double precision' },
53         float8             => { data_type => 'double precision' },
54         real               => { data_type => 'real' },
55         float4             => { data_type => 'real' },
56         'float(24)'        => { data_type => 'real' },
57         'float(25)'        => { data_type => 'double precision' },
58         'float(53)'        => { data_type => 'double precision' },
59         float              => { data_type => 'double precision' },
60
61         numeric        => { data_type => 'numeric' },
62         decimal        => { data_type => 'numeric' },
63         'numeric(6,3)' => { data_type => 'numeric', size => [6,3] },
64         'decimal(6,3)' => { data_type => 'numeric', size => [6,3] },
65
66         # Bit String Types
67         'bit varying(2)' => { data_type => 'varbit', size => 2 },
68         'varbit(2)'      => { data_type => 'varbit', size => 2 },
69         'varbit'         => { data_type => 'varbit' },
70         bit              => { data_type => 'bit', size => 1 },
71         'bit(3)'         => { data_type => 'bit', size => 3 },
72
73         # Network Types
74         inet    => { data_type => 'inet' },
75         cidr    => { data_type => 'cidr' },
76         macaddr => { data_type => 'macaddr' },
77
78         # Geometric Types
79         point   => { data_type => 'point' },
80         line    => { data_type => 'line' },
81         lseg    => { data_type => 'lseg' },
82         box     => { data_type => 'box' },
83         path    => { data_type => 'path' },
84         polygon => { data_type => 'polygon' },
85         circle  => { data_type => 'circle' },
86
87         # Character Types
88         'character varying(2)'           => { data_type => 'varchar', size => 2 },
89         'varchar(2)'                     => { data_type => 'varchar', size => 2 },
90         'character(2)'                   => { data_type => 'char', size => 2 },
91         'char(2)'                        => { data_type => 'char', size => 2 },
92         'character'                      => { data_type => 'char', size => 1 },
93         'char'                           => { data_type => 'char', size => 1 },
94         text                             => { data_type => 'text' },
95         # varchar with no size has unlimited size, we rewrite to 'text'
96         varchar                          => { data_type => 'text',
97                                               original => { data_type => 'varchar' } },
98
99         # Datetime Types
100         date                             => { data_type => 'date' },
101         interval                         => { data_type => 'interval' },
102         'interval(2)'                    => { data_type => 'interval', size => 2 },
103         time                             => { data_type => 'time' },
104         'time(2)'                        => { data_type => 'time', size => 2 },
105         'time without time zone'         => { data_type => 'time' },
106         'time(2) without time zone'      => { data_type => 'time', size => 2 },
107         'time with time zone'            => { data_type => 'time with time zone' },
108         'time(2) with time zone'         => { data_type => 'time with time zone', size => 2 },
109         timestamp                        => { data_type => 'timestamp' },
110         'timestamp default now()'
111                                          => { data_type => 'timestamp', default_value => \'current_timestamp',
112                                               original => { default_value => \'now()' } },
113         'timestamp(2)'                   => { data_type => 'timestamp', size => 2 },
114         'timestamp without time zone'    => { data_type => 'timestamp' },
115         'timestamp(2) without time zone' => { data_type => 'timestamp', size => 2 },
116
117         'timestamp with time zone'       => { data_type => 'timestamp with time zone' },
118         'timestamp(2) with time zone'    => { data_type => 'timestamp with time zone', size => 2 },
119
120         # Blob Types
121         bytea => { data_type => 'bytea' },
122
123         # Enum Types
124         pg_loader_test_enum => { data_type => 'enum', extra => { custom_type_name => 'pg_loader_test_enum',
125                                                                  list => [ qw/foo bar baz/] } },
126     },
127     pre_create => [
128         q{
129             CREATE TYPE pg_loader_test_enum AS ENUM (
130                 'foo', 'bar', 'baz'
131             )
132         },
133     ],
134     extra       => {
135         create => [
136             q{
137                 CREATE SCHEMA dbicsl_test
138             },
139             q{
140                 CREATE SEQUENCE dbicsl_test.myseq
141             },
142             q{
143                 CREATE TABLE pg_loader_test1 (
144                     id INTEGER NOT NULL DEFAULT nextval('dbicsl_test.myseq') PRIMARY KEY,
145                     value VARCHAR(100)
146                 )
147             },
148             qq{
149                 COMMENT ON TABLE pg_loader_test1 IS 'The\15\12Table ∑'
150             },
151             qq{
152                 COMMENT ON COLUMN pg_loader_test1.value IS 'The\15\12Column'
153             },
154             q{
155                 CREATE TABLE pg_loader_test2 (
156                     id SERIAL PRIMARY KEY,
157                     value VARCHAR(100)
158                 )
159             },
160             q{
161                 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'
162             },
163             q{
164                 CREATE SCHEMA "dbicsl-test"
165             },
166             q{
167                 CREATE TABLE "dbicsl-test".pg_loader_test3 (
168                     id SERIAL PRIMARY KEY,
169                     value VARCHAR(100)
170                 )
171             },
172             q{
173                 CREATE TABLE "dbicsl-test".pg_loader_test4 (
174                     id SERIAL PRIMARY KEY,
175                     value VARCHAR(100),
176                     three_id INTEGER UNIQUE REFERENCES "dbicsl-test".pg_loader_test3 (id)
177                 )
178             },
179             q{
180                 CREATE SCHEMA "dbicsl.test"
181             },
182             q{
183                 CREATE TABLE "dbicsl.test".pg_loader_test5 (
184                     id SERIAL PRIMARY KEY,
185                     value VARCHAR(100),
186                     pg_loader_test3_id INTEGER NOT NULL REFERENCES "dbicsl-test".pg_loader_test3 (id)
187                 )
188             },
189             q{
190                 CREATE TABLE "dbicsl.test".pg_loader_test6 (
191                     id SERIAL PRIMARY KEY,
192                     value VARCHAR(100),
193                     five_id INTEGER UNIQUE REFERENCES "dbicsl.test".pg_loader_test5 (id)
194                 )
195             },
196             q{
197                 CREATE TABLE "dbicsl-test".pg_loader_test7 (
198                     id SERIAL PRIMARY KEY,
199                     value VARCHAR(100),
200                     pg_loader_test6_id INTEGER NOT NULL REFERENCES "dbicsl.test".pg_loader_test6 (id)
201                 )
202             },
203         ],
204         pre_drop_ddl => [
205             'DROP SCHEMA dbicsl_test CASCADE',
206             'DROP SCHEMA "dbicsl-test" CASCADE',
207             'DROP SCHEMA "dbicsl.test" CASCADE',
208             'DROP TYPE pg_loader_test_enum',
209         ],
210         drop  => [ qw/ pg_loader_test1 pg_loader_test2 / ],
211         count => 4 + 21 * 2,
212         run   => sub {
213             my ($schema, $monikers, $classes) = @_;
214
215             is $schema->source($monikers->{pg_loader_test1})->column_info('id')->{sequence},
216                 'dbicsl_test.myseq',
217                 'qualified sequence detected';
218
219             my $class    = $classes->{pg_loader_test1};
220             my $filename = $schema->_loader->get_dump_filename($class);
221
222             my $code = decode('UTF-8', scalar slurp $filename);
223
224             like $code, qr/^=head1 NAME\n\n^$class - The\nTable ∑\n\n^=cut\n/m,
225                 'table comment';
226
227             like $code, qr/^=head2 value\n\n(.+:.+\n)+\nThe\nColumn\n\n/m,
228                 'column comment and attrs';
229
230             $class    = $classes->{pg_loader_test2};
231             $filename = $schema->_loader->get_dump_filename($class);
232
233             $code = decode('UTF-8', scalar slurp $filename);
234
235             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,
236                 'long table comment is in DESCRIPTION';
237
238             foreach my $db_schema (['dbicsl-test', 'dbicsl.test'], '%') {
239                 lives_and {
240                     no_warnings {
241                         make_schema_at(
242                             'PGMultiSchema',
243                             {
244                                 naming => 'current',
245                                 preserve_case => 1,
246                                 db_schema => $db_schema,
247                             },
248                             [ $dsn, $user, $password, {
249                                 on_connect_do  => [ 'SET client_min_messages=WARNING' ],
250                             } ],
251                         );
252                     };
253                 }
254     'created dynamic schema for "dbicsl-test" and "dbicsl.test" schemas with no warnings';
255
256                 my ($rsrc, %uniqs, $rel_info);
257
258                 lives_and {
259                     ok $rsrc = PGMultiSchema->source('PgLoaderTest3');
260                 } 'got source for table in schema name with dash';
261
262                 is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
263                     'column in schema name with dash';
264
265                 is try { $rsrc->column_info('value')->{data_type} }, 'varchar',
266                     'column in schema name with dash';
267
268                 is try { $rsrc->column_info('value')->{size} }, 100,
269                     'column in schema name with dash';
270
271                 $rel_info = try { $rsrc->relationship_info('pg_loader_test4') };
272
273                 is_deeply $rel_info->{cond}, {
274                     'foreign.three_id' => 'self.id'
275                 }, 'relationship in schema name with dash';
276
277                 is $rel_info->{attrs}{accessor}, 'single',
278                     'relationship in schema name with dash';
279
280                 is $rel_info->{attrs}{join_type}, 'LEFT',
281                     'relationship in schema name with dash';
282
283                 lives_and {
284                     ok $rsrc = PGMultiSchema->source('PgLoaderTest4');
285                 } 'got source for table in schema name with dash';
286
287                 %uniqs = try { $rsrc->unique_constraints };
288
289                 is keys %uniqs, 2,
290                     'got unique and primary constraint in schema name with dash';
291
292                 lives_and {
293                     ok $rsrc = PGMultiSchema->source('PgLoaderTest5');
294                 } 'got source for table in schema name with dot';
295
296                 is try { $rsrc->column_info('id')->{is_auto_increment} }, 1,
297                     'column in schema name with dot introspected correctly';
298
299                 is try { $rsrc->column_info('value')->{data_type} }, 'varchar',
300                     'column in schema name with dash introspected correctly';
301
302                 is try { $rsrc->column_info('value')->{size} }, 100,
303                     'column in schema name with dash introspected correctly';
304
305                 $rel_info = try { $rsrc->relationship_info('pg_loader_test6') };
306
307                 is_deeply $rel_info->{cond}, {
308                     'foreign.five_id' => 'self.id'
309                 }, 'relationship in schema name with dot';
310
311                 is $rel_info->{attrs}{accessor}, 'single',
312                     'relationship in schema name with dot';
313
314                 is $rel_info->{attrs}{join_type}, 'LEFT',
315                     'relationship in schema name with dot';
316
317                 lives_and {
318                     ok $rsrc = PGMultiSchema->source('PgLoaderTest6');
319                 } 'got source for table in schema name with dot';
320
321                 %uniqs = try { $rsrc->unique_constraints };
322
323                 is keys %uniqs, 2,
324                     'got unique and primary constraint in schema name with dot';
325
326                 lives_and {
327                     ok PGMultiSchema->source('PgLoaderTest5')
328                         ->has_relationship('pg_loader_test3');
329                 } 'cross-schema relationship in multi-db_schema';
330
331                 lives_and {
332                     ok PGMultiSchema->source('PgLoaderTest7')
333                         ->has_relationship('pg_loader_test6');
334                 } 'cross-schema relationship in multi-db_schema';
335             }
336         },
337     },
338 );
339
340 if( !$dsn || !$user ) {
341     $tester->skip_tests('You need to set the DBICTEST_PG_DSN, _USER, and _PASS environment variables');
342 }
343 else {
344     $tester->run_tests();
345 }
346 # vim:et sw=4 sts=4 tw=0: