Introduced new wrapper functions to make the code more readable, new extension ext...
[mailer.git] / inc / db / lib-mysql3.php
1 <?php
2 /************************************************************************
3  * Mailer v0.2.1-FINAL                                Start: 08/29/2004 *
4  * ===================                          Last change: 08/29/2004 *
5  *                                                                      *
6  * -------------------------------------------------------------------- *
7  * File              : lib-mysql3.php                                   *
8  * -------------------------------------------------------------------- *
9  * Short description : Database layer for MySQL +3.x server             *
10  * -------------------------------------------------------------------- *
11  * Kurzbeschreibung  : Datenbankschicht fuer MySQL +3.x Server          *
12  * -------------------------------------------------------------------- *
13  * $Revision::                                                        $ *
14  * $Date::                                                            $ *
15  * $Tag:: 0.2.1-FINAL                                                 $ *
16  * $Author::                                                          $ *
17  * Needs to be in all Files and every File needs "svn propset           *
18  * svn:keywords Date Revision" (autoprobset!) at least!!!!!!            *
19  * -------------------------------------------------------------------- *
20  * Copyright (c) 2003 - 2009 by Roland Haeder                           *
21  * Copyright (c) 2009, 2010 by Mailer Developer Team                    *
22  * For more information visit: http://www.mxchange.org                  *
23  *                                                                      *
24  * This program is free software; you can redistribute it and/or modify *
25  * it under the terms of the GNU General Public License as published by *
26  * the Free Software Foundation; either version 2 of the License, or    *
27  * (at your option) any later version.                                  *
28  *                                                                      *
29  * This program is distributed in the hope that it will be useful,      *
30  * but WITHOUT ANY WARRANTY; without even the implied warranty of       *
31  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
32  * GNU General Public License for more details.                         *
33  *                                                                      *
34  * You should have received a copy of the GNU General Public License    *
35  * along with this program; if not, write to the Free Software          *
36  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,               *
37  * MA  02110-1301  USA                                                  *
38  ************************************************************************/
39
40 // Some security stuff...
41 if (!defined('__SECURITY')) {
42         die();
43 } // END - if
44
45 // SQL queries
46 function SQL_QUERY ($sqlString, $F, $L) {
47         // Trim SQL string
48         $sqlString = trim($sqlString);
49
50         // Link is up?
51         if (!SQL_IS_LINK_UP()) {
52                 // We should not quietly ignore this!
53                 debug_report_bug(sprintf("Cannot query database: sqlString=%s,file=%s,line=%s",
54                         $sqlString,
55                         basename($F),
56                         $L
57                 ));
58
59                 // Return 'false' because it has failed
60                 return false;
61         } elseif (empty($sqlString)) {
62                 // Empty SQL string!
63                 debug_report_bug(sprintf("SQL string is empty. Please fix this. file=%s, line=%s",
64                         basename($F),
65                         $L
66                 ));
67
68                 // This is invalid, of course
69                 return false;
70         }
71
72         // Remove \t, \n and \r from queries they may confuse some MySQL version I have heard
73         $sqlString = str_replace("\t", ' ', str_replace("\n", ' ', str_replace("\r", ' ', $sqlString)));
74
75         // Replace {PER}
76         $sqlString = str_replace('{PER}', '%', $sqlString);
77
78         // Compile config entries out
79         $eval = "\$sqlString = \"".FILTER_COMPILE_CONFIG(escapeQuotes($sqlString))."\";";
80         eval($eval);
81
82         // Starting time
83         $querytimeBefore = microtime(true);
84
85         // Run SQL command
86         //* DEBUG: */ print('F=' . basename($F) . ',L=' . $L . 'sql=' . htmlentities($sqlString) . '<br />');
87         $result = mysql_query($sqlString, SQL_GET_LINK())
88                 or addFatalMessage(__FUNCTION__, __LINE__, $F . ' (' . $L . '):' . mysql_error() . '<br />
89 Query string:<br />
90 ' . $sqlString);
91         //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'sql=' . $sqlString . ',numRows=' . SQL_NUMROWS($result) . ',affected=' . SQL_AFFECTEDROWS());
92
93         // Calculate query time
94         $queryTime = microtime(true) - $querytimeBefore;
95
96         // Add this query to array including timing
97         addSqlToDebug($result, $sqlString, $queryTime, $F, $L);
98
99         // Save last successfull query
100         setConfigEntry('db_last_query', $sqlString);
101
102         // Count all query times
103         incrementConfigEntry('sql_time', $queryTime);
104
105         // Count this query
106         incrementConfigEntry('sql_count');
107
108         // Debug output
109         if ((getOutputMode() != 1) && (isDebugModeEnabled()) && (isSqlDebuggingEnabled())) {
110                 //
111                 // Debugging stuff...
112                 //
113                 $fp = fopen(getConfig('CACHE_PATH') . 'mysql.log', 'a') or app_die(__FILE__, __LINE__, 'Cannot write mysql.log!');
114                 if (!isset($GLOBALS['sql_first_entry'])) {
115                         // Write first entry
116                         fwrite($fp, 'Module=' . getModule() . "\n");
117                         $GLOBALS['sql_first_entry'] = true;
118                 } // END - if
119                 fwrite($fp, $F . '(LINE=' . $L . '|NUM=' . SQL_NUMROWS($result) . '|AFFECTED=' . SQL_AFFECTEDROWS() . '|QUERYTIME:' . $queryTime . '): ' . str_replace("\r", '', str_replace("\n", ' ', $sqlString)) . "\n");
120                 fclose($fp);
121         } // END - if
122
123         // Count DB hits
124         if (!isStatsEntrySet('db_hits')) {
125                 // Count in dummy variable
126                 setStatsEntry('db_hits', 1);
127         } else {
128                 // Count to config array
129                 incrementStatsEntry('db_hits');
130         }
131
132         // Return the result
133         return $result;
134 }
135
136 // SQL num rows
137 function SQL_NUMROWS ($result) {
138         // Valid link resource?
139         if (!SQL_IS_LINK_UP()) return false;
140
141         // Link is not up, no rows by default
142         $lines = false;
143
144         // Is the result a valid resource?
145         if (is_resource($result)) {
146                 // Get the count of rows from database
147                 $lines = mysql_num_rows($result);
148
149                 // Is the result empty? Then we have an error!
150                 if (empty($lines)) $lines = '0';
151         } else {
152                 // No resource given, please fix this
153                 trigger_error('No resource given! result[]=' . gettype($result));
154         }
155
156         // Return lines
157         return $lines;
158 }
159
160 // SQL affected rows
161 function SQL_AFFECTEDROWS() {
162         // Valid link resource?
163         if (!SQL_IS_LINK_UP()) return false;
164
165         // Get affected rows
166         $lines = mysql_affected_rows(SQL_GET_LINK());
167
168         // Return it
169         return $lines;
170 }
171
172 // SQL fetch row
173 function SQL_FETCHROW ($result) {
174         // Is a result resource set?
175         if ((!is_resource($result)) || (!SQL_IS_LINK_UP())) return false;
176
177         // Fetch the data and return it
178         return mysql_fetch_row($result);
179 }
180
181 // SQL fetch array
182 function SQL_FETCHARRAY ($res, $nr=0, $remove_numerical=true) {
183         // Is a result resource set?
184         if ((!is_resource($res)) || (!SQL_IS_LINK_UP())) return false;
185
186         // Initialize array
187         $row = array();
188
189         // Load row from database
190         $row = mysql_fetch_array($res);
191
192         // Return only arrays here
193         if (is_array($row)) {
194                 // Shall we remove numerical data here automatically?
195                 if ($remove_numerical) {
196                         // So let's remove all numerical elements to save memory!
197                         $max = count($row);
198                         for ($idx = '0'; $idx < ($max / 2); $idx++) {
199                                 // Remove entry
200                                 unset($row[$idx]);
201                         } // END - for
202                 } // END - if
203
204                 // Return row
205                 return $row;
206         } else {
207                 // Return a false, else some loops would go endless...
208                 return false;
209         }
210 }
211
212 // SQL result
213 function SQL_RESULT ($res, $row, $field = '0') {
214         // Is $res valid?
215         if ((!is_resource($res)) || (!SQL_IS_LINK_UP())) return false;
216
217         // Run the result command and return the result
218         $result = mysql_result($res, $row, $field);
219         return $result;
220 }
221
222 // SQL connect
223 function SQL_CONNECT ($host, $login, $password, $F, $L) {
224         // Try to connect
225         $connect = mysql_connect($host, $login, $password) or addFatalMessage(__FUNCTION__, __LINE__, $F." (".$L."):".mysql_error());
226
227         // Set the link resource
228         SQL_SET_LINK($connect);
229
230         // Destroy cache
231         unset($GLOBALS['is_sql_link_up']);
232 }
233
234 // SQL select database
235 function SQL_SELECT_DB ($dbName, $F, $L) {
236         // Is there still a valid link? If not, skip it.
237         if (!SQL_IS_LINK_UP()) return false;
238
239         // Return the result
240         return mysql_select_db($dbName, SQL_GET_LINK()) or addFatalMessage(__FUNCTION__, __LINE__, $F." (".$L."):".mysql_error());
241 }
242
243 // SQL close link
244 function SQL_CLOSE ($F, $L) {
245         if (!SQL_IS_LINK_UP()) {
246                 // Skip double close
247                 return false;
248         } // END - if
249
250         // Close database link and forget the link
251         $close = mysql_close(SQL_GET_LINK())
252                 or addFatalMessage(__FUNCTION__, __LINE__, $F . ' (' . $L . '):'.mysql_error());
253
254         // Close link
255         SQL_SET_LINK(null);
256
257         // Destroy cache
258         unset($GLOBALS['is_sql_link_up']);
259
260         // Return the result
261         return $close;
262 }
263
264 // SQL free result
265 function SQL_FREERESULT ($result) {
266         if ((!is_resource($result)) || (!SQL_IS_LINK_UP())) {
267                 // Abort here
268                 return false;
269         } // END - if
270
271         $res = mysql_free_result($result);
272         return $res;
273 }
274
275 // SQL string escaping
276 function SQL_QUERY_ESC ($qstring, $data, $F, $L, $run=true, $strip=true, $secure=true) {
277         // Link is there?
278         if ((!SQL_IS_LINK_UP()) || (!is_array($data))) return false;
279
280         // Escape all data
281         $dataSecured['__sql_string'] = $qstring;
282         foreach ($data as $key => $value) {
283                 $dataSecured[$key] = SQL_ESCAPE($value, $secure, $strip);
284         } // END - foreach
285
286         // Generate query
287         $query = call_user_func_array('sprintf', $dataSecured);
288
289         // Debugging
290         //
291         //* DEBUG: */ $fp = fopen(getConfig('CACHE_PATH') . 'escape_debug.log', 'a') or app_die(__FILE__, __LINE__, "Cannot write debug.log!");
292         //* DEBUG: */ fwrite($fp, $F.'('.$L."): ".str_replace("\r", '', str_replace("\n", ' ', $eval))."\n");
293         //* DEBUG: */ fclose($fp);
294
295         if ($run === true) {
296                 // Run SQL query (default)
297                 return SQL_QUERY($query, $F, $L);
298         } else {
299                 // Return secured string
300                 return $query;
301         }
302 }
303
304 // Get id from last INSERT command
305 function SQL_INSERTID () {
306         if (!SQL_IS_LINK_UP()) return false;
307         return mysql_insert_id();
308 }
309
310 // Escape a string for the database
311 function SQL_ESCAPE ($str, $secureString=true, $strip=true) {
312         // Do we have cache?
313         if (!isset($GLOBALS['sql_escapes'][''.$str.''])) {
314                 // Secure string first? (which is the default behaviour!)
315                 if ($secureString === true) {
316                         // Then do it here
317                         $str = secureString($str, $strip);
318                 } // END - if
319
320                 if (!SQL_IS_LINK_UP()) {
321                         // Fall-back to escapeQuotes() when there is no link
322                         $ret = escapeQuotes($str);
323                 } elseif (function_exists('mysql_real_escape_string')) {
324                         // The new and improved version
325                         //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'str='.$str);
326                         $ret = mysql_real_escape_string($str, SQL_GET_LINK());
327                 } elseif (function_exists('mysql_escape_string')) {
328                         // The obsolete function
329                         $ret = mysql_escape_string($str, SQL_GET_LINK());
330                 } else {
331                         // If nothing else works, fall back to escapeQuotes() again
332                         $ret = escapeQuotes($str);
333                 }
334
335                 // Cache result
336                 $GLOBALS['sql_escapes'][''.$str.''] = $ret;
337         } // END - if
338
339         // Return it
340         return $GLOBALS['sql_escapes'][''.$str.''];
341 }
342
343 // SELECT query string from table, columns and so on... ;-)
344 function SQL_RESULT_FROM_ARRAY ($table, $columns, $idRow, $id, $F, $L) {
345         // Is columns an array?
346         if (!is_array($columns)) {
347                 // No array
348                 debug_report_bug(sprintf("columns is not an array. %s != array, file=%s, line=%s",
349                         gettype($columns),
350                         basename($F),
351                         $L
352                 ));
353
354                 // Abort here with 'false'
355                 return false;
356         } // END  - if
357
358         // Prepare the SQL statement
359         $sql = "SELECT `".implode("`,`", $columns)."` FROM `{?_MYSQL_PREFIX?}_%s` WHERE `%s`='%s' LIMIT 1";
360
361         // Return the result
362         return SQL_QUERY_ESC($sql,
363                 array(
364                         $table,
365                         $idRow,
366                         bigintval($id),
367                 ), $F, $L
368         );
369 }
370
371 // ALTER TABLE wrapper function
372 function SQL_ALTER_TABLE ($sql, $F, $L) {
373         // Abort if link is down
374         if (!SQL_IS_LINK_UP()) return false;
375
376         // This is the default result...
377         $result = false;
378
379         // Determine index/fulltext/unique word
380         $noIndex = (
381         (
382                 strpos($sql, 'INDEX') === false
383         ) && (
384                 strpos($sql, 'KEY') === false
385         ) && (
386                 strpos($sql, 'FULLTEXT') === false
387         ) && (
388                 strpos($sql, 'UNIQUE') === false
389         )
390         );
391
392         // Extract table name
393         $tableArray = explode(' ', $sql);
394         $tableName = str_replace('`', '', $tableArray[2]);
395
396         // Shall we add/drop?
397         if (((strpos($sql, 'ADD') !== false) || (strpos($sql, 'DROP') !== false)) && ($noIndex === true)) {
398                 // Try two columns, one should fix
399                 foreach (array(4,5) as $idx) {
400                         // And column name as well
401                         $columnName = str_replace('`', '', $tableArray[$idx]);
402
403                         // Get column information
404                         $result = SQL_QUERY_ESC("SHOW COLUMNS FROM `%s` LIKE '%s'",
405                                 array($tableName, $columnName), __FILE__, __LINE__);
406
407                         // Do we have no entry on ADD or an entry on DROP?
408                         // 123           4       4     3    3      4           4          32    23           4       4     3    3      4            4          321
409                         if (((SQL_HASZERONUMS($result)) && (strpos($sql, 'ADD') !== false)) || ((SQL_NUMROWS($result) == 1) && (strpos($sql, 'DROP') !== false))) {
410                                 // Do the query
411                                 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'Executing: ' . $sql);
412                                 $result = SQL_QUERY($sql, $F, $L, false);
413
414                                 // Skip further attempt(s)
415                                 break;
416                         } elseif ((((SQL_NUMROWS($result) == 1) && (strpos($sql, 'ADD') !== false)) || ((SQL_HASZERONUMS($result)) && (strpos($sql, 'DROP') !== false))) && ($columnName != 'KEY')) {
417                                 // Abort here because it is alreay there
418                                 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'Skipped: ' . $sql);
419                                 break;
420                         } elseif ($columnName != 'KEY') {
421                                 // Something didn't fit
422                                 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'Possible problem: ' . $sql);
423                         }
424                 } // END - foreach
425         } elseif ((getConfig('_TABLE_TYPE') == 'InnoDB') && (strpos($sql, 'FULLTEXT') !== false)) {
426                 // Skip this query silently
427                 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, sprintf("Skipped FULLTEXT: sql=%s,file=%s,line=%s", $sql, $F, $L));
428         } elseif ($noIndex === false) {
429                 // And column name as well
430                 //* DEBUG: */ print __LINE__.':tableArray=<pre>' . print_r($tableArray, true) . '</pre>';
431                 $keyName = str_replace('`', '', $tableArray[5]);
432
433                 // Is this "UNIQUE" or so? FULLTEXT has been handled the elseif() block above
434                 if (in_array(strtoupper($keyName), array('INDEX', 'UNIQUE', 'KEY', 'FULLTEXT'))) {
435                         // Init loop
436                         $begin = 1; $keyName = ',';
437                         while (strpos($keyName, ',') !== false) {
438                                 // Use last
439                                 $keyName = str_replace('`', '', $tableArray[count($tableArray) - $begin]);
440                                 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, $keyName . '----------------' . $begin);
441
442                                 // Remove brackes
443                                 $keyName = str_replace('(', '', str_replace(')', '', $keyName));
444                                 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, $keyName . '----------------' . $begin);
445
446                                 // Continue
447                                 $begin++;
448                         } // END while
449                 } // END - if
450
451                 // Show indexes
452                 $result = SQL_QUERY_ESC("SHOW INDEX FROM `%s`", array($tableName), __FILE__, __LINE__);
453
454                 // Non-skipping is default for ADD
455                 $skip = false;
456
457                 // But should we DROP?
458                 if ($tableArray[3] == 'DROP') {
459                         // Then skip if nothing found!
460                         $skip = true;
461                         //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'Going to drop key ' . $keyName);
462                 } // END - if
463
464                 // Walk through all
465                 while ($content = SQL_FETCHARRAY($result)) {
466                         // Is it found?
467                         //* DEBUG: */ print(__LINE__.':columnName='.$keyName.',content=<pre>' . print_r($content, true) . '</pre>');
468                         if (($content['Key_name'] == $keyName) && ($tableArray[3] == 'ADD')) {
469                                 // Skip this query!
470                                 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, sprintf("ADD: Skiped: %s", $sql));
471                                 $skip = true;
472                                 break;
473                         } elseif (($content['Key_name'] == $keyName) && ($tableArray[3] == 'DROP')) {
474                                 // Don't skip this!
475                                 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, sprintf("DROP: Not skiped: %s", $sql));
476                                 $skip = false;
477                                 break;
478                         }
479                 } // END - while
480
481                 // Free result
482                 SQL_FREERESULT($result);
483
484                 // Shall we run it?
485                 if ($skip === false) {
486                         // Send it to the SQL_QUERY() function
487                         //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, $sql);
488                         $result = SQL_QUERY($sql, $F, $L, false);
489                 } else {
490                         // Not executed
491                         //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'Not executed: ' . $sql);
492                 }
493         } else {
494                 // Other ALTER TABLE query
495                 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, $sql);
496                 $result = SQL_QUERY($sql, $F, $L, false);
497         }
498
499         // Return result
500         return $result;
501 }
502
503 // Getter for SQL link
504 function SQL_GET_LINK () {
505         // Init link
506         $link = null;
507
508         // Is it in the globals?
509         if (isset($GLOBALS['sql_link'])) {
510                 // Then take it
511                 $link = $GLOBALS['sql_link'];
512         } // END - if
513
514         // Return it
515         return $link;
516 }
517
518 // Setter for link
519 function SQL_SET_LINK ($link) {
520         // Is this a resource or null?
521         if ((!is_resource($link)) && (!is_null($link))) {
522                 // This should never happen!
523                 debug_report_bug(sprintf("link is not resource or null. Type: %s", gettype($link)));
524         } // END - if
525
526         // Set it
527         $GLOBALS['sql_link'] = $link;
528 }
529
530 // Checks if the link is up
531 function SQL_IS_LINK_UP () {
532         // Default is not up
533         $linkUp = false;
534
535         // Do we have cached this?
536         if (isset($GLOBALS['is_sql_link_up'])) {
537                 // Then use this
538                 $linkUp = $GLOBALS['is_sql_link_up'];
539         } else {
540                 // Get it
541                 $linkUp = is_resource(SQL_GET_LINK());
542
543                 // And cache it
544                 $GLOBALS['is_sql_link_up'] = $linkUp;
545         }
546
547         // Return the result
548         return $linkUp;
549 }
550
551 // Wrapper function to make code more readable
552 function SQL_HASZERONUMS ($result) {
553         // Just pass it through
554         return (SQL_NUMROWS($result) === 0);
555 }
556
557 // [EOF]
558 ?>