Skip to content
This repository

Pdomysql #1403

Closed
wants to merge 2 commits into from

5 participants

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

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
Owner

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
Owner

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
Owner

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
Owner

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
Owner

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
Owner

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
Owner

Ian, how about we do this in the FW?

Andrew Eddie eddieajau closed this March 15, 2013
Andrew Eddie eddieajau referenced this pull request in joomla/joomla-framework March 15, 2013
Closed

Replace MySQL driver with PDO version #82

Simon Asika asika32764 referenced this pull request in joomla/joomla-framework September 16, 2013
Merged

Replace MySQL driver with PDO version #82 #218

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 2 unique commits by 1 author.

Jul 23, 2012
ianmacl Implementing Mysql driver on PDO 443d0be
ianmacl Fixed character set issues 83fd5dd
This page is out of date. Refresh to see the latest.
496  libraries/joomla/database/driver/mysql.php
@@ -17,7 +17,7 @@
17 17
  * @see         http://dev.mysql.com/doc/
18 18
  * @since       12.1
19 19
  */
20  
-class JDatabaseDriverMysql extends JDatabaseDriverMysqli
  20
+class JDatabaseDriverMysql extends JDatabaseDriverPdo
21 21
 {
22 22
 	/**
23 23
 	 * The name of the database driver.
@@ -28,36 +28,54 @@ class JDatabaseDriverMysql extends JDatabaseDriverMysqli
28 28
 	public $name = 'mysql';
29 29
 
30 30
 	/**
31  
-	 * Constructor.
  31
+	 * The character(s) used to quote SQL statement names such as table names or field names,
  32
+	 * etc. The child classes should define this as necessary.  If a single character string the
  33
+	 * same character is used for both sides of the quoted name, else the first character will be
  34
+	 * used for the opening quote and the second for the closing quote.
32 35
 	 *
33  
-	 * @param   array  $options  Array of database options with keys: host, user, password, database, select.
  36
+	 * @var    string
  37
+	 * @since  12.1
  38
+	 */
  39
+	protected $nameQuote = '`';
  40
+
  41
+	/**
  42
+	 * The null or zero representation of a timestamp for the database driver.  This should be
  43
+	 * defined in child classes to hold the appropriate value for the engine.
34 44
 	 *
35  
-	 * @since   12.1
  45
+	 * @var    string
  46
+	 * @since  12.1
36 47
 	 */
37  
-	public function __construct($options)
38  
-	{
39  
-		// Get some basic values from the options.
40  
-		$options['host'] = (isset($options['host'])) ? $options['host'] : 'localhost';
41  
-		$options['user'] = (isset($options['user'])) ? $options['user'] : 'root';
42  
-		$options['password'] = (isset($options['password'])) ? $options['password'] : '';
43  
-		$options['database'] = (isset($options['database'])) ? $options['database'] : '';
44  
-		$options['select'] = (isset($options['select'])) ? (bool) $options['select'] : true;
  48
+	protected $nullDate = '0000-00-00 00:00:00';
45 49
 
46  
-		// Finalize initialisation.
47  
-		parent::__construct($options);
48  
-	}
  50
+	/**
  51
+	 * @var    string  The minimum supported database version.
  52
+	 * @since  12.1
  53
+	 */
  54
+	protected static $dbMinimum = '5.0.4';
49 55
 
50 56
 	/**
51  
-	 * Destructor.
  57
+	 * Constructor.
  58
+	 *
  59
+	 * @param   array  $options  Array of database options with keys: host, user, password, database, select.
52 60
 	 *
53 61
 	 * @since   12.1
54 62
 	 */
55  
-	public function __destruct()
  63
+	public function __construct($options)
56 64
 	{
57  
-		if (is_resource($this->connection))
  65
+		// Get some basic values from the options.
  66
+		$options['driver'] = 'mysql';
  67
+		$options['charset']    = (isset($options['charset'])) ? $options['charset']   : 'utf8';
  68
+
  69
+		// Setting the charset in the DSN doesn't work until PHP 5.3.6
  70
+		if (version_compare(PHP_VERSION, '5.3.6', '<'))
58 71
 		{
59  
-			mysql_close($this->connection);
  72
+			$options['driverOptions'] = array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8');
60 73
 		}
  74
+
  75
+		$this->charset = $options['charset'];
  76
+
  77
+		// Finalize initialisation.
  78
+		parent::__construct($options);
61 79
 	}
62 80
 
63 81
 	/**
@@ -65,347 +83,409 @@ public function __destruct()
65 83
 	 *
66 84
 	 * @return  void  Returns void if the database connected successfully.
67 85
 	 *
68  
-	 * @since   12.1
  86
+	 * @since   12.2
69 87
 	 * @throws  RuntimeException
70 88
 	 */
71 89
 	public function connect()
72 90
 	{
73  
-		if ($this->connection)
74  
-		{
75  
-			return;
76  
-		}
77  
-
78  
-		// Make sure the MySQL extension for PHP is installed and enabled.
79  
-		if (!function_exists('mysql_connect'))
80  
-		{
81  
-			throw new RuntimeException('Could not connect to MySQL.');
82  
-		}
83  
-
84  
-		// Attempt to connect to the server.
85  
-		if (!($this->connection = @ mysql_connect($this->options['host'], $this->options['user'], $this->options['password'], true)))
86  
-		{
87  
-			throw new RuntimeException('Could not connect to MySQL.');
88  
-		}
  91
+		parent::connect();
89 92
 
90  
-		// Set sql_mode to non_strict mode
91  
-		mysql_query("SET @@SESSION.sql_mode = '';", $this->connection);
92  
-
93  
-		// If auto-select is enabled select the given database.
94  
-		if ($this->options['select'] && !empty($this->options['database']))
95  
-		{
96  
-			$this->select($this->options['database']);
97  
-		}
98  
-
99  
-		// Set charactersets (needed for MySQL 4.1.2+).
100  
-		$this->setUTF();
  93
+		$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  94
+		$this->connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
101 95
 	}
102 96
 
103 97
 	/**
104  
-	 * Disconnects the database.
  98
+	 * Test to see if the MySQL connector is available.
105 99
 	 *
106  
-	 * @return  void
  100
+	 * @return  boolean  True on success, false otherwise.
107 101
 	 *
108 102
 	 * @since   12.1
109 103
 	 */
110  
-	public function disconnect()
  104
+	public static function isSupported()
111 105
 	{
112  
-		// Close the connection.
113  
-		mysql_close($this->connection);
114  
-
115  
-		$this->connection = null;
  106
+		return in_array('mysql', PDO::getAvailableDrivers());
116 107
 	}
117 108
 
118 109
 	/**
119  
-	 * Method to escape a string for usage in an SQL statement.
  110
+	 * Drops a table from the database.
120 111
 	 *
121  
-	 * @param   string   $text   The string to be escaped.
122  
-	 * @param   boolean  $extra  Optional parameter to provide extra escaping.
  112
+	 * @param   string   $tableName  The name of the database table to drop.
  113
+	 * @param   boolean  $ifExists   Optionally specify that the table must exist before it is dropped.
123 114
 	 *
124  
-	 * @return  string  The escaped string.
  115
+	 * @return  JDatabaseDriverMysql  Returns this object to support chaining.
125 116
 	 *
126 117
 	 * @since   12.1
  118
+	 * @throws  RuntimeException
127 119
 	 */
128  
-	public function escape($text, $extra = false)
  120
+	public function dropTable($tableName, $ifExists = true)
129 121
 	{
130 122
 		$this->connect();
131 123
 
132  
-		$result = mysql_real_escape_string($text, $this->getConnection());
  124
+		$query = $this->getQuery(true);
133 125
 
134  
-		if ($extra)
135  
-		{
136  
-			$result = addcslashes($result, '%_');
137  
-		}
  126
+		$query->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS ' : '') . $this->quoteName($tableName));
138 127
 
139  
-		return $result;
140  
-	}
  128
+		$this->setQuery($query);
141 129
 
142  
-	/**
143  
-	 * Test to see if the MySQL connector is available.
144  
-	 *
145  
-	 * @return  boolean  True on success, false otherwise.
146  
-	 *
147  
-	 * @since   12.1
148  
-	 */
149  
-	public static function isSupported()
150  
-	{
151  
-		return (function_exists('mysql_connect'));
  130
+		$this->execute();
  131
+
  132
+		return $this;
152 133
 	}
153 134
 
154 135
 	/**
155  
-	 * Determines if the connection to the server is active.
  136
+	 * Method to get the database collation in use by sampling a text field of a table in the database.
156 137
 	 *
157  
-	 * @return  boolean  True if connected to the database engine.
  138
+	 * @return  mixed  The collation in use by the database (string) or boolean false if not supported.
158 139
 	 *
159 140
 	 * @since   12.1
  141
+	 * @throws  RuntimeException
160 142
 	 */
161  
-	public function connected()
  143
+	public function getCollation()
162 144
 	{
163  
-		if (is_resource($this->connection))
164  
-		{
165  
-			return @mysql_ping($this->connection);
166  
-		}
  145
+		$this->connect();
167 146
 
168  
-		return false;
  147
+		$this->setQuery('SHOW FULL COLUMNS FROM #__users');
  148
+		$array = $this->loadAssocList();
  149
+		return $array['2']['Collation'];
169 150
 	}
170 151
 
171 152
 	/**
172  
-	 * Get the number of affected rows for the previous executed SQL statement.
  153
+	 * Select a database for use.
173 154
 	 *
174  
-	 * @return  integer  The number of affected rows.
  155
+	 * @param   string  $database  The name of the database to select for use.
175 156
 	 *
176  
-	 * @since   12.1
  157
+	 * @return  boolean  True if the database was successfully selected.
  158
+	 *
  159
+	 * @since   11.1
  160
+	 * @throws  RuntimeException
177 161
 	 */
178  
-	public function getAffectedRows()
  162
+	public function select($database)
179 163
 	{
180 164
 		$this->connect();
181 165
 
182  
-		return mysql_affected_rows($this->connection);
  166
+		$this->setQuery('USE ' . $this->quoteName($database));
  167
+
  168
+		$this->execute();
  169
+
  170
+		return $this;
183 171
 	}
184 172
 
185 173
 	/**
186  
-	 * Get the number of returned rows for the previous executed SQL statement.
  174
+	 * Wrap an SQL statement identifier name such as column, table or database names in quotes to prevent injection
  175
+	 * risks and reserved word conflicts.
187 176
 	 *
188  
-	 * @param   resource  $cursor  An optional database cursor resource to extract the row count from.
  177
+	 * @param   mixed  $name  The identifier name to wrap in quotes, or an array of identifier names to wrap in quotes.
  178
+	 *                        Each type supports dot-notation name.
  179
+	 * @param   mixed  $as    The AS query part associated to $name. It can be string or array, in latter case it has to be
  180
+	 *                        same length of $name; if is null there will not be any AS part for string or array element.
189 181
 	 *
190  
-	 * @return  integer   The number of returned rows.
  182
+	 * @return  mixed  The quote wrapped name, same type of $name.
191 183
 	 *
192  
-	 * @since   12.1
  184
+	 * @since   11.1
193 185
 	 */
194  
-	public function getNumRows($cursor = null)
  186
+	public function quoteName($name, $as = null)
195 187
 	{
196  
-		$this->connect();
  188
+		if (is_string($name))
  189
+		{
  190
+			$quotedName = $this->quoteNameStr(explode('.', $name));
  191
+
  192
+			$quotedAs = '';
  193
+			if (!is_null($as))
  194
+			{
  195
+				settype($as, 'array');
  196
+				$quotedAs .= ' AS ' . $this->quoteNameStr($as);
  197
+			}
197 198
 
198  
-		return mysql_num_rows($cursor ? $cursor : $this->cursor);
  199
+			return $quotedName . $quotedAs;
  200
+		}
  201
+		else
  202
+		{
  203
+			$fin = array();
  204
+
  205
+			if (is_null($as))
  206
+			{
  207
+				foreach ($name as $str)
  208
+				{
  209
+					$fin[] = $this->quoteName($str);
  210
+				}
  211
+			}
  212
+			elseif (is_array($name) && (count($name) == count($as)))
  213
+			{
  214
+				$count = count($name);
  215
+				for ($i = 0; $i < $count; $i++)
  216
+				{
  217
+					$fin[] = $this->quoteName($name[$i], $as[$i]);
  218
+				}
  219
+			}
  220
+
  221
+			return $fin;
  222
+		}
199 223
 	}
200 224
 
201 225
 	/**
202  
-	 * Get the version of the database connector.
  226
+	 * Quote strings coming from quoteName call.
203 227
 	 *
204  
-	 * @return  string  The database connector version.
  228
+	 * @param   array  $strArr  Array of strings coming from quoteName dot-explosion.
205 229
 	 *
206  
-	 * @since   12.1
  230
+	 * @return  string  Dot-imploded string of quoted parts.
  231
+	 *
  232
+	 * @since 11.3
207 233
 	 */
208  
-	public function getVersion()
  234
+	protected function quoteNameStr($strArr)
209 235
 	{
210  
-		$this->connect();
  236
+		$parts = array();
  237
+		$q = $this->nameQuote;
211 238
 
212  
-		return mysql_get_server_info($this->connection);
  239
+		foreach ($strArr as $part)
  240
+		{
  241
+			if (is_null($part))
  242
+			{
  243
+				continue;
  244
+			}
  245
+
  246
+			$part = str_replace('`', '``', $part);
  247
+
  248
+			if (strlen($q) == 1)
  249
+			{
  250
+				$parts[] = $q . $part . $q;
  251
+			}
  252
+			else
  253
+			{
  254
+				$parts[] = $q{0} . $part . $q{1};
  255
+			}
  256
+		}
  257
+
  258
+		return implode('.', $parts);
213 259
 	}
214 260
 
215 261
 	/**
216  
-	 * Method to get the auto-incremented value from the last INSERT statement.
  262
+	 * Shows the table CREATE statement that creates the given tables.
  263
+	 *
  264
+	 * @param   mixed  $tables  A table name or a list of table names.
217 265
 	 *
218  
-	 * @return  integer  The value of the auto-increment field from the last inserted row.
  266
+	 * @return  array  A list of the create SQL for the tables.
219 267
 	 *
220 268
 	 * @since   12.1
  269
+	 * @throws  RuntimeException
221 270
 	 */
222  
-	public function insertid()
  271
+	public function getTableCreate($tables)
223 272
 	{
224 273
 		$this->connect();
225 274
 
226  
-		return mysql_insert_id($this->connection);
  275
+		// Initialise variables.
  276
+		$result = array();
  277
+
  278
+		// Sanitize input to an array and iterate over the list.
  279
+		settype($tables, 'array');
  280
+		foreach ($tables as $table)
  281
+		{
  282
+			$this->setQuery('SHOW CREATE TABLE ' . $this->quoteName($table));
  283
+
  284
+			$row = $this->loadRow();
  285
+
  286
+			// Populate the result array based on the create statements.
  287
+			$result[$table] = $row[1];
  288
+		}
  289
+
  290
+		return $result;
227 291
 	}
228 292
 
229 293
 	/**
230  
-	 * Execute the SQL statement.
  294
+	 * Retrieves field information about a given table.
  295
+	 *
  296
+	 * @param   string   $table     The name of the database table.
  297
+	 * @param   boolean  $typeOnly  True to only return field types.
231 298
 	 *
232  
-	 * @return  mixed  A database cursor resource on success, boolean false on failure.
  299
+	 * @return  array  An array of fields for the database table.
233 300
 	 *
234 301
 	 * @since   12.1
235 302
 	 * @throws  RuntimeException
236 303
 	 */
237  
-	public function execute()
  304
+	public function getTableColumns($table, $typeOnly = true)
238 305
 	{
239 306
 		$this->connect();
240 307
 
241  
-		if (!is_resource($this->connection))
242  
-		{
243  
-			JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database');
244  
-			throw new RuntimeException($this->errorMsg, $this->errorNum);
245  
-		}
  308
+		$result = array();
246 309
 
247  
-		// Take a local copy so that we don't modify the original query and cause issues later
248  
-		$sql = $this->replacePrefix((string) $this->sql);
249  
-		if ($this->limit > 0 || $this->offset > 0)
250  
-		{
251  
-			$sql .= ' LIMIT ' . $this->offset . ', ' . $this->limit;
252  
-		}
  310
+		$query = $this->getQuery(true);
253 311
 
254  
-		// If debugging is enabled then let's log the query.
255  
-		if ($this->debug)
256  
-		{
257  
-			// Increment the query counter and add the query to the object queue.
258  
-			$this->count++;
259  
-			$this->log[] = $sql;
  312
+		// Set the query to get the table fields statement.
  313
+		$this->setQuery('SHOW FULL COLUMNS FROM ' . $this->quoteName($table));
260 314
 
261  
-			JLog::add($sql, JLog::DEBUG, 'databasequery');
262  
-		}
263  
-
264  
-		// Reset the error values.
265  
-		$this->errorNum = 0;
266  
-		$this->errorMsg = '';
267  
-
268  
-		// Execute the query. Error suppression is used here to prevent warnings/notices that the connection has been lost.
269  
-		$this->cursor = @mysql_query($sql, $this->connection);
  315
+		$fields = $this->loadObjectList();
270 316
 
271  
-		// If an error occurred handle it.
272  
-		if (!$this->cursor)
  317
+		// If we only want the type as the value add just that to the list.
  318
+		if ($typeOnly)
273 319
 		{
274  
-			// Check if the server was disconnected.
275  
-			if (!$this->connected())
  320
+			foreach ($fields as $field)
276 321
 			{
277  
-				try
278  
-				{
279  
-					// Attempt to reconnect.
280  
-					$this->connection = null;
281  
-					$this->connect();
282  
-				}
283  
-				// If connect fails, ignore that exception and throw the normal exception.
284  
-				catch (RuntimeException $e)
285  
-				{
286  
-					// Get the error number and message.
287  
-					$this->errorNum = (int) mysql_errno($this->connection);
288  
-					$this->errorMsg = (string) mysql_error($this->connection) . ' SQL=' . $sql;
289  
-
290  
-					// Throw the normal query exception.
291  
-					JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'databasequery');
292  
-					throw new RuntimeException($this->errorMsg, $this->errorNum);
293  
-				}
294  
-
295  
-				// Since we were able to reconnect, run the query again.
296  
-				return $this->execute();
  322
+				$result[$field->Field] = preg_replace("/[(0-9)]/", '', $field->Type);
297 323
 			}
298  
-			// The server was not disconnected.
299  
-			else
  324
+		}
  325
+		// If we want the whole field data object add that to the list.
  326
+		else
  327
+		{
  328
+			foreach ($fields as $field)
300 329
 			{
301  
-				// Get the error number and message.
302  
-				$this->errorNum = (int) mysql_errno($this->connection);
303  
-				$this->errorMsg = (string) mysql_error($this->connection) . ' SQL=' . $sql;
304  
-
305  
-				// Throw the normal query exception.
306  
-				JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'databasequery');
307  
-				throw new RuntimeException($this->errorMsg, $this->errorNum);
  330
+				$result[$field->Field] = $field;
308 331
 			}
309 332
 		}
310 333
 
311  
-		return $this->cursor;
  334
+		return $result;
312 335
 	}
313 336
 
314 337
 	/**
315  
-	 * Select a database for use.
  338
+	 * Get the details list of keys for a table.
316 339
 	 *
317  
-	 * @param   string  $database  The name of the database to select for use.
  340
+	 * @param   string  $table  The name of the table.
318 341
 	 *
319  
-	 * @return  boolean  True if the database was successfully selected.
  342
+	 * @return  array  An array of the column specification for the table.
320 343
 	 *
321 344
 	 * @since   12.1
322 345
 	 * @throws  RuntimeException
323 346
 	 */
324  
-	public function select($database)
  347
+	public function getTableKeys($table)
325 348
 	{
326 349
 		$this->connect();
327 350
 
328  
-		if (!$database)
329  
-		{
330  
-			return false;
331  
-		}
  351
+		$query = $this->getQuery(true);
332 352
 
333  
-		if (!mysql_select_db($database, $this->connection))
334  
-		{
335  
-			throw new RuntimeException('Could not connect to database');
336  
-		}
  353
+		// Get the details columns information.
  354
+		$this->setQuery('SHOW KEYS FROM ' . $this->quoteName($table));
337 355
 
338  
-		return true;
  356
+		$keys = $this->loadObjectList();
  357
+
  358
+		return $keys;
339 359
 	}
340 360
 
341 361
 	/**
342  
-	 * Set the connection to use UTF-8 character encoding.
  362
+	 * Method to get an array of all tables in the database.
343 363
 	 *
344  
-	 * @return  boolean  True on success.
  364
+	 * @return  array  An array of all the tables in the database.
345 365
 	 *
346 366
 	 * @since   12.1
  367
+	 * @throws  RuntimeException
347 368
 	 */
348  
-	public function setUTF()
  369
+	public function getTableList()
349 370
 	{
350 371
 		$this->connect();
351 372
 
352  
-		return mysql_set_charset('utf8', $this->connection);
  373
+		// Set the query to get the tables statement.
  374
+		$this->setQuery('SHOW TABLES');
  375
+		$tables = $this->loadColumn();
  376
+
  377
+		return $tables;
353 378
 	}
354 379
 
355 380
 	/**
356  
-	 * Method to fetch a row from the result set cursor as an array.
357  
-	 *
358  
-	 * @param   mixed  $cursor  The optional result set cursor from which to fetch the row.
  381
+	 * Get the version of the database connector.
359 382
 	 *
360  
-	 * @return  mixed  Either the next row from the result set or false if there are no more rows.
  383
+	 * @return  string  The database connector version.
361 384
 	 *
362 385
 	 * @since   12.1
363 386
 	 */
364  
-	protected function fetchArray($cursor = null)
  387
+	public function getVersion()
365 388
 	{
366  
-		return mysql_fetch_row($cursor ? $cursor : $this->cursor);
  389
+		$this->connect();
  390
+
  391
+		return $this->getOption(PDO::ATTR_SERVER_VERSION);
367 392
 	}
368 393
 
369 394
 	/**
370  
-	 * Method to fetch a row from the result set cursor as an associative array.
  395
+	 * Locks a table in the database.
371 396
 	 *
372  
-	 * @param   mixed  $cursor  The optional result set cursor from which to fetch the row.
  397
+	 * @param   string  $table  The name of the table to unlock.
373 398
 	 *
374  
-	 * @return  mixed  Either the next row from the result set or false if there are no more rows.
  399
+	 * @return  JDatabaseMySQL  Returns this object to support chaining.
375 400
 	 *
376 401
 	 * @since   12.1
  402
+	 * @throws  RuntimeException
377 403
 	 */
378  
-	protected function fetchAssoc($cursor = null)
  404
+	public function lockTable($table)
379 405
 	{
380  
-		return mysql_fetch_assoc($cursor ? $cursor : $this->cursor);
  406
+		$query = $this->getQuery(true);
  407
+
  408
+		$this->setQuery('LOCK TABLES ' . $this->quoteName($table) . ' WRITE');
  409
+
  410
+		$this->setQuery($query)->exec();
  411
+
  412
+		return $this;
381 413
 	}
382 414
 
383 415
 	/**
384  
-	 * Method to fetch a row from the result set cursor as an object.
  416
+	 * Renames a table in the database.
385 417
 	 *
386  
-	 * @param   mixed   $cursor  The optional result set cursor from which to fetch the row.
387  
-	 * @param   string  $class   The class name to use for the returned row object.
  418
+	 * @param   string  $oldTable  The name of the table to be renamed
  419
+	 * @param   string  $newTable  The new name for the table.
  420
+	 * @param   string  $backup    Not used by MySQL.
  421
+	 * @param   string  $prefix    Not used by MySQL.
388 422
 	 *
389  
-	 * @return  mixed   Either the next row from the result set or false if there are no more rows.
  423
+	 * @return  JDatabaseDriverMysql  Returns this object to support chaining.
390 424
 	 *
391 425
 	 * @since   12.1
  426
+	 * @throws  RuntimeException
392 427
 	 */
393  
-	protected function fetchObject($cursor = null, $class = 'stdClass')
  428
+	public function renameTable($oldTable, $newTable, $backup = null, $prefix = null)
394 429
 	{
395  
-		return mysql_fetch_object($cursor ? $cursor : $this->cursor, $class);
  430
+		$query = $this->getQuery(true);
  431
+
  432
+		$this->setQuery('RENAME TABLE ' . $this->quoteName($oldTable) . ' TO ' . $this->quoteName($newTable));
  433
+
  434
+		return $this;
396 435
 	}
397 436
 
398 437
 	/**
399  
-	 * Method to free up the memory used for the result set.
  438
+	 * Method to escape a string for usage in an SQL statement.
  439
+	 *
  440
+	 * Oracle escaping reference:
  441
+	 * http://www.orafaq.com/wiki/SQL_FAQ#How_does_one_escape_special_characters_when_writing_SQL_queries.3F
  442
+	 *
  443
+	 * SQLite escaping notes:
  444
+	 * http://www.sqlite.org/faq.html#q14
  445
+	 *
  446
+	 * Method body is as implemented by the Zend Framework
  447
+	 *
  448
+	 * Note: Using query objects with bound variables is
  449
+	 * preferable to the below.
400 450
 	 *
401  
-	 * @param   mixed  $cursor  The optional result set cursor from which to fetch the row.
  451
+	 * @param   string   $text   The string to be escaped.
  452
+	 * @param   boolean  $extra  Unused optional parameter to provide extra escaping.
402 453
 	 *
403  
-	 * @return  void
  454
+	 * @return  string  The escaped string.
404 455
 	 *
405 456
 	 * @since   12.1
406 457
 	 */
407  
-	protected function freeResult($cursor = null)
  458
+	public function escape($text, $extra = false)
408 459
 	{
409  
-		mysql_free_result($cursor ? $cursor : $this->cursor);
  460
+		$this->connect();
  461
+
  462
+		if (is_int($text) || is_float($text))
  463
+		{
  464
+			return $text;
  465
+		}
  466
+
  467
+		$result = substr($this->connection->quote($text), 1, -1);
  468
+
  469
+		if ($extra)
  470
+		{
  471
+			$result = addcslashes($result, '%_');
  472
+		}
  473
+
  474
+		return $result;
  475
+	}
  476
+
  477
+	/**
  478
+	 * Unlocks tables in the database.
  479
+	 *
  480
+	 * @return  JDatabaseMySQL  Returns this object to support chaining.
  481
+	 *
  482
+	 * @since   12.1
  483
+	 * @throws  RuntimeException
  484
+	 */
  485
+	public function unlockTables()
  486
+	{
  487
+		$this->setQuery('UNLOCK TABLES')->execute();
  488
+
  489
+		return $this;
410 490
 	}
411 491
 }
2  libraries/joomla/database/driver/pdo.php
@@ -206,6 +206,8 @@ public function connect()
206 206
 				$replace = array('#HOST#', '#PORT#', '#DBNAME#');
207 207
 				$with = array($this->options['host'], $this->options['port'], $this->options['database']);
208 208
 
  209
+				$format .= ';charset=' . $this->options['charset'];
  210
+
209 211
 				break;
210 212
 
211 213
 			case 'oci':
39  libraries/joomla/database/iterator/mysql.php
@@ -14,45 +14,8 @@
14 14
  *
15 15
  * @package     Joomla.Platform
16 16
  * @subpackage  Database
17  
- * @see         http://dev.mysql.com/doc/
18 17
  * @since       12.1
19 18
  */
20  
-class JDatabaseIteratorMysql extends JDatabaseIterator
  19
+class JDatabaseIteratorMysql extends JDatabaseIteratorPdo
21 20
 {
22  
-	/**
23  
-	 * Get the number of rows in the result set for the executed SQL given by the cursor.
24  
-	 *
25  
-	 * @return  integer  The number of rows in the result set.
26  
-	 *
27  
-	 * @since   12.1
28  
-	 * @see     Countable::count()
29  
-	 */
30  
-	public function count()
31  
-	{
32  
-		return mysql_num_rows($this->cursor);
33  
-	}
34  
-
35  
-	/**
36  
-	 * Method to fetch a row from the result set cursor as an object.
37  
-	 *
38  
-	 * @return  mixed   Either the next row from the result set or false if there are no more rows.
39  
-	 *
40  
-	 * @since   12.1
41  
-	 */
42  
-	protected function fetchObject()
43  
-	{
44  
-		return mysql_fetch_object($this->cursor, $this->class);
45  
-	}
46  
-
47  
-	/**
48  
-	 * Method to free up the memory used for the result set.
49  
-	 *
50  
-	 * @return  void
51  
-	 *
52  
-	 * @since   12.1
53  
-	 */
54  
-	protected function freeResult()
55  
-	{
56  
-		mysql_free_result($this->cursor);
57  
-	}
58 21
 }
190  libraries/joomla/database/query/mysql.php
@@ -16,6 +16,194 @@
16 16
  * @subpackage  Database
17 17
  * @since       11.1
18 18
  */
19  
-class JDatabaseQueryMysql extends JDatabaseQueryMysqli
  19
+class JDatabaseQueryMysql extends JDatabaseQuery implements JDatabaseQueryPreparable, JDatabaseQueryLimitable
20 20
 {
  21
+	/**
  22
+	 * @var    interger  The offset for the result set.
  23
+	 * @since  12.1
  24
+	 */
  25
+	protected $offset;
  26
+
  27
+	/**
  28
+	 * @var    integer  The limit for the result set.
  29
+	 * @since  12.1
  30
+	 */
  31
+	protected $limit;
  32
+
  33
+	/**
  34
+	 * @var mixed
  35
+	 * @since 12.1
  36
+	 */
  37
+	protected $bounded = array();
  38
+
  39
+	/**
  40
+	 * Method to add a variable to an internal array that will be bound to a prepared SQL statement before query execution. Also
  41
+	 * removes a variable that has been bounded from the internal bounded array when the passed in value is null.
  42
+	 *
  43
+	 * @param   string|integer  $key            The key that will be used in your SQL query to reference the value. Usually of
  44
+	 *                                          the form ':key', but can also be an integer.
  45
+	 * @param   mixed           &$value         The value that will be bound. The value is passed by reference to support output
  46
+	 *                                          parameters such as those possible with stored procedures.
  47
+	 * @param   integer         $dataType       Constant corresponding to a SQL datatype.
  48
+	 * @param   integer         $length         The length of the variable. Usually required for OUTPUT parameters.
  49
+	 * @param   array           $driverOptions  Optional driver options to be used.
  50
+	 *
  51
+	 * @return  JDatabaseQuery
  52
+	 *
  53
+	 * @since   12.1
  54
+	 */
  55
+	public function bind($key = null, &$value = null, $dataType = PDO::PARAM_STR, $length = 0, $driverOptions = array())
  56
+	{
  57
+		// Case 1: Empty Key (reset $bounded array)
  58
+		if (empty($key))
  59
+		{
  60
+			$this->bounded = array();
  61
+			return $this;
  62
+		}
  63
+
  64
+		// Case 2: Key Provided, null value (unset key from $bounded array)
  65
+		if (is_null($value))
  66
+		{
  67
+			if (isset($this->bounded[$key]))
  68
+			{
  69
+				unset($this->bounded[$key]);
  70
+			}
  71
+
  72
+			return $this;
  73
+		}
  74
+
  75
+		$obj = new stdClass;
  76
+
  77
+		$obj->value = &$value;
  78
+		$obj->dataType = $dataType;
  79
+		$obj->length = $length;
  80
+		$obj->driverOptions = $driverOptions;
  81
+
  82
+		// Case 3: Simply add the Key/Value into the bounded array
  83
+		$this->bounded[$key] = $obj;
  84
+
  85
+		return $this;
  86
+	}
  87
+
  88
+	/**
  89
+	 * Retrieves the bound parameters array when key is null and returns it by reference. If a key is provided then that item is
  90
+	 * returned.
  91
+	 *
  92
+	 * @param   mixed  $key  The bounded variable key to retrieve.
  93
+	 *
  94
+	 * @return  mixed
  95
+	 *
  96
+	 * @since   12.1
  97
+	 */
  98
+	public function &getBounded($key = null)
  99
+	{
  100
+		if (empty($key))
  101
+		{
  102
+			return $this->bounded;
  103
+		}
  104
+		else
  105
+		{
  106
+			if (isset($this->bounded[$key]))
  107
+			{
  108
+				return $this->bounded[$key];
  109
+			}
  110
+		}
  111
+	}
  112
+
  113
+	/**
  114
+	 * Clear data from the query or a specific clause of the query.
  115
+	 *
  116
+	 * @param   string  $clause  Optionally, the name of the clause to clear, or nothing to clear the whole query.
  117
+	 *
  118
+	 * @return  JDatabaseQuery  Returns this object to allow chaining.
  119
+	 *
  120
+	 * @since   12.1
  121
+	 */
  122
+	public function clear($clause = null)
  123
+	{
  124
+		switch ($clause)
  125
+		{
  126
+			case null:
  127
+				$this->bounded = array();
  128
+				break;
  129
+		}
  130
+
  131
+		parent::clear($clause);
  132
+
  133
+		return $this;
  134
+	}
  135
+
  136
+	/**
  137
+	 * Method to modify a query already in string format with the needed
  138
+	 * additions to make the query limited to a particular number of
  139
+	 * results, or start at a particular offset.
  140
+	 *
  141
+	 * @param   string   $query   The query in string format
  142
+	 * @param   integer  $limit   The limit for the result set
  143
+	 * @param   integer  $offset  The offset for the result set
  144
+	 *
  145
+	 * @return string
  146
+	 *
  147
+	 * @since 12.1
  148
+	 */
  149
+	public function processLimit($query, $limit, $offset = 0)
  150
+	{
  151
+		if ($limit > 0 || $offset > 0)
  152
+		{
  153
+			$query .= ' LIMIT ' . $offset . ', ' . $limit;
  154
+		}
  155
+
  156
+		return $query;
  157
+	}
  158
+
  159
+	/**
  160
+	 * Concatenates an array of column names or values.
  161
+	 *
  162
+	 * @param   array   $values     An array of values to concatenate.
  163
+	 * @param   string  $separator  As separator to place between each value.
  164
+	 *
  165
+	 * @return  string  The concatenated values.
  166
+	 *
  167
+	 * @since   11.1
  168
+	 */
  169
+	public function concatenate($values, $separator = null)
  170
+	{
  171
+		if ($separator)
  172
+		{
  173
+			$concat_string = 'CONCAT_WS(' . $this->quote($separator);
  174
+
  175
+			foreach ($values as $value)
  176
+			{
  177
+				$concat_string .= ', ' . $value;
  178
+			}
  179
+
  180
+			return $concat_string . ')';
  181
+		}
  182
+		else
  183
+		{
  184
+			return 'CONCAT(' . implode(',', $values) . ')';
  185
+		}
  186
+	}
  187
+
  188
+	/**
  189
+	 * Sets the offset and limit for the result set, if the database driver supports it.
  190
+	 *
  191
+	 * Usage:
  192
+	 * $query->setLimit(100, 0); (retrieve 100 rows, starting at first record)
  193
+	 * $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th record)
  194
+	 *
  195
+	 * @param   integer  $limit   The limit for the result set
  196
+	 * @param   integer  $offset  The offset for the result set
  197
+	 *
  198
+	 * @return  JDatabaseQuery  Returns this object to allow chaining.
  199
+	 *
  200
+	 * @since   12.1
  201
+	 */
  202
+	public function setLimit($limit = 0, $offset = 0)
  203
+	{
  204
+		$this->limit  = (int) $limit;
  205
+		$this->offset = (int) $offset;
  206
+
  207
+		return $this;
  208
+	}
21 209
 }
3  tests/suites/database/driver/mysql/JDatabaseMySQLTest.php
@@ -445,10 +445,9 @@ public function testQuery()
445 445
 	{
446 446
 		self::$driver->setQuery("REPLACE INTO `jos_dbtest` SET `id` = 5, `title` = 'testTitle'");
447 447
 
448  
-		$this->assertThat(self::$driver->execute(), $this->isTrue(), __LINE__);
  448
+		$this->assertThat((bool)self::$driver->execute(), $this->isTrue(), __LINE__);
449 449
 
450 450
 		$this->assertThat(self::$driver->insertid(), $this->equalTo(5), __LINE__);
451  
-
452 451
 	}
453 452
 
454 453
 	/**
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.