3.4.0-rc1
[phpmyadmin/thilanka.git] / libraries / import / sql.php
blob0e61080cac634d0788980994585a8d8c5fbf0262
1 <?php
2 /* vim: set expandtab sw=4 ts=4 sts=4: */
3 /**
4 * SQL import plugin for phpMyAdmin
6 * @package phpMyAdmin-Import
7 */
8 if (! defined('PHPMYADMIN')) {
9 exit;
12 /**
15 if (isset($plugin_list)) {
16 $plugin_list['sql'] = array(
17 'text' => __('SQL'),
18 'extension' => 'sql',
19 'options_text' => __('Options'),
21 $compats = PMA_DBI_getCompatibilities();
22 if (count($compats) > 0) {
23 $values = array();
24 foreach($compats as $val) {
25 $values[$val] = $val;
27 $plugin_list['sql']['options'] = array(
28 array('type' => 'begin_group', 'name' => 'general_opts'),
29 array(
30 'type' => 'select',
31 'name' => 'compatibility',
32 'text' => __('SQL compatibility mode:'),
33 'values' => $values,
34 'doc' => array(
35 'manual_MySQL_Database_Administration',
36 'Server_SQL_mode',
39 array(
40 'type' => 'bool',
41 'name' => 'no_auto_value_on_zero',
42 'text' => __('Do not use <code>AUTO_INCREMENT</code> for zero values'),
43 'doc' => array(
44 'manual_MySQL_Database_Administration',
45 'Server_SQL_mode',
46 'sqlmode_no_auto_value_on_zero'
50 array('type' => 'end_group'),
54 /* We do not define function when plugin is just queried for information above */
55 return;
58 $buffer = '';
59 // Defaults for parser
60 $sql = '';
61 $start_pos = 0;
62 $i = 0;
63 $len= 0;
64 $big_value = 2147483647;
65 $delimiter_keyword = 'DELIMITER '; // include the space because it's mandatory
66 $length_of_delimiter_keyword = strlen($delimiter_keyword);
68 if (isset($_POST['sql_delimiter'])) {
69 $sql_delimiter = $_POST['sql_delimiter'];
70 } else {
71 $sql_delimiter = ';';
74 // Handle compatibility options
75 $sql_modes = array();
76 if (isset($_REQUEST['sql_compatibility']) && 'NONE' != $_REQUEST['sql_compatibility']) {
77 $sql_modes[] = $_REQUEST['sql_compatibility'];
79 if (isset($_REQUEST['sql_no_auto_value_on_zero'])) {
80 $sql_modes[] = 'NO_AUTO_VALUE_ON_ZERO';
82 if (count($sql_modes) > 0) {
83 PMA_DBI_try_query('SET SQL_MODE="' . implode(',', $sql_modes) . '"');
85 unset($sql_modes);
87 /**
88 * will be set in PMA_importGetNextChunk()
90 * @global boolean $GLOBALS['finished']
92 $GLOBALS['finished'] = false;
94 while (!($GLOBALS['finished'] && $i >= $len) && !$error && !$timeout_passed) {
95 $data = PMA_importGetNextChunk();
96 if ($data === FALSE) {
97 // subtract data we didn't handle yet and stop processing
98 $offset -= strlen($buffer);
99 break;
100 } elseif ($data === TRUE) {
101 // Handle rest of buffer
102 } else {
103 // Append new data to buffer
104 $buffer .= $data;
105 // free memory
106 unset($data);
107 // Do not parse string when we're not at the end and don't have ; inside
108 if ((strpos($buffer, $sql_delimiter, $i) === FALSE) && !$GLOBALS['finished']) {
109 continue;
112 // Current length of our buffer
113 $len = strlen($buffer);
115 // Grab some SQL queries out of it
116 while ($i < $len) {
117 $found_delimiter = false;
118 // Find first interesting character
119 $old_i = $i;
120 // this is about 7 times faster that looking for each sequence i
121 // one by one with strpos()
122 if (preg_match('/(\'|"|#|-- |\/\*|`|(?i)(?<![A-Z0-9_])' . $delimiter_keyword . ')/', $buffer, $matches, PREG_OFFSET_CAPTURE, $i)) {
123 // in $matches, index 0 contains the match for the complete
124 // expression but we don't use it
125 $first_position = $matches[1][1];
126 } else {
127 $first_position = $big_value;
130 * @todo we should not look for a delimiter that might be
131 * inside quotes (or even double-quotes)
133 // the cost of doing this one with preg_match() would be too high
134 $first_sql_delimiter = strpos($buffer, $sql_delimiter, $i);
135 if ($first_sql_delimiter === FALSE) {
136 $first_sql_delimiter = $big_value;
137 } else {
138 $found_delimiter = true;
141 // set $i to the position of the first quote, comment.start or delimiter found
142 $i = min($first_position, $first_sql_delimiter);
144 if ($i == $big_value) {
145 // none of the above was found in the string
147 $i = $old_i;
148 if (!$GLOBALS['finished']) {
149 break;
151 // at the end there might be some whitespace...
152 if (trim($buffer) == '') {
153 $buffer = '';
154 $len = 0;
155 break;
157 // We hit end of query, go there!
158 $i = strlen($buffer) - 1;
161 // Grab current character
162 $ch = $buffer[$i];
164 // Quotes
165 if (strpos('\'"`', $ch) !== FALSE) {
166 $quote = $ch;
167 $endq = FALSE;
168 while (!$endq) {
169 // Find next quote
170 $pos = strpos($buffer, $quote, $i + 1);
172 * Behave same as MySQL and accept end of query as end of backtick.
173 * I know this is sick, but MySQL behaves like this:
175 * SELECT * FROM `table
177 * is treated like
179 * SELECT * FROM `table`
181 if ($pos === FALSE && $quote == '`' && $found_delimiter) {
182 $pos = $first_sql_delimiter - 1;
183 // No quote? Too short string
184 } elseif ($pos === FALSE) {
185 // We hit end of string => unclosed quote, but we handle it as end of query
186 if ($GLOBALS['finished']) {
187 $endq = TRUE;
188 $i = $len - 1;
190 $found_delimiter = false;
191 break;
193 // Was not the quote escaped?
194 $j = $pos - 1;
195 while ($buffer[$j] == '\\') $j--;
196 // Even count means it was not escaped
197 $endq = (((($pos - 1) - $j) % 2) == 0);
198 // Skip the string
199 $i = $pos;
201 if ($first_sql_delimiter < $pos) {
202 $found_delimiter = false;
205 if (!$endq) {
206 break;
208 $i++;
209 // Aren't we at the end?
210 if ($GLOBALS['finished'] && $i == $len) {
211 $i--;
212 } else {
213 continue;
217 // Not enough data to decide
218 if ((($i == ($len - 1) && ($ch == '-' || $ch == '/'))
219 || ($i == ($len - 2) && (($ch == '-' && $buffer[$i + 1] == '-')
220 || ($ch == '/' && $buffer[$i + 1] == '*')))) && !$GLOBALS['finished']) {
221 break;
224 // Comments
225 if ($ch == '#'
226 || ($i < ($len - 1) && $ch == '-' && $buffer[$i + 1] == '-'
227 && (($i < ($len - 2) && $buffer[$i + 2] <= ' ')
228 || ($i == ($len - 1) && $GLOBALS['finished'])))
229 || ($i < ($len - 1) && $ch == '/' && $buffer[$i + 1] == '*')
231 // Copy current string to SQL
232 if ($start_pos != $i) {
233 $sql .= substr($buffer, $start_pos, $i - $start_pos);
235 // Skip the rest
236 $start_of_comment = $i;
237 // do not use PHP_EOL here instead of "\n", because the export
238 // file might have been produced on a different system
239 $i = strpos($buffer, $ch == '/' ? '*/' : "\n", $i);
240 // didn't we hit end of string?
241 if ($i === FALSE) {
242 if ($GLOBALS['finished']) {
243 $i = $len - 1;
244 } else {
245 break;
248 // Skip *
249 if ($ch == '/') {
250 $i++;
252 // Skip last char
253 $i++;
254 // We need to send the comment part in case we are defining
255 // a procedure or function and comments in it are valuable
256 $sql .= substr($buffer, $start_of_comment, $i - $start_of_comment);
257 // Next query part will start here
258 $start_pos = $i;
259 // Aren't we at the end?
260 if ($i == $len) {
261 $i--;
262 } else {
263 continue;
266 // Change delimiter, if redefined, and skip it (don't send to server!)
267 if (strtoupper(substr($buffer, $i, $length_of_delimiter_keyword)) == $delimiter_keyword
268 && ($i + $length_of_delimiter_keyword < $len)) {
269 // look for EOL on the character immediately after 'DELIMITER '
270 // (see previous comment about PHP_EOL)
271 $new_line_pos = strpos($buffer, "\n", $i + $length_of_delimiter_keyword);
272 // it might happen that there is no EOL
273 if (FALSE === $new_line_pos) {
274 $new_line_pos = $len;
276 $sql_delimiter = substr($buffer, $i + $length_of_delimiter_keyword, $new_line_pos - $i - $length_of_delimiter_keyword);
277 $i = $new_line_pos + 1;
278 // Next query part will start here
279 $start_pos = $i;
280 continue;
283 // End of SQL
284 if ($found_delimiter || ($GLOBALS['finished'] && ($i == $len - 1))) {
285 $tmp_sql = $sql;
286 if ($start_pos < $len) {
287 $length_to_grab = $i - $start_pos;
289 if (! $found_delimiter) {
290 $length_to_grab++;
292 $tmp_sql .= substr($buffer, $start_pos, $length_to_grab);
293 unset($length_to_grab);
295 // Do not try to execute empty SQL
296 if (! preg_match('/^([\s]*;)*$/', trim($tmp_sql))) {
297 $sql = $tmp_sql;
298 PMA_importRunQuery($sql, substr($buffer, 0, $i + strlen($sql_delimiter)));
299 $buffer = substr($buffer, $i + strlen($sql_delimiter));
300 // Reset parser:
301 $len = strlen($buffer);
302 $sql = '';
303 $i = 0;
304 $start_pos = 0;
305 // Any chance we will get a complete query?
306 //if ((strpos($buffer, ';') === FALSE) && !$GLOBALS['finished']) {
307 if ((strpos($buffer, $sql_delimiter) === FALSE) && !$GLOBALS['finished']) {
308 break;
310 } else {
311 $i++;
312 $start_pos = $i;
315 } // End of parser loop
316 } // End of import loop
317 // Commit any possible data in buffers
318 PMA_importRunQuery('', substr($buffer, 0, $len));
319 PMA_importRunQuery();