RT# 82942 Add FS::DBI, to fix database connection encoding bug
authorMitch Jackson <mitch@freeside.biz>
Sun, 3 Mar 2019 21:35:25 +0000 (16:35 -0500)
committerroot <root@freeside-deb8-fs4>
Mon, 4 Mar 2019 01:22:39 +0000 (20:22 -0500)
- Add FS::DBI
  - Drop-in replacement for DBI
  - Ensures client_encoding is set to UTF8 for DBD::Pg
- Implement FS::DBI in FS::UID, where nearly all freeside
  database connections are established

FS/FS/DBI.pm [new file with mode: 0644]
FS/FS/UID.pm

diff --git a/FS/FS/DBI.pm b/FS/FS/DBI.pm
new file mode 100644 (file)
index 0000000..c6ff125
--- /dev/null
@@ -0,0 +1,72 @@
+package FS::DBI;
+use strict;
+use warnings;
+use base qw( DBI );
+
+=head1 NAME
+
+FS::DBI - Freeside wrapper for DBI
+
+=head1 SYNOPSIS
+
+  use FS::DBI;
+  
+  $dbh = FS::DBI->connect( @args );
+  $dbh->do(
+    'UPDATE table SET foo = ? WHERE bar = ?',
+    undef,
+    $foo, $bar
+  ) or die $dbh->errstr;
+
+See L<DBI>
+
+=head1 DESCRIPTION
+
+Allow Freeside to manage how DBI is used when necessary
+
+=head2 Legacy databases and DBD::Pg v3.0+
+
+Breaking behavior was introduced in DBD::Pg version 3.0.0
+in regards to L<DBD::Pg/pg_enable_utf8>.
+
+Some freedside databases are legacy databases with older encodings
+and locales. pg_enable_utf8 no longer sets client_encoding to utf8
+on non-utf8 databases, causing crashes and data corruption.
+
+FS::DBI->connect() enforces utf8 client_encoding on all DBD::Pg connections
+
+=head1 METHODS
+
+=head2 connect @connect_args
+
+For usage, see L<DBI/connect>
+
+Force utf8 client_encoding on DBD::Pg connections
+
+=cut
+
+sub connect {
+  my $class = shift;
+  my $dbh = $class->SUPER::connect( @_ );
+
+  if ( $_[0] =~ /^DBI::Pg/ ) {
+    $dbh->do('SET client_encoding TO UTF8;')
+      or die sprintf 'Error setting client_encoding to UTF8: %s', $dbh->errstr;
+
+    # DBD::Pg requires touching this attribute when changing the client_encoding
+    # on an already established connection, to get expected behavior.
+    $dbh->{pg_enable_utf8} = -1;
+  }
+
+  $dbh;
+}
+
+# Stub required to subclass DBI
+package FS::DBI::st;
+use base qw( DBI::st );
+
+# Stub required to subclass DBI
+package FS::DBI::db;
+use base qw( DBI::db );
+
+1;
index 9592447..eeaeff2 100644 (file)
@@ -11,7 +11,7 @@ use subs qw(
 );
 use Exporter;
 use Carp qw(carp croak cluck confess);
-use DBI;
+use FS::DBI;
 use IO::File;
 use FS::CurrentUser;
 
@@ -230,14 +230,16 @@ sub callback_setup {
 
 
 sub myconnect {
-  my $handle = DBI->connect( getsecrets(@_), { 'AutoCommit'         => 0,
-                                               'ChopBlanks'         => 1,
-                                               'ShowErrorStatement' => 1,
-                                               'pg_enable_utf8'     => 1,
-                                               #'mysql_enable_utf8'  => 1,
-                                             }
-                           )
-    or die "DBI->connect error: $DBI::errstr\n";
+  my $handle = FS::DBI->connect(
+    getsecrets( @_ ),
+    {
+      'AutoCommit'         => 0,
+      'ChopBlanks'         => 1,
+      'ShowErrorStatement' => 1,
+      'pg_enable_utf8'     => 1,
+      # 'mysql_enable_utf8'  => 1,
+    }
+  ) or die "FS::DBI->connect error: $FS::DBI::errstr\n";
 
   if ( $schema ) {
     use DBIx::DBSchema::_util qw(_load_driver ); #quelle hack