Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set strict mode for compatibility with Mojo::Pg and to avoid data loss #29

Closed
shadowcat-mst opened this issue Mar 22, 2017 · 5 comments
Closed
Assignees

Comments

@shadowcat-mst
Copy link
Contributor

https://metacpan.org/pod/DBIx::Class::Storage::DBI::mysql#set_strict_mode hugely reduces mysql's tendency to eat data, and would make Mojo::mysql much more of a drop-in replacement than Mojo::Pg - otherwise the migration path is "switch to Mojo::mysql and add validation code before every INSERT/UPDATE/DELETE to work around mysql's lossy defaults".

At the very least it'd be nice to have it as an option, documented for people migrating from a saner database - I wish I'd turned it on by default in DBIx::Class but it's too late for me now; your choice as to whether it's also too late for you.

@jhthorsen jhthorsen self-assigned this Mar 22, 2017
@jhthorsen
Copy link
Owner

I threw together this:

diff --git a/lib/Mojo/mysql.pm b/lib/Mojo/mysql.pm
index e374841..4aa99fc 100644
--- a/lib/Mojo/mysql.pm
+++ b/lib/Mojo/mysql.pm
@@ -73,6 +73,17 @@ sub from_string {

 sub new { @_ > 1 ? shift->SUPER::new->from_string(@_) : shift->SUPER::new }

+sub strict_mode {
+  my $self = shift;
+  my $strict = shift // 1;
+
+  warn "[Mojo::mysql] strict_mode($strict)\n" if $ENV{DBI_TRACE};
+  delete @$self{qw(pid queue)};
+  $self->unsubscribe(connection => \&_set_strict_mode);
+  $self->on(connection => \&_set_strict_mode) if $strict;
+  $self;
+}
+
 sub _dequeue {
   my $self = shift;
   my $dbh;
@@ -101,6 +112,11 @@ sub _enqueue {
   shift @{$self->{queue}} while @{$self->{queue}} > $self->max_connections;
 }

+sub _set_strict_mode {
+  $_[1]->do(q[SET SQL_MODE = CONCAT('ANSI,TRADITIONAL,ONLY_FULL_GROUP_BY,', @@sql_mode)]);
+  $_[1]->do(q[SET SQL_AUTO_IS_NULL = 0]);
+}
+
 1;

 =encoding utf8
diff --git a/t/strict-mode.t b/t/strict-mode.t
new file mode 100644
index 0000000..ccda7b6
--- /dev/null
+++ b/t/strict-mode.t
@@ -0,0 +1,32 @@
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
+use Mojo::Base -strict;
+use Test::More;
+use Mojo::IOLoop;
+use Mojo::mysql;
+
+plan skip_all => 'TEST_ONLINE=mysql://root@/test' unless $ENV{TEST_ONLINE};
+
+my $mysql = Mojo::mysql->new($ENV{TEST_ONLINE});
+ok $mysql->db->ping, 'connected';
+
+my $db = $mysql->db;
+
+$db->query('drop table if exists strict_mode_test_table');
+$db->query('create table strict_mode_test_table (foo varchar(4))');
+
+$db->query('SET SQL_MODE = ""');    # make sure this fails, even in mysql 5.7
+$db->insert(strict_mode_test_table => {foo => 'y' x 10});
+is $db->select('strict_mode_test_table')->hash->{foo}, 'yyyy', 'fetch invalid data';
+
+is $mysql->strict_mode, $mysql, 'enabled strict mode';
+is @{$mysql->subscribers('connection')}, 1, 'add connection event handler';
+$db = $mysql->db;
+eval { $db->insert(strict_mode_test_table => {foo => 'too_long'}) };
+like $@, qr{Data too long.*foo}, 'too long string';
+
+is $mysql->strict_mode(0), $mysql, 'disable strict mode';
+is @{$mysql->subscribers('connection')}, 0, 'removed connection event handler';
+
+$db->query('drop table if exists strict_mode_test_table');
+
+done_testing;

Does that make sense?

@jhthorsen
Copy link
Owner

I don't think I can turn it on by default, but I would like to add a warning, if strict_mode() has not been called. Could even make it into a constructor..?

@shadowcat-mst
Copy link
Contributor Author

shadowcat-mst commented Mar 23, 2017 via email

jhthorsen pushed a commit that referenced this issue Mar 25, 2017
 - Add strict_mode() method and constructor #29
@jhthorsen
Copy link
Owner

jhthorsen commented Mar 25, 2017

Mojo::mysql 1.01 is on its way to CPAN.

Thanks for helping out @shadowcat-mst 👍

@jhthorsen
Copy link
Owner

jhthorsen commented Mar 26, 2017

Would love to hear your feedback @shadowcat-mst or if you have changes to which command to run in the future. I decided to update the SYNOPSIS to use strict_mode() instead of new().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants