Currently connection options are set using $this->init('SET NAMES...'), $this->init('SET sql_mode=...') to set NAMES/COLLATE and SQL_MODE. This patch moves the connection options into a configurable array. The basis for this patch being that we are unable to modify the sql_mode used when connecting to MySQL - which has been associated with a number of bugs and unexpected behavior (#1136854: MySQL 5.5 breaks speedy testing).

Sampling from MySQL changelogs:

The mysql_odbc_escape_string() C API function has been removed. It has multi-byte character escaping issues, doesn't honor the NO_BACKSLASH_ESCAPES SQL mode and is not needed anymore by Connector/ODBC as of 3.51.17. (Bug #29592, #41728)

Changing the SQL mode to cause dates with “zero” parts to be considered invalid (such as '1000-00-00') could result in indexed and nonindexed searches returning different results for a column that contained such dates. (Bug #31928)

Incompatible Change: With ONLY_FULL_GROUP_BY SQL mode enabled, queries such as SELECT a FROM t1 HAVING COUNT(*)>2 were not being rejected as they should have been. (Bug #31794)

mysql_install_db failed if the server was running with an SQL mode of TRADITIONAL. This program now resets the SQL mode internally to avoid this problem. (Bug #34159)

The NO_BACKSLASH_ESCAPES SQL mode was ignored for LOAD DATA INFILE and SELECT INTO ... OUTFILE. The setting is taken into account now. (Bug #37114)

Prepared statements permitted invalid dates to be inserted when the ALLOW_INVALID_DATES SQL mode was not enabled. (Bug #40365)

I hit an issue with ANSI_QUOTES and tungsten-replicator (unsupported) that breaks replication, and was surprised to find the sql_mode's were hardcoded.

The goal for this issue is to make sql_mode configurable. I see this having two paths:

  • a one-off for sql_mode
  • a rewrite of the handling of mysql connection options/init commands

Update: we are unable to execute more than one query in MYSQL_ATTR_INIT_COMMAND (https://bugs.php.net/bug.php?id=44081)

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

basic’s picture

This also adds functionality of setting SQL Modes via settings.php.

basic’s picture

This patch cleans up how the default expr's are set for names, collation and sql_mode, and adds an extra array that accepts custom connection/init variables. I like the idea of keeping defaults set in database.inc but allowing an easy override as well as ability to add additional connection options.

basic’s picture

I just thought of a case where one would want to set sql_mode=''. Added additional logic for this.

bdragon’s picture

Status: Needs review » Needs work

Setting to CNW. The patch removes some important documentation regarding WHY our default mode is what it is.

Also, I think the default.settings.php documentation needs to have a better warning that normal users should not monkey with the defaults.

-    // Force MySQL's behavior to conform more closely to SQL standards.
-    // This allows Drupal to run almost seamlessly on many different
-    // kinds of database systems. These settings force MySQL to behave
-    // the same as postgresql, or sqlite in regards to syntax interpretation
-    // and invalid data handling. See http://drupal.org/node/344575 for
-    // further discussion. Also, as MySQL 5.5 changed the meaning of
-    // TRADITIONAL we need to spell out the modes one by one.
basic’s picture

Status: Needs work » Needs review
FileSize
7.02 KB

I've updated documentation as requested. I've also added the ability to override postgres init options and documented them.

basic’s picture

After a discussion with chx about how to best implement this for D8, we decided this was clean, simple and allows more flexibility for the 1% of users that need it.

Status: Needs review » Needs work

The last submitted patch, configurable_sql_mode-1309278-8.patch, failed testing.

bdragon’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, configurable_sql_mode-1309278-8.patch, failed testing.

basic’s picture

Title: Move MySQL PDO connection options to PDO::MYSQL_ATTR_INIT_COMMAND » Move MySQL PDO connection options to an array which can be overridden
Status: Needs work » Needs review
FileSize
4.12 KB

A bug in the PDO driver appears to prevent us from executing more than one query in MYSQL_ATTR_INIT_COMMAND (https://bugs.php.net/bug.php?id=44081).

I've moved these back to $this->query() and they pass local testing.

basic’s picture

Oops, I realized that schema.inc also needed to be updated, and we were using exec() instead of query() here.

basic’s picture

Clean up of schema.inc implementation

sun’s picture

Touching the SQL MODE is fine.

But before touching charsets or collations, you need to read all nitty-gritty details in #772678: Database default collation is not respected

sun’s picture

Issue summary: View changes

Update description to reflect bug in PDO

basic’s picture

Issue summary: View changes

Add more detail to Issue description

basic’s picture

Title: Move MySQL PDO connection options to an array which can be overridden » Make MySQL connection options such as sql_mode configurable
Niklas Fiekas’s picture

Closed #1308874: Unable to override sql_mode as a duplicate. First get this done, then backport (maybe).

basic’s picture

We need to make a decision on whether we want to:

  • Make all init_commands configurable (names + collate, sql_mode, and any arbitrary commands)
  • Make only sql_mode configurable as a one-off similar to 'collation' (and make arbitrary init_commands a separate issue)

Each approach has some pros and cons. We can be more strict about a one-off for 'sql_mode', but as soon as we add another default connection option for mysql, we have to add another one-off. Making all init_commands configurable gives us all the flexibility in the world, thus making it easier to accidentally break installations.

Before this issue can progress, I believe we need to decide on what changes we are comfortable with (a one-off or a new init_commands array).

Thoughts? Questions?

NOTE: this is a MySQL specific issue - sql_mode was one of the first things ripped out of Drizzle.

Niklas Fiekas’s picture

Status: Needs review » Needs work

As discussed in IRC I am in favor of having init_commands instead of extra properties for every connection setting I'll ever need. SQL_BIG_SELECTS just to name one other.

However I don't think we should include charsets, collations and what not there. Drupal uses UTF-8.

basic’s picture

I am leaning toward a solution that leaves alone the current handling of SET NAMES, and COLLATE - where we always SET NAMES utf8, and only ['database']['collation'] allows for an override. We can then move sql_mode to ['database']['init_commands']['sql_mode'], which allows defaults to be overridden and arbitrary init_commands to be defined (such as ['database']['init_commands']['big_selects'] => 'SET SQL_BIG_SELECTS=1').

Crell’s picture

I would much rather go minimalist here. We spent probably 80 hours or more between a half dozen people trying to figure out the right magic incantation of configuration options to make MySQL behave like a standard SQL database rather than a 1997-era card catalog that tries to be smarter than you (aka MySQL 3). I am extremely reluctant to let people break that who don't know what they're doing (which is pretty much everyone, including me).

Changing the character encoding is asking for trouble, as you can't change the encoding of the HTTP traffic. Try to write to the database in a different character set than the data coming from the browser and you're in for a world of pain.

There is an "out" now for really edge cases: Subclass the MySQL driver to your own custom driver and change the settings there. Works great. :-)

So I'm not entirely convinced the benefit here is worth the risk of toppling the house of cards that is MySQL.

basic’s picture

I think the ability to change character encoding is agreed upon - this patch won't be doing that. This patch includes the init_commands array to enable users to set other non-default session variables, and moves SET sql_mode to the init_commands array.

basic’s picture

Status: Needs work » Needs review
Crell’s picture

I am more comfortable with #22, although still skeptical. I'd like to get David Strauss to weigh in here.

basic’s picture

I sent David an email with details on the issue (as requested by him). I'm hoping he has some time to reply here soon.

chx’s picture

@CrellI I think there are N ways to configure Drupal from settings.php in a way that it breaks rather subtly. Also in the light of #1314214: MySQL driver does not support full UTF-8 (emojis, asian symbols, mathematical symbols) allowing overriding SET NAMES is a good idea, isn't it?

David Strauss’s picture

I have a couple questions about the use cases supporting the inclusion of this patch:

* Is it possible to achieve the same changes by updating my.cnf? Because this patch is to support a <1% use case, a solution that involves re-configuring MySQL is acceptable.
* Is it possible to just do this by sub-classing the MySQL driver? Again, for <1% use cases, heavy solutions are okay.

Most of the changelog excerpts and examples are not terribly convincing for why Drupal should offer yet another configuration option, especially in the form of advertising it in default.settings.php. Pretty much all of the excepts and examples either don't substantially affect Drupal projects of any size (like issues around stored procedures), are about accommodating bugs in fairly obscure systems (like tungsten-replicator), or are only encountered outside Drupal's DB interface (like running INFILE from outside Drupal).

basic’s picture

Is it possible to achieve the same changes by updating my.cnf? Because this patch is to support a <1% use case, a solution that involves re-configuring MySQL is acceptable.

No, Drupal sets names, collation and sql_mode per session. This overrides any defaults set in my.cnf (which is good, default is still latin1).

Is it possible to just do this by sub-classing the MySQL driver? Again, for <1% use cases, heavy solutions are okay.

Yes, but sub-classing the driver to work around mysql session variables that we hardcode in the driver doesn't seem like the best way forward. Most people will just hack core's mysql driver to work around issues. Why not make this easier to achieve without hacking core?

#1149372: Don't hack core: sql_mode
#1136854: MySQL 5.5 breaks speedy testing
#1314214: MySQL driver does not support full UTF-8 (emojis, asian symbols, mathematical symbols)

Damien Tournoud’s picture

I'm with basic here: it is really useful in the special cases to be able to modify connection options and initialization commands. Of course, you can *always* subclass the driver, but doing so will break a lot of things that actually rely on the driver name (especially: Drush).

I think we should extend this concept a little bit:

  • Extend it to all the database engines in Core
  • Allow PDO driver options to be configurable (this is especially useful for PDO::ATTR_TIMEOUT which controls the amount of time the driver will wait for a connection to be established, and is awfully long by default - 30s)
sun’s picture

@Damien Tournoud's suggestions make sense to me.

However, let's keep the charset issue separated in #1314214: MySQL driver does not support full UTF-8 (emojis, asian symbols, mathematical symbols), please.

basic’s picture

@DamZ

Extend it to all the database engines in Core

PDO options, init commands, or both? Other database engines handle collation/encoding a little differently, and sql_mode is a mysql specific setting - I'm sure you already know that drizzle removed sql_mode's (and forces utf8).

Allow PDO driver options to be configurable (this is especially useful for PDO::ATTR_TIMEOUT which controls the amount of time the driver will wait for a connection to be established, and is awfully long by default - 30s)

This is something that I think makes sense to allow modifying across all database drivers. It looks to be pretty straight forward.

Damien Tournoud’s picture

I was thinking both init commands and PDO driver options. Of course, both of those have different meanings on different database engines, but both are useful.

David Strauss’s picture

I'm more okay with this change if equivalent functionality is available for other engines.

On the issue of not being able to use a subclassed driver, that sounds like a bug. Code should be checking whether something is an instance of the type they need, not whether the class names matches a string.

basic’s picture

This adds support for setting pdo driver options and init commands via settings.php for all database drivers.

basic’s picture

Rewrite for the new file structure.

basic’s picture

Using git format-patch. Why? Because everyone else is doing it.

basic’s picture

Fixing comments and documentation.

Damien Tournoud’s picture

Status: Needs review » Reviewed & tested by the community

Approved here. This gives some much needed flexibility in tuning those parameters, which is especially important in large-scale deployments.

Damien Tournoud’s picture

And I would like to see this backported to 7.x as well.

basic’s picture

Version: 8.x-dev » 7.x-dev
FileSize
7.18 KB

Backport to 7.x

basic’s picture

Version: 7.x-dev » 8.x-dev

Assuming tests pass for 7.x, moving back to 8.x

sun’s picture

Looks good to me as well, and safe for backporting.

catch’s picture

Title: Make MySQL connection options such as sql_mode configurable » Make PDO connection options configurable
Assigned: basic » Crell

Assigning this to Crell for a final glance over if he's got time. I'm happy committing this myself though.

webchick’s picture

Status: Reviewed & tested by the community » Needs review
Issue tags: +Needs backport to D7

Fixing tag.

Also confirmed with Crell on IRC that he'd like one last look at this before committing. Bumping back down to needs review.

Crell’s picture

Status: Needs review » Needs work

Ugh. Sorry for taking so long to respond here, everyone. I'm more comfortable with the patch in #41 (for both D7 and D8), with the following caveat:

+++ b/sites/default/default.settings.php
@@ -153,6 +153,21 @@
+ * Advanced users can add/override arbitrary init commands and pdo settings.
+ * @see DatabaseConnection_mysql::__construct
+ * @see DatabaseConnection_pgsql::__construct
+ * @see DatabaseConnection_sqlite::__construct
+ * @code
+ * $databases['default']['default'] = array(
+ *   'init_commands' => array(
+ *     'big_selects' => 'SET SQL_BIG_SELECTS=1',
+ *   ),
+ *   'pdo' => array(
+ *     PDO::ATTR_TIMEOUT => 5,
+ *   ),
+ * );
+ * @endcode

While this is true, I believe it should have a big warning on it that the default settings are designed for database portability and changing them may cause unexpected behavior, including potentially data loss. This is NOT something that anyone should be playing with without really knowing what they're doing.

Let's improve the documentation there to warn people away. After that, I'm comfortable committing this.

basic’s picture

Assigned: Crell » basic
Status: Needs work » Needs review

I worked a bit with webchick to better document this functionality.

How does this look/sound?

diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php
index 330ebcc..04cf1a8 100644
--- a/sites/default/default.settings.php
+++ b/sites/default/default.settings.php
@@ -153,6 +153,30 @@
  * @endcode
  * NOTE: MySQL and SQLite's definition of a schema is a database.
  *
+ * Advanced users can add or override initial commands to execute when
+ * connecting to the database server, as well as PDO connection settings. For
+ * example, to enable MySQL SELECT queries to exceed the max_join_size system
+ * variable, and to reduce the database connection timeout to 5 seconds:
+ *
+ * @code
+ * $databases['default']['default'] = array(
+ *   'init_commands' => array(
+ *     'big_selects' => 'SET SQL_BIG_SELECTS=1',
+ *   ),
+ *   'pdo' => array(
+ *     PDO::ATTR_TIMEOUT => 5,
+ *   ),
+ * );
+ * @endcode
+ *
+ * WARNING: These defaults are designed for database portability. Changing them
+ * may cause unexpected behavior, including potential data loss. Make
+ * absolutely sure you know what you are doing before modifying these values!
+ *
+ * @see DatabaseConnection_mysql::__construct
+ * @see DatabaseConnection_pgsql::__construct
+ * @see DatabaseConnection_sqlite::__construct
+ *
  * Database configuration format:
  * @code
  *   $databases['default']['default'] = array(
Crell’s picture

#47 looks fine to me. Thanks!

moshe weitzman’s picture

The last sentence in the warning is pure nanny talk IMO. Otherwise, nice patch.

Niklas Fiekas’s picture

Here's a reroll removing the last sentence and moving to the new directory structure.

Crell’s picture

Status: Needs review » Reviewed & tested by the community

OK, let's do. I'm OK with backporting this as well as there is no API change.

Niklas Fiekas’s picture

And the D7 port. Only the paths differ.

catch’s picture

Version: 8.x-dev » 7.x-dev
Status: Reviewed & tested by the community » Needs review

Looks good to me. Committed/pushed to 8.x.

Moving to 7.x - it'll need a patch with no suffix for the testbot to have a crack at it.

Niklas Fiekas’s picture

Yay! The 7.x patch again.

basic’s picture

Thanks Niklas, I was gone for the Thanksgiving holiday and I'm glad to see this get pushed through. FWIW http://drupal.org/files/configurable_sql_mode-1309278-38.patch was already updated to the new directory structure for 8.x, I hope that wasn't too much trouble :)

Niklas Fiekas’s picture

Lol, no, it was pretty straight forward - looks like I must have accidantely started from your D7 backport :)

basic’s picture

Status: Needs review » Reviewed & tested by the community

This should be is ready for D7

webchick’s picture

Status: Reviewed & tested by the community » Fixed

EXCELLENT. This type of backwards-compatible capability enhancement is super exciting to see, and I know Rudy's been working on it for quite awhile. Seems to have buy-in from the DBTNG maintainers, and shouldn't break anything in existing installations. In case it does, we have a good month or so to find out.

Committed and pushed to 7.x! Thanks!!

phayes’s picture

Status: Fixed » Needs work

Hi Folks,

Forgive me if I'm wrong, but one of the goals of this change is to allow the charset to be configurable in order to allow the following bugs to be fixed:
#1314214: MySQL driver does not support full UTF-8 (emojis, asian symbols, mathematical symbols)
#1199736: Error on pasting in different language

We need to able able to make the "SET NAMES" command configurable between "utf8" and "utf8mb4"

Niklas Fiekas’s picture

I think we decided to leave the charset out of this, see #15, #19, #30. Not sure, though ...

basic’s picture

Status: Needs work » Fixed

Automatically closed -- issue fixed for 2 weeks with no activity.

Anonymous’s picture

Issue summary: View changes

Updated issue summary.