root/trunk/lib/SGL/Sql.php

Revision 3125, 13.8 kB (checked in by demian, 3 weeks ago)

bugfix to trunk merge -3

Line 
1 <?php
2 /* Reminder: always indent with 4 spaces (no tabs). */
3 // +---------------------------------------------------------------------------+
4 // | Copyright (c) 2006, Demian Turner                                         |
5 // | All rights reserved.                                                      |
6 // |                                                                           |
7 // | Redistribution and use in source and binary forms, with or without        |
8 // | modification, are permitted provided that the following conditions        |
9 // | are met:                                                                  |
10 // |                                                                           |
11 // | o Redistributions of source code must retain the above copyright          |
12 // |   notice, this list of conditions and the following disclaimer.           |
13 // | o Redistributions in binary form must reproduce the above copyright       |
14 // |   notice, this list of conditions and the following disclaimer in the     |
15 // |   documentation and/or other materials provided with the distribution.    |
16 // | o The names of the authors may not be used to endorse or promote          |
17 // |   products derived from this software without specific prior written      |
18 // |   permission.                                                             |
19 // |                                                                           |
20 // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS       |
21 // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT         |
22 // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR     |
23 // | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT      |
24 // | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,     |
25 // | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT          |
26 // | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,     |
27 // | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY     |
28 // | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT       |
29 // | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE     |
30 // | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.      |
31 // |                                                                           |
32 // +---------------------------------------------------------------------------+
33 // | Seagull 0.6                                                               |
34 // +---------------------------------------------------------------------------+
35 // | Sql.php                                                                   |
36 // +---------------------------------------------------------------------------+
37 // | Author:   Demian Turner <demian@phpkitchen.com>                           |
38 // +---------------------------------------------------------------------------+
39 // $Id: Sql.php,v 1.23 2005/06/14 00:19:22 demian Exp $
40
41 /**
42  * Provides SQL schema and data parsing/executing methods.
43  *
44  * @package SGL
45  * @author  Demian Turner <demian@phpkitchen.com>
46  * @version $Revision: 1.23 $
47  */
48 class SGL_Sql
49 {
50     /**
51      * Simple function that opens a file with sql statements and executes them
52      * using DB
53       *
54      * @author  Gerry Lachac <glachac@tethermedia.com>
55      * @access  public
56      * @static
57      * @param   string  $filename   File with SQL statements to execute
58      * @return  void
59      */
60     function parse($filename, $errorReporting = E_ALL, $executerCallback = null)
61     {
62         //  Optionally shut off error reporting if logging isn't set up correctly yet
63         $originalErrorLevel = error_reporting();
64         error_reporting($errorReporting);
65
66         if (! ($fp = fopen($filename, 'r')) ) {
67             return false;
68         }
69
70         $sql = '';
71         $c = &SGL_Config::singleton();
72         $conf = $c->getAll();
73
74         $isMysql323 = false;
75         if        ($conf['db']['type'] == 'mysql_SGL'
76                 || $conf['db']['type'] == 'mysql'
77                 || $conf['db']['type'] == 'mysqli'
78                 || $conf['db']['type'] == 'mysqli_SGL') {
79             $aEnvData = unserialize(file_get_contents(SGL_VAR_DIR . '/env.php'));
80             if (isset($aEnvData['db_info']) && ereg('3.23', $aEnvData['db_info']['version'])) {
81                 $isMysql323 = true;
82             }
83         }
84
85         // Iterate through each line in the file.
86         $aLines = array();
87         while (!feof($fp)) {
88
89             // Read lines, concat together until we see a semi-colon
90             $line = fgets($fp, 32768);
91
92             // Check for various comment types
93             if (preg_match("/^\s*(--)|^\s*#/", $line)) {
94                 continue;
95             }
96             if (preg_match("/insert/i", $line) && preg_match("/\{SGL_NEXT_ID\}/", $line)) {
97                 $tableName = SGL_Sql::extractTableNameFromInsertStatement($line);
98                 $nextId = SGL_Sql::getNextId($tableName);
99                 $line = SGL_Sql::rewriteWithAutoIncrement($line, $nextId);
100             }
101
102             // prefix table name in statement
103             if (!empty($conf['db']['prefix'])) {
104                 $statementType = '';
105                 if (preg_match('/create table/i', $line)) {
106                     $statementType = 'createTable';
107                 } elseif (preg_match('/insert into/i', $line)) {
108                     $statementType = 'insert';
109                 } elseif (preg_match('/select(.*?)from/i', $line)) {
110                     $statementType = 'select';
111                 } elseif (preg_match('/create (unique )?index/i', $line)) {
112                     $statementType = 'createIndex';
113                 } elseif (preg_match('/delete from/i', $line)) {
114                     $statementType = 'delete';
115                 } elseif (preg_match('/alter table/i', $line)) {
116                     $statementType = 'alterTable';
117                 } elseif (preg_match('/references/i', $line)) {
118                     $statementType = 'ref';
119                 } elseif (preg_match('/add constraint/i', $line)) {
120                     $statementType = 'addConstraint';
121                 } elseif (preg_match('/create sequence/i', $line)) {
122                     $statementType = 'createSequence';
123                 }
124                 if (!empty($statementType)) {
125                     $line = SGL_Sql::prefixTableNameInStatement($line, $statementType);
126                 }
127             }
128
129             $sql .= $line;
130
131             if (!preg_match("/;\s*$/", $sql)) {
132                 continue;
133             }
134
135             // strip semi-colons for MaxDB, Oracle and mysql 3.23
136             if ($conf['db']['type'] == 'oci8_SGL' || $conf['db']['type'] == 'odbc' || $isMysql323) {
137                 $sql = preg_replace("/;\s*$/", '', $sql);
138             }
139
140             // support for mysql cluster
141             if ($conf['db']['type'] == 'mysql_SGL'
142                     && $conf['db']['mysqlCluster'] == true
143                     && preg_match('/create table/i', $sql)) {
144                 if (preg_match('/(type|engine)(\s*)=(\s*)(myisam|innodb)/i', $sql)) {
145                     $sql = preg_replace('/(type|engine)(\s*)=(\s*)(myisam|innodb)/i', 'engine=ndbcluster', $sql);
146                 } elseif (preg_match('/\)\s*;\s*$/', $sql)) {
147                     $sql = preg_replace('/;\s*$/', 'engine=ndbcluster;', $sql);
148                 }
149             }
150
151             // Execute the statement.
152             if (!is_null($executerCallback) && is_callable($executerCallback)) {
153                 $res = call_user_func_array(
154                     array($executerCallback[0], $executerCallback[1]), $sql);
155                 //  handle error
156                 if (PEAR::isError($res)) {
157                     return $res;
158                 }
159             }
160             $aLines[] = $sql;
161             $sql = '';
162         }
163         fclose($fp);
164         //  reset orig error level
165         error_reporting($originalErrorLevel);
166         return implode("\n", $aLines);
167     }
168
169     function execute($sql)
170     {
171         // Get database handle based on working config.ini
172         $locator = &SGL_ServiceLocator::singleton();
173         $dbh = $locator->get('DB');
174         if (!$dbh) {
175             $dbh = & SGL_DB::singleton();
176             $locator->register('DB', $dbh);
177         }
178         $res = $dbh->query($sql);
179         return $res;
180     }
181
182     function getNextId($tableName)
183     {
184         // Get database handle based on working config.ini
185         $locator = &SGL_ServiceLocator::singleton();
186         $dbh = $locator->get('DB');
187         if (!$dbh) {
188             $dbh = & SGL_DB::singleton();
189             $locator->register('DB', $dbh);
190         }
191         return $dbh->nextId($tableName);
192     }
193
194     function extractTableNameFromInsertStatement($str)
195     {
196         $pattern = '/^(INSERT INTO)(\W+)(\w+)(\W+)(.*)/i';
197         preg_match($pattern, $str, $matches);
198         $tableName = SGL_Sql::addTablePrefix($matches[3]);
199         return $tableName;
200     }
201
202     /**
203      * Given a CREATE TABLE string, will extract the table name.
204      *
205      * @param string $str
206      * @return string
207      * @todo consider using SQL_Parser, 19kb lib
208      */
209     function extractTableNameFromCreateStatement($str)
210     {
211         //  main pattern, 5th group, matches any alphanum char plus _ and -
212         $pattern = '/(CREATE TABLE)(\W+)(IF NOT EXISTS)?(\W+)?([A-Za-z0-9_-]+)(\W+)?/i';
213         preg_match($pattern, $str, $matches);
214         $tableName = SGL_Sql::addTablePrefix($matches[5]);
215         return $tableName;
216     }
217
218     function rewriteWithAutoIncrement($str, $nextId)
219     {
220         $res = str_replace('{SGL_NEXT_ID}', $nextId, $str);
221         return $res;
222     }
223
224     function extractTableNamesFromSchema($data)
225     {
226         if (is_file($data)) {
227             $aLines = file($data);
228         } elseif (is_string($data)) {
229             $aLines = explode("\n", $data);
230         } else {
231             return SGL::raiseError('unexpected input', SGL_ERROR_INVALIDARGS);
232         }
233         $aTablesNames = array();
234         foreach ($aLines as $line) {
235             if (preg_match("/create table/i", $line)) {
236                 $aTablesNames[] = SGL_Sql::extractTableNameFromCreateStatement($line);
237             }
238         }
239         return $aTablesNames;
240     }
241
242     function getDbShortnameFromType($dbType)
243     {
244         switch ($dbType) {
245         case 'pgsql':
246             $shortName = 'pg';
247             break;
248
249         case 'mysql_SGL':
250         case 'mysqli_SGL':
251         case 'mysql':
252         case 'mysqli':
253             $shortName = 'my';
254             break;
255
256         case 'oci8_SGL':
257             $shortName = 'oci';
258             break;
259         }
260         return $shortName;
261     }
262
263     /**
264      * Prefix given table name.
265      *
266      * @param  string $tableName  table name
267      * @return string
268      */
269     function addTablePrefix($tableName)
270     {
271         $c = &SGL_Config::singleton();
272         $prefix = $c->get(array('db' => 'prefix'));
273         return $prefix . $tableName;
274     }
275
276     /**
277      * Prefix table name in SQL statement.
278      *
279      * @param  string $str   initial string (statement)
280      * @param  string $type  query type
281      * @return string
282      */
283     function prefixTableNameInStatement($str, $type)
284     {
285         switch ($type) {
286             case 'select':
287                 $pattern     = '/(SELECT)(.*?)(FROM)(\W+)?([A-Za-z0-9_-]+)(\W+)?/i';
288                 $replacement = '${1}${2}${3}${4}' .
289                     SGL_Sql::addTablePrefix('$5') . '${6}';
290                 break;
291
292             case 'insert':
293                 $pattern     = '/(INSERT INTO)(\W+)?([A-Za-z0-9_-]+)(\W+)?/i';
294                 $replacement = '${1}${2}' .
295                     SGL_Sql::addTablePrefix('$3') . '${4}';
296                 break;
297
298             case 'delete':
299                 $pattern     = '/(DELETE FROM)(\W+)?([A-Za-z0-9_-]+)(\W+)?/i';
300                 $replacement = '${1}${2}' . SGL_Sql::addTablePrefix('$3') . '${4}';
301                 break;
302
303             case 'createTable':
304                 $pattern     = '/(CREATE TABLE)(\W+)(IF NOT EXISTS)?(\W+)?([A-Za-z0-9_-]+)(\W+)?/i';
305                 $replacement = '${1}${2}${3}${4}' .
306                     SGL_Sql::addTablePrefix('$5') . '${6}';
307                 break;
308
309             case 'createIndex':
310                 $pattern     = '/(CREATE)(.*?)(INDEX)(\W+)?([A-Za-z0-9_-]+)(\W+)?' .
311                     '(.*?)(ON)(\W+)?([A-Za-z0-9_-]+)(\W+)?/i';
312                 $replacement = '${1}${2}${3}${4}' .
313                     SGL_Sql::addTablePrefix('$5') . '${6}${7}${8}${9}' .
314                     SGL_Sql::addTablePrefix('$10') . '${11}';
315                 break;
316
317             case 'alterTable':
318                 // prefix sub-statements on the same line
319                 if (preg_match('/add constraint/i', $str)) {
320                     $str = SGL_Sql::prefixTableNameInStatement($str, 'addConstraint');
321                 }
322                 if (preg_match('/references/i', $str)) {
323                     $str = SGL_Sql::prefixTableNameInStatement($str, 'ref');
324                 }
325                 $pattern     = '/(ALTER)(.*?)(TABLE)(\W+)?([A-Za-z0-9_-]+)(\W+)?/i';
326                 $replacement = '${1}${2}${3}${4}' .
327                     SGL_Sql::addTablePrefix('$5') . '${6}';
328                 break;
329
330             case 'addConstraint':
331                 $pattern     = '/(ADD CONSTRAINT)(\W+)?([A-Za-z0-9_-]+)(\W+)?/i';
332                 $replacement = '${1}${2}' . SGL_Sql::addTablePrefix('$3') . '${4}';
333                 break;
334
335             case 'ref':
336                 $pattern     = '/(REFERENCES)(\W+)?([A-Za-z0-9_-]+)(\W+)?/i';
337                 $replacement = '${1}${2}' . SGL_Sql::addTablePrefix('$3') . '${4}';
338                 break;
339
340             case 'createSequence':
341                 $pattern     = '/(CREATE)(.*?)(SEQUENCE)(\W+)?([A-Za-z0-9_-]+)/i';
342                 $replacement = '${1}${2}${3}${4}' . SGL_Sql::addTablePrefix('$5');
343                 break;
344
345             default:
346                 return SGL::raiseError('Unknown replacement format',
347                     SGL_ERROR_INVALIDARGS);
348         }
349         $str = preg_replace($pattern, $replacement, $str);
350         return $str;
351     }
352 }
353 ?>
354
Note: See TracBrowser for help on using the browser.