From: Tina Mueller Date: Fri, 14 Aug 2015 11:51:53 +0000 (+0200) Subject: Add a proof of concept test for copy() with assymetric IC::DT X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=dbsrgits%2FDBIx-Class.git;a=commitdiff_plain;h=11f335cd8c3310770f6c8d0724a54dd528119734 Add a proof of concept test for copy() with assymetric IC::DT What a mess. The core of the problem is that some of our IC::DT in/deflator pairs are *not* symmetric. That is for things to roundtrip one needs values to pass through the database, which is configured "just properly wrong" to perform the second half of this evil dance. Of course this break copy() and likely other things I do not know about. Given there is nothing one can do about the core problem (huge install base) a minimally invasive workaround has been devised and tested here. Refer to `git show 993fa9b | perl -ne 'print if 110..134'` for the exact snippet you need to place in your base result class to make everything work again. This is terrible. -- ribasushi For completeness: here is a full list of individual inflators (as of Apr 2016) and which ones are broken beyond repair: ACCESS 2016-04-13 07:42:58 2016-04-13T07:42:58 2016-04-13 07:42:58 (via DateTime::Format::MySQL) DB2 2016-04-13-07.42.58 2016-04-13T07:42:58 2016-04-13-07.42.58 (via DateTime::Format::DB2) MSSQL 2016-04-13 07:42:58.000 2016-04-13T07:42:58 2016-04-13 07:42:58.000 (via DBIx::Class::Storage::DBI::MSSQL::DateTime::Format) Pg 2016-04-13 07:42:58+0000 2016-04-13T07:42:58 2016-04-13 07:42:58+0000 (via DateTime::Format::Pg) ADO 2016-04-13 07:42:58 2016-04-13T07:42:58 2016-04-13 07:42:58 (via DateTime::Format::MySQL) NoBindVars 2016-04-13 07:42:58 2016-04-13T07:42:58 2016-04-13 07:42:58 (via DateTime::Format::MySQL) SQLAnywhere 2016-04-13 07:42:58.000000 2016-04-13T07:42:58 2016-04-13 07:42:58.000000 (via DateTime::Format::Strptime) AutoCast 2016-04-13 07:42:58 2016-04-13T07:42:58 2016-04-13 07:42:58 (via DateTime::Format::MySQL) Firebird 2016-04-13 07:42:58.0000 2016-04-13T07:42:58 2016-04-13 07:42:58.0000 (via DBIx::Class::Storage::DBI::InterBase::DateTime::Format) Informix 2016-04-13 07:42:58.00000 2016-04-13T07:42:58 2016-04-13 07:42:58.00000 (via DBIx::Class::Storage::DBI::Informix::DateTime::Format) ODBC 2016-04-13 07:42:58 2016-04-13T07:42:58 2016-04-13 07:42:58 (via DateTime::Format::MySQL) Sybase 2016-04-13 07:42:58 2016-04-13T07:42:58 2016-04-13 07:42:58 (via DateTime::Format::MySQL) mysql 2016-04-13 07:42:58 2016-04-13T07:42:58 2016-04-13 07:42:58 (via DateTime::Format::MySQL) InterBase 2016-04-13 07:42:58.0000 2016-04-13T07:42:58 2016-04-13 07:42:58.0000 (via DBIx::Class::Storage::DBI::InterBase::DateTime::Format) Oracle 2016-04-13 07:42:58 2016-04-13T07:42:58 2016-04-13 07:42:58 (via DateTime::Format::MySQL) SQLite 2016-04-13 07:42:58 2016-04-13T07:42:58 2016-04-13 07:42:58 (via DateTime::Format::SQLite) Firebird::Common 2016-04-13 07:42:58.0000 2016-04-13T07:42:58 2016-04-13 07:42:58.0000 (via DBIx::Class::Storage::DBI::InterBase::DateTime::Format) Sybase::FreeTDS 2016-04-13 07:42:58 2016-04-13T07:42:58 2016-04-13 07:42:58 (via DateTime::Format::MySQL) Sybase::MSSQL 2016-04-13 07:42:58.000 Your datetime does not match your pattern. at (via DBIx::Class::Storage::DBI::Sybase::Microsoft_SQL_Server::DateTime::Format) Sybase::Microsoft_SQL_Server 2016-04-13 07:42:58.000 Your datetime does not match your pattern. at (via DBIx::Class::Storage::DBI::Sybase::Microsoft_SQL_Server::DateTime::Format) Sybase::ASE 04/13/2016 07:42:58.000 Your datetime does not match your pattern. at (via DBIx::Class::Storage::DBI::Sybase::ASE::DateTime::Format) Sybase::Microsoft_SQL_Server::NoBindVars 2016-04-13 07:42:59.000 Your datetime does not match your pattern. at (via DBIx::Class::Storage::DBI::Sybase::Microsoft_SQL_Server::DateTime::Format) Sybase::ASE::NoBindVars 04/13/2016 07:42:59.000 Your datetime does not match your pattern. at (via DBIx::Class::Storage::DBI::Sybase::ASE::DateTime::Format) Oracle::WhereJoins 2016-04-13 07:42:59 2016-04-13T07:42:59 2016-04-13 07:42:59 (via DateTime::Format::Oracle) Oracle::Generic 2016-04-13 07:42:59 2016-04-13T07:42:59 2016-04-13 07:42:59 (via DateTime::Format::Oracle) ODBC::ACCESS 2016-04-13 07:42:59 2016-04-13T07:42:59 2016-04-13 07:42:59 (via DBIx::Class::Storage::DBI::ODBC::ACCESS::DateTime::Format) ODBC::DB2_400_SQL 2016-04-13-07.42.59 2016-04-13T07:42:59 2016-04-13-07.42.59 (via DateTime::Format::DB2) ODBC::SQL_Anywhere 2016-04-13 07:42:59.000000 2016-04-13T07:42:59 2016-04-13 07:42:59.000000 (via DateTime::Format::Strptime) ODBC::Firebird 2016-04-13 07:42:59.0000 2016-04-13T07:42:59 2016-04-13 07:42:59.0000 (via DBIx::Class::Storage::DBI::InterBase::DateTime::Format) ODBC::Microsoft_SQL_Server 2016-04-13 07:42:59.000 2016-04-13T07:42:59 2016-04-13 07:42:59.000 (via DBIx::Class::Storage::DBI::MSSQL::DateTime::Format) ADO::MS_Jet 04/13/2016 07:42:59 AM 2016-04-13T07:42:59 04/13/2016 07:42:59 AM (via DBIx::Class::Storage::DBI::ADO::MS_Jet::DateTime::Format) ADO::Microsoft_SQL_Server 04/13/2016 07:42:59 AM 2016-04-13T07:42:59 04/13/2016 07:42:59 AM (via DBIx::Class::Storage::DBI::ADO::Microsoft_SQL_Server::DateTime::Format) ... and the program that produces the above ... ~$ perl -I lib -MDateTime -MFile::Find -e ' find({ no_chdir => 1, follow_fast => 1, wanted => sub { -f _ or next; $_ =~ m{DBIx/Class/Storage/DBI/(?!Replicated|IdentityInsert|.*?Cursor|UniqueIdentifier)(.+)\.pm} or next; list_dt_state($1); }}, "lib" ); sub list_dt_state { ( my $id = shift ) =~ s|/|::|g; my $s = "DBIx::Class::Storage::DBI::$id"; my $p = eval "local \$SIG{__WARN__} = sub {}; require $s; $s->build_datetime_parser" or ( printf "%s: %s\n", $id, substr $@, 0, 45 and next ); my $as_string = $p->format_datetime( DateTime->now ); my $half_trip = eval { $p->parse_datetime( $as_string ) } || substr $@, 0, 45; my $full_trip = $@ ? "" : eval { $p->format_datetime( $half_trip ) } || substr $@, 0, 45; printf "%-30s %-26s %-20s %-26s (via %s)\n", $id, $as_string, $half_trip, $full_trip, ( ref $p || $p ), ; } ' --- diff --git a/Changes b/Changes index 2fc18e9..9f8ec85 100644 --- a/Changes +++ b/Changes @@ -62,6 +62,8 @@ Revision history for DBIx::Class autoinc value when inserting rows containing blobs (GH#82) * Misc + - Add explicit test for pathological example of asymmetric IC::DT setup + working with copy() in t/icdt/engine_specific/sybase.t (GH#84) - Fix invalid variable names in ResultSource::View examples - Typo fixes from downstream debian packagers (RT#112007) - Skip tests in a way more intelligent and speedy manner when optional diff --git a/t/icdt/engine_specific/sybase.t b/t/icdt/engine_specific/sybase.t index 72a8bdb..993fa9b 100644 --- a/t/icdt/engine_specific/sybase.t +++ b/t/icdt/engine_specific/sybase.t @@ -7,6 +7,7 @@ use warnings; use Test::More; use Test::Exception; use DBIx::Class::_Util 'scope_guard'; +use Sub::Name; use DBICTest; @@ -94,7 +95,7 @@ SQL %$create_extra, })); ok( $row = $schema->resultset($source) - ->search({ $pk => $row->$pk }, { select => [$col] }) + ->search({ $pk => $row->$pk }, { select => [$pk, $col] }) ->first ); is( $row->$col, $dt, "$type roundtrip" ); @@ -102,6 +103,46 @@ SQL cmp_ok( $row->$col->nanosecond, '==', $sample_dt->{nanosecond}, 'DateTime fractional portion roundtrip' ) if exists $sample_dt->{nanosecond}; + + # Testing an ugly half-solution + # + # copy() uses get_columns() + # + # The values should survive a roundtrip also, but they don't + # because the Sybase ICDT setup is asymmetric + # One *has* to force an inflation/deflation cycle to make the + # values usable to the database + # + # This can be done by marking the columns as dirty, and there + # are tests for this already in t/inflate/serialize.t + # + # But even this isn't enough - one has to reload the RDBMS-formatted + # values once done, otherwise the copy is just as useless... sigh + # + # Adding the test here to validate the technique works + # UGH! + { + no warnings 'once'; + local *DBICTest::BaseResult::copy = subname 'DBICTest::BaseResult::copy' => sub { + my $self = shift; + + $self->make_column_dirty($_) for keys %{{ $self->get_inflated_columns }}; + + my $cp = $self->next::method(@_); + + $cp->discard_changes({ columns => [ keys %{{ $cp->get_columns }} ] }); + }; + Class::C3->reinitialize if DBIx::Class::_ENV_::OLD_MRO; + + my $cp = $row->copy; + ok( $cp->in_storage ); + is( $cp->$col, $dt, "$type copy logical roundtrip" ); + + $cp->discard_changes({ select => [ $pk, $col ] }); + is( $cp->$col, $dt, "$type copy server roundtrip" ); + } + + Class::C3->reinitialize if DBIx::Class::_ENV_::OLD_MRO; } # test a computed datetime column