Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Pdomysql #1403

Closed
wants to merge 2 commits into from

5 participants

ianmacl Rouven Weßling Sam Moffatt Don Gilbert Andrew Eddie
ianmacl

Now that the stuff in JDatabaseMysql is no longer needed it makes sense to implement a PDO-MySQL based driver since according to http://php.net/manual/en/function.mysql-connect.php use of the MySQL extensions is discouraged. This pull request replaces the old Mysql driver with a PDO based driver and makes it possible to use prepared statements with MySQL.

Rouven Weßling
Collaborator

Interesting stuff. But wouldn't that be better as new driver? (Mysqlpdo for example)

ianmacl

Mysqlpdo is kind of awkward. Seems to make more sense just to use this as eventually we'll want to be PDO only IMO.

Rouven Weßling
Collaborator

We do? Mysqli has a couple of nice things (asynchronous queries mostly) that PDO doesn't (yet) offer that could be useful in the future. From what we use right now there not really a problem with PDO's feature set thou.

Prepared statements can be done with mysqli thou, we need to abstract the statement class thou. I like the approach of Doctrine DBAL that emulates a (large) subset of the PDOStatement library for mysqli.

Sam Moffatt

I'm skeptical of pulling everything over to PDO or that we will be PDO for everything at some point in the abstract future. PDO in my experience comes with a large selection of it's own baggage which you have to learn and understand the nuance as well as working out the particular nuances of each database layer as well. There was a time when PDO wasn't even maintained by anyone in the PHP core but I'm not sure if this is still a true statement. PDO will always lag behind what native drivers provide simply due to it's design. It is likely that we'll live in a world of PDO + native drivers for a while yet.

ianmacl

Either way, I think we can agree that we want to get rid of the MySQL driver and from a naming perspective calling this one MySQL makes sense, IMO. There is precedence in that other PDO based drivers aren't prefixed.

Anyway, if the decision is to rename it Mysqlpdo, that's fine. I just think it is awkward.

Don Gilbert
Collaborator

Where does this stand? I'm with @ianmacl that we should be 100% PDO for our database drivers. Other major PHP frameworks (Laravel, FuelPHP, even CodeIgniter) are either already 100% PDO with their own specific implementations (such as Laravel's Eloquent ORM - smexy!) or have plans to get that way. I don't see why Joomla should be any different in those regards.

@realityking - your reasoning that PDO lags behind native drivers is entirely valid. However, there are MANY parts of Joomla that lag behind the native abilities of PHP. (Namespaces, anyone?) So I don't think that it's a good enough reason to hold off on getting the joomla platform entirely shifted over to PDO.

And my vote (as you can guess) for naming it Mysqlpdo, is no.

Sam Moffatt

The reason that Joomla lags behind is because we purposely hold ourselves back to ensure that the majority of the hosting providers can run the application. The Platform only just switched to PHP 5.3 because of the CMS and because there is a lag between what PHP supports and what hosting providers offer. The lack of support for features I a sense is intentional. If anything this makes the reason PDO lags an even more significant reason: a significant number of our customers are on a lagging PHP version.

Given that I'm probably ready to contribute a replacement Oralce driver that uses the native OCI functions instead of PDO version and in the past I've talked to Louis about issues with SQLite PDO I'm not in favour of PDO in general. There are numerous issues and incompatibilities that PDO tries to hide badly. I think for me the most interesting thing that prompted the shift for OCI for me from the PDO Oracle was that Propel, which is a reasonable mature PRM for PHP, actually failback to the OCI functions to make up for the fact that PDO Oracle outright doesn't work. There are plenty of other places and while at the moment the PDO library is presently being supported, there was a time during the early 5.0 series where it had no maintainer at all. And in a sense not all of the drivers are well supported.

Given there is an existing MySQL driver, I think we should keep it clear for the time being.

ianmacl

I'm not really arguing that we shouldn't keep the Mysqli driver around. If we don't want to go the whole hog down the PDO path, that's fine. It obviously seems to be something that differs on a case by case basis. These days there are three MySQL APIs - MySQL, MySQLi and PDO-MySQL. The PHP website states fairly clearly that out of the three, MySQL is the one not to use. My impression is that as far as MySQL is concerned, MySQLi and PDO are about on par as far as support is concerned with at least a few of the core developers preferring PDO. Thus I think it is a reasonable choice to support MySQLi and PDO but not MySQL.

As far as PDO in general, I guess it really depends on the vendor and what sort of support they choose to provide. It seems Microsoft has gotten on board with PDO but it appears that Oracle has stuck with their own OCI driver. Which is fine. It seems evident that we have to choose APIs carefully based on the DB in question. I would argue that for MySQL PDO is a reasonable choice, but it really isn't a hill I'm willing to die on. I think we should add support for PDO-Mysql, and if we'd rather call it pdomysql or whatever, I'm fine with that. I will even do the work do rename it if we can come to a decision.

Don Gilbert
Collaborator

Also relevant - https://wiki.php.net/rfc/mysql_deprecation

This is an RFC that proposes to generate E_DEPRECATED errors when connecting to a MySQL database with the ext/mysql API. Opened Nov 12th.

ianmacl

So the deprecation is really beside the point, although it should be a pretty clear indicator that ext/mysql is on its way out when as of today there is a 25-12 vote in favour of adding deprecation notices. I think we can all agree that we probably don't need the Mysql driver as it is today around anymore - the CMS defaults to MySQLi and I'm not really aware of anything in the way of issues that have arisen.

The only thing I think we have to figure out is what we want to call this set of classes.

Andrew Eddie

Ian, how about we do this in the FW?

Andrew Eddie eddieajau closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jul 24, 2012
  1. ianmacl
  2. ianmacl

    Fixed character set issues

    ianmacl authored
This page is out of date. Refresh to see the latest.
496 libraries/joomla/database/driver/mysql.php
View
@@ -17,7 +17,7 @@
* @see http://dev.mysql.com/doc/
* @since 12.1
*/
-class JDatabaseDriverMysql extends JDatabaseDriverMysqli
+class JDatabaseDriverMysql extends JDatabaseDriverPdo
{
/**
* The name of the database driver.
@@ -28,36 +28,54 @@ class JDatabaseDriverMysql extends JDatabaseDriverMysqli
public $name = 'mysql';
/**
- * Constructor.
+ * The character(s) used to quote SQL statement names such as table names or field names,
+ * etc. The child classes should define this as necessary. If a single character string the
+ * same character is used for both sides of the quoted name, else the first character will be
+ * used for the opening quote and the second for the closing quote.
*
- * @param array $options Array of database options with keys: host, user, password, database, select.
+ * @var string
+ * @since 12.1
+ */
+ protected $nameQuote = '`';
+
+ /**
+ * The null or zero representation of a timestamp for the database driver. This should be
+ * defined in child classes to hold the appropriate value for the engine.
*
- * @since 12.1
+ * @var string
+ * @since 12.1
*/
- public function __construct($options)
- {
- // Get some basic values from the options.
- $options['host'] = (isset($options['host'])) ? $options['host'] : 'localhost';
- $options['user'] = (isset($options['user'])) ? $options['user'] : 'root';
- $options['password'] = (isset($options['password'])) ? $options['password'] : '';
- $options['database'] = (isset($options['database'])) ? $options['database'] : '';
- $options['select'] = (isset($options['select'])) ? (bool) $options['select'] : true;
+ protected $nullDate = '0000-00-00 00:00:00';
- // Finalize initialisation.
- parent::__construct($options);
- }
+ /**
+ * @var string The minimum supported database version.
+ * @since 12.1
+ */
+ protected static $dbMinimum = '5.0.4';
/**
- * Destructor.
+ * Constructor.
+ *
+ * @param array $options Array of database options with keys: host, user, password, database, select.
*
* @since 12.1
*/
- public function __destruct()
+ public function __construct($options)
{
- if (is_resource($this->connection))
+ // Get some basic values from the options.
+ $options['driver'] = 'mysql';
+ $options['charset'] = (isset($options['charset'])) ? $options['charset'] : 'utf8';
+
+ // Setting the charset in the DSN doesn't work until PHP 5.3.6
+ if (version_compare(PHP_VERSION, '5.3.6', '<'))
{
- mysql_close($this->connection);
+ $options['driverOptions'] = array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8');
}
+
+ $this->charset = $options['charset'];
+
+ // Finalize initialisation.
+ parent::__construct($options);
}
/**
@@ -65,347 +83,409 @@ public function __destruct()
*
* @return void Returns void if the database connected successfully.
*
- * @since 12.1
+ * @since 12.2
* @throws RuntimeException
*/
public function connect()
{
- if ($this->connection)
- {
- return;
- }
-
- // Make sure the MySQL extension for PHP is installed and enabled.
- if (!function_exists('mysql_connect'))
- {
- throw new RuntimeException('Could not connect to MySQL.');
- }
-
- // Attempt to connect to the server.
- if (!($this->connection = @ mysql_connect($this->options['host'], $this->options['user'], $this->options['password'], true)))
- {
- throw new RuntimeException('Could not connect to MySQL.');
- }
+ parent::connect();
- // Set sql_mode to non_strict mode
- mysql_query("SET @@SESSION.sql_mode = '';", $this->connection);
-
- // If auto-select is enabled select the given database.
- if ($this->options['select'] && !empty($this->options['database']))
- {
- $this->select($this->options['database']);
- }
-
- // Set charactersets (needed for MySQL 4.1.2+).
- $this->setUTF();
+ $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+ $this->connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
}
/**
- * Disconnects the database.
+ * Test to see if the MySQL connector is available.
*
- * @return void
+ * @return boolean True on success, false otherwise.
*
* @since 12.1
*/
- public function disconnect()
+ public static function isSupported()
{
- // Close the connection.
- mysql_close($this->connection);
-
- $this->connection = null;
+ return in_array('mysql', PDO::getAvailableDrivers());
}
/**
- * Method to escape a string for usage in an SQL statement.
+ * Drops a table from the database.
*
- * @param string $text The string to be escaped.
- * @param boolean $extra Optional parameter to provide extra escaping.
+ * @param string $tableName The name of the database table to drop.
+ * @param boolean $ifExists Optionally specify that the table must exist before it is dropped.
*
- * @return string The escaped string.
+ * @return JDatabaseDriverMysql Returns this object to support chaining.
*
* @since 12.1
+ * @throws RuntimeException
*/
- public function escape($text, $extra = false)
+ public function dropTable($tableName, $ifExists = true)
{
$this->connect();
- $result = mysql_real_escape_string($text, $this->getConnection());
+ $query = $this->getQuery(true);
- if ($extra)
- {
- $result = addcslashes($result, '%_');
- }
+ $query->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS ' : '') . $this->quoteName($tableName));
- return $result;
- }
+ $this->setQuery($query);
- /**
- * Test to see if the MySQL connector is available.
- *
- * @return boolean True on success, false otherwise.
- *
- * @since 12.1
- */
- public static function isSupported()
- {
- return (function_exists('mysql_connect'));
+ $this->execute();
+
+ return $this;
}
/**
- * Determines if the connection to the server is active.
+ * Method to get the database collation in use by sampling a text field of a table in the database.
*
- * @return boolean True if connected to the database engine.
+ * @return mixed The collation in use by the database (string) or boolean false if not supported.
*
* @since 12.1
+ * @throws RuntimeException
*/
- public function connected()
+ public function getCollation()
{
- if (is_resource($this->connection))
- {
- return @mysql_ping($this->connection);
- }
+ $this->connect();
- return false;
+ $this->setQuery('SHOW FULL COLUMNS FROM #__users');
+ $array = $this->loadAssocList();
+ return $array['2']['Collation'];
}
/**
- * Get the number of affected rows for the previous executed SQL statement.
+ * Select a database for use.
*
- * @return integer The number of affected rows.
+ * @param string $database The name of the database to select for use.
*
- * @since 12.1
+ * @return boolean True if the database was successfully selected.
+ *
+ * @since 11.1
+ * @throws RuntimeException
*/
- public function getAffectedRows()
+ public function select($database)
{
$this->connect();
- return mysql_affected_rows($this->connection);
+ $this->setQuery('USE ' . $this->quoteName($database));
+
+ $this->execute();
+
+ return $this;
}
/**
- * Get the number of returned rows for the previous executed SQL statement.
+ * Wrap an SQL statement identifier name such as column, table or database names in quotes to prevent injection
+ * risks and reserved word conflicts.
*
- * @param resource $cursor An optional database cursor resource to extract the row count from.
+ * @param mixed $name The identifier name to wrap in quotes, or an array of identifier names to wrap in quotes.
+ * Each type supports dot-notation name.
+ * @param mixed $as The AS query part associated to $name. It can be string or array, in latter case it has to be
+ * same length of $name; if is null there will not be any AS part for string or array element.
*
- * @return integer The number of returned rows.
+ * @return mixed The quote wrapped name, same type of $name.
*
- * @since 12.1
+ * @since 11.1
*/
- public function getNumRows($cursor = null)
+ public function quoteName($name, $as = null)
{
- $this->connect();
+ if (is_string($name))
+ {
+ $quotedName = $this->quoteNameStr(explode('.', $name));
+
+ $quotedAs = '';
+ if (!is_null($as))
+ {
+ settype($as, 'array');
+ $quotedAs .= ' AS ' . $this->quoteNameStr($as);
+ }
- return mysql_num_rows($cursor ? $cursor : $this->cursor);
+ return $quotedName . $quotedAs;
+ }
+ else
+ {
+ $fin = array();
+
+ if (is_null($as))
+ {
+ foreach ($name as $str)
+ {
+ $fin[] = $this->quoteName($str);
+ }
+ }
+ elseif (is_array($name) && (count($name) == count($as)))
+ {
+ $count = count($name);
+ for ($i = 0; $i < $count; $i++)
+ {
+ $fin[] = $this->quoteName($name[$i], $as[$i]);
+ }
+ }
+
+ return $fin;
+ }
}
/**
- * Get the version of the database connector.
+ * Quote strings coming from quoteName call.
*
- * @return string The database connector version.
+ * @param array $strArr Array of strings coming from quoteName dot-explosion.
*
- * @since 12.1
+ * @return string Dot-imploded string of quoted parts.
+ *
+ * @since 11.3
*/
- public function getVersion()
+ protected function quoteNameStr($strArr)
{
- $this->connect();
+ $parts = array();
+ $q = $this->nameQuote;
- return mysql_get_server_info($this->connection);
+ foreach ($strArr as $part)
+ {
+ if (is_null($part))
+ {
+ continue;
+ }
+
+ $part = str_replace('`', '``', $part);
+
+ if (strlen($q) == 1)
+ {
+ $parts[] = $q . $part . $q;
+ }
+ else
+ {
+ $parts[] = $q{0} . $part . $q{1};
+ }
+ }
+
+ return implode('.', $parts);
}
/**
- * Method to get the auto-incremented value from the last INSERT statement.
+ * Shows the table CREATE statement that creates the given tables.
+ *
+ * @param mixed $tables A table name or a list of table names.
*
- * @return integer The value of the auto-increment field from the last inserted row.
+ * @return array A list of the create SQL for the tables.
*
* @since 12.1
+ * @throws RuntimeException
*/
- public function insertid()
+ public function getTableCreate($tables)
{
$this->connect();
- return mysql_insert_id($this->connection);
+ // Initialise variables.
+ $result = array();
+
+ // Sanitize input to an array and iterate over the list.
+ settype($tables, 'array');
+ foreach ($tables as $table)
+ {
+ $this->setQuery('SHOW CREATE TABLE ' . $this->quoteName($table));
+
+ $row = $this->loadRow();
+
+ // Populate the result array based on the create statements.
+ $result[$table] = $row[1];
+ }
+
+ return $result;
}
/**
- * Execute the SQL statement.
+ * Retrieves field information about a given table.
+ *
+ * @param string $table The name of the database table.
+ * @param boolean $typeOnly True to only return field types.
*
- * @return mixed A database cursor resource on success, boolean false on failure.
+ * @return array An array of fields for the database table.
*
* @since 12.1
* @throws RuntimeException
*/
- public function execute()
+ public function getTableColumns($table, $typeOnly = true)
{
$this->connect();
- if (!is_resource($this->connection))
- {
- JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database');
- throw new RuntimeException($this->errorMsg, $this->errorNum);
- }
+ $result = array();
- // Take a local copy so that we don't modify the original query and cause issues later
- $sql = $this->replacePrefix((string) $this->sql);
- if ($this->limit > 0 || $this->offset > 0)
- {
- $sql .= ' LIMIT ' . $this->offset . ', ' . $this->limit;
- }
+ $query = $this->getQuery(true);
- // If debugging is enabled then let's log the query.
- if ($this->debug)
- {
- // Increment the query counter and add the query to the object queue.
- $this->count++;
- $this->log[] = $sql;
+ // Set the query to get the table fields statement.
+ $this->setQuery('SHOW FULL COLUMNS FROM ' . $this->quoteName($table));
- JLog::add($sql, JLog::DEBUG, 'databasequery');
- }
-
- // Reset the error values.
- $this->errorNum = 0;
- $this->errorMsg = '';
-
- // Execute the query. Error suppression is used here to prevent warnings/notices that the connection has been lost.
- $this->cursor = @mysql_query($sql, $this->connection);
+ $fields = $this->loadObjectList();
- // If an error occurred handle it.
- if (!$this->cursor)
+ // If we only want the type as the value add just that to the list.
+ if ($typeOnly)
{
- // Check if the server was disconnected.
- if (!$this->connected())
+ foreach ($fields as $field)
{
- try
- {
- // Attempt to reconnect.
- $this->connection = null;
- $this->connect();
- }
- // If connect fails, ignore that exception and throw the normal exception.
- catch (RuntimeException $e)
- {
- // Get the error number and message.
- $this->errorNum = (int) mysql_errno($this->connection);
- $this->errorMsg = (string) mysql_error($this->connection) . ' SQL=' . $sql;
-
- // Throw the normal query exception.
- JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'databasequery');
- throw new RuntimeException($this->errorMsg, $this->errorNum);
- }
-
- // Since we were able to reconnect, run the query again.
- return $this->execute();
+ $result[$field->Field] = preg_replace("/[(0-9)]/", '', $field->Type);
}
- // The server was not disconnected.
- else
+ }
+ // If we want the whole field data object add that to the list.
+ else
+ {
+ foreach ($fields as $field)
{
- // Get the error number and message.
- $this->errorNum = (int) mysql_errno($this->connection);
- $this->errorMsg = (string) mysql_error($this->connection) . ' SQL=' . $sql;
-
- // Throw the normal query exception.
- JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'databasequery');
- throw new RuntimeException($this->errorMsg, $this->errorNum);
+ $result[$field->Field] = $field;
}
}
- return $this->cursor;
+ return $result;
}
/**
- * Select a database for use.
+ * Get the details list of keys for a table.
*
- * @param string $database The name of the database to select for use.
+ * @param string $table The name of the table.
*
- * @return boolean True if the database was successfully selected.
+ * @return array An array of the column specification for the table.
*
* @since 12.1
* @throws RuntimeException
*/
- public function select($database)
+ public function getTableKeys($table)
{
$this->connect();
- if (!$database)
- {
- return false;
- }
+ $query = $this->getQuery(true);
- if (!mysql_select_db($database, $this->connection))
- {
- throw new RuntimeException('Could not connect to database');
- }
+ // Get the details columns information.
+ $this->setQuery('SHOW KEYS FROM ' . $this->quoteName($table));
- return true;
+ $keys = $this->loadObjectList();
+
+ return $keys;
}
/**
- * Set the connection to use UTF-8 character encoding.
+ * Method to get an array of all tables in the database.
*
- * @return boolean True on success.
+ * @return array An array of all the tables in the database.
*
* @since 12.1
+ * @throws RuntimeException
*/
- public function setUTF()
+ public function getTableList()
{
$this->connect();
- return mysql_set_charset('utf8', $this->connection);
+ // Set the query to get the tables statement.
+ $this->setQuery('SHOW TABLES');
+ $tables = $this->loadColumn();
+
+ return $tables;
}
/**
- * Method to fetch a row from the result set cursor as an array.
- *
- * @param mixed $cursor The optional result set cursor from which to fetch the row.
+ * Get the version of the database connector.
*
- * @return mixed Either the next row from the result set or false if there are no more rows.
+ * @return string The database connector version.
*
* @since 12.1
*/
- protected function fetchArray($cursor = null)
+ public function getVersion()
{
- return mysql_fetch_row($cursor ? $cursor : $this->cursor);
+ $this->connect();
+
+ return $this->getOption(PDO::ATTR_SERVER_VERSION);
}
/**
- * Method to fetch a row from the result set cursor as an associative array.
+ * Locks a table in the database.
*
- * @param mixed $cursor The optional result set cursor from which to fetch the row.
+ * @param string $table The name of the table to unlock.
*
- * @return mixed Either the next row from the result set or false if there are no more rows.
+ * @return JDatabaseMySQL Returns this object to support chaining.
*
* @since 12.1
+ * @throws RuntimeException
*/
- protected function fetchAssoc($cursor = null)
+ public function lockTable($table)
{
- return mysql_fetch_assoc($cursor ? $cursor : $this->cursor);
+ $query = $this->getQuery(true);
+
+ $this->setQuery('LOCK TABLES ' . $this->quoteName($table) . ' WRITE');
+
+ $this->setQuery($query)->exec();
+
+ return $this;
}
/**
- * Method to fetch a row from the result set cursor as an object.
+ * Renames a table in the database.
*
- * @param mixed $cursor The optional result set cursor from which to fetch the row.
- * @param string $class The class name to use for the returned row object.
+ * @param string $oldTable The name of the table to be renamed
+ * @param string $newTable The new name for the table.
+ * @param string $backup Not used by MySQL.
+ * @param string $prefix Not used by MySQL.
*
- * @return mixed Either the next row from the result set or false if there are no more rows.
+ * @return JDatabaseDriverMysql Returns this object to support chaining.
*
* @since 12.1
+ * @throws RuntimeException
*/
- protected function fetchObject($cursor = null, $class = 'stdClass')
+ public function renameTable($oldTable, $newTable, $backup = null, $prefix = null)
{
- return mysql_fetch_object($cursor ? $cursor : $this->cursor, $class);
+ $query = $this->getQuery(true);
+
+ $this->setQuery('RENAME TABLE ' . $this->quoteName($oldTable) . ' TO ' . $this->quoteName($newTable));
+
+ return $this;
}
/**
- * Method to free up the memory used for the result set.
+ * Method to escape a string for usage in an SQL statement.
+ *
+ * Oracle escaping reference:
+ * http://www.orafaq.com/wiki/SQL_FAQ#How_does_one_escape_special_characters_when_writing_SQL_queries.3F
+ *
+ * SQLite escaping notes:
+ * http://www.sqlite.org/faq.html#q14
+ *
+ * Method body is as implemented by the Zend Framework
+ *
+ * Note: Using query objects with bound variables is
+ * preferable to the below.
*
- * @param mixed $cursor The optional result set cursor from which to fetch the row.
+ * @param string $text The string to be escaped.
+ * @param boolean $extra Unused optional parameter to provide extra escaping.
*
- * @return void
+ * @return string The escaped string.
*
* @since 12.1
*/
- protected function freeResult($cursor = null)
+ public function escape($text, $extra = false)
{
- mysql_free_result($cursor ? $cursor : $this->cursor);
+ $this->connect();
+
+ if (is_int($text) || is_float($text))
+ {
+ return $text;
+ }
+
+ $result = substr($this->connection->quote($text), 1, -1);
+
+ if ($extra)
+ {
+ $result = addcslashes($result, '%_');
+ }
+
+ return $result;
+ }
+
+ /**
+ * Unlocks tables in the database.
+ *
+ * @return JDatabaseMySQL Returns this object to support chaining.
+ *
+ * @since 12.1
+ * @throws RuntimeException
+ */
+ public function unlockTables()
+ {
+ $this->setQuery('UNLOCK TABLES')->execute();
+
+ return $this;
}
}
2  libraries/joomla/database/driver/pdo.php
View
@@ -206,6 +206,8 @@ public function connect()
$replace = array('#HOST#', '#PORT#', '#DBNAME#');
$with = array($this->options['host'], $this->options['port'], $this->options['database']);
+ $format .= ';charset=' . $this->options['charset'];
+
break;
case 'oci':
39 libraries/joomla/database/iterator/mysql.php
View
@@ -14,45 +14,8 @@
*
* @package Joomla.Platform
* @subpackage Database
- * @see http://dev.mysql.com/doc/
* @since 12.1
*/
-class JDatabaseIteratorMysql extends JDatabaseIterator
+class JDatabaseIteratorMysql extends JDatabaseIteratorPdo
{
- /**
- * Get the number of rows in the result set for the executed SQL given by the cursor.
- *
- * @return integer The number of rows in the result set.
- *
- * @since 12.1
- * @see Countable::count()
- */
- public function count()
- {
- return mysql_num_rows($this->cursor);
- }
-
- /**
- * Method to fetch a row from the result set cursor as an object.
- *
- * @return mixed Either the next row from the result set or false if there are no more rows.
- *
- * @since 12.1
- */
- protected function fetchObject()
- {
- return mysql_fetch_object($this->cursor, $this->class);
- }
-
- /**
- * Method to free up the memory used for the result set.
- *
- * @return void
- *
- * @since 12.1
- */
- protected function freeResult()
- {
- mysql_free_result($this->cursor);
- }
}
190 libraries/joomla/database/query/mysql.php
View
@@ -16,6 +16,194 @@
* @subpackage Database
* @since 11.1
*/
-class JDatabaseQueryMysql extends JDatabaseQueryMysqli
+class JDatabaseQueryMysql extends JDatabaseQuery implements JDatabaseQueryPreparable, JDatabaseQueryLimitable
{
+ /**
+ * @var interger The offset for the result set.
+ * @since 12.1
+ */
+ protected $offset;
+
+ /**
+ * @var integer The limit for the result set.
+ * @since 12.1
+ */
+ protected $limit;
+
+ /**
+ * @var mixed
+ * @since 12.1
+ */
+ protected $bounded = array();
+
+ /**
+ * Method to add a variable to an internal array that will be bound to a prepared SQL statement before query execution. Also
+ * removes a variable that has been bounded from the internal bounded array when the passed in value is null.
+ *
+ * @param string|integer $key The key that will be used in your SQL query to reference the value. Usually of
+ * the form ':key', but can also be an integer.
+ * @param mixed &$value The value that will be bound. The value is passed by reference to support output
+ * parameters such as those possible with stored procedures.
+ * @param integer $dataType Constant corresponding to a SQL datatype.
+ * @param integer $length The length of the variable. Usually required for OUTPUT parameters.
+ * @param array $driverOptions Optional driver options to be used.
+ *
+ * @return JDatabaseQuery
+ *
+ * @since 12.1
+ */
+ public function bind($key = null, &$value = null, $dataType = PDO::PARAM_STR, $length = 0, $driverOptions = array())
+ {
+ // Case 1: Empty Key (reset $bounded array)
+ if (empty($key))
+ {
+ $this->bounded = array();
+ return $this;
+ }
+
+ // Case 2: Key Provided, null value (unset key from $bounded array)
+ if (is_null($value))
+ {
+ if (isset($this->bounded[$key]))
+ {
+ unset($this->bounded[$key]);
+ }
+
+ return $this;
+ }
+
+ $obj = new stdClass;
+
+ $obj->value = &$value;
+ $obj->dataType = $dataType;
+ $obj->length = $length;
+ $obj->driverOptions = $driverOptions;
+
+ // Case 3: Simply add the Key/Value into the bounded array
+ $this->bounded[$key] = $obj;
+
+ return $this;
+ }
+
+ /**
+ * Retrieves the bound parameters array when key is null and returns it by reference. If a key is provided then that item is
+ * returned.
+ *
+ * @param mixed $key The bounded variable key to retrieve.
+ *
+ * @return mixed
+ *
+ * @since 12.1
+ */
+ public function &getBounded($key = null)
+ {
+ if (empty($key))
+ {
+ return $this->bounded;
+ }
+ else
+ {
+ if (isset($this->bounded[$key]))
+ {
+ return $this->bounded[$key];
+ }
+ }
+ }
+
+ /**
+ * Clear data from the query or a specific clause of the query.
+ *
+ * @param string $clause Optionally, the name of the clause to clear, or nothing to clear the whole query.
+ *
+ * @return JDatabaseQuery Returns this object to allow chaining.
+ *
+ * @since 12.1
+ */
+ public function clear($clause = null)
+ {
+ switch ($clause)
+ {
+ case null:
+ $this->bounded = array();
+ break;
+ }
+
+ parent::clear($clause);
+
+ return $this;
+ }
+
+ /**
+ * Method to modify a query already in string format with the needed
+ * additions to make the query limited to a particular number of
+ * results, or start at a particular offset.
+ *
+ * @param string $query The query in string format
+ * @param integer $limit The limit for the result set
+ * @param integer $offset The offset for the result set
+ *
+ * @return string
+ *
+ * @since 12.1
+ */
+ public function processLimit($query, $limit, $offset = 0)
+ {
+ if ($limit > 0 || $offset > 0)
+ {
+ $query .= ' LIMIT ' . $offset . ', ' . $limit;
+ }
+
+ return $query;
+ }
+
+ /**
+ * Concatenates an array of column names or values.
+ *
+ * @param array $values An array of values to concatenate.
+ * @param string $separator As separator to place between each value.
+ *
+ * @return string The concatenated values.
+ *
+ * @since 11.1
+ */
+ public function concatenate($values, $separator = null)
+ {
+ if ($separator)
+ {
+ $concat_string = 'CONCAT_WS(' . $this->quote($separator);
+
+ foreach ($values as $value)
+ {
+ $concat_string .= ', ' . $value;
+ }
+
+ return $concat_string . ')';
+ }
+ else
+ {
+ return 'CONCAT(' . implode(',', $values) . ')';
+ }
+ }
+
+ /**
+ * Sets the offset and limit for the result set, if the database driver supports it.
+ *
+ * Usage:
+ * $query->setLimit(100, 0); (retrieve 100 rows, starting at first record)
+ * $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th record)
+ *
+ * @param integer $limit The limit for the result set
+ * @param integer $offset The offset for the result set
+ *
+ * @return JDatabaseQuery Returns this object to allow chaining.
+ *
+ * @since 12.1
+ */
+ public function setLimit($limit = 0, $offset = 0)
+ {
+ $this->limit = (int) $limit;
+ $this->offset = (int) $offset;
+
+ return $this;
+ }
}
3  tests/suites/database/driver/mysql/JDatabaseMySQLTest.php
View
@@ -445,10 +445,9 @@ public function testQuery()
{
self::$driver->setQuery("REPLACE INTO `jos_dbtest` SET `id` = 5, `title` = 'testTitle'");
- $this->assertThat(self::$driver->execute(), $this->isTrue(), __LINE__);
+ $this->assertThat((bool)self::$driver->execute(), $this->isTrue(), __LINE__);
$this->assertThat(self::$driver->insertid(), $this->equalTo(5), __LINE__);
-
}
/**
Something went wrong with that request. Please try again.