From 8dc2079d437246c1b2f98be2e5621b27b44221d5 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Tue, 4 Feb 2014 20:59:30 -0500 Subject: [PATCH] Add `explain` method to query builder --- classes/idb_sql.php | 10 +++++- classes/iquery_builder.php | 12 +++++++- classes/query_builder.php | 27 ++++++++++++++-- drivers/firebird/firebird_sql.php | 15 ++++++++- drivers/mysql/mysql_driver.php | 2 +- drivers/mysql/mysql_sql.php | 15 ++++++++- drivers/odbc/odbc_driver.php | 2 +- drivers/odbc/odbc_sql.php | 15 ++++++++- drivers/pgsql/pgsql_sql.php | 15 ++++++++- drivers/sqlite/sqlite_sql.php | 15 ++++++++- tests/databases/firebird/firebird-qb.php | 1 + tests/databases/mysql/mysql-qb.php | 31 +++++++++++++++++++ tests/databases/pgsql/pgsql-qb.php | 39 ++++++++++++++++++++++++ tests/databases/sqlite/sqlite-qb.php | 24 +++++++++++++++ 14 files changed, 212 insertions(+), 11 deletions(-) diff --git a/classes/idb_sql.php b/classes/idb_sql.php index 0e63ca9..144ba11 100644 --- a/classes/idb_sql.php +++ b/classes/idb_sql.php @@ -31,6 +31,14 @@ interface iDB_SQL { * @return string */ public function limit($sql, $limit, $offset=FALSE); + + /** + * Modify the query to get the query plan + * + * @param string $sql + * @return string + */ + public function explain($sql); /** * Get the sql for random ordering @@ -113,4 +121,4 @@ interface iDB_SQL { public function column_list($table); } -// End of db_sql.php +// End of db_sql.php \ No newline at end of file diff --git a/classes/iquery_builder.php b/classes/iquery_builder.php index 91cbe04..22e9ded 100644 --- a/classes/iquery_builder.php +++ b/classes/iquery_builder.php @@ -85,6 +85,15 @@ interface iQuery_Builder { * @return $this */ public function distinct(); + + // -------------------------------------------------------------------------- + + /** + * Shows the query plan for the query + * + * @return $this + */ + public function explain(); // -------------------------------------------------------------------------- @@ -181,9 +190,10 @@ interface iQuery_Builder { * * @param mixed $key * @param mixed $val + @ @param bool $escape * @return $this */ - public function where($key, $val=array()); + public function where($key, $val=array(), $escape = NULL); // -------------------------------------------------------------------------- diff --git a/classes/query_builder.php b/classes/query_builder.php index 574aec6..93c4c76 100644 --- a/classes/query_builder.php +++ b/classes/query_builder.php @@ -92,6 +92,9 @@ class Query_Builder implements iQuery_Builder { // List of sql queries executed public $queries; + + // Whether to do only an explain on the query + protected $explain = FALSE; // -------------------------------------------------------------------------- // ! Methods @@ -267,6 +270,19 @@ class Query_Builder implements iQuery_Builder { $this->select_string = ' DISTINCT '.$this->select_string; return $this; } + + // -------------------------------------------------------------------------- + + /** + * Tell the database to give you the query plan instead of result set + * + * @return $this + */ + public function explain() + { + $this->explain = TRUE; + return $this; + } // -------------------------------------------------------------------------- @@ -593,9 +609,10 @@ class Query_Builder implements iQuery_Builder { * * @param mixed $key * @param mixed $val + * @param mixed $escape * @return $this */ - public function where($key, $val=array()) + public function where($key, $val=array(), $escape=NULL) { return $this->_where_string($key, $val, 'AND'); } @@ -1382,8 +1399,14 @@ class Query_Builder implements iQuery_Builder { { $sql = $this->sql->limit($sql, $this->limit, $this->offset); } + + // See what needs to happen to only return the query plan + if (isset($this->explain) && $this->explain === TRUE) + { + $sql = $this->sql->explain($sql); + } return $sql; } } -// End of query_builder.php +// End of query_builder.php \ No newline at end of file diff --git a/drivers/firebird/firebird_sql.php b/drivers/firebird/firebird_sql.php index a07f087..a0b96a9 100644 --- a/drivers/firebird/firebird_sql.php +++ b/drivers/firebird/firebird_sql.php @@ -45,6 +45,19 @@ class Firebird_SQL implements iDB_SQL { return $sql; } + + // -------------------------------------------------------------------------- + + /** + * Get the query plan for the sql query + * + * @param string $sql + * @return string + */ + public function explain($sql) + { + return $sql; + } // -------------------------------------------------------------------------- @@ -255,4 +268,4 @@ SQL; } } -//End of firebird_sql.php +//End of firebird_sql.php \ No newline at end of file diff --git a/drivers/mysql/mysql_driver.php b/drivers/mysql/mysql_driver.php index 457d2b3..1710e06 100644 --- a/drivers/mysql/mysql_driver.php +++ b/drivers/mysql/mysql_driver.php @@ -65,4 +65,4 @@ class MySQL extends DB_PDO { $this->query("TRUNCATE `{$table}`"); } } -//End of mysql_driver.php +//End of mysql_driver.php \ No newline at end of file diff --git a/drivers/mysql/mysql_sql.php b/drivers/mysql/mysql_sql.php index c25c114..0b80f16 100644 --- a/drivers/mysql/mysql_sql.php +++ b/drivers/mysql/mysql_sql.php @@ -38,6 +38,19 @@ class MySQL_SQL implements iDB_SQL { return $sql." LIMIT {$offset}, {$limit}"; } + + // -------------------------------------------------------------------------- + + /** + * Get the query plan for the sql query + * + * @param string $sql + * @return string + */ + public function explain($sql) + { + return "EXPLAIN EXTENDED {$sql}"; + } // -------------------------------------------------------------------------- @@ -178,4 +191,4 @@ class MySQL_SQL implements iDB_SQL { return "SHOW FULL COLUMNS FROM {$table}"; } } -//End of mysql_sql.php +//End of mysql_sql.php \ No newline at end of file diff --git a/drivers/odbc/odbc_driver.php b/drivers/odbc/odbc_driver.php index 487c65a..0b04c04 100644 --- a/drivers/odbc/odbc_driver.php +++ b/drivers/odbc/odbc_driver.php @@ -70,4 +70,4 @@ class ODBC extends DB_PDO { return NULL; } } -// End of odbc_driver.php +// End of odbc_driver.php \ No newline at end of file diff --git a/drivers/odbc/odbc_sql.php b/drivers/odbc/odbc_sql.php index d4d88a6..75a31d3 100644 --- a/drivers/odbc/odbc_sql.php +++ b/drivers/odbc/odbc_sql.php @@ -33,6 +33,19 @@ class ODBC_SQL implements iDB_SQL { { return $sql; } + + // -------------------------------------------------------------------------- + + /** + * Get the query plan for the sql query + * + * @param string $sql + * @return string + */ + public function explain($sql) + { + return $sql; + } // -------------------------------------------------------------------------- @@ -168,4 +181,4 @@ class ODBC_SQL implements iDB_SQL { } } -// End of odbc_sql.php +// End of odbc_sql.php \ No newline at end of file diff --git a/drivers/pgsql/pgsql_sql.php b/drivers/pgsql/pgsql_sql.php index 2f6851f..efb75fb 100644 --- a/drivers/pgsql/pgsql_sql.php +++ b/drivers/pgsql/pgsql_sql.php @@ -39,6 +39,19 @@ class PgSQL_SQL implements iDB_SQL { return $sql; } + + // -------------------------------------------------------------------------- + + /** + * Get the query plan for the sql query + * + * @param string $sql + * @return string + */ + public function explain($sql) + { + return "EXPLAIN VERBOSE {$sql}"; + } // -------------------------------------------------------------------------- @@ -227,4 +240,4 @@ SQL; SQL; } } -//End of pgsql_manip.php +//End of pgsql_manip.php \ No newline at end of file diff --git a/drivers/sqlite/sqlite_sql.php b/drivers/sqlite/sqlite_sql.php index b10a467..f68f42a 100644 --- a/drivers/sqlite/sqlite_sql.php +++ b/drivers/sqlite/sqlite_sql.php @@ -38,6 +38,19 @@ class SQLite_SQL implements iDB_SQL { return $sql." LIMIT {$offset}, {$limit}"; } + + // -------------------------------------------------------------------------- + + /** + * Get the query plan for the sql query + * + * @param string $sql + * @return string + */ + public function explain($sql) + { + return "EXPLAIN QUERY PLAN {$sql}"; + } // -------------------------------------------------------------------------- @@ -180,4 +193,4 @@ SQL; } } -//End of sqlite_sql.php +//End of sqlite_sql.php \ No newline at end of file diff --git a/tests/databases/firebird/firebird-qb.php b/tests/databases/firebird/firebird-qb.php index 90af362..d07ea9e 100644 --- a/tests/databases/firebird/firebird-qb.php +++ b/tests/databases/firebird/firebird-qb.php @@ -90,4 +90,5 @@ class FirebirdQBTest extends QBTest { $this->assertTrue(is_array($res)); } + } \ No newline at end of file diff --git a/tests/databases/mysql/mysql-qb.php b/tests/databases/mysql/mysql-qb.php index 2341e2c..e10b79a 100644 --- a/tests/databases/mysql/mysql-qb.php +++ b/tests/databases/mysql/mysql-qb.php @@ -49,4 +49,35 @@ class MySQLQBTest extends QBTest { { $this->assertTrue(in_array('mysql', PDO::getAvailableDrivers())); } + + // -------------------------------------------------------------------------- + + public function testQueryExplain() + { + $query = $this->db->select('id, key as k, val') + ->explain() + ->where('id >', 1) + ->where('id <', 900) + ->get('create_test', 2, 1); + + $res = $query->fetchAll(PDO::FETCH_ASSOC); + + $expected = array ( + array ( + 'id' => '1', + 'select_type' => 'SIMPLE', + 'table' => 'create_test', + 'type' => 'range', + 'possible_keys' => 'PRIMARY', + 'key' => 'PRIMARY', + 'key_len' => '4', + 'ref' => NULL, + 'rows' => '1', + 'filtered' => '100.00', + 'Extra' => 'Using where', + ) + ); + + $this->assertEqual($expected, $res); + } } \ No newline at end of file diff --git a/tests/databases/pgsql/pgsql-qb.php b/tests/databases/pgsql/pgsql-qb.php index ff039ac..fab1333 100644 --- a/tests/databases/pgsql/pgsql-qb.php +++ b/tests/databases/pgsql/pgsql-qb.php @@ -48,4 +48,43 @@ class PgSQLQBTest extends QBTest { { $this->assertTrue(in_array('pgsql', PDO::getAvailableDrivers())); } + + // -------------------------------------------------------------------------- + + public function testQueryExplain() + { + $query = $this->db->select('id, key as k, val') + ->explain() + ->where('id >', 1) + ->where('id <', 900) + ->get('create_test', 2, 1); + + $res = $query->fetchAll(PDO::FETCH_ASSOC); + + $expected = array ( + array ( + 'QUERY PLAN' => 'Limit (cost=6.41..10.64 rows=2 width=68)', + ), + array ( + 'QUERY PLAN' => ' Output: id, key, val', + ), + array ( + 'QUERY PLAN' => ' -> Bitmap Heap Scan on public.create_test (cost=4.29..12.76 rows=4 width=68)', + ), + array ( + 'QUERY PLAN' => ' Output: id, key, val', + ), + array ( + 'QUERY PLAN' => ' Recheck Cond: ((create_test.id > 1) AND (create_test.id < 900))', + ), + array ( + 'QUERY PLAN' => ' -> Bitmap Index Scan on create_test_pkey (cost=0.00..4.29 rows=4 width=0)', + ), + array ( + 'QUERY PLAN' => ' Index Cond: ((create_test.id > 1) AND (create_test.id < 900))', + ), + ); + + $this->assertEqual($expected, $res); + } } \ No newline at end of file diff --git a/tests/databases/sqlite/sqlite-qb.php b/tests/databases/sqlite/sqlite-qb.php index 4f69c17..b80f3f2 100644 --- a/tests/databases/sqlite/sqlite-qb.php +++ b/tests/databases/sqlite/sqlite-qb.php @@ -60,4 +60,28 @@ $this->assertNull($query); } + + // -------------------------------------------------------------------------- + + public function testQueryExplain() + { + $query = $this->db->select('id, key as k, val') + ->explain() + ->where('id >', 1) + ->where('id <', 900) + ->get('create_test', 2, 1); + + $res = $query->fetchAll(PDO::FETCH_ASSOC); + + $expected = array ( + array ( + 'selectid' => '0', + 'order' => '0', + 'from' => '0', + 'detail' => 'SEARCH TABLE create_test USING INTEGER PRIMARY KEY (rowid>? AND rowidassertEqual($expected, $res); + } } \ No newline at end of file