Merge 'sybase_computed_columns' into 'trunk'
Rafael Kitover [Tue, 2 Feb 2010 22:23:26 +0000 (22:23 +0000)]
r22895@hlagh (orig r8473):  caelum | 2010-01-30 03:57:26 -0500
branch to fix computed columns in Sybase ASE
r22911@hlagh (orig r8489):  caelum | 2010-01-31 07:18:33 -0500
empty insert into a Sybase table with computed columns and either data_type => undef or default_value => SCALARREF works now
r22912@hlagh (orig r8490):  caelum | 2010-01-31 07:39:32 -0500
add POD about computed columns and timestamps for Sybase
r22918@hlagh (orig r8496):  caelum | 2010-02-01 05:09:07 -0500
update POD about Schema::Loader for Sybase

lib/DBIx/Class/Storage/DBI/Sybase/ASE.pm
t/746sybase.t
t/inflate/datetime_sybase.t
t/lib/DBICTest/Schema/ComputedColumn.pm [new file with mode: 0644]
t/lib/sqlite.sql

index 45d95da..10cc9c8 100644 (file)
@@ -353,10 +353,19 @@ sub insert {
 
   # check for empty insert
   # INSERT INTO foo DEFAULT VALUES -- does not work with Sybase
-  # try to insert explicit 'DEFAULT's instead (except for identity)
+  # try to insert explicit 'DEFAULT's instead (except for identity, timestamp
+  # and computed columns)
   if (not %$to_insert) {
     for my $col ($source->columns) {
       next if $col eq $identity_col;
+
+      my $info = $source->column_info($col);
+
+      next if ref $info->{default_value} eq 'SCALAR'
+        || (exists $info->{data_type} && (not defined $info->{data_type}));
+
+      next if $info->{data_type} && $info->{data_type} =~ /^timestamp\z/i;
+
       $to_insert->{$col} = \'DEFAULT';
     }
   }
@@ -935,12 +944,8 @@ sub _svp_rollback {
 
 =head1 Schema::Loader Support
 
-There is an experimental branch of L<DBIx::Class::Schema::Loader> that will
-allow you to dump a schema from most (if not all) versions of Sybase.
-
-It is available via subversion from:
-
-  http://dev.catalyst.perl.org/repos/bast/branches/DBIx-Class-Schema-Loader/current/
+As of version C<0.05000>, L<DBIx::Class::Schema::Loader> should work well with
+most (if not all) versions of Sybase ASE.
 
 =head1 FreeTDS
 
@@ -1093,6 +1098,42 @@ loading your app, if it doesn't match the character set of your database.
 When inserting IMAGE columns using this method, you'll need to use
 L</connect_call_blob_setup> as well.
 
+=head1 COMPUTED COLUMNS
+
+If you have columns such as:
+
+  created_dtm AS getdate()
+
+represent them in your Result classes as:
+
+  created_dtm => {
+    data_type => undef,
+    default_value => \'getdate()',
+    is_nullable => 0,
+  }
+
+The C<data_type> must exist and must be C<undef>. Then empty inserts will work
+on tables with such columns.
+
+=head1 TIMESTAMP COLUMNS
+
+C<timestamp> columns in Sybase ASE are not really timestamps, see:
+L<http://dba.fyicenter.com/Interview-Questions/SYBASE/The_timestamp_datatype_in_Sybase_.html>.
+
+They should be defined in your Result classes as:
+
+  ts => {
+    data_type => 'timestamp',
+    is_nullable => 0,
+    inflate_datetime => 0,
+  }
+
+The C<<inflate_datetime => 0>> is necessary if you use
+L<DBIx::Class::InflateColumn::DateTime>, and most people do, and still want to
+be able to read these values.
+
+The values will come back as hexadecimal.
+
 =head1 TODO
 
 =over
index 82ad9c2..441a258 100644 (file)
@@ -9,7 +9,7 @@ use DBICTest;
 
 my ($dsn, $user, $pass) = @ENV{map { "DBICTEST_SYBASE_${_}" } qw/DSN USER PASS/};
 
-my $TESTS = 63 + 2;
+my $TESTS = 66 + 2;
 
 if (not ($dsn && $user)) {
   plan skip_all =>
@@ -575,6 +575,35 @@ SQL
     'updated money value to NULL round-trip'
   );
   diag $@ if $@;
+
+# Test computed columns and timestamps
+  $schema->storage->dbh_do (sub {
+      my ($storage, $dbh) = @_;
+      eval { $dbh->do("DROP TABLE computed_column_test") };
+      $dbh->do(<<'SQL');
+CREATE TABLE computed_column_test (
+   id INT IDENTITY PRIMARY KEY,
+   a_computed_column AS getdate(),
+   a_timestamp timestamp,
+   charfield VARCHAR(20) DEFAULT 'foo' 
+)
+SQL
+  });
+
+  require DBICTest::Schema::ComputedColumn;
+  $schema->register_class(
+    ComputedColumn => 'DBICTest::Schema::ComputedColumn'
+  );
+
+  ok (($rs = $schema->resultset('ComputedColumn')),
+    'got rs for ComputedColumn');
+
+  lives_ok { $row = $rs->create({}) }
+    'empty insert for a table with computed columns survived';
+
+  lives_ok {
+    $row->update({ charfield => 'bar' })
+  } 'update of a table with computed columns survived';
 }
 
 is $ping_count, 0, 'no pings';
@@ -583,6 +612,6 @@ is $ping_count, 0, 'no pings';
 END {
   if (my $dbh = eval { $schema->storage->_dbh }) {
     eval { $dbh->do("DROP TABLE $_") }
-      for qw/artist bindtype_test money_test/;
+      for qw/artist bindtype_test money_test computed_column_test/;
   }
 }
index bbd603a..2b1fbed 100644 (file)
@@ -17,9 +17,6 @@ if (not ($dsn && $user)) {
   if ($@) {
     plan skip_all => 'needs DateTime and DateTime::Format::Sybase for testing';
   }
-  else {
-    plan tests => (4 * 2 * 2) + 2; # (tests * dt_types * storage_types) + storage_tests
-  }
 }
 
 my @storage_types = (
@@ -57,9 +54,9 @@ for my $storage_type (@storage_types) {
     $schema->storage->dbh->do(<<"SQL");
 CREATE TABLE track (
    trackid INT IDENTITY PRIMARY KEY,
-   cd INT,
-   position INT,
-   $col $type,
+   cd INT NULL,
+   position INT NULL,
+   $col $type NULL
 )
 SQL
     ok(my $dt = DateTime::Format::Sybase->parse_datetime($sample_dt));
@@ -75,8 +72,33 @@ SQL
     );
     is( $row->$col, $dt, 'DateTime roundtrip' );
   }
+
+  # test a computed datetime column
+  eval { $schema->storage->dbh->do("DROP TABLE track") };
+  $schema->storage->dbh->do(<<"SQL");
+CREATE TABLE track (
+   trackid INT IDENTITY PRIMARY KEY,
+   cd INT NULL,
+   position INT NULL,
+   title VARCHAR(100) NULL,
+   last_updated_on DATETIME NULL,
+   last_updated_at AS getdate(),
+   small_dt SMALLDATETIME NULL
+)
+SQL
+
+  my $now     = DateTime->now;
+  sleep 1;
+  my $new_row = $schema->resultset('Track')->create({});
+  $new_row->discard_changes;
+
+  lives_and {
+    cmp_ok (($new_row->last_updated_at - $now)->seconds, '>=', 1)
+  } 'getdate() computed column works';
 }
 
+done_testing;
+
 # clean up our mess
 END {
   if (my $dbh = eval { $schema->storage->_dbh }) {
diff --git a/t/lib/DBICTest/Schema/ComputedColumn.pm b/t/lib/DBICTest/Schema/ComputedColumn.pm
new file mode 100644 (file)
index 0000000..6832b3e
--- /dev/null
@@ -0,0 +1,34 @@
+package # hide from PAUSE 
+    DBICTest::Schema::ComputedColumn;
+
+# for sybase and mssql computed column tests
+
+use base qw/DBICTest::BaseResult/;
+
+__PACKAGE__->table('computed_column_test');
+
+__PACKAGE__->add_columns(
+  'id' => {
+    data_type => 'integer',
+    is_auto_increment => 1,
+  },
+  'a_computed_column' => {
+    data_type => undef,
+    is_nullable => 0,
+    default_value => \'getdate()',
+  },
+  'a_timestamp' => {
+    data_type => 'timestamp',
+    is_nullable => 0,
+  },
+  'charfield' => {
+    data_type => 'varchar',
+    size => 20,
+    default_value => 'foo',
+    is_nullable => 0,
+  }
+);
+
+__PACKAGE__->set_primary_key('id');
+
+1;
index 880227f..4d7905f 100644 (file)
@@ -1,6 +1,6 @@
 -- 
 -- Created by SQL::Translator::Producer::SQLite
--- Created on Thu Jan 28 11:26:22 2010
+-- Created on Sat Jan 30 19:18:55 2010
 -- 
 ;