Merge 'trunk' into 'sybase'
Rafael Kitover [Fri, 12 Jun 2009 00:05:30 +0000 (00:05 +0000)]
r5443@hlagh (orig r6597):  ribasushi | 2009-06-10 05:48:39 -0700
Adjust changelog
r5446@hlagh (orig r6600):  ribasushi | 2009-06-10 06:50:43 -0700
Release 0.08104
r5467@hlagh (orig r6614):  ribasushi | 2009-06-11 05:29:48 -0700
Move around inflation tests
r5468@hlagh (orig r6615):  ribasushi | 2009-06-11 05:32:07 -0700
explicitly remove manifest on author mode make
r5469@hlagh (orig r6616):  ribasushi | 2009-06-11 06:02:41 -0700
IC::DT changes:
Switch SQLite storage to DT::F::SQLite
Fix exception when undef_if_invalid and timezone are both set on a column
Split t/89inflate_datetime into separate tests
Adjust makefile author dependencies
r5470@hlagh (orig r6617):  ribasushi | 2009-06-11 06:07:41 -0700
Move file_column test to inflate/ too
r5472@hlagh (orig r6620):  ribasushi | 2009-06-11 07:16:20 -0700
 r5713@Thesaurus (orig r5712):  ribasushi | 2009-03-08 23:53:28 +0100
 Branch for datatype-aware updates
 r6604@Thesaurus (orig r6603):  ribasushi | 2009-06-10 18:08:25 +0200
 Test for type-aware update
 r6607@Thesaurus (orig r6606):  ribasushi | 2009-06-10 19:57:04 +0200
 Datatype aware update works
 r6609@Thesaurus (orig r6608):  ribasushi | 2009-06-10 20:06:40 +0200
 Whoops
 r6614@Thesaurus (orig r6613):  ribasushi | 2009-06-11 09:23:54 +0200
 Add attribute doc
 r6620@Thesaurus (orig r6619):  ribasushi | 2009-06-11 16:15:53 +0200
 Use equality, not comparison

r5474@hlagh (orig r6622):  ribasushi | 2009-06-11 07:21:53 -0700
Changes
r5486@hlagh (orig r6635):  ribasushi | 2009-06-11 12:29:10 -0700
Release 0.08105
r5487@hlagh (orig r6636):  frew | 2009-06-11 13:02:11 -0700
Escape filename so that t\9... won't look like octal
r5488@hlagh (orig r6637):  ribasushi | 2009-06-11 13:09:55 -0700
Adjust tests for the DT::F::MySQL -> DT::F::SQLite change
r5489@hlagh (orig r6638):  arcanez | 2009-06-11 13:29:53 -0700
cookbook tweak for count distinct
r5490@hlagh (orig r6639):  ribasushi | 2009-06-11 14:39:12 -0700
Factor out as_query properly
r5491@hlagh (orig r6640):  ribasushi | 2009-06-11 14:50:41 -0700
Release 0.08106
r5493@hlagh (orig r6642):  caelum | 2009-06-11 16:48:28 -0700
add support for DBICTEST_MSSQL_PERL5LIB to 74mssql.t

26 files changed:
Changes
Makefile.PL
lib/DBIx/Class.pm
lib/DBIx/Class/InflateColumn/DateTime.pm
lib/DBIx/Class/Manual/Cookbook.pod
lib/DBIx/Class/ResultSet.pm
lib/DBIx/Class/ResultSource.pm
lib/DBIx/Class/Row.pm
lib/DBIx/Class/Storage/DBI.pm
lib/DBIx/Class/Storage/DBI/SQLite.pm
t/36datetime.t
t/60core.t
t/74mssql.t
t/89inflate_datetime.t [deleted file]
t/95sql_maker.t
t/inflate/core.t [moved from t/68inflate.t with 89% similarity]
t/inflate/datetime.t [new file with mode: 0644]
t/inflate/datetime_mysql.t [new file with mode: 0644]
t/inflate/datetime_pg.t [new file with mode: 0644]
t/inflate/file_column.t [moved from t/96file_column.t with 96% similarity]
t/inflate/hri.t [moved from t/68inflate_resultclass_hashrefinflator.t with 100% similarity]
t/inflate/serialize.t [moved from t/68inflate_serialize.t with 100% similarity]
t/lib/DBICTest/Schema/Event.pm
t/lib/DBICTest/Schema/EventTZ.pm
t/lib/DBICTest/Schema/EventTZDeprecated.pm
t/update/type_aware.t [new file with mode: 0644]

diff --git a/Changes b/Changes
index ab51a54..ade471a 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,16 +1,41 @@
 Revision history for DBIx::Class
 
+0.08106 2009-06-11 21:42:00 (UTC)
+        - Switched SQLite storage driver to DateTime::Format::SQLite
+          (proper timezone handling)
+        - Fix more test problems
+
+0.08105 2009-06-11 19:04:00 (UTC)
+        - Update of numeric columns now properly uses != to determine
+          dirtyness instead of the usual eq
+        - Fixes to IC::DT tests
+        - Fixed exception when undef_if_invalid and timezone are both set on 
+          an invalid datetime column
+
+0.08104 2009-06-10 13:38:00 (UTC)
         - order_by now can take \[$sql, @bind] as in
           order_by => { -desc => \['colA LIKE ?', 'somestring'] }
         - SQL::Abstract errors are now properly croak()ed with the
           correct trace
         - populate() now properly reports the dataset slice in case of
           an exception
-        - fixed corner case when populate() erroneously falls back to
+        - Fixed corner case when populate() erroneously falls back to
           create()
-        - work around braindead mysql when doing subquery counts on
+        - Work around braindead mysql when doing subquery counts on
           resultsets containing identically named columns from several
           tables
+        - Fixed m2m add_to_$rel to invoke find_or_create on the far
+          side of the relation, to avoid duplicates
+        - DBIC now properly handles empty inserts (invoking all default
+          values from the DB, normally via INSERT INTO tbl DEFAULT VALUES
+        - Fix find_or_new/create to stop returning random rows when 
+          default value insert is requested (RT#28875)
+        - Make IC::DT extra warning state the column name too
+        - It is now possible to transparrently search() on columns
+          requiring DBI bind (i.e. PostgreSQL BLOB)
+        - as_query is now a Storage::DBI method, so custom cursors can
+          be seamlessly used
+        - Fix search_related regression introduced in 0.08103
 
 0.08103 2009-05-26 19:50:00 (UTC)
         - Multiple $resultset -> count/update/delete fixes. Now any
index ebdf8b0..a9ad2d4 100644 (file)
@@ -73,12 +73,17 @@ my %force_requires_if_author = (
   'Test::Memory::Cycle'       => 0,
   'Devel::Cycle'              => 1.10,
 
+  # t/inflate/datetime*.t
+  # t/72.pg
+  # t/36datetime.t
   # t/60core.t
+  'DateTime::Format::SQLite'  => 0,
   'DateTime::Format::MySQL'   => 0,
-  
-  # t/89inflate_datetime.t
   'DateTime::Format::Pg'      => 0,
 
+  # t/96_is_deteministic_value.t
+  'DateTime::Format::Strptime' => 0,
+
   # t/72pg.t
   $ENV{DBICTEST_PG_DSN}
     ? ('Sys::SigAction'=> 0)
@@ -92,7 +97,6 @@ my %force_requires_if_author = (
   'namespace::clean'              => 0.11,
   'Hash::Merge',                  => 0.11,
 
-  # t/96_is_deteministic_value.t
   # t/746sybase.t
   'DateTime::Format::Strptime' => 0,
 );
@@ -113,12 +117,16 @@ EOW
     build_requires ($module => $force_requires_if_author{$module});
   }
 
+  print "Regenerating README\n";
   system('pod2text lib/DBIx/Class.pm > README');
-}
 
-auto_provides;
+  if (-f 'MANIFEST') {
+    print "Removing MANIFEST\n";
+    unlink 'MANIFEST';
+  }
+}
 
-auto_install;
+auto_install();
 
 WriteAll();
 
index 3f8acd3..6b28058 100644 (file)
@@ -24,7 +24,7 @@ sub component_base_class { 'DBIx::Class' }
 # i.e. first release of 0.XX *must* be 0.XX000. This avoids fBSD ports
 # brain damage and presumably various other packaging systems too
 
-$VERSION = '0.08103';
+$VERSION = '0.08106';
 
 $VERSION = eval $VERSION; # numify for warning-free dev releases
 
index d006e55..5646768 100644 (file)
@@ -148,9 +148,13 @@ sub register_column {
         {
           inflate => sub {
             my ($value, $obj) = @_;
+
             my $dt = eval { $obj->_inflate_to_datetime( $value, \%info ) };
-            $self->throw_exception ("Error while inflating ${value} for ${column} on ${self}: $@")
-              if $@ and not $undef_if_invalid;
+            if (my $err = $@ ) {
+              return undef if ($undef_if_invalid);
+              $self->throw_exception ("Error while inflating ${value} for ${column} on ${self}: $err");
+            }
+
             $dt->set_time_zone($timezone) if $timezone;
             $dt->set_locale($locale) if $locale;
             return $dt;
index 574a8e1..b13b05a 100644 (file)
@@ -279,7 +279,7 @@ any of your aliases using either of these:
   my $count = $rs->count;
 
   # Equivalent SQL:
-  # SELECT COUNT( DISTINCT( me.name ) ) FROM artist me 
+  # SELECT COUNT( * ) FROM (SELECT me.name FROM artist me GROUP BY me.name) count_subq: 
 
 =head2 Grouping results
 
index 515e45f..c220dd9 100644 (file)
@@ -1927,7 +1927,13 @@ B<NOTE>: This feature is still experimental.
 
 sub as_query {
   my $self = shift;
-  return $self->result_source->storage->as_query($self->_resolved_attrs);
+
+  my $attrs = $self->_resolved_attrs_copy;
+
+  my ($sqlbind, $bind_attrs) = $self->result_source->storage
+    ->_select_args_to_query ($attrs->{from}, $attrs->{select}, $attrs->{where}, $attrs);
+
+  return $sqlbind;
 }
 
 =head2 find_or_new
index c8f7e8d..aa636a7 100644 (file)
@@ -116,6 +116,18 @@ automatically set. This is used to determine which columns to empty
 when cloning objects using L<DBIx::Class::Row/copy>. It is also used by
 L<DBIx::Class::Schema/deploy>.
 
+=item is_numeric
+
+Set this to a true or false value (not C<undef>) to explicitly specify
+if this column contains numeric data. This controls how set_column
+decides whether to consider a column dirty after an update: if
+C<is_numeric> is true a numeric comparison C<< != >> will take place
+instead of the usual C<eq>
+
+If not specified the storage class will attempt to figure this out on
+first access to the column, based on the column C<data_type>. The
+result will be cached in this attribute.
+
 =item is_foreign_key
 
 Set this to a true value for a column that contains a key from a
index c08c00d..b2eee57 100644 (file)
@@ -769,8 +769,39 @@ sub set_column {
   my $old_value = $self->get_column($column);
 
   $self->store_column($column, $new_value);
-  $self->{_dirty_columns}{$column} = 1
-    if (defined $old_value xor defined $new_value) || (defined $old_value && $old_value ne $new_value);
+
+  my $dirty;
+  if (defined $old_value xor defined $new_value) {
+    $dirty = 1;
+  }
+  elsif (not defined $old_value) {  # both undef
+    $dirty = 0;
+  }
+  elsif ($old_value eq $new_value) {
+    $dirty = 0;
+  }
+  else {  # do a numeric comparison if datatype allows it
+    my $colinfo = $self->column_info ($column);
+
+    # cache for speed
+    if (not defined $colinfo->{is_numeric}) {
+      $colinfo->{is_numeric} =
+        $self->result_source->schema->storage->is_datatype_numeric ($colinfo->{data_type})
+          ? 1
+          : 0
+        ;
+    }
+
+    if ($colinfo->{is_numeric}) {
+      $dirty = $old_value != $new_value;
+    }
+    else {
+      $dirty = 1;
+    }
+  }
+
+  # sadly the update code just checks for keys, not for their value
+  $self->{_dirty_columns}{$column} = 1 if $dirty;
 
   # XXX clear out the relation cache for this column
   delete $self->{related_resultsets}{$column};
index c95195f..7b392cb 100644 (file)
@@ -910,37 +910,6 @@ sub _prep_for_execute {
   return ($sql, \@bind);
 }
 
-=head2 as_query
-
-=over 4
-
-=item Arguments: $rs_attrs
-
-=item Return Value: \[ $sql, @bind ]
-
-=back
-
-Returns the SQL statement and bind vars that would result from the given
-ResultSet attributes (does not actually run a query)
-
-=cut
-
-sub as_query {
-  my ($self, $rs_attr) = @_;
-
-  my $sql_maker = $self->sql_maker;
-  local $sql_maker->{for};
-
-  # my ($op, $bind, $ident, $bind_attrs, $select, $cond, $order, $rows, $offset) = $self->_select_args(...);
-  my @args = $self->_select_args($rs_attr->{from}, $rs_attr->{select}, $rs_attr->{where}, $rs_attr);
-
-  # my ($sql, $bind) = $self->_prep_for_execute($op, $bind, $ident, [ $select, $cond, $order, $rows, $offset ]);
-  my ($sql, $bind) = $self->_prep_for_execute(
-    @args[0 .. 2],
-    [ @args[4 .. $#args] ],
-  );
-  return \[ "($sql)", @{ $bind || [] }];
-}
 
 sub _fix_bind_params {
     my ($self, @bind) = @_;
@@ -1243,6 +1212,23 @@ sub _select {
   return $self->_execute($self->_select_args(@_));
 }
 
+sub _select_args_to_query {
+  my $self = shift;
+
+  my $sql_maker = $self->sql_maker;
+  local $sql_maker->{for};
+
+  # my ($op, $bind, $ident, $bind_attrs, $select, $cond, $order, $rows, $offset) 
+  #  = $self->_select_args($ident, $select, $cond, $attrs);
+  my ($op, $bind, $ident, $bind_attrs, @args) =
+    $self->_select_args(@_);
+
+  # my ($sql, $bind) = $self->_prep_for_execute($op, $bind, $ident, [ $select, $cond, $order, $rows, $offset ]);
+  my ($sql, $prepared_bind) = $self->_prep_for_execute($op, $bind, $ident, \@args);
+
+  return \[ "($sql)", @{ $prepared_bind || [] }];
+}
+
 sub _select_args {
   my ($self, $ident, $select, $condition, $attrs) = @_;
 
@@ -1582,6 +1568,27 @@ sub bind_attribute_by_data_type {
     return;
 }
 
+=head2 is_datatype_numeric
+
+Given a datatype from column_info, returns a boolean value indicating if
+the current RDBMS considers it a numeric value. This controls how
+L<DBIx::Class::Row/set_column> decides whether to mark the column as
+dirty - when the datatype is deemed numeric a C<< != >> comparison will
+be performed instead of the usual C<eq>.
+
+=cut
+
+sub is_datatype_numeric {
+  my ($self, $dt) = @_;
+
+  return 0 unless $dt;
+
+  return $dt =~ /^ (?:
+    numeric | int(?:eger)? | (?:tiny|small|medium|big)int | dec(?:imal)? | real | float | double (?: \s+ precision)? | (?:big)?serial
+  ) $/ix;
+}
+
+
 =head2 create_ddl_dir (EXPERIMENTAL)
 
 =over 4
index 5e125c9..7fada30 100644 (file)
@@ -45,6 +45,8 @@ sub backup
   return $backupfile;
 }
 
+sub datetime_parser_type { return "DateTime::Format::SQLite"; } 
+
 1;
 
 =head1 NAME
index d0d6aef..380242d 100644 (file)
@@ -5,9 +5,8 @@ use Test::More;
 use lib qw(t/lib);
 use DBICTest;
 
-eval { require DateTime::Format::MySQL };
-
-plan $@ ? ( skip_all => 'Requires DateTime::Format::MySQL' )
+eval { require DateTime::Format::SQLite };
+plan $@ ? ( skip_all => 'Requires DateTime::Format::SQLite' )
         : ( tests => 3 );
 
 my $schema = DBICTest->init_schema(
@@ -24,8 +23,6 @@ is(
 
 my $parser = $schema->storage->datetime_parser();
 
-# We're currently expecting a MySQL parser. May change in future.
-is($parser, 'DateTime::Format::MySQL', 'Got expected datetime_parser');
-
+is($parser, 'DateTime::Format::SQLite', 'Got expected storage-set datetime_parser');
 isa_ok($schema->storage, 'DBIx::Class::Storage::DBI::SQLite', 'storage');
 
index 629d49d..b3eda4a 100644 (file)
@@ -11,7 +11,7 @@ my $schema = DBICTest->init_schema();
 
 plan tests => 106;
 
-eval { require DateTime::Format::MySQL };
+eval { require DateTime::Format::SQLite };
 my $NO_DTFM = $@ ? 1 : 0;
 
 my @art = $schema->resultset("Artist")->search({ }, { order_by => 'name DESC'});
@@ -195,7 +195,7 @@ is( $schema->resultset("Track")->find(100)->title, 'Insert or Update - updated',
 
 # get_inflated_columns w/relation and accessor alias
 SKIP: {
-    skip "This test requires DateTime::Format::MySQL", 8 if $NO_DTFM;
+    skip "This test requires DateTime::Format::SQLite", 8 if $NO_DTFM;
 
     isa_ok($new->updated_date, 'DateTime', 'have inflated object via accessor');
     my %tdata = $new->get_inflated_columns;
@@ -389,7 +389,7 @@ lives_ok (sub { my $newlink = $newbook->link}, "stringify to false value doesn't
 
 # test get_inflated_columns with objects
 SKIP: {
-    skip "This test requires DateTime::Format::MySQL", 5 if $NO_DTFM;
+    skip "This test requires DateTime::Format::SQLite", 5 if $NO_DTFM;
     my $event = $schema->resultset('Event')->search->first;
     my %edata = $event->get_inflated_columns;
     is($edata{'id'}, $event->id, 'got id');
index 49f7967..72e6552 100644 (file)
@@ -1,6 +1,13 @@
 use strict;
 use warnings;  
 
+# use this if you keep a copy of DBD::Sybase linked to FreeTDS somewhere else
+BEGIN {
+  if (my $lib_dirs = $ENV{DBICTEST_MSSQL_PERL5LIB}) {
+    unshift @INC, $_ for split /:/, $lib_dirs;
+  }
+}
+
 use Test::More;
 use lib qw(t/lib);
 use DBICTest;
diff --git a/t/89inflate_datetime.t b/t/89inflate_datetime.t
deleted file mode 100644 (file)
index d165c33..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-use strict;
-use warnings;
-
-use Test::More;
-use lib qw(t/lib);
-use DBICTest;
-
-{
-  local $SIG{__WARN__} = sub { warn @_ if $_[0] !~ /extra \=\> .+? has been deprecated/ };
-  DBICTest::Schema->load_classes('EventTZDeprecated');
-  DBICTest::Schema->load_classes('EventTZPg');
-}
-
-my $schema = DBICTest->init_schema();
-
-plan tests => 57;
-
-SKIP: {
-  eval { require DateTime::Format::MySQL };
-  skip "Need DateTime::Format::MySQL for inflation tests", 50  if $@;
-
-
-# inflation test
-my $event = $schema->resultset("Event")->find(1);
-
-isa_ok($event->starts_at, 'DateTime', 'DateTime returned');
-
-# klunky, but makes older Test::More installs happy
-my $starts = $event->starts_at;
-is("$starts", '2006-04-25T22:24:33', 'Correct date/time');
-
-TODO: {
-  local $TODO = "We can't do this yet before 0.09" if DBIx::Class->VERSION < 0.09;
-
-  ok(my $row =
-    $schema->resultset('Event')->search({ starts_at => $starts })->single);
-  is(eval { $row->id }, 1, 'DT in search');
-
-  ok($row =
-    $schema->resultset('Event')->search({ starts_at => { '>=' => $starts } })->single);
-  is(eval { $row->id }, 1, 'DT in search with condition');
-}
-
-# create using DateTime
-my $created = $schema->resultset('Event')->create({
-    starts_at => DateTime->new(year=>2006, month=>6, day=>18),
-    created_on => DateTime->new(year=>2006, month=>6, day=>23)
-});
-my $created_start = $created->starts_at;
-
-isa_ok($created->starts_at, 'DateTime', 'DateTime returned');
-is("$created_start", '2006-06-18T00:00:00', 'Correct date/time');
-
-## timestamp field
-isa_ok($event->created_on, 'DateTime', 'DateTime returned');
-
-## varchar fields
-isa_ok($event->varchar_date, 'DateTime', 'DateTime returned');
-isa_ok($event->varchar_datetime, 'DateTime', 'DateTime returned');
-
-## skip inflation field
-isnt(ref($event->skip_inflation), 'DateTime', 'No DateTime returned for skip inflation column');
-
-# klunky, but makes older Test::More installs happy
-my $createo = $event->created_on;
-is("$createo", '2006-06-22T21:00:05', 'Correct date/time');
-
-my $created_cron = $created->created_on;
-
-isa_ok($created->created_on, 'DateTime', 'DateTime returned');
-is("$created_cron", '2006-06-23T00:00:00', 'Correct date/time');
-
-
-# Test "timezone" parameter
-
-foreach my $tbl (qw/EventTZ EventTZDeprecated/) {
-  my $event_tz = $schema->resultset($tbl)->create({
-      starts_at => DateTime->new(year=>2007, month=>12, day=>31, time_zone => "America/Chicago" ),
-      created_on => DateTime->new(year=>2006, month=>1, day=>31,
-          hour => 13, minute => 34, second => 56, time_zone => "America/New_York" ),
-  });
-
-  is ($event_tz->starts_at->day_name, "Montag", 'Locale de_DE loaded: day_name');
-  is ($event_tz->starts_at->month_name, "Dezember", 'Locale de_DE loaded: month_name');
-  is ($event_tz->created_on->day_name, "Tuesday", 'Default locale loaded: day_name');
-  is ($event_tz->created_on->month_name, "January", 'Default locale loaded: month_name');
-
-  my $starts_at = $event_tz->starts_at;
-  is("$starts_at", '2007-12-31T00:00:00', 'Correct date/time using timezone');
-
-  my $created_on = $event_tz->created_on;
-  is("$created_on", '2006-01-31T12:34:56', 'Correct timestamp using timezone');
-  is($event_tz->created_on->time_zone->name, "America/Chicago", "Correct timezone");
-
-  my $loaded_event = $schema->resultset($tbl)->find( $event_tz->id );
-
-  isa_ok($loaded_event->starts_at, 'DateTime', 'DateTime returned');
-  $starts_at = $loaded_event->starts_at;
-  is("$starts_at", '2007-12-31T00:00:00', 'Loaded correct date/time using timezone');
-  is($starts_at->time_zone->name, 'America/Chicago', 'Correct timezone');
-
-  isa_ok($loaded_event->created_on, 'DateTime', 'DateTime returned');
-  $created_on = $loaded_event->created_on;
-  is("$created_on", '2006-01-31T12:34:56', 'Loaded correct timestamp using timezone');
-  is($created_on->time_zone->name, 'America/Chicago', 'Correct timezone');
-
-  # Test floating timezone warning
-  # We expect one warning
-  SKIP: {
-      skip "ENV{DBIC_FLOATING_TZ_OK} was set, skipping", 1 if $ENV{DBIC_FLOATING_TZ_OK};
-      local $SIG{__WARN__} = sub {
-          like(
-              shift,
-              qr/You're using a floating timezone, please see the documentation of DBIx::Class::InflateColumn::DateTime for an explanation/,
-              'Floating timezone warning'
-          );
-      };
-      my $event_tz_floating = $schema->resultset($tbl)->create({
-          starts_at => DateTime->new(year=>2007, month=>12, day=>31, ),
-          created_on => DateTime->new(year=>2006, month=>1, day=>31,
-              hour => 13, minute => 34, second => 56, ),
-      });
-      delete $SIG{__WARN__};
-  };
-
-  # This should fail to set
-  my $prev_str = "$created_on";
-  $loaded_event->update({ created_on => '0000-00-00' });
-  is("$created_on", $prev_str, "Don't update invalid dates");
-
-  my $invalid = $schema->resultset('Event')->create({
-      starts_at  => '0000-00-00',
-      created_on => $created_on
-  });
-
-  is( $invalid->get_column('starts_at'), '0000-00-00', "Invalid date stored" );
-  is( $invalid->starts_at, undef, "Inflate to undef" );
-
-  $invalid->created_on('0000-00-00');
-  $invalid->update;
-
-  {
-      local $@;
-      eval { $invalid->created_on };
-      like( $@, qr/invalid date format/i, "Invalid date format exception");
-  }
-}
-
-## varchar field using inflate_date => 1
-my $varchar_date = $event->varchar_date;
-is("$varchar_date", '2006-07-23T00:00:00', 'Correct date/time');
-
-## varchar field using inflate_datetime => 1
-my $varchar_datetime = $event->varchar_datetime;
-is("$varchar_datetime", '2006-05-22T19:05:07', 'Correct date/time');
-
-## skip inflation field
-my $skip_inflation = $event->skip_inflation;
-is ("$skip_inflation", '2006-04-21 18:04:06', 'Correct date/time');
-
-} # Skip if no MySQL DT::Formatter
-
-SKIP: {
-  eval { require DateTime::Format::Pg };
-  skip ('Need DateTime::Format::Pg for timestamp inflation tests', 3) if $@;
-
-  my $event = $schema->resultset("EventTZPg")->find(1);
-  $event->update({created_on => '2009-01-15 17:00:00+00'});
-  $event->discard_changes;
-  isa_ok($event->created_on, "DateTime") or diag $event->created_on;
-  is($event->created_on->time_zone->name, "America/Chicago", "Timezone changed");
-  # Time zone difference -> -6hours
-  is($event->created_on->iso8601, "2009-01-15T11:00:00", "Time with TZ correct");
-}
index d99d201..c4a65a2 100644 (file)
@@ -51,6 +51,7 @@ my $sql_maker = $schema->storage->sql_maker;
 
 # Make sure the carp/croak override in SQLA works (via SQLAHacks)
 my $file = __FILE__;
+$file = "\Q$file\E";
 throws_ok (sub {
   $schema->resultset ('Artist')->search ({}, { order_by => { -asc => 'stuff', -desc => 'staff' } } )->as_query;
 }, qr/$file/, 'Exception correctly croak()ed');
similarity index 89%
rename from t/68inflate.t
rename to t/inflate/core.t
index 3aa428d..cb5bcaf 100644 (file)
@@ -10,15 +10,12 @@ my $schema = DBICTest->init_schema();
 eval { require DateTime };
 plan skip_all => "Need DateTime for inflation tests" if $@;
 
-plan tests => 21;
+plan tests => 22;
 
-$schema->class('CD')
-#DBICTest::Schema::CD
-->inflate_column( 'year',
+$schema->class('CD') ->inflate_column( 'year',
     { inflate => sub { DateTime->new( year => shift ) },
       deflate => sub { shift->year } }
 );
-Class::C3->reinitialize;
 
 # inflation test
 my $cd = $schema->resultset("CD")->find(3);
@@ -66,6 +63,16 @@ eval { $cd->set_inflated_column('year', \'year + 1') };
 ok(!$@, 'set_inflated_column to "year + 1"');
 $cd->update;
 
+TODO: {
+  local $TODO = 'this was left in without a TODO - should it work?';
+
+  eval {
+    $cd->store_inflated_column('year', \'year + 1');
+    is_deeply( $cd->year, \'year + 1', 'deflate ok' );
+  };
+  ok(!$@, 'store_inflated_column to "year + 1"');
+}
+
 $cd = $schema->resultset("CD")->find(3);                 
 is( $cd->year->year, $before_year+1, 'deflate ok' );
 
@@ -104,9 +111,3 @@ my $copy = $cd->copy({ year => $now, title => "zemoose" });
 
 isnt( $copy->year->year, $before_year, "copy" );
  
-# eval { $cd->store_inflated_column('year', \'year + 1') };
-# print STDERR "ERROR: $@" if($@);
-# ok(!$@, 'store_inflated_column to "year + 1"');
-
-# is_deeply( $cd->year, \'year + 1', 'deflate ok' );
-
diff --git a/t/inflate/datetime.t b/t/inflate/datetime.t
new file mode 100644 (file)
index 0000000..ae8fc3b
--- /dev/null
@@ -0,0 +1,76 @@
+use strict;
+use warnings;
+
+use Test::More;
+use lib qw(t/lib);
+use DBICTest;
+
+my $schema = DBICTest->init_schema();
+
+eval { require DateTime::Format::SQLite };
+plan $@
+  ? ( skip_all => "Need DateTime::Format::SQLite for DT inflation tests" )
+  : ( tests => 18 )
+;
+
+# inflation test
+my $event = $schema->resultset("Event")->find(1);
+
+isa_ok($event->starts_at, 'DateTime', 'DateTime returned');
+
+# klunky, but makes older Test::More installs happy
+my $starts = $event->starts_at;
+is("$starts", '2006-04-25T22:24:33', 'Correct date/time');
+
+TODO: {
+  local $TODO = "We can't do this yet before 0.09" if DBIx::Class->VERSION < 0.09;
+
+  ok(my $row =
+    $schema->resultset('Event')->search({ starts_at => $starts })->single);
+  is(eval { $row->id }, 1, 'DT in search');
+
+  ok($row =
+    $schema->resultset('Event')->search({ starts_at => { '>=' => $starts } })->single);
+  is(eval { $row->id }, 1, 'DT in search with condition');
+}
+
+# create using DateTime
+my $created = $schema->resultset('Event')->create({
+    starts_at => DateTime->new(year=>2006, month=>6, day=>18),
+    created_on => DateTime->new(year=>2006, month=>6, day=>23)
+});
+my $created_start = $created->starts_at;
+
+isa_ok($created->starts_at, 'DateTime', 'DateTime returned');
+is("$created_start", '2006-06-18T00:00:00', 'Correct date/time');
+
+## timestamp field
+isa_ok($event->created_on, 'DateTime', 'DateTime returned');
+
+## varchar fields
+isa_ok($event->varchar_date, 'DateTime', 'DateTime returned');
+isa_ok($event->varchar_datetime, 'DateTime', 'DateTime returned');
+
+## skip inflation field
+isnt(ref($event->skip_inflation), 'DateTime', 'No DateTime returned for skip inflation column');
+
+# klunky, but makes older Test::More installs happy
+my $createo = $event->created_on;
+is("$createo", '2006-06-22T21:00:05', 'Correct date/time');
+
+my $created_cron = $created->created_on;
+
+isa_ok($created->created_on, 'DateTime', 'DateTime returned');
+is("$created_cron", '2006-06-23T00:00:00', 'Correct date/time');
+
+## varchar field using inflate_date => 1
+my $varchar_date = $event->varchar_date;
+is("$varchar_date", '2006-07-23T00:00:00', 'Correct date/time');
+
+## varchar field using inflate_datetime => 1
+my $varchar_datetime = $event->varchar_datetime;
+is("$varchar_datetime", '2006-05-22T19:05:07', 'Correct date/time');
+
+## skip inflation field
+my $skip_inflation = $event->skip_inflation;
+is ("$skip_inflation", '2006-04-21 18:04:06', 'Correct date/time');
diff --git a/t/inflate/datetime_mysql.t b/t/inflate/datetime_mysql.t
new file mode 100644 (file)
index 0000000..51368ad
--- /dev/null
@@ -0,0 +1,97 @@
+use strict;
+use warnings;
+
+use Test::More;
+use Test::Exception;
+use lib qw(t/lib);
+use DBICTest;
+use DBICTest::Schema;
+
+{
+  local $SIG{__WARN__} = sub { warn @_ if $_[0] !~ /extra \=\> .+? has been deprecated/ };
+  DBICTest::Schema->load_classes('EventTZ');
+  DBICTest::Schema->load_classes('EventTZDeprecated');
+}
+
+eval { require DateTime::Format::MySQL };
+plan $@ 
+  ? ( skip_all => "Need DateTime::Format::MySQL for inflation tests")
+  : ( tests => 33 )
+;
+
+my $schema = DBICTest->init_schema();
+
+# Test "timezone" parameter
+foreach my $tbl (qw/EventTZ EventTZDeprecated/) {
+  my $event_tz = $schema->resultset($tbl)->create({
+      starts_at => DateTime->new(year=>2007, month=>12, day=>31, time_zone => "America/Chicago" ),
+      created_on => DateTime->new(year=>2006, month=>1, day=>31,
+          hour => 13, minute => 34, second => 56, time_zone => "America/New_York" ),
+  });
+
+  is ($event_tz->starts_at->day_name, "Montag", 'Locale de_DE loaded: day_name');
+  is ($event_tz->starts_at->month_name, "Dezember", 'Locale de_DE loaded: month_name');
+  is ($event_tz->created_on->day_name, "Tuesday", 'Default locale loaded: day_name');
+  is ($event_tz->created_on->month_name, "January", 'Default locale loaded: month_name');
+
+  my $starts_at = $event_tz->starts_at;
+  is("$starts_at", '2007-12-31T00:00:00', 'Correct date/time using timezone');
+
+  my $created_on = $event_tz->created_on;
+  is("$created_on", '2006-01-31T12:34:56', 'Correct timestamp using timezone');
+  is($event_tz->created_on->time_zone->name, "America/Chicago", "Correct timezone");
+
+  my $loaded_event = $schema->resultset($tbl)->find( $event_tz->id );
+
+  isa_ok($loaded_event->starts_at, 'DateTime', 'DateTime returned');
+  $starts_at = $loaded_event->starts_at;
+  is("$starts_at", '2007-12-31T00:00:00', 'Loaded correct date/time using timezone');
+  is($starts_at->time_zone->name, 'America/Chicago', 'Correct timezone');
+
+  isa_ok($loaded_event->created_on, 'DateTime', 'DateTime returned');
+  $created_on = $loaded_event->created_on;
+  is("$created_on", '2006-01-31T12:34:56', 'Loaded correct timestamp using timezone');
+  is($created_on->time_zone->name, 'America/Chicago', 'Correct timezone');
+
+  # Test floating timezone warning
+  # We expect one warning
+  SKIP: {
+      skip "ENV{DBIC_FLOATING_TZ_OK} was set, skipping", 1 if $ENV{DBIC_FLOATING_TZ_OK};
+      local $SIG{__WARN__} = sub {
+          like(
+              shift,
+              qr/You're using a floating timezone, please see the documentation of DBIx::Class::InflateColumn::DateTime for an explanation/,
+              'Floating timezone warning'
+          );
+      };
+      my $event_tz_floating = $schema->resultset($tbl)->create({
+          starts_at => DateTime->new(year=>2007, month=>12, day=>31, ),
+          created_on => DateTime->new(year=>2006, month=>1, day=>31,
+              hour => 13, minute => 34, second => 56, ),
+      });
+      delete $SIG{__WARN__};
+  };
+
+  # This should fail to set
+  my $prev_str = "$created_on";
+  $loaded_event->update({ created_on => '0000-00-00' });
+  is("$created_on", $prev_str, "Don't update invalid dates");
+}
+
+# Test invalid DT
+my $invalid = $schema->resultset('EventTZ')->create({
+  starts_at  => '0000-00-00',
+  created_on => DateTime->now,
+});
+
+is( $invalid->get_column('starts_at'), '0000-00-00', "Invalid date stored" );
+is( $invalid->starts_at, undef, "Inflate to undef" );
+
+$invalid->created_on('0000-00-00');
+$invalid->update;
+
+throws_ok (
+  sub { $invalid->created_on },
+  qr/invalid date format/i,
+  "Invalid date format exception"
+);
diff --git a/t/inflate/datetime_pg.t b/t/inflate/datetime_pg.t
new file mode 100644 (file)
index 0000000..054edf1
--- /dev/null
@@ -0,0 +1,30 @@
+use strict;
+use warnings;
+
+use Test::More;
+use lib qw(t/lib);
+use DBICTest;
+
+{
+  local $SIG{__WARN__} = sub { warn @_ if $_[0] !~ /extra \=\> .+? has been deprecated/ };
+  DBICTest::Schema->load_classes('EventTZPg');
+}
+
+eval { require DateTime::Format::Pg };
+plan $@
+  ? ( skip_all =>  'Need DateTime::Format::Pg for timestamp inflation tests')
+  : ( tests => 3 )
+;
+
+
+my $schema = DBICTest->init_schema();
+
+{
+  my $event = $schema->resultset("EventTZPg")->find(1);
+  $event->update({created_on => '2009-01-15 17:00:00+00'});
+  $event->discard_changes;
+  isa_ok($event->created_on, "DateTime") or diag $event->created_on;
+  is($event->created_on->time_zone->name, "America/Chicago", "Timezone changed");
+  # Time zone difference -> -6hours
+  is($event->created_on->iso8601, "2009-01-15T11:00:00", "Time with TZ correct");
+}
similarity index 96%
rename from t/96file_column.t
rename to t/inflate/file_column.t
index f4a166f..a9a75f0 100644 (file)
@@ -13,8 +13,8 @@ my $schema = DBICTest->init_schema();
 plan tests => 10;
 
 my $rs = $schema->resultset('FileColumn');
-my $fname = '96file_column.t';
-my $source_file = file('t', $fname);
+my $source_file = file(__FILE__);
+my $fname = $source_file->basename;
 my $fh = $source_file->open('r') or die "failed to open $source_file: $!\n";
 my $fc = eval {
     $rs->create({ file => { handle => $fh, filename => $fname } })
index caecdc1..c56c1bd 100644 (file)
@@ -10,7 +10,7 @@ __PACKAGE__->table('event');
 
 __PACKAGE__->add_columns(
   id => { data_type => 'integer', is_auto_increment => 1 },
-  starts_at => { data_type => 'datetime', datetime_undef_if_invalid => 1 },
+  starts_at => { data_type => 'datetime' },
   created_on => { data_type => 'timestamp' },
   varchar_date => { data_type => 'varchar', inflate_date => 1, size => 20, is_nullable => 1 },
   varchar_datetime => { data_type => 'varchar', inflate_datetime => 1, size => 20, is_nullable => 1 },
index 321b279..2d8df28 100644 (file)
@@ -10,10 +10,15 @@ __PACKAGE__->table('event');
 
 __PACKAGE__->add_columns(
   id => { data_type => 'integer', is_auto_increment => 1 },
-  starts_at => { data_type => 'datetime', timezone => "America/Chicago", locale => 'de_DE' },
+  starts_at => { data_type => 'datetime', timezone => "America/Chicago", locale => 'de_DE', datetime_undef_if_invalid => 1 },
   created_on => { data_type => 'timestamp', timezone => "America/Chicago", floating_tz_ok => 1 },
 );
 
 __PACKAGE__->set_primary_key('id');
 
+sub _datetime_parser {
+  require DateTime::Format::MySQL;
+  DateTime::Format::MySQL->new();
+}
+
 1;
index 8a9a11d..a667976 100644 (file)
@@ -16,4 +16,10 @@ __PACKAGE__->add_columns(
 
 __PACKAGE__->set_primary_key('id');
 
+sub _datetime_parser {
+  require DateTime::Format::MySQL;
+  DateTime::Format::MySQL->new();
+}
+
+
 1;
diff --git a/t/update/type_aware.t b/t/update/type_aware.t
new file mode 100644 (file)
index 0000000..05f86c3
--- /dev/null
@@ -0,0 +1,27 @@
+use strict;
+use warnings;  
+
+use Test::More;
+use lib qw(t/lib);
+use DBICTest;
+
+my $schema = DBICTest->init_schema();
+
+plan tests => 4;
+
+my $artist = $schema->resultset ('Artist')->first;
+ok (!$artist->get_dirty_columns, 'Artist is clean' );
+
+$artist->rank (13);
+ok (!$artist->get_dirty_columns, 'Artist is clean after num value update' );
+$artist->discard_changes;
+
+$artist->rank ('13.00');
+ok (!$artist->get_dirty_columns, 'Artist is clean after string value update' );
+$artist->discard_changes;
+
+# override column info
+$artist->result_source->column_info ('rank')->{is_numeric} = 0;
+$artist->rank ('13.00');
+ok ($artist->get_dirty_columns, 'Artist is updated after is_numeric override' );
+$artist->discard_changes;