root/releases/0.9.1/lib/datalib.php

Revision 1421, 57.3 kB (checked in by misja, 1 year ago)

Misja Hoebe <misja@curverider.co.uk> Merge r1420 into trunk

  • Property svn:eol-style set to native
Line 
1 <?php
2
3 /**
4  * Library of functions for database manipulation.
5  * This library is most of lib/datalib.php from moodle
6  * http://moodle.org || http://sourceforge.net/projects/moodle
7  * Copyright (C) 2001-2003  Martin Dougiamas  http://dougiamas.com
8  * @author Martin Dougiamas and many others
9  * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
10  */
11
12
13 /// FUNCTIONS FOR DATABASE HANDLING  ////////////////////////////////
14
15 /**
16  * Execute a given sql command string
17  *
18  * Completely general function - it just runs some SQL and reports success.
19  *
20  * @uses $db
21  * @param string $command The sql string you wish to be executed.
22  * @param bool $feedback Set this argument to true if the results generated should be printed. Default is true.
23  * @return string
24  */
25 function execute_sql($command, $feedback=true) {
26 /// Completely general function - it just runs some SQL and reports success.
27
28     global $db, $CFG;
29
30     $olddebug = $db->debug;
31
32     if (!$feedback) {
33         $db->debug = false;
34     }
35     
36     if (defined('ELGG_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
37
38     $result = $db->Execute($command);
39
40     $db->debug = $olddebug;
41
42     if ($result) {
43         // elggcache_purge(); // TODO - should probably be here, given function can do anything, but very inefficient
44         if ($feedback && $CFG->debug > 7) {
45             notify(__gettext('Success'), 'notifysuccess');
46         }
47         return true;
48     } else {
49         if ($feedback) {
50             echo '<p><span class="error">'. __gettext('Error') .'</span></p>';
51         }
52         if (!empty($CFG->dblogerror)) {
53             $debug = debug_backtrace();
54             foreach ($debug as $d) {
55                 if (strpos($d['file'],'datalib') === false) {
56                     error_log("SQL ".$db->ErrorMsg()." in {$d['file']} on line {$d['line']}. STATEMENT:  $command");
57                     break;
58                 }
59             }
60         }
61         return false;
62     }
63 }
64 /**
65 * on DBs that support it, switch to transaction mode and begin a transaction
66 * you'll need to ensure you call commit_sql() or your changes *will* be lost
67 * this is _very_ useful for massive updates
68 */
69 function begin_sql() {
70 /// Completely general function - it just runs some SQL and reports success.
71
72     global $CFG;
73     if ($CFG->dbtype === 'postgres7') {
74         return execute_sql('BEGIN', false);
75     }
76     return true;
77 }
78 /**
79 * on DBs that support it, commit the transaction
80 */
81 function rollback_sql() {
82 /// Completely general function - it just runs some SQL and reports success.
83
84     global $CFG;
85     if ($CFG->dbtype === 'postgres7') {
86         return execute_sql('ROLLBACK', false);
87     }
88     return true;
89 }
90
91
92
93 /**
94  * returns db specific uppercase function
95  */
96 function db_uppercase() {
97     global $CFG;
98     switch (strtolower($CFG->dbtype)) {
99
100     case "postgres7":
101         return "upper";
102
103     case "mysql":
104     default:
105         return "ucase";
106
107     }
108 }
109
110 /**
111  * returns db specific lowercase function
112  */
113 function db_lowercase() {
114     global $CFG;
115     switch (strtolower($CFG->dbtype)) {
116
117     case "postgres7":
118         return "lower";
119
120     case "mysql":
121     default:
122         return "lcase";
123
124     }
125 }
126
127 /**
128 * on DBs that support it, commit the transaction
129 */
130 function commit_sql() {
131 /// Completely general function - it just runs some SQL and reports success.
132
133     global $CFG;
134     if ($CFG->dbtype === 'postgres7') {
135         return execute_sql('COMMIT', false);
136     }
137     return true;
138 }
139
140 /**
141  * Run an arbitrary sequence of semicolon-delimited SQL commands
142  *
143  * Assumes that the input text (file or string) consists of
144  * a number of SQL statements ENDING WITH SEMICOLONS.  The
145  * semicolons MUST be the last character in a line.
146  * Lines that are blank or that start with "#" or "--" (postgres) are ignored.
147  * Only tested with mysql dump files (mysqldump -p -d moodle)
148  *
149  * @uses $CFG
150  * @param string $sqlfile The path where a file with sql commands can be found on the server.
151  * @param string $sqlstring If no path is supplied then a string with semicolon delimited sql
152  * commands can be supplied in this argument.
153  * @return bool Returns true if databse was modified successfully.
154  */
155 function modify_database($sqlfile='', $sqlstring='') {
156
157     global $CFG, $METATABLES, $db;
158
159     $success = true// Let's be optimistic
160
161     if (!empty($sqlfile)) {
162         if (!is_readable($sqlfile)) {
163             $success = false;
164             echo '<p>Tried to modify database, but "'. $sqlfile .'" doesn\'t exist!</p>';
165             return $success;
166         } else {
167             $lines = file($sqlfile);
168         }
169     } else {
170         $sqlstring = trim($sqlstring);
171         if ($sqlstring{strlen($sqlstring)-1} != ";") {
172             $sqlstring .= ";"; // add it in if it's not there.
173         }
174         $lines[] = $sqlstring;
175     }
176
177     $command = '';
178
179     foreach ($lines as $line) {
180         $line = rtrim($line);
181         $length = strlen($line);
182
183         if ($length and $line[0] <> '#' and $line[0].$line[1] <> '--') {
184             if (substr($line, $length-1, 1) == ';') {
185                 $line = substr($line, 0, $length-1);   // strip ;
186                 $command .= $line;
187                 $command = str_replace('prefix_', $CFG->prefix, $command); // Table prefixes
188                 if (! execute_sql($command)) {
189                     $success = false;
190                 }
191                 $command = '';
192             } else {
193                 $command .= $line;
194             }
195         }
196     }
197
198     $METATABLES = $db->Metatables();
199     elggcache_purge();
200     
201     return $success;
202
203 }
204
205 /// FUNCTIONS TO MODIFY TABLES ////////////////////////////////////////////
206
207 /**
208  * Add a new field to a table, or modify an existing one (if oldfield is defined).
209  *
210  * @uses $CFG
211  * @uses $db
212  * @param string $table ?
213  * @param string $oldfield ?
214  * @param string $field ?
215  * @param string $type ?
216  * @param string $size ?
217  * @param string $signed ?
218  * @param string $default ?
219  * @param string $null ?
220  * @todo Finish documenting this function
221  */
222
223 function table_column($table, $oldfield, $field, $type='integer', $size='10',
224                       $signed='unsigned', $default='0', $null='not null', $after='') {
225     global $CFG, $db;
226     
227     elggcache_cachepurgetype($table);
228
229     if (empty($oldfield) && !empty($field)) { //adding
230         // check it doesn't exist first.
231         if ($columns = $db->MetaColumns($CFG->prefix . $table)) {
232             foreach ($columns as $c) {
233                 if ($c->name == $field) {
234                     $oldfield = $field;
235                 }
236             }
237         }
238     }
239
240     switch (strtolower($CFG->dbtype)) {
241
242         case 'mysql':
243         case 'mysqlt':
244
245             switch (strtolower($type)) {
246                 case 'text':
247                     $type = 'TEXT';
248                     $signed = '';
249                     break;
250                 case 'integer':
251                     $type = 'INTEGER('. $size .')';
252                     break;
253                 case 'varchar':
254                     $type = 'VARCHAR('. $size .')';
255                     $signed = '';
256                     break;
257                 case 'char':
258                     $type = 'CHAR('. $size .')';
259                     $signed = '';
260                     break;
261             }
262
263             if (!empty($oldfield)) {
264                 $operation = 'CHANGE '. $oldfield .' '. $field;
265             } else {
266                 $operation = 'ADD '. $field;
267             }
268
269             $default = 'DEFAULT \''. $default .'\'';
270
271             if (!empty($after)) {
272                 $after = 'AFTER `'. $after .'`';
273             }
274
275             return execute_sql('ALTER TABLE '. $CFG->prefix . $table .' '. $operation .' '. $type .' '. $signed .' '. $default .' '. $null .' '. $after);
276
277         case 'postgres7':        // From Petri Asikainen
278             //Check db-version
279             $dbinfo = $db->ServerInfo();
280             $dbver = substr($dbinfo['version'],0,3);
281
282             //to prevent conflicts with reserved words
283             $realfield = '"'. $field .'"';
284             $field = '"'. $field .'_alter_column_tmp"';
285             $oldfield = '"'. $oldfield .'"';
286
287             switch (strtolower($type)) {
288                 case 'tinyint':
289                 case 'integer':
290                     if ($size <= 4) {
291                         $type = 'INT2';
292                     }
293                     if ($size <= 10) {
294                         $type = 'INT';
295                     }
296                     if  ($size > 10) {
297                         $type = 'INT8';
298                     }
299                     break;
300                 case 'varchar':
301                     $type = 'VARCHAR('. $size .')';
302                     break;
303                 case 'char':
304                     $type = 'CHAR('. $size .')';
305                     $signed = '';
306                     break;
307             }
308
309             $default = '\''. $default .'\'';
310
311             //After is not implemented in postgesql
312             //if (!empty($after)) {
313             //    $after = "AFTER '$after'";
314             //}
315
316             //Use transactions
317             execute_sql('BEGIN');
318
319             //Always use temporary column
320             execute_sql('ALTER TABLE '. $CFG->prefix . $table .' ADD COLUMN '. $field .' '. $type);
321             //Add default values
322             execute_sql('UPDATE '. $CFG->prefix . $table .' SET '. $field .'='. $default);
323
324
325             if ($dbver >= '7.3') {
326                 // modifying 'not null' is posible before 7.3
327                 //update default values to table
328                 if (strtoupper($null) == 'NOT NULL') {
329                     execute_sql('UPDATE '. $CFG->prefix . $table .' SET '. $field .'='. $default .' WHERE '. $field .' IS NULL');
330                     execute_sql('ALTER TABLE '. $CFG->prefix . $table .' ALTER COLUMN '. $field .' SET '. $null);
331                 } else {
332                     execute_sql('ALTER TABLE '. $CFG->prefix . $table .' ALTER COLUMN '. $field .' DROP NOT NULL');
333                 }
334             }
335
336             execute_sql('ALTER TABLE '. $CFG->prefix . $table .' ALTER COLUMN '. $field .' SET DEFAULT '. $default);
337
338             if ( $oldfield != '""' ) {
339
340                 // We are changing the type of a column. This may require doing some casts...
341                 $casting = '';
342                 $oldtype = column_type($table, $oldfield);
343                 $newtype = column_type($table, $field);
344
345                 // Do we need a cast?
346                 if($newtype == 'N' && $oldtype == 'C') {
347                     $casting = 'CAST(CAST('.$oldfield.' AS TEXT) AS REAL)';
348                 }
349                 else if($newtype == 'I' && $oldtype == 'C') {
350                     $casting = 'CAST(CAST('.$oldfield.' AS TEXT) AS INTEGER)';
351                 }
352                 else {
353                     $casting = $oldfield;
354                 }
355
356                 // Run the update query, casting as necessary
357                 execute_sql('UPDATE '. $CFG->prefix . $table .' SET '. $field .' = '. $casting);
358                 execute_sql('ALTER TABLE  '. $CFG->prefix . $table .' DROP COLUMN '. $oldfield);
359             }
360
361             execute_sql('ALTER TABLE '. $CFG->prefix . $table .' RENAME COLUMN '. $field .' TO '. $realfield);
362
363             return execute_sql('COMMIT');
364
365         default:
366             switch (strtolower($type)) {
367                 case 'integer':
368                     $type = 'INTEGER';
369                     break;
370                 case 'varchar':
371                     $type = 'VARCHAR';
372                     break;
373             }
374
375             $default = 'DEFAULT \''. $default .'\'';
376
377             if (!empty($after)) {
378                 $after = 'AFTER '. $after;
379             }
380
381             if (!empty($oldfield)) {
382                 execute_sql('ALTER TABLE '. $CFG->prefix . $table .' RENAME COLUMN '. $oldfield .' '. $field);
383             } else {
384                 execute_sql('ALTER TABLE '. $CFG->prefix . $table .' ADD COLUMN '. $field .' '. $type);
385             }
386
387             execute_sql('ALTER TABLE '. $CFG->prefix . $table .' ALTER COLUMN '. $field .' SET '. $null);
388             return execute_sql('ALTER TABLE '. $CFG->prefix . $table .' ALTER COLUMN '. $field .' SET '. $default);
389     }
390 }
391
392 /**
393  * Get the data type of a table column, using an ADOdb MetaType() call.
394  *
395  * @uses $CFG
396  * @uses $db
397  * @param string $table The name of the database table
398  * @param string $column The name of the field in the table
399  * @return string Field type or false if error
400  */
401
402 function column_type($table, $column) {
403     global $CFG, $db;
404
405     if (defined('ELGG_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
406
407     if(!$rs = $db->Execute('SELECT '.$column.' FROM '.$CFG->prefix.$table.' LIMIT 0')) {
408         return false;
409     }
410     
411     $field = $rs->FetchField(0);
412     return $rs->MetaType($field->type);
413 }
414
415
416 /// GENERIC FUNCTIONS TO CHECK AND COUNT RECORDS ////////////////////////////////////////
417
418 /**
419  * Test whether a record exists in a table where all the given fields match the given values.
420  *
421  * The record to test is specified by giving up to three fields that must
422  * equal the corresponding values.
423  *
424  * @uses $CFG
425  * @param string $table The table to check.
426  * @param string $field1 the first field to check (optional).
427  * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
428  * @param string $field2 the second field to check (optional).
429  * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
430  * @param string $field3 the third field to check (optional).
431  * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
432  * @return bool true if a matching record exists, else false.
433  */
434 function record_exists($table, $field1=null, $value1=null, $field2=null, $value2=null, $field3=null, $value3=null) {
435
436     global $CFG;
437
438     $select = where_clause_prepared($field1, $field2, $field3);
439
440     $values = where_values_prepared($value1, $value2, $value3);
441     
442     return record_exists_sql('SELECT * FROM '. $CFG->prefix . $table .' '. $select .' LIMIT 1',$values);
443 }
444
445
446 /**
447 * Determine whether a specified record exists.
448 *
449 * This function returns true if the SQL executed returns records.
450 *
451 * @uses $CFG
452 * @uses $db
453 * @param string $sql The SQL statement to be executed. If using $values, placeholder ?s are expected. If not, the string should be escaped correctly.
454 * @param array $values When using prepared statements, this is the value array. Optional.
455 * @return bool
456 */
457 function record_exists_sql($sql,$values=null) {
458
459     global $CFG, $db;
460
461     if (defined('ELGG_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
462
463     $rs = false;
464     if (!empty($values) && is_array($values) && count($values) > 0) {
465         $stmt = $db->Prepare($sql);
466         $rs = $db->Execute($stmt,$values);
467     } else {
468         $rs = $db->Execute($sql);
469     }
470     
471     if (empty($rs)) {
472         if (isset($CFG->debug) and $CFG->debug > 7) {
473             notify($db->ErrorMsg().'<br /><br />'.$sql);
474         }
475         if (!empty($CFG->dblogerror)) {
476             $debug = debug_backtrace();
477             foreach ($debug as $d) {
478                 if (strpos($d['file'],'datalib') === false) {
479                     error_log("SQL ".$db->ErrorMsg()." in {$d['file']} on line {$d['line']}. STATEMENT:  $sql");
480                     break;
481                 }
482             }
483         }
484         return false;
485     }
486
487     if ( $rs->RecordCount() ) {
488         return true;
489     } else {
490         return false;
491     }
492     }
493
494
495 /**
496  * Count the records in a table where all the given fields match the given values.
497  *
498  * @uses $CFG
499  * @param string $table The table to query.
500  * @param string $field1 the first field to check (optional).
501  * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
502  * @param string $field2 the second field to check (optional).
503  * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
504  * @param string $field3 the third field to check (optional).
505  * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
506  * @return int The count of records returned from the specified criteria.
507  */
508 function count_records($table, $field1=null, $value1=null, $field2=null, $value2=null, $field3=null, $value3=null) {
509
510     global $CFG;
511
512     $select = where_clause_prepared($field1, $field2, $field3);
513
514     $values = where_values_prepared($value1, $value2, $value3);
515
516     return count_records_sql('SELECT COUNT(*) FROM '. $CFG->prefix . $table .' '. $select, $values);
517 }
518
519 /**
520  * Get all the records and count them
521  *
522  * @uses $CFG
523  * @param string $table The database table to be checked against.
524  * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
525  * @param array $values if using a prepared statement with placeholders in $select, pass values here. optional
526  * @param string $countitem The count string to be used in the SQL call. Default is COUNT(*).
527  * @return int The count of records returned from the specified criteria.
528  */
529 function count_records_select($table, $select='', $values=null, $countitem='COUNT(*)') {
530
531     global $CFG;
532
533     if ($select) {
534         $select = 'WHERE '.$select;
535     }
536
537     return count_records_sql('SELECT '. $countitem .' FROM '. $CFG->prefix . $table .' '. $select, $values);
538 }
539
540
541 /**
542  * Get all the records returned from the specified SQL call and return the count of them
543  *
544  * @uses $CFG
545  * @uses $db
546  * @param string $sql The SQL string you wish to be executed.
547  * @return int The count of records returned from the specified SQL string.
548  */
549 function count_records_sql($sql, $values=null) {
550
551  &nbs