From: Rafael Kitover Date: Sun, 21 Mar 2010 22:03:01 +0000 (-0400) Subject: better type info for Sybase ASE and better data type tests X-Git-Tag: 0.06000~39 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=5163dc4a1bc63e65a670f6d2f6a381f8eef5534a;p=dbsrgits%2FDBIx-Class-Schema-Loader.git better type info for Sybase ASE and better data type tests --- diff --git a/lib/DBIx/Class/Schema/Loader/DBI/Sybase.pm b/lib/DBIx/Class/Schema/Loader/DBI/Sybase.pm index 186581f..865cb92 100644 --- a/lib/DBIx/Class/Schema/Loader/DBI/Sybase.pm +++ b/lib/DBIx/Class/Schema/Loader/DBI/Sybase.pm @@ -10,20 +10,12 @@ our $VERSION = '0.05003'; =head1 NAME -DBIx::Class::Schema::Loader::DBI::Sybase - DBIx::Class::Schema::Loader::DBI Sybase Implementation. - -=head1 SYNOPSIS - - package My::Schema; - use base qw/DBIx::Class::Schema::Loader/; - - __PACKAGE__->loader_options( debug => 1 ); - - 1; +DBIx::Class::Schema::Loader::DBI::Sybase - DBIx::Class::Schema::Loader::DBI +Sybase ASE Implementation. =head1 DESCRIPTION -See L. +See L and L. =cut @@ -229,11 +221,11 @@ sub _columns_info_for { my $dbh = $self->schema->storage->dbh; my $sth = $dbh->prepare(qq{ -SELECT c.name name, t.name type, cm.text deflt, c.prec prec, c.scale scale, - c.length len +SELECT c.name name, bt.name base_type, ut.name user_type, cm.text deflt, c.prec prec, c.scale scale, c.length len FROM syscolumns c JOIN sysobjects o ON c.id = o.id -LEFT JOIN systypes t ON c.type = t.type AND c.usertype = t.usertype +LEFT JOIN systypes bt ON c.type = bt.type +LEFT JOIN systypes ut ON c.usertype = ut.usertype LEFT JOIN syscomments cm ON cm.id = CASE WHEN c.cdefault = 0 THEN c.computedcol ELSE c.cdefault END WHERE o.name = @{[ $dbh->quote($table) ]} AND o.type = 'U' @@ -243,7 +235,7 @@ WHERE o.name = @{[ $dbh->quote($table) ]} AND o.type = 'U' my $info = $sth->fetchall_hashref('name'); while (my ($col, $res) = each %$result) { - my $data_type = $res->{data_type} = $info->{$col}{type}; + my $data_type = $res->{data_type} = $info->{$col}{user_type} || $info->{$col}{base_type}; if ($data_type && $data_type =~ /^timestamp\z/i) { $res->{inflate_datetime} = 0; @@ -257,6 +249,9 @@ WHERE o.name = @{[ $dbh->quote($table) ]} AND o.type = 'U' if ($function =~ /^getdate\b/) { $res->{inflate_datetime} = 1; } + + delete $res->{size}; + $res->{data_type} = undef; } elsif ($default =~ /^DEFAULT \s+ (\S+)/ix) { my ($constant_default) = $1 =~ /^['"\[\]]?(.*?)['"\[\]]?\z/; @@ -264,19 +259,35 @@ WHERE o.name = @{[ $dbh->quote($table) ]} AND o.type = 'U' } } -# XXX we need to handle "binary precision" for FLOAT(X) -# (see: http://msdn.microsoft.com/en-us/library/aa258876(SQL.80).aspx ) if (my $data_type = $res->{data_type}) { - if ($data_type =~ /^(?:text|unitext|image|bigint|int|integer|smallint|tinyint|real|double|double precision|float|date|time|datetime|smalldatetime|money|smallmoney|timestamp|bit)\z/i) { + if ($data_type eq 'int') { + $data_type = $res->{data_type} = 'integer'; + } + elsif ($data_type eq 'decimal') { + $data_type = $res->{data_type} = 'numeric'; + } + + if ($data_type =~ /^(?:text|unitext|image|bigint|integer|smallint|tinyint|real|double|double precision|float|date|time|datetime|smalldatetime|money|smallmoney|timestamp|bit)\z/i) { delete $res->{size}; } - elsif ($data_type =~ /^(?:numeric|decimal)\z/i) { - $res->{size} = [ $info->{$col}{prec}, $info->{$col}{scale} ]; + elsif ($data_type eq 'numeric') { + my ($prec, $scale) = @{$info->{$col}}{qw/prec scale/}; + + if ($prec == 18 && $scale == 0) { + delete $res->{size}; + } + else { + $res->{size} = [ $prec, $scale ]; + } } elsif ($data_type =~ /^(?:unichar|univarchar)\z/i) { $res->{size} /= 2; } } + + if ($data_type eq 'float') { + $res->{data_type} = $info->{$col}{len} <= 4 ? 'real' : 'double precision'; + } } return $result; @@ -299,6 +310,7 @@ sub _extra_column_info { =head1 SEE ALSO +L, L, L, L diff --git a/lib/DBIx/Class/Schema/Loader/DBI/Sybase/Common.pm b/lib/DBIx/Class/Schema/Loader/DBI/Sybase/Common.pm index 676efd8..0278cf4 100644 --- a/lib/DBIx/Class/Schema/Loader/DBI/Sybase/Common.pm +++ b/lib/DBIx/Class/Schema/Loader/DBI/Sybase/Common.pm @@ -10,12 +10,12 @@ our $VERSION = '0.05003'; =head1 NAME -DBIx::Class::Schema::Loader::DBI::Sybase::Common - Common functions for Sybase +DBIx::Class::Schema::Loader::DBI::Sybase::Common - Common methods for Sybase and MSSQL =head1 DESCRIPTION -See L. +See L and L. =cut @@ -80,6 +80,8 @@ sub _columns_info_for { L, L, +L, +L, L L, L, diff --git a/t/12pg_common.t b/t/12pg_common.t index 157decf..723fc5a 100644 --- a/t/12pg_common.t +++ b/t/12pg_common.t @@ -16,47 +16,47 @@ my $tester = dbixcsl_common_tests->new( user => $user, password => $password, data_types => { - 'bigint' => { data_type => 'bigint' }, - 'int8' => { data_type => 'bigint' }, - 'bigserial' => { data_type => 'bigint', is_auto_increment => 1 }, - 'serial8' => { data_type => 'bigint', is_auto_increment => 1 }, - 'bit' => { data_type => 'bit' }, - 'boolean' => { data_type => 'boolean' }, - 'bool' => { data_type => 'boolean' }, - 'box' => { data_type => 'box' }, - 'bytea' => { data_type => 'bytea' }, - 'cidr' => { data_type => 'cidr' }, - 'circle' => { data_type => 'circle' }, - 'date' => { data_type => 'date' }, + bigint => { data_type => 'bigint' }, + int8 => { data_type => 'bigint' }, + bigserial => { data_type => 'bigint', is_auto_increment => 1 }, + serial8 => { data_type => 'bigint', is_auto_increment => 1 }, + bit => { data_type => 'bit' }, + boolean => { data_type => 'boolean' }, + bool => { data_type => 'boolean' }, + box => { data_type => 'box' }, + bytea => { data_type => 'bytea' }, + cidr => { data_type => 'cidr' }, + circle => { data_type => 'circle' }, + date => { data_type => 'date' }, 'double precision' => { data_type => 'double precision' }, - 'float8' => { data_type => 'double precision' }, - 'inet' => { data_type => 'inet' }, - 'integer' => { data_type => 'integer' }, - 'int' => { data_type => 'integer' }, - 'int4' => { data_type => 'integer' }, - 'interval' => { data_type => 'interval' }, + float8 => { data_type => 'double precision' }, + inet => { data_type => 'inet' }, + integer => { data_type => 'integer' }, + int => { data_type => 'integer' }, + int4 => { data_type => 'integer' }, + interval => { data_type => 'interval' }, 'interval(2)' => { size => 2, data_type => 'interval' }, - 'line' => { data_type => 'line' }, - 'lseg' => { data_type => 'lseg' }, - 'macaddr' => { data_type => 'macaddr' }, - 'money' => { data_type => 'money' }, - 'path' => { data_type => 'path' }, - 'point' => { data_type => 'point' }, - 'polygon' => { data_type => 'polygon' }, - 'real' => { data_type => 'real' }, - 'float4' => { data_type => 'real' }, - 'smallint' => { data_type => 'smallint' }, - 'int2' => { data_type => 'smallint' }, - 'serial' => { data_type => 'integer', is_auto_increment => 1 }, - 'serial4' => { data_type => 'integer', is_auto_increment => 1 }, - 'text' => { data_type => 'text' }, - 'time' => { data_type => 'time without time zone' }, + line => { data_type => 'line' }, + lseg => { data_type => 'lseg' }, + macaddr => { data_type => 'macaddr' }, + money => { data_type => 'money' }, + path => { data_type => 'path' }, + point => { data_type => 'point' }, + polygon => { data_type => 'polygon' }, + real => { data_type => 'real' }, + float4 => { data_type => 'real' }, + smallint => { data_type => 'smallint' }, + int2 => { data_type => 'smallint' }, + serial => { data_type => 'integer', is_auto_increment => 1 }, + serial4 => { data_type => 'integer', is_auto_increment => 1 }, + text => { data_type => 'text' }, + time => { data_type => 'time without time zone' }, 'time(2)' => { size => 2, data_type => 'time without time zone' }, 'time without time zone' => { data_type => 'time without time zone' }, 'time(2) without time zone' => { size => 2, data_type => 'time without time zone' }, 'time with time zone' => { data_type => 'time with time zone' }, 'time(2) with time zone' => { size => 2, data_type => 'time with time zone' }, - 'timestamp' => { data_type => 'timestamp without time zone' }, + timestamp => { data_type => 'timestamp without time zone' }, 'timestamp(2)' => { size => 2, data_type => 'timestamp without time zone' }, 'timestamp without time zone' => { data_type => 'timestamp without time zone' }, 'timestamp(2) without time zone' => { size => 2, data_type => 'timestamp without time zone' }, @@ -70,11 +70,11 @@ my $tester = dbixcsl_common_tests->new( 'char(2)' => { size => 2, data_type => 'character' }, 'numeric(6, 3)' => { size => [6,3], data_type => 'numeric' }, 'decimal(6, 3)' => { size => [6,3], data_type => 'numeric' }, - 'numeric' => { data_type => 'numeric' }, - 'decimal' => { data_type => 'numeric' }, + numeric => { data_type => 'numeric' }, + decimal => { data_type => 'numeric' }, 'float(24)' => { data_type => 'real' }, 'float(53)' => { data_type => 'double precision' }, - 'float' => { data_type => 'double precision' }, + float => { data_type => 'double precision' }, }, extra => { create => [ diff --git a/t/15sybase_common.t b/t/15sybase_common.t index 83b24fe..cace91e 100644 --- a/t/15sybase_common.t +++ b/t/15sybase_common.t @@ -17,135 +17,45 @@ my $tester = dbixcsl_common_tests->new( dsn => $dsn, user => $user, password => $password, - extra => { - create => [ - q{ - CREATE TABLE sybase_loader_test1 ( - id INTEGER IDENTITY NOT NULL PRIMARY KEY, - ts timestamp, - computed_dt AS getdate() - ) - }, # Test data types, see http://ispirer.com/wiki/sqlways/sybase/data-types -# XXX handle FLOAT(P) at some point -# ( http://msdn.microsoft.com/en-us/library/aa258876(SQL.80).aspx ) - q{ - CREATE TABLE sybase_loader_test2 ( - id INTEGER IDENTITY NOT NULL PRIMARY KEY, - a_text TEXT, - a_unitext UNITEXT, - an_image IMAGE, - a_bigint BIGINT, - an_int INT, - an_integer INTEGER, - a_smallint SMALLINT, - a_tinyint TINYINT, - a_real REAL, - a_double_precision DOUBLE PRECISION, - a_date DATE, - a_time TIME, - a_datetime DATETIME, - a_smalldatetime SMALLDATETIME, - a_money MONEY, - a_smallmoney SMALLMONEY, - a_timestamp timestamp, - a_bit BIT, - a_char_with_precision CHAR(2), - an_nchar_with_precision NCHAR(2), - a_unichar_with_precision UNICHAR(2), - a_varchar_with_precision VARCHAR(2), - an_nvarchar_with_precision NVARCHAR(2), - a_univarchar_with_precision UNIVARCHAR(2), - a_float FLOAT, - a_binary_with_precision BINARY(2), - a_varbinary_with_precision VARBINARY(2), - the_numeric NUMERIC(6,3), - the_decimal DECIMAL(6,3) - ) - }, - ], - drop => [ qw/ sybase_loader_test1 sybase_loader_test2 / ], - count => 37, - run => sub { - my ($schema, $monikers, $classes) = @_; - - my $rs = $schema->resultset($monikers->{sybase_loader_test1}); - my $rsrc = $rs->result_source; - - is $rsrc->column_info('id')->{data_type}, - 'int', - 'INTEGER IDENTITY data_type is correct'; - - is $rsrc->column_info('id')->{is_auto_increment}, - 1, - 'INTEGER IDENTITY is_auto_increment => 1'; - - is $rsrc->column_info('ts')->{data_type}, - 'timestamp', - 'timestamps have the correct data_type'; - - is $rsrc->column_info('ts')->{inflate_datetime}, - 0, - 'timestamps have inflate_datetime => 0'; - - ok ((exists $rsrc->column_info('computed_dt')->{data_type} - && (not defined $rsrc->column_info('computed_dt')->{data_type})), - 'data_type for computed column exists and is undef') - or diag "Data type is: ", - $rsrc->column_info('computed_dt')->{data_type} - ; - - my $computed_dt_default = - $rsrc->column_info('computed_dt')->{default_value}; - - ok ((ref $computed_dt_default eq 'SCALAR'), - 'default_value for computed column is a scalar ref') - or diag "default_value is: ", $computed_dt_default - ; - - eval { is $$computed_dt_default, - 'getdate()', - 'default_value for computed column is correct' }; - - $rsrc = $schema->resultset($monikers->{sybase_loader_test2}) - ->result_source; - my @type_columns = grep /^a/, $rsrc->columns; - my @without_precision = grep !/_with_precision\z/, @type_columns; - my @with_precision = grep /_with_precision\z/, @type_columns; - my %with_precision; - @with_precision{ - apply { s/_with_precision\z// } @with_precision - } = (); - - for my $col (@without_precision) { - my ($data_type) = $col =~ /^an?_(.*)/; - $data_type =~ s/_/ /g; - - ok((not exists $rsrc->column_info($col)->{size}), - "$data_type " . - (exists $with_precision{$col} ? 'without precision ' : '') . - "has no 'size' column_info") - or diag "size is ".$rsrc->column_info($col)->{size}."\n"; - } - - for my $col (@with_precision) { - my ($data_type) = $col =~ /^an?_(.*)_with_precision\z/; - ($data_type = uc $data_type) =~ s/_/ /g; - my $size = $rsrc->column_info($col)->{size}; - - is $size, 2, - "$data_type with precision has a correct 'size' column_info"; - } - - is_deeply $rsrc->column_info('the_numeric')->{size}, [6,3], - 'size for NUMERIC(precision, scale) is correct'; - - is_deeply $rsrc->column_info('the_decimal')->{size}, [6,3], - 'size for DECIMAL(precision, scale) is correct'; - - is $schema->resultset($monikers->{loader_test35})->result_source->column_info('a_function')->{inflate_datetime}, 1, - 'AS getdate() columns get inflate_datetime => 1'; - }, + data_types => { + 'integer identity' => { data_type => 'integer', is_auto_increment => 1 }, + 'AS getdate()' => { data_type => undef, inflate_datetime => 1, default_value => \'getdate()' }, + text => { data_type => 'text' }, + unitext => { data_type => 'unitext' }, + image => { data_type => 'image' }, + bigint => { data_type => 'bigint' }, + int => { data_type => 'integer' }, + integer => { data_type => 'integer' }, + smallint => { data_type => 'smallint' }, + tinyint => { data_type => 'tinyint' }, + date => { data_type => 'date' }, + time => { data_type => 'time' }, + datetime => { data_type => 'datetime' }, + smalldatetime => { data_type => 'smalldatetime' }, + money => { data_type => 'money' }, + smallmoney => { data_type => 'smallmoney' }, + timestamp => { data_type => 'timestamp', inflate_datetime => 0 }, + bit => { data_type => 'bit' }, + 'char(2)' => { data_type => 'char', size => 2 }, + 'nchar(2)' => { data_type => 'nchar', size => 2 }, + 'unichar(2)' => { data_type => 'unichar', size => 2 }, + 'varchar(2)' => { data_type => 'varchar', size => 2 }, + 'nvarchar(2)' => { data_type => 'nvarchar', size => 2 }, + 'univarchar(2)' => { data_type => 'univarchar', size => 2 }, + 'binary(2)' => { data_type => 'binary', size => 2 }, + 'varbinary(2)' => { data_type => 'varbinary', size => 2 }, + 'double precision' => { data_type => 'double precision' }, + real => { data_type => 'real' }, + float => { data_type => 'double precision' }, + 'float(14)' => { data_type => 'real' }, + 'float(15)' => { data_type => 'real' }, + 'float(16)' => { data_type => 'double precision' }, + 'float(48)' => { data_type => 'double precision' }, + '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' }, }, ); diff --git a/t/lib/dbixcsl_common_tests.pm b/t/lib/dbixcsl_common_tests.pm index dd97705..6b0a0d7 100644 --- a/t/lib/dbixcsl_common_tests.pm +++ b/t/lib/dbixcsl_common_tests.pm @@ -1556,12 +1556,15 @@ sub setup_data_type_tests { while (my ($col_def, $expected_info) = each %$types) { my $have_size = $col_def =~ /\(/ ? 1 : 0; - (my $type_alias = $col_def) =~ s/\([^()]+\)//g; + (my $type_alias = lc($col_def)) =~ s/\([^()]+\)//g; $type_alias =~ s/\s/_/g; + $type_alias =~ s/\W//g; - my $col_name = $type_alias . ($have_size ? '_with_size' : ''); + my @size = grep defined($_), $col_def =~ /\( \s* (\d+) \s* ,? \s* (\d+)?/x; - $col_name .= $seen_col_names{$col_name} if $seen_col_names{$col_name}++; + my $col_name = $type_alias . ($have_size ? "_with_size_".(join '_', @size) : ''); + + $col_name .= "_$seen_col_names{$col_name}" if $seen_col_names{$col_name}++; $ddl .= " $col_name $col_def,\n";