root/releases/0.6/lib/datalib.php

Revision 269, 54.5 kB (checked in by ben, 3 years ago)

--

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