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,