diff -ruN DB/common.php DB/common.php --- DB/common.php 2020-04-20 05:45:59.000000000 +1000 +++ DB/common.php 2020-08-01 14:46:53.146175149 +1000 @@ -147,7 +147,7 @@ */ function __construct() { - $this->PEAR('DB_Error'); + parent::__construct('DB_Error'); } // }}} @@ -1150,7 +1147,22 @@ */ function modifyQuery($query) { - return $query; + // CRM-20445 Add query dispatcher to allow query modification. + // This section of code may run hundreds or thousands of times in a given request. + // Consequently, it is micro-optimized to use single lookup in typical case. + if (!isset(Civi::$statics['db_common_dispatcher'])) { + if (class_exists('Civi\Core\Container') && \Civi\Core\Container::isContainerBooted()) { + Civi::$statics['db_common_dispatcher'] = Civi\Core\Container::singleton()->get('dispatcher'); + } + else { + return $query; + } + } + + $e = new \Civi\Core\Event\QueryEvent($query); + Civi::$statics['db_common_dispatcher']->dispatch('civi.db.query', $e); + // CRM-20445 ends. + return $e->query; } // }}} @@ -1882,7 +1894,7 @@ * * @see PEAR_Error */ - function &raiseError($code = DB_ERROR, $mode = null, $options = null, + function raiseError($code = DB_ERROR, $mode = null, $options = null, $userinfo = null, $nativecode = null, $dummy1 = null, $dummy2 = null) { @@ -2255,6 +2267,20 @@ } // }}} + // {{{ lastInsertId() + + /** + * Get the most recently inserted Id + * + * @throws RuntimeException + */ + function lastInsertId() + { + throw new \RuntimeException("Not implemented: " . get_class($this) . '::lastInsertId'); + } + + // }}} + } /* diff -ruN DB/mysqli.php DB/mysqli.php --- DB/mysqli.php 2020-04-20 05:45:59.000000000 +1000 +++ DB/mysqli.php 2020-08-01 14:51:11.871272253 +1000 @@ -112,9 +112,12 @@ 1136 => DB_ERROR_VALUE_COUNT_ON_ROW, 1142 => DB_ERROR_ACCESS_VIOLATION, 1146 => DB_ERROR_NOSUCHTABLE, + 1205 => DB_ERROR_LOCK_TIMEOUT, + 1213 => DB_ERROR_DEADLOCK, 1216 => DB_ERROR_CONSTRAINT, 1217 => DB_ERROR_CONSTRAINT, - 1356 => DB_ERROR_DIVZERO, + 1356 => DB_ERROR_INVALID_VIEW, + 1365 => DB_ERROR_DIVZERO, 1451 => DB_ERROR_CONSTRAINT, 1452 => DB_ERROR_CONSTRAINT, ); @@ -314,7 +317,8 @@ $dsn['password'], $dsn['database'], $dsn['port'], - $dsn['socket'])) + $dsn['socket'], + MYSQLI_CLIENT_SSL)) { $this->connection = $init; } @@ -1087,6 +1091,14 @@ } // }}} + // {{{ lastInsertId() + + function lastInsertId() + { + return mysqli_insert_id($this->connection); + } + + // }}} } diff -ruN DB/mysql.php DB/mysql.php --- DB/mysql.php 2020-04-20 05:45:59.000000000 +1000 +++ DB/mysql.php 2020-08-01 14:48:58.276132870 +1000 @@ -109,9 +109,12 @@ 1136 => DB_ERROR_VALUE_COUNT_ON_ROW, 1142 => DB_ERROR_ACCESS_VIOLATION, 1146 => DB_ERROR_NOSUCHTABLE, + 1205 => DB_ERROR_LOCK_TIMEOUT, + 1213 => DB_ERROR_DEADLOCK, 1216 => DB_ERROR_CONSTRAINT, 1217 => DB_ERROR_CONSTRAINT, - 1356 => DB_ERROR_DIVZERO, + 1356 => DB_ERROR_INVALID_VIEW, + 1365 => DB_ERROR_DIVZERO, 1451 => DB_ERROR_CONSTRAINT, 1452 => DB_ERROR_CONSTRAINT, ); @@ -1021,6 +1024,14 @@ } // }}} + // {{{ lastInsertId() + + function lastInsertId() + { + return mysql_insert_id($this->connection); + } + + // }}} } diff -ruN DB/storage.php DB/storage.php --- DB/storage.php 2020-04-20 05:45:59.000000000 +1000 +++ DB/storage.php 2020-08-01 15:24:40.814621336 +1000 @@ -96,7 +96,7 @@ */ function __construct($table, $keycolumn, &$dbh, $validator = null) { - $this->PEAR('DB_Error'); + parent::__construct('DB_Error'); $this->_table = $table; $this->_keycolumn = $keycolumn; $this->_dbh = $dbh; diff -ruN DB.php DB.php --- DB.php 2020-04-20 05:45:59.000000000 +1000 +++ DB.php 2020-08-01 14:43:45.338870953 +1000 @@ -182,6 +182,11 @@ define('DB_ERROR_CONSTRAINT_NOT_NULL',-29); /** + * Invalid view or no permissions + */ +define('DB_ERROR_INVALID_VIEW', -100); + +/** * Database lock timeout exceeded. */ define('DB_ERROR_LOCK_TIMEOUT', -30); @@ -641,8 +646,12 @@ . 'CREATE|DROP|' . 'LOAD DATA|SELECT .* INTO .* FROM|COPY|' . 'ALTER|GRANT|REVOKE|' + // CRM_Core_Transaction Tests fail without the following line. + . 'SAVEPOINT|ROLLBACK|' . 'LOCK|UNLOCK'; - if (preg_match('/^\s*"?(' . $manips . ')\s+/i', $query)) { + // First strip any leading comments + $queryString = (substr($query, 0, 2) === '/*') ? substr($query, strpos($query, '*/') + 2) : $query; + if (preg_match('/^\s*"?(' . $manips . ')\s+/i', $queryString)) { return true; } return false; @@ -678,6 +687,7 @@ DB_ERROR_INVALID_DATE => 'invalid date or time', DB_ERROR_INVALID_DSN => 'invalid DSN', DB_ERROR_INVALID_NUMBER => 'invalid number', + DB_ERROR_INVALID_VIEW => 'invalid view', DB_ERROR_MISMATCH => 'mismatch', DB_ERROR_NEED_MORE_DATA => 'insufficient data supplied', DB_ERROR_NODBSELECTED => 'no database selected', @@ -744,6 +754,16 @@ */ public static function parseDSN($dsn) { + + if (defined('DB_DSN_MODE') && DB_DSN_MODE === 'auto') { + if (extension_loaded('mysqli')) { + $dsn = preg_replace('/^mysql:/', 'mysqli:', $dsn); + } + else { + $dsn = preg_replace('/^mysqli:/', 'mysql:', $dsn); + } + } + $parsed = array( 'phptype' => false, 'dbsyntax' => false,