support the DATE data type for Firebird
Rafael Kitover [Thu, 11 Feb 2010 12:27:19 +0000 (12:27 +0000)]
lib/DBIx/Class/Storage/DBI/InterBase.pm
lib/DBIx/Class/Storage/DBI/ODBC/Firebird.pm
t/inflate/datetime_firebird.t
t/lib/DBICTest/Schema/Event.pm
t/lib/sqlite.sql

index 5f9e799..2ba2542 100644 (file)
@@ -24,11 +24,8 @@ L<DBIx::Class::InflateColumn::DateTime> support.
 
 For ODBC support, see L<DBIx::Class::Storage::DBI::ODBC::Firebird>.
 
-To turn on L<DBIx::Class::InflateColumn::DateTime> support, add:
-
-    on_connect_call => 'datetime_setup'
-
-to your L<DBIx::Class::Storage::DBI/connect_info>.
+To turn on L<DBIx::Class::InflateColumn::DateTime> support, see
+L</connect_call_datetime_setup>.
 
 =cut
 
@@ -188,6 +185,15 @@ See L<DBD::InterBase> for more details.
 The C<TIMESTAMP> data type supports up to 4 digits after the decimal point for
 second precision. The full precision is used.
 
+The C<DATE> data type stores the date portion only, and it B<MUST> be declared
+with:
+
+  data_type => 'date'
+
+in your Result class.
+
+Timestamp columns can be declared with either C<datetime> or C<timestamp>.
+
 You will need the L<DateTime::Format::Strptime> module for inflation to work.
 
 For L<DBIx::Class::Storage::DBI::ODBC::Firebird>, this is a noop and sub-second
@@ -201,18 +207,56 @@ sub connect_call_datetime_setup {
   $self->_get_dbh->{ib_time_all} = 'ISO';
 }
 
+sub datetime_parser_type {
+  'DBIx::Class::Storage::DBI::InterBase::DateTime::Format'
+}
 
-# from MSSQL
+package # hide from PAUSE
+  DBIx::Class::Storage::DBI::InterBase::DateTime::Format;
 
-sub build_datetime_parser {
-  my $self = shift;
-  my $type = "DateTime::Format::Strptime";
-  eval "use ${type}";
-  $self->throw_exception("Couldn't load ${type}: $@") if $@;
-  return $type->new(
-    pattern => '%Y-%m-%d %H:%M:%S.%4N', # %F %T
+my $timestamp_format = '%Y-%m-%d %H:%M:%S.%4N'; # %F %T
+my $date_format      = '%Y-%m-%d';
+
+my ($timestamp_parser, $date_parser);
+
+sub parse_datetime {
+  shift;
+  require DateTime::Format::Strptime;
+  $timestamp_parser ||= DateTime::Format::Strptime->new(
+    pattern  => $timestamp_format,
+    on_error => 'croak',
+  );
+  return $timestamp_parser->parse_datetime(shift);
+}
+
+sub format_datetime {
+  shift;
+  require DateTime::Format::Strptime;
+  $timestamp_parser ||= DateTime::Format::Strptime->new(
+    pattern  => $timestamp_format,
+    on_error => 'croak',
+  );
+  return $timestamp_parser->format_datetime(shift);
+}
+
+sub parse_date {
+  shift;
+  require DateTime::Format::Strptime;
+  $date_parser ||= DateTime::Format::Strptime->new(
+    pattern  => $date_format,
+    on_error => 'croak',
+  );
+  return $date_parser->parse_datetime(shift);
+}
+
+sub format_date {
+  shift;
+  require DateTime::Format::Strptime;
+  $date_parser ||= DateTime::Format::Strptime->new(
+    pattern  => $date_format,
     on_error => 'croak',
   );
+  return $date_parser->format_datetime(shift);
 }
 
 1;
index e143b5b..cb36879 100644 (file)
@@ -21,22 +21,9 @@ L<http://www.firebirdnews.org/?p=1324>
 
 =cut
 
-# XXX seemingly no equivalent to ib_time_all in DBD::InterBase via ODBC
+# XXX seemingly no equivalent to ib_time_all from DBD::InterBase via ODBC
 sub connect_call_datetime_setup { 1 }
 
-# from MSSQL
-
-sub build_datetime_parser {
-  my $self = shift;
-  my $type = "DateTime::Format::Strptime";
-  eval "use ${type}";
-  $self->throw_exception("Couldn't load ${type}: $@") if $@;
-  return $type->new(
-    pattern => '%Y-%m-%d %H:%M:%S', # %F %T
-    on_error => 'croak',
-  );
-}
-
 # we don't need DBD::InterBase-specific initialization
 sub _init { 1 }
 
@@ -46,6 +33,39 @@ sub _set_sql_dialect { 1 }
 # releasing savepoints doesn't work, but that shouldn't matter
 sub _svp_release { 1 }
 
+sub datetime_parser_type {
+  'DBIx::Class::Storage::DBI::ODBC::Firebird::DateTime::Format'
+}
+
+package # hide from PAUSE
+  DBIx::Class::Storage::DBI::ODBC::Firebird::DateTime::Format;
+
+# inherit parse/format date
+our @ISA = 'DBIx::Class::Storage::DBI::InterBase::DateTime::Format';
+
+my $timestamp_format = '%Y-%m-%d %H:%M:%S'; # %F %T, no fractional part
+my $timestamp_parser;
+
+sub parse_datetime {
+  shift;
+  require DateTime::Format::Strptime;
+  $timestamp_parser ||= DateTime::Format::Strptime->new(
+    pattern  => $timestamp_format,
+    on_error => 'croak',
+  );
+  return $timestamp_parser->parse_datetime(shift);
+}
+
+sub format_datetime {
+  shift;
+  require DateTime::Format::Strptime;
+  $timestamp_parser ||= DateTime::Format::Strptime->new(
+    pattern  => $timestamp_format,
+    on_error => 'croak',
+  );
+  return $timestamp_parser->format_datetime(shift);
+}
+
 1;
 
 =head1 CAVEATS
@@ -57,11 +77,6 @@ sub _svp_release { 1 }
 This driver (unlike L<DBD::InterBase>) does not currently support reading or
 writing C<TIMESTAMP> values with sub-second precision.
 
-=item *
-
-Releasing savepoints does not work, but you should still be able to safely use
-savepoints.
-
 =back
 
 =head1 AUTHOR
index 714c127..1a3136e 100644 (file)
@@ -33,40 +33,50 @@ my @info = (
 my $schema;
 
 foreach my $conn_idx (0..$#info) {
-  my ($dsn, $user, $pass) = @{ $info[$conn_idx] };
+  my ($dsn, $user, $pass) = @{ $info[$conn_idx] || [] };
 
   next unless $dsn;
 
   $schema = DBICTest::Schema->connect($dsn, $user, $pass, {
+    quote_char => '"',
+    name_sep   => '.', 
     on_connect_call => [ 'datetime_setup' ],
   });
 
   my $sg = Scope::Guard->new(\&cleanup);
 
-  eval { $schema->storage->dbh->do("DROP TABLE event") };
-  $schema->storage->dbh->do(<<"SQL");
-  CREATE TABLE event (
-    id INT PRIMARY KEY,
-    created_on TIMESTAMP
+  eval { $schema->storage->dbh->do('DROP TABLE "event"') };
+  $schema->storage->dbh->do(<<'SQL');
+  CREATE TABLE "event" (
+    "id" INT PRIMARY KEY,
+    "starts_at" DATE,
+    "created_on" TIMESTAMP
   )
 SQL
-  my $now = DateTime->now;
-  $now->set_nanosecond(555600000);
+  my $rs   = $schema->resultset('Event');
+
+  my $dt = DateTime->now;
+  $dt->set_nanosecond($dsn =~ /odbc/i ? 0 : 555600000);
+
+  my $date_only = DateTime->new(
+    year => $dt->year, month => $dt->month, day => $dt->day
+  );
+
   my $row;
-  ok( $row = $schema->resultset('Event')->create({
-        id => 1,
-        created_on => $now,
-      }));
-  ok( $row = $schema->resultset('Event')
-    ->search({ id => 1 }, { select => ['created_on'] })
+  ok( $row = $rs->create({
+    id => 1,
+    starts_at => $date_only, 
+    created_on => $dt,
+  }));
+  ok( $row = $rs->search({ id => 1 }, { select => [qw/starts_at created_on/] })
     ->first
   );
-  is $row->created_on, $now, 'DateTime roundtrip';
+  is $row->created_on, $dt, 'TIMESTAMP as DateTime roundtrip';
 
-  if ($conn_idx == 0) { # skip for ODBC
-    cmp_ok $row->created_on->nanosecond, '==', 555600000,
-      'fractional part of a second survived';
-  }
+  cmp_ok $row->created_on->nanosecond, '==', $dt->nanosecond,
+    'fractional part of a second survived' if 0+$dt->nanosecond;
+
+  is $row->starts_at, $date_only, 'DATE as DateTime roundtrip';
 }
 
 done_testing;
@@ -80,5 +90,5 @@ sub cleanup {
   };
   return unless $dbh;
 
-  eval { $dbh->do("DROP TABLE $_") } for qw/event/;
+  eval { $dbh->do(qq{DROP TABLE "$_"}) } for qw/event/;
 }
index 22b655e..0c477c8 100644 (file)
@@ -10,7 +10,10 @@ __PACKAGE__->table('event');
 
 __PACKAGE__->add_columns(
   id => { data_type => 'integer', is_auto_increment => 1 },
-  starts_at => { data_type => 'datetime' },
+
+# this MUST be 'date' for the Firebird tests
+  starts_at => { data_type => 'date' },
+
   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 1459edc..4dcc31b 100644 (file)
@@ -1,6 +1,6 @@
 -- 
 -- Created by SQL::Translator::Producer::SQLite
--- Created on Sat Feb  6 08:01:11 2010
+-- Created on Thu Feb 11 07:23:33 2010
 -- 
 ;
 
@@ -69,7 +69,7 @@ CREATE TABLE encoded (
 --
 CREATE TABLE event (
   id INTEGER PRIMARY KEY NOT NULL,
-  starts_at datetime NOT NULL,
+  starts_at date NOT NULL,
   created_on timestamp NOT NULL,
   varchar_date varchar(20),
   varchar_datetime varchar(20),