1 <?php
  2 
  3 /**
  4  * Class to execute a sql dump.
  5  *
  6  * @package redaxo\core\sql
  7  */
  8 class rex_sql_util
  9 {
 10     /**
 11      * Allgemeine funktion die eine Datenbankspalte fortlaufend durchnummeriert.
 12      * Dies ist z.B. nützlich beim Umgang mit einer Prioritäts-Spalte.
 13      *
 14      * @param string $tableName       Name der Datenbanktabelle
 15      * @param string $priorColumnName Name der Spalte in der Tabelle, in der die Priorität (Integer) gespeichert wird
 16      * @param string $whereCondition  Where-Bedingung zur Einschränkung des ResultSets
 17      * @param string $orderBy         Sortierung des ResultSets
 18      * @param int    $startBy         Startpriorität
 19      */
 20     public static function organizePriorities($tableName, $priorColumnName, $whereCondition = '', $orderBy = '', $startBy = 1)
 21     {
 22         // Datenbankvariable initialisieren
 23         $qry = 'SET @count=' . ($startBy - 1);
 24         $sql = rex_sql::factory();
 25         $sql->setQuery($qry);
 26 
 27         // Spalte updaten
 28         $qry = 'UPDATE ' . $tableName . ' SET ' . $priorColumnName . ' = ( SELECT @count := @count +1 )';
 29 
 30         if ($whereCondition != '') {
 31             $qry .= ' WHERE ' . $whereCondition;
 32         }
 33 
 34         if ($orderBy != '') {
 35             $qry .= ' ORDER BY ' . $orderBy;
 36         }
 37 
 38         $sql->setQuery($qry);
 39     }
 40 
 41     /**
 42      * Importiert die gegebene SQL-Datei in die Datenbank.
 43      *
 44      * @param string $file
 45      * @param bool   $debug
 46      *
 47      * @throws rex_sql_exception
 48      *
 49      * @return bool true bei Erfolg
 50      */
 51     public static function importDump($file, $debug = false)
 52     {
 53         $sql = rex_sql::factory();
 54         $sql->setDebug($debug);
 55         $error = '';
 56 
 57         foreach (self::readSqlDump($file) as $query) {
 58             try {
 59                 $sql->setQuery(self::prepareQuery($query));
 60             } catch (rex_sql_exception $e) {
 61                 $error .= $e->getMessage() . "\n<br />";
 62             }
 63         }
 64         if ($error) {
 65             throw new rex_sql_exception($error, null, $sql);
 66         }
 67 
 68         return true;
 69     }
 70 
 71     private static function prepareQuery($qry)
 72     {
 73         // rex::getUser() gibts im Setup nicht
 74         $user = rex::getUser() ? rex::getUser()->getValue('login') : '';
 75 
 76         $qry = str_replace('%USER%', $user, $qry);
 77         $qry = str_replace('%TIME%', time(), $qry);
 78         $qry = str_replace('%TABLE_PREFIX%', rex::getTablePrefix(), $qry);
 79         $qry = str_replace('%TEMP_PREFIX%', rex::getTempPrefix(), $qry);
 80 
 81         return $qry;
 82     }
 83 
 84     /**
 85      * Reads a file and split all statements in it.
 86      *
 87      * @param string $file Path to the SQL-dump-file
 88      *
 89      * @return array
 90      */
 91     private static function readSqlDump($file)
 92     {
 93         if (is_file($file) && is_readable($file)) {
 94             $ret = [];
 95             $sqlsplit = [];
 96             $fileContent = file_get_contents($file);
 97             self::splitSqlFile($sqlsplit, $fileContent, '');
 98 
 99             if (is_array($sqlsplit)) {
100                 foreach ($sqlsplit as $qry) {
101                     $ret[] = $qry['query'];
102                 }
103             }
104 
105             return $ret;
106         }
107 
108         return false;
109     }
110 
111     /**
112      * Removes comment lines and splits up large sql files into individual queries.
113      *
114      * Last revision: September 23, 2001 - gandon
115      *
116      * @param array  $ret     the splitted sql commands
117      * @param string $sql     the sql commands
118      * @param int    $release the MySQL release number (because certains php3 versions
119      *                        can't get the value of a constant from within a function)
120      *
121      * @return bool always true
122      */
123     // Taken from phpmyadmin (read_dump.lib.php: PMA_splitSqlFile)
124     public static function splitSqlFile(&$ret, $sql, $release)
125     {
126         // do not trim, see bug #1030644
127         //$sql          = trim($sql);
128         $sql = rtrim($sql, "\n\r");
129         $sql_len = strlen($sql);
130         $string_start = '';
131         $in_string = false;
132         $nothing = true;
133         $time0 = time();
134 
135         for ($i = 0; $i < $sql_len; ++$i) {
136             $char = $sql[$i];
137 
138             // We are in a string, check for not escaped end of strings except for
139             // backquotes that can't be escaped
140             if ($in_string) {
141                 for (; ;) {
142                     $i = strpos($sql, $string_start, $i);
143                     // No end of string found -> add the current substring to the
144                     // returned array
145                     if (!$i) {
146                         $ret[] = $sql;
147                         return true;
148                     }
149                     // Backquotes or no backslashes before quotes: it's indeed the
150                     // end of the string -> exit the loop
151                     if ($string_start == '`' || $sql[$i - 1] != '\\') {
152                         $string_start = '';
153                         $in_string = false;
154                         break;
155                     }
156                     // one or more Backslashes before the presumed end of string...
157 
158                     // ... first checks for escaped backslashes
159                     $j = 2;
160                     $escaped_backslash = false;
161                     while ($i - $j > 0 && $sql[$i - $j] == '\\') {
162                         $escaped_backslash = !$escaped_backslash;
163                         ++$j;
164                     }
165                     // ... if escaped backslashes: it's really the end of the
166                     // string -> exit the loop
167                     if ($escaped_backslash) {
168                         $string_start = '';
169                         $in_string = false;
170                         break;
171                     }
172                     // ... else loop
173 
174                     ++$i;
175 
176                     // end if...elseif...else
177                 } // end for
178             } // end if (in string)
179 
180             // lets skip comments (/*, -- and #)
181             elseif (($char == '-' && $sql_len > $i + 2 && $sql[$i + 1] == '-' && $sql[$i + 2] <= ' ') || $char == '#' || ($char == '/' && $sql_len > $i + 1 && $sql[$i + 1] == '*')) {
182                 $i = strpos($sql, $char == '/' ? '*/' : "\n", $i);
183                 // didn't we hit end of string?
184                 if ($i === false) {
185                     break;
186                 }
187                 if ($char == '/') {
188                     ++$i;
189                 }
190             }
191 
192             // We are not in a string, first check for delimiter...
193             elseif ($char == ';') {
194                 // if delimiter found, add the parsed part to the returned array
195                 $ret[] = ['query' => substr($sql, 0, $i), 'empty' => $nothing];
196                 $nothing = true;
197                 $sql = ltrim(substr($sql, min($i + 1, $sql_len)));
198                 $sql_len = strlen($sql);
199                 if ($sql_len) {
200                     $i = -1;
201                 } else {
202                     // The submited statement(s) end(s) here
203                     return true;
204                 }
205             } // end else if (is delimiter)
206 
207             // ... then check for start of a string,...
208             elseif (($char == '"') || ($char == '\'') || ($char == '`')) {
209                 $in_string = true;
210                 $nothing = false;
211                 $string_start = $char;
212             } // end else if (is start of string)
213 
214             elseif ($nothing) {
215                 $nothing = false;
216             }
217 
218             // loic1: send a fake header each 30 sec. to bypass browser timeout
219             $time1 = time();
220             if ($time1 >= $time0 + 30) {
221                 $time0 = $time1;
222                 header('X-pmaPing: Pong');
223             } // end if
224         } // end for
225 
226         // add any rest to the returned array
227         if (!empty($sql) && preg_match('@[^[:space:]]+@', $sql)) {
228             $ret[] = ['query' => $sql, 'empty' => $nothing];
229         }
230 
231         return true;
232     }
233 }
234