-
Notifications
You must be signed in to change notification settings - Fork 21
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
MySQL/MariaDB: Use strict SQL mode instead of just ANSI_QUOTES #624
Conversation
Quoter provides utility functions for quoting table names and columns, where the quotation mark depends on the database driver used. This is in preparation for using strict SQL mode instead of just ANSI_QUOTES. Also Vitess, which will be supported in the near future, does not support ANSI_QUOTES.
// Set strict SQL mode, i.e. trigger an error if an incorrect value is inserted into a column. | ||
config.Params = map[string]string{"sql_mode": "TRADITIONAL"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this do differently compared to not setting sql_mode
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to explicitly enable strict mode instead of letting something else decide it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MariaDB's default SQL_MODE
depends on the MariaDB version and, even with current MariaDB versions, does not equal TRADITIONAL
.
MariaDB [icingadb]> SELECT @@SQL_MODE, @@GLOBAL.SQL_MODE\G
*************************** 1. row ***************************
@@SQL_MODE: STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
@@GLOBAL.SQL_MODE: STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
1 row in set (0.000 sec)
MariaDB [icingadb]> SET sql_mode = 'TRADITIONAL';
Query OK, 0 rows affected (0.001 sec)
MariaDB [icingadb]> SELECT @@SQL_MODE, @@GLOBAL.SQL_MODE\G
*************************** 1. row ***************************
@@SQL_MODE: STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
@@GLOBAL.SQL_MODE: STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
1 row in set (0.000 sec)
Furthermore, MySQL's default SQL_MODE
is also something else.
Fortunately, both MySQL's as well as MariaDB's TRADITIONAL
mode maps to the same mode list.
Thus, explicitly setting the SQL_MODE
to TRADITIONAL
sounds like a good idea.
@@ -32,6 +32,7 @@ type DB struct { | |||
logger *logging.Logger | |||
tableSemaphores map[string]*semaphore.Weighted | |||
tableSemaphoresMu sync.Mutex | |||
quoter *Quoter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering: why hide this inside a member instead of having the functions directly available on DB
(maybe by embedding the struct/pointer)? Would make the calls a bit nicer and the functions wouldn't look out of place there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't think of that tbh.
// QuoteColumnList quotes the given columns into a single comma concatenated string | ||
// so that they can be safely used as a column list for SELECT and INSERT statements. | ||
func (q *Quoter) QuoteColumnList(columns []string) string { | ||
return fmt.Sprintf("%[1]s%s%[1]s", q.quoteCharacter, strings.Join(columns, q.quoteCharacter+", "+q.quoteCharacter)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mixing numbered and unnumbered placeholders looks strange. At least I can't tell without double-checking which argument an unnumbered placeholder would refer to after some numbered ones.
So maybe at least go with %[1]s%[2]s%[1]s
here.
But it's probably easier to not use printf
here at all, q.quoteCharacter + strings.Join(columns, q.quoteCharacter+", "+q.quoteCharacter)) + q.quoteCharacter
is shorter, even though it repeats the name quoteCharacter
yet another time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I can do that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding a few tests for this file should be easy by initializing with &Quoter{"`"}
. Testing NewQuoter()
would probably be annoying without a database connection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using MySQL quotes on both Vitess and, while on it, on MySQL is absolutely legit. For 1.2.0.
@@ -61,7 +61,8 @@ func (d *Database) Open(logger *logging.Logger) (*icingadb.DB, error) { | |||
|
|||
config.DBName = d.Database | |||
config.Timeout = time.Minute | |||
config.Params = map[string]string{"sql_mode": "ANSI_QUOTES"} | |||
// Set strict SQL mode, i.e. trigger an error if an incorrect value is inserted into a column. | |||
config.Params = map[string]string{"sql_mode": "TRADITIONAL"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For 1.1.1 we should just set TRADITIONAL,ANSI_QUOTES
here. This is a bugfix release. PoC:
MariaDB [idb]> SET SQL_MODE='TRADITIONAL';
Query OK, 0 rows affected (0,003 sec)
MariaDB [idb]> SET SQL_MODE='TRADITIONAL,ANSI_QUOTES';
Query OK, 0 rows affected (0,003 sec)
MariaDB [idb]> select @@SQL_MODE\G
*************************** 1. row ***************************
@@SQL_MODE: ANSI_QUOTES,STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
1 row in set (0,003 sec)
MariaDB [idb]> SET SQL_MODE='TRADITIONAL,ANSI_QUOTES,LOLCAT';
ERROR 1231 (42000): Variable 'sql_mode' can't be set to the value of 'LOLCAT'
MariaDB [idb]>
I mean the missing of strict mode is not really bug in itself. Enabling it, at best, changes nothing, at worst, it exposes other bugs. In doubt, exposing would mean crashing the Icinga DB process. So better just do this as a whole in 1.2.0. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have successfully tested this PR and a rebased version on the main
branch.
Should the SQL_MODE
also be reflected in the schema.sql file?
Otherwise, there is nothing I would add to the already written comments.
// Set strict SQL mode, i.e. trigger an error if an incorrect value is inserted into a column. | ||
config.Params = map[string]string{"sql_mode": "TRADITIONAL"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MariaDB's default SQL_MODE
depends on the MariaDB version and, even with current MariaDB versions, does not equal TRADITIONAL
.
MariaDB [icingadb]> SELECT @@SQL_MODE, @@GLOBAL.SQL_MODE\G
*************************** 1. row ***************************
@@SQL_MODE: STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
@@GLOBAL.SQL_MODE: STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
1 row in set (0.000 sec)
MariaDB [icingadb]> SET sql_mode = 'TRADITIONAL';
Query OK, 0 rows affected (0.001 sec)
MariaDB [icingadb]> SELECT @@SQL_MODE, @@GLOBAL.SQL_MODE\G
*************************** 1. row ***************************
@@SQL_MODE: STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
@@GLOBAL.SQL_MODE: STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
1 row in set (0.000 sec)
Furthermore, MySQL's default SQL_MODE
is also something else.
Fortunately, both MySQL's as well as MariaDB's TRADITIONAL
mode maps to the same mode list.
Thus, explicitly setting the SQL_MODE
to TRADITIONAL
sounds like a good idea.
#699 sets |
Accidentally disabling strict mode was already fixed in #699 So only the other point remains which seems to only be relevant if we'd want to so support Vitess (#606). |
I doubt that these changes will be necessary in the near future, so I am closing this PR. |
This PR
Quoter
that provides utility functions for quoting table names and columns, where the quotation mark depends on the database driver usedANSI_QUOTES
SQL mode withTRADITIONAL
which enables strict mode.Previously, setting
ANSI_QUOTES
overrode the server defaults, which were most likely stricter. Also Vitess, which will be supported in the near future, does not supportANSI_QUOTES
.refs #606