public function process($tokens) { $currCategory = "TABLE_NAME"; $result = array('base_expr' => false, 'name' => false, 'no_quotes' => false, 'create-def' => false, 'options' => false, 'like' => false, 'select-option' => false); $expr = array(); $base_expr = ''; $skip = 0; foreach ($tokens as $token) { $trim = trim($token); $base_expr .= $token; if ($skip > 0) { $skip--; continue; } if ($skip < 0) { break; } if ($trim === "") { continue; } $upper = strtoupper($trim); switch ($upper) { case ',': # it is possible to separate the table options with comma! if ($prevCategory === 'CREATE_DEF') { $last = array_pop($result['options']); $last['delim'] = ','; $result['options'][] = $last; $base_expr = ""; } continue 2; case 'UNION': if ($prevCategory === 'CREATE_DEF') { $expr[] = $this->getReservedType($trim); $currCategory = 'UNION'; continue 2; } break; case 'LIKE': # like without parenthesis if ($prevCategory === 'TABLE_NAME') { $currCategory = $upper; continue 2; } break; case '=': # the optional operator if ($prevCategory === 'TABLE_OPTION') { $expr[] = $this->getOperatorType($trim); continue 2; # don't change the category } break; case 'CHARACTER': if ($prevCategory === 'CREATE_DEF') { $expr[] = $this->getReservedType($trim); $currCategory = 'TABLE_OPTION'; } if ($prevCategory === 'TABLE_OPTION') { # add it to the previous DEFAULT $expr[] = $this->getReservedType($trim); continue 2; } break; case 'SET': if ($prevCategory === 'TABLE_OPTION') { # add it to a previous CHARACTER $expr[] = $this->getReservedType($trim); $currCategory = 'CHARSET'; continue 2; } break; case 'COLLATE': if ($prevCategory === 'TABLE_OPTION') { # add it to the previous DEFAULT $expr[] = $this->getReservedType($trim); $currCategory = 'COLLATE'; continue 2; } break; case 'DIRECTORY': if ($currCategory === 'INDEX_DIRECTORY' || $currCategory === 'DATA_DIRECTORY') { # after INDEX or DATA $expr[] = $this->getReservedType($trim); continue 2; } break; case 'INDEX': if ($prevCategory === 'CREATE_DEF') { $expr[] = $this->getReservedType($trim); $currCategory = 'INDEX_DIRECTORY'; continue 2; } break; case 'DATA': if ($prevCategory === 'CREATE_DEF') { $expr[] = $this->getReservedType($trim); $currCategory = 'DATA_DIRECTORY'; continue 2; } break; case 'INSERT_METHOD': case 'DELAY_KEY_WRITE': case 'ROW_FORMAT': case 'PASSWORD': case 'MAX_ROWS': case 'MIN_ROWS': case 'PACK_KEYS': case 'CHECKSUM': case 'COMMENT': case 'CONNECTION': case 'AUTO_INCREMENT': case 'AVG_ROW_LENGTH': case 'ENGINE': case 'TYPE': case 'STATS_AUTO_RECALC': case 'STATS_PERSISTENT': case 'KEY_BLOCK_SIZE': if ($prevCategory === 'CREATE_DEF') { $expr[] = $this->getReservedType($trim); $currCategory = $prevCategory = 'TABLE_OPTION'; continue 2; } break; case 'DYNAMIC': case 'FIXED': case 'COMPRESSED': case 'REDUNDANT': case 'COMPACT': case 'NO': case 'FIRST': case 'LAST': case 'DEFAULT': if ($prevCategory === 'CREATE_DEF') { # DEFAULT before CHARACTER SET and COLLATE $expr[] = $this->getReservedType($trim); $currCategory = 'TABLE_OPTION'; } if ($prevCategory === 'TABLE_OPTION') { # all assignments with the keywords $expr[] = $this->getReservedType($trim); $result['options'][] = array('expr_type' => ExpressionType::EXPRESSION, 'base_expr' => trim($base_expr), 'delim' => ' ', 'sub_tree' => $expr); $this->clear($expr, $base_expr, $currCategory); } break; case 'IGNORE': case 'REPLACE': $expr[] = $this->getReservedType($trim); $result['select-option'] = array('base_expr' => trim($base_expr), 'duplicates' => $trim, 'as' => false, 'sub_tree' => $expr); continue 2; case 'AS': $expr[] = $this->getReservedType($trim); if (!isset($result['select-option']['duplicates'])) { $result['select-option']['duplicates'] = false; } $result['select-option']['as'] = true; $result['select-option']['base_expr'] = trim($base_expr); $result['select-option']['sub_tree'] = $expr; continue 2; case 'PARTITION': # TODO: parse partition options $skip = -1; break; default: switch ($currCategory) { case 'CHARSET': # the charset name $expr[] = $this->getConstantType($trim); $result['options'][] = array('expr_type' => ExpressionType::CHARSET, 'base_expr' => trim($base_expr), 'delim' => ' ', 'sub_tree' => $expr); $this->clear($expr, $base_expr, $currCategory); break; case 'COLLATE': # the collate name $expr[] = $this->getConstantType($trim); $result['options'][] = array('expr_type' => ExpressionType::COLLATE, 'base_expr' => trim($base_expr), 'delim' => ' ', 'sub_tree' => $expr); $this->clear($expr, $base_expr, $currCategory); break; case 'DATA_DIRECTORY': # we have the directory name $expr[] = $this->getConstantType($trim); $result['options'][] = array('expr_type' => ExpressionType::DIRECTORY, 'kind' => 'DATA', 'base_expr' => trim($base_expr), 'delim' => ' ', 'sub_tree' => $expr); $this->clear($expr, $base_expr, $prevCategory); continue 3; case 'INDEX_DIRECTORY': # we have the directory name $expr[] = $this->getConstantType($trim); $result['options'][] = array('expr_type' => ExpressionType::DIRECTORY, 'kind' => 'INDEX', 'base_expr' => trim($base_expr), 'delim' => ' ', 'sub_tree' => $expr); $this->clear($expr, $base_expr, $prevCategory); continue 3; case 'TABLE_NAME': $result['base_expr'] = $result['name'] = $trim; $result['no_quotes'] = $this->revokeQuotation($trim); $this->clear($expr, $base_expr, $prevCategory); break; case 'LIKE': $result['like'] = array('expr_type' => ExpressionType::TABLE, 'table' => $trim, 'base_expr' => $trim, 'no_quotes' => $this->revokeQuotation($trim)); $this->clear($expr, $base_expr, $currCategory); break; case '': # after table name if ($prevCategory === 'TABLE_NAME' && $upper[0] === '(' && substr($upper, -1) === ')') { $unparsed = $this->splitSQLIntoTokens($this->removeParenthesisFromStart($trim)); $processor = new CreateDefinitionProcessor(); $coldef = $processor->process($unparsed); $result['create-def'] = array('expr_type' => ExpressionType::BRACKET_EXPRESSION, 'base_expr' => $base_expr, 'sub_tree' => $coldef['create-def']); $expr = array(); $base_expr = ''; $currCategory = 'CREATE_DEF'; } break; case 'UNION': # TODO: this token starts and ends with parenthesis # and contains a list of table names (comma-separated) # split the token and add the list as subtree # we must change the DefaultProcessor $unparsed = $this->splitSQLIntoTokens($this->removeParenthesisFromStart($trim)); $expr[] = array('expr_type' => ExpressionType::BRACKET_EXPRESSION, 'base_expr' => $trim, 'sub_tree' => '***TODO***'); $result['options'][] = array('expr_type' => ExpressionType::UNION, 'base_expr' => trim($base_expr), 'delim' => ' ', 'sub_tree' => $expr); $this->clear($expr, $base_expr, $currCategory); break; default: # strings and numeric constants $expr[] = $this->getConstantType($trim); $result['options'][] = array('expr_type' => ExpressionType::EXPRESSION, 'base_expr' => trim($base_expr), 'delim' => ' ', 'sub_tree' => $expr); $this->clear($expr, $base_expr, $currCategory); break; } break; } $prevCategory = $currCategory; $currCategory = ""; } if ($result['like'] === false) { unset($result['like']); } if ($result['select-option'] === false) { unset($result['select-option']); } return $result; }
protected function processCreateDefinition($tokens) { $processor = new CreateDefinitionProcessor(); return $processor->process($tokens); }