Support identity columns in PostgreSQL v10
[dbsrgits/DBIx-Class-Schema-Loader.git] / t / 10_03pg_common.t
index 0c66a3c..5c03b68 100644 (file)
@@ -1,7 +1,9 @@
+use DBIx::Class::Schema::Loader::Optional::Dependencies
+    -skip_all_without => 'test_rdbms_pg';
+
 use strict;
 use warnings;
 use utf8;
-use DBIx::Class::Optional::Dependencies;
 use DBIx::Class::Schema::Loader 'make_schema_at';
 use DBIx::Class::Schema::Loader::Utils qw/no_warnings slurp_file/;
 use Test::More;
@@ -20,7 +22,7 @@ my $dsn      = $ENV{DBICTEST_PG_DSN} || '';
 my $user     = $ENV{DBICTEST_PG_USER} || '';
 my $password = $ENV{DBICTEST_PG_PASS} || '';
 
-my $tester = dbixcsl_common_tests->new(
+dbixcsl_common_tests->new(
     vendor      => 'Pg',
     auto_inc_pk => 'SERIAL NOT NULL PRIMARY KEY',
     dsn         => $dsn,
@@ -38,8 +40,8 @@ my $tester = dbixcsl_common_tests->new(
         # http://www.postgresql.org/docs/7.4/interactive/datatype.html
         #
         # Numeric Types
-       boolean     => { data_type => 'boolean' },
-       bool        => { data_type => 'boolean' },
+        boolean     => { data_type => 'boolean' },
+        bool        => { data_type => 'boolean' },
         'bool default false'
                     => { data_type => 'boolean', default_value => \'false' },
         'bool default true'
@@ -49,100 +51,108 @@ my $tester = dbixcsl_common_tests->new(
         'bool default 1::bool'
                     => { data_type => 'boolean', default_value => \'true' },
 
-       bigint      => { data_type => 'bigint' },
-       int8        => { data_type => 'bigint' },
-       bigserial   => { data_type => 'bigint', is_auto_increment => 1 },
-       serial8     => { data_type => 'bigint', is_auto_increment => 1 },
-       integer     => { data_type => 'integer' },
-       int         => { data_type => 'integer' },
-       int4        => { data_type => 'integer' },
-       serial      => { data_type => 'integer', is_auto_increment => 1 },
-       serial4     => { data_type => 'integer', is_auto_increment => 1 },
-       smallint    => { data_type => 'smallint' },
-       int2        => { data_type => 'smallint' },
-
-       money       => { data_type => 'money' },
-
-       'double precision' => { data_type => 'double precision' },
-       float8             => { data_type => 'double precision' },
-       real               => { data_type => 'real' },
-       float4             => { data_type => 'real' },
+        bigint      => { data_type => 'bigint' },
+        int8        => { data_type => 'bigint' },
+        bigserial   => { data_type => 'bigint', is_auto_increment => 1 },
+        serial8     => { data_type => 'bigint', is_auto_increment => 1 },
+        integer     => { data_type => 'integer' },
+        int         => { data_type => 'integer' },
+        int4        => { data_type => 'integer' },
+        serial      => { data_type => 'integer', is_auto_increment => 1 },
+        serial4     => { data_type => 'integer', is_auto_increment => 1 },
+        smallint    => { data_type => 'smallint' },
+        int2        => { data_type => 'smallint' },
+
+        money       => { data_type => 'money' },
+
+        'double precision' => { data_type => 'double precision' },
+        float8             => { data_type => 'double precision' },
+        real               => { data_type => 'real' },
+        float4             => { data_type => 'real' },
         'float(24)'        => { data_type => 'real' },
         'float(25)'        => { data_type => 'double precision' },
         'float(53)'        => { data_type => 'double precision' },
         float              => { data_type => 'double precision' },
 
-        numeric        => { data_type => 'numeric' },
-        decimal        => { data_type => 'numeric' },
-       'numeric(6,3)' => { data_type => 'numeric', size => [6,3] },
-       'decimal(6,3)' => { data_type => 'numeric', size => [6,3] },
+        numeric            => { data_type => 'numeric' },
+        decimal            => { data_type => 'numeric' },
+        'numeric(6,3)'     => { data_type => 'numeric', size => [6,3] },
+        'decimal(6,3)'     => { data_type => 'numeric', size => [6,3] },
 
         # Bit String Types
-       'bit varying(2)' => { data_type => 'varbit', size => 2 },
-       'varbit(2)'      => { data_type => 'varbit', size => 2 },
-       'varbit'         => { data_type => 'varbit' },
-       bit              => { data_type => 'bit', size => 1 },
-       'bit(3)'         => { data_type => 'bit', size => 3 },
+        'bit varying(2)' => { data_type => 'varbit', size => 2 },
+        'varbit(2)'      => { data_type => 'varbit', size => 2 },
+        'bit varying'    => { data_type => 'varbit' },
+        'varbit'         => { data_type => 'varbit' },
+        bit              => { data_type => 'bit', size => 1 },
+        'bit(3)'         => { data_type => 'bit', size => 3 },
 
         # Network Types
-       inet    => { data_type => 'inet' },
-       cidr    => { data_type => 'cidr' },
-       macaddr => { data_type => 'macaddr' },
+        inet    => { data_type => 'inet' },
+        cidr    => { data_type => 'cidr' },
+        macaddr => { data_type => 'macaddr' },
 
         # Geometric Types
-       point   => { data_type => 'point' },
-       line    => { data_type => 'line' },
-       lseg    => { data_type => 'lseg' },
-       box     => { data_type => 'box' },
-       path    => { data_type => 'path' },
-       polygon => { data_type => 'polygon' },
-       circle  => { data_type => 'circle' },
+        point   => { data_type => 'point' },
+        line    => { data_type => 'line' },
+        lseg    => { data_type => 'lseg' },
+        box     => { data_type => 'box' },
+        path    => { data_type => 'path' },
+        polygon => { data_type => 'polygon' },
+        circle  => { data_type => 'circle' },
 
         # Character Types
-       'character varying(2)'           => { data_type => 'varchar', size => 2 },
-       'varchar(2)'                     => { data_type => 'varchar', size => 2 },
-       'character(2)'                   => { data_type => 'char', size => 2 },
-       'char(2)'                        => { data_type => 'char', size => 2 },
+        'character varying(2)'           => { data_type => 'varchar', size => 2 },
+        'varchar(2)'                     => { data_type => 'varchar', size => 2 },
+        'character(2)'                   => { data_type => 'char', size => 2 },
+        'char(2)'                        => { data_type => 'char', size => 2 },
         # check that default null is correctly rewritten
         'char(3) default null'           => { data_type => 'char', size => 3,
                                               default_value => \'null' },
-       'character'                      => { data_type => 'char', size => 1 },
-       'char'                           => { data_type => 'char', size => 1 },
-       text                             => { data_type => 'text' },
+        'character'                      => { data_type => 'char', size => 1 },
+        'char'                           => { data_type => 'char', size => 1 },
+        text                             => { data_type => 'text' },
         # varchar with no size has unlimited size, we rewrite to 'text'
-       varchar                          => { data_type => 'text',
+        varchar                          => { data_type => 'text',
                                               original => { data_type => 'varchar' } },
         # check default null again (to make sure ref is safe)
         'varchar(3) default null'        => { data_type => 'varchar', size => 3,
                                               default_value => \'null' },
 
         # Datetime Types
-       date                             => { data_type => 'date' },
-       interval                         => { data_type => 'interval' },
-       'interval(2)'                    => { data_type => 'interval', size => 2 },
-       time                             => { data_type => 'time' },
-       'time(2)'                        => { data_type => 'time', size => 2 },
-       'time without time zone'         => { data_type => 'time' },
-       'time(2) without time zone'      => { data_type => 'time', size => 2 },
-       'time with time zone'            => { data_type => 'time with time zone' },
-       'time(2) with time zone'         => { data_type => 'time with time zone', size => 2 },
-       timestamp                        => { data_type => 'timestamp' },
-        'timestamp default now()'
-                                         => { data_type => 'timestamp', default_value => \'current_timestamp',
+        date                             => { data_type => 'date' },
+        interval                         => { data_type => 'interval' },
+        'interval(0)'                    => { data_type => 'interval', size => 0 },
+        'interval(2)'                    => { data_type => 'interval', size => 2 },
+        time                             => { data_type => 'time' },
+        'time(0)'                        => { data_type => 'time', size => 0 },
+        'time(2)'                        => { data_type => 'time', size => 2 },
+        'time without time zone'         => { data_type => 'time' },
+        'time(0) without time zone'      => { data_type => 'time', size => 0 },
+        'time with time zone'            => { data_type => 'time with time zone' },
+        'time(0) with time zone'         => { data_type => 'time with time zone', size => 0 },
+        'time(2) with time zone'         => { data_type => 'time with time zone', size => 2 },
+        timestamp                        => { data_type => 'timestamp' },
+        'timestamp default now()'        => { data_type => 'timestamp',
+                                              default_value => \'current_timestamp',
                                               original => { default_value => \'now()' } },
-       'timestamp(2)'                   => { data_type => 'timestamp', size => 2 },
-       'timestamp without time zone'    => { data_type => 'timestamp' },
-       'timestamp(2) without time zone' => { data_type => 'timestamp', size => 2 },
+        'timestamp(0)'                   => { data_type => 'timestamp', size => 0 },
+        'timestamp(2)'                   => { data_type => 'timestamp', size => 2 },
+        'timestamp without time zone'    => { data_type => 'timestamp' },
+        'timestamp(0) without time zone' => { data_type => 'timestamp', size => 0 },
+        'timestamp(2) without time zone' => { data_type => 'timestamp', size => 2 },
 
-       'timestamp with time zone'       => { data_type => 'timestamp with time zone' },
-       'timestamp(2) with time zone'    => { data_type => 'timestamp with time zone', size => 2 },
+        'timestamp with time zone'       => { data_type => 'timestamp with time zone' },
+        'timestamp(0) with time zone'    => { data_type => 'timestamp with time zone', size => 0 },
+        'timestamp(2) with time zone'    => { data_type => 'timestamp with time zone', size => 2 },
 
         # Blob Types
-       bytea => { data_type => 'bytea' },
+        bytea => { data_type => 'bytea' },
 
         # Enum Types
-        pg_loader_test_enum => { data_type => 'enum', extra => { custom_type_name => 'pg_loader_test_enum',
-                                                                 list => [ qw/foo bar baz/] } },
+        pg_loader_test_enum => { data_type => 'enum',
+                                 extra => { custom_type_name => 'pg_loader_test_enum',
+                                            list => [ qw/foo bar baz/] } },
     },
     pre_create => [
         q{
@@ -216,9 +226,12 @@ my $tester = dbixcsl_common_tests->new(
                 )
             },
             q{
+                CREATE TYPE "dbicsl.test".pg_loader_test_enum2 AS ENUM ('wibble','wobble')
+            },
+            q{
                 CREATE TABLE "dbicsl.test".pg_loader_test7 (
                     id SERIAL PRIMARY KEY,
-                    value VARCHAR(100),
+                    value "dbicsl.test".pg_loader_test_enum2,
                     six_id INTEGER UNIQUE REFERENCES "dbicsl.test".pg_loader_test6 (id)
                 )
             },
@@ -284,7 +297,7 @@ my $tester = dbixcsl_common_tests->new(
             'DROP VIEW pg_loader_test11',
         ],
         drop  => [ qw/pg_loader_test1 pg_loader_test2 pg_loader_test9 pg_loader_test10 pg_loader_test12/ ],
-        count => 10 + 30 * 2,
+        count => 11 + 33 * 2,   # regular + multi-schema * 2
         run   => sub {
             my ($schema, $monikers, $classes) = @_;
 
@@ -404,8 +417,10 @@ my $tester = dbixcsl_common_tests->new(
 
                 delete $uniqs{primary};
 
-                is_deeply ((values %uniqs)[0], ['four_id'],
-                    'unique constraint is correct in schema name with dash');
+                is_deeply(
+                    (values %uniqs)[0], ['four_id'],
+                    'unique constraint is correct in schema name with dash'
+                );
 
                 lives_and {
                     ok $rsrc = $test_schema->source('PgLoaderTest6');
@@ -442,6 +457,13 @@ my $tester = dbixcsl_common_tests->new(
 
                 lives_and {
                     ok $rsrc = $test_schema->source('PgLoaderTest7');
+                    my $col_info = $rsrc->column_info('value');
+                    is $col_info->{data_type}, 'enum',
+                        'enum column in schema name with dot';
+                    is $col_info->{extra}{custom_type_name}, '"dbicsl.test".pg_loader_test_enum2',
+                        'original data type for enum in schema name with dot';
+                    is_deeply $col_info->{extra}{list}, [qw(wibble wobble)],
+                        'value list for for enum in schema name with dot';
                 } 'got source for table in schema name with dot';
 
                 %uniqs = try { $rsrc->unique_constraints };
@@ -451,8 +473,10 @@ my $tester = dbixcsl_common_tests->new(
 
                 delete $uniqs{primary};
 
-                is_deeply ((values %uniqs)[0], ['six_id'],
-                    'unique constraint is correct in schema name with dot');
+                is_deeply(
+                    (values %uniqs)[0], ['six_id'],
+                    'unique constraint is correct in schema name with dot'
+                );
 
                 lives_and {
                     ok $test_schema->source('PgLoaderTest6')
@@ -476,8 +500,13 @@ my $tester = dbixcsl_common_tests->new(
             }
 
             # test that views are marked as such
-            isa_ok $schema->resultset($monikers->{pg_loader_test11})->result_source, 'DBIx::Class::ResultSource::View',
-                'views have table_class set correctly';
+            my $view_source = $schema->resultset($monikers->{pg_loader_test11})->result_source;
+            isa_ok $view_source, 'DBIx::Class::ResultSource::View',
+                'view result source';
+
+            like $view_source->view_definition,
+                qr/\A \s* select\b .* \bfrom \s+ pg_loader_test1 \s* \z/imsx,
+                'view definition';
 
             is_deeply
                 { $schema->source($monikers->{pg_loader_test12})->unique_constraints },
@@ -485,17 +514,38 @@ my $tester = dbixcsl_common_tests->new(
                 'unique indexes are dumped correctly';
         },
     },
-);
-
-if( !$dsn || !$user ) {
-    $tester->skip_tests('You need to set the DBICTEST_PG_DSN, _USER, and _PASS environment variables');
-}
-elsif (!DBIx::Class::Optional::Dependencies->req_ok_for ('rdbms_pg')) {
-    $tester->skip_tests('You need to install ' . DBIx::Class::Optional::Dependencies->req_missing_for ('rdbms_pg'));
-}
-else {
-    $tester->run_tests();
-}
+    cond_extra => [
+        {
+            desc => 'identity columns',
+            skip => 'requires PostgreSQL 10',
+            cond => sub { $_[0]->{pg_server_version} >= 100000 },
+            create => [
+                q{
+                    create table pg_loader_test13 (
+                        always integer generated always as identity,
+                        by_default integer generated by default as identity
+                    )
+                },
+            ],
+            drop => [ qw(pg_loader_test13) ],
+            run => sub {
+                my ($schema, $monikers, $classes) = @_;
+
+                my $rsrc13 = $schema->source($monikers->{pg_loader_test13});
+                for my $col (qw(by_default always)) {
+                    my $info = $rsrc13->column_info($col);
+                    (my $when = uc $col) =~ tr/_/ /;
+
+                    ok $info->{is_auto_increment},
+                        "is_auto_increment for GENERATED $when AS IDENTITY";
+
+                    is $info->{extra}{generated_as_identity}, $col,
+                        "generated_as_identity for GENERATED $when AS IDENTITY";
+                }
+            },
+        },
+    ],
+)->run_tests();
 
 END {
     rmtree EXTRA_DUMP_DIR unless $ENV{SCHEMA_LOADER_TESTS_NOCLEANUP};