home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / SQL / Parser.php < prev   
Encoding:
PHP Script  |  2008-07-02  |  42.0 KB  |  1,122 lines

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3. // +----------------------------------------------------------------------+
  4. // | Copyright (c) 2002-2004 Brent Cook                                        |
  5. // +----------------------------------------------------------------------+
  6. // | This library is free software; you can redistribute it and/or        |
  7. // | modify it under the terms of the GNU Lesser General Public           |
  8. // | License as published by the Free Software Foundation; either         |
  9. // | version 2.1 of the License, or (at your option) any later version.   |
  10. // |                                                                      |
  11. // | This library is distributed in the hope that it will be useful,      |
  12. // | but WITHOUT ANY WARRANTY; without even the implied warranty of       |
  13. // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    |
  14. // | Lesser General Public License for more details.                      |
  15. // |                                                                      |
  16. // | You should have received a copy of the GNU Lesser General Public     |
  17. // | License along with this library; if not, write to the Free Software  |
  18. // | Foundation, Inc., 59 Temple Place, Suite 330,Boston,MA 02111-1307 USA|
  19. // +----------------------------------------------------------------------+
  20. // | Authors: Brent Cook <busterbcook@yahoo.com>                          |
  21. // |          Jason Pell <jasonpell@hotmail.com>                          |
  22. // |          Lauren Matheson <inan@canada.com>                           |
  23. // |          John Griffin <jgriffin316@netscape.net>                     |
  24. // +----------------------------------------------------------------------+
  25. //
  26. // $Id: Parser.php,v 1.23 2004/05/11 05:09:02 busterb Exp $
  27. //
  28.  
  29. require_once 'PEAR.php';
  30. require_once 'SQL/Lexer.php';
  31.  
  32. /**
  33.  * A sql parser
  34.  *
  35.  * @author  Brent Cook <busterbcook@yahoo.com>
  36.  * @version 0.5
  37.  * @access  public
  38.  * @package SQL_Parser
  39.  */
  40. class SQL_Parser
  41. {
  42.     var $lexer;
  43.     var $token;
  44.  
  45. // symbol definitions
  46.     var $functions = array();
  47.     var $types = array();
  48.     var $symbols = array();
  49.     var $operators = array();
  50.     var $synonyms = array();
  51.  
  52.     var $dialects = array("ANSI", "MySQL");
  53.  
  54. // {{{ function SQL_Parser($string = null)
  55.     function SQL_Parser($string = null, $dialect = "ANSI") {
  56.         $this->setDialect($dialect);
  57.  
  58.         if (is_string($string)) {
  59.             $this->lexer = new Lexer($string, 1);
  60.             $this->lexer->symbols =& $this->symbols;
  61.         }
  62.     }
  63. // }}}
  64.  
  65. // {{{ function setDialect($dialect)
  66.     function setDialect($dialect) {
  67.         if (in_array($dialect, $this->dialects)) {
  68.             include 'SQL/Dialect_'.$dialect.'.php';
  69.             $this->types = array_flip($dialect['types']);
  70.             $this->functions = array_flip($dialect['functions']);
  71.             $this->operators = array_flip($dialect['operators']);
  72.             $this->commands = array_flip($dialect['commands']);
  73.             $this->synonyms = $dialect['synonyms'];
  74.             $this->symbols = array_merge(
  75.                 $this->types,
  76.                 $this->functions,
  77.                 $this->operators,
  78.                 $this->commands,
  79.                 array_flip($dialect['reserved']),
  80.                 array_flip($dialect['conjunctions']));
  81.         } else {
  82.             return $this->raiseError('Unknown SQL dialect:'.$dialect);
  83.         }
  84.     }
  85. // }}}
  86.  
  87. // {{{ getParams(&$values, &$types)
  88.     function getParams(&$values, &$types) {
  89.         $values = array();
  90.         $types = array();
  91.         while ($this->token != ')') {
  92.             $this->getTok();
  93.             if ($this->isVal() || ($this->token == 'ident')) {
  94.                 $values[] = $this->lexer->tokText;
  95.                 $types[] = $this->token;
  96.             } elseif ($this->token == ')') {
  97.                 return false;
  98.             } else {
  99.                 return $this->raiseError('Expected a value');
  100.             }
  101.             $this->getTok();
  102.             if (($this->token != ',') && ($this->token != ')')) {
  103.                 return $this->raiseError('Expected , or )');
  104.             }
  105.         }
  106.     }
  107. // }}}
  108.  
  109.     // {{{ raiseError($message)
  110.     function raiseError($message) {
  111.         $end = 0;
  112.         if ($this->lexer->string != '') {
  113.             while (($this->lexer->lineBegin+$end < $this->lexer->stringLen)
  114.                && ($this->lexer->string{$this->lexer->lineBegin+$end} != "\n")){
  115.                 ++$end;
  116.             }
  117.         }
  118.         
  119.         $message = 'Parse error: '.$message.' on line '.
  120.             ($this->lexer->lineNo+1)."\n";
  121.         $message .= substr($this->lexer->string, $this->lexer->lineBegin, $end)."\n";
  122.         $length = is_null($this->token) ? 0 : strlen($this->lexer->tokText);
  123.         $message .= str_repeat(' ', abs($this->lexer->tokPtr - 
  124.                                $this->lexer->lineBegin - $length))."^";
  125.         $message .= ' found: "'.$this->lexer->tokText.'"';
  126.  
  127.         return PEAR::raiseError($message);
  128.     }
  129.     // }}}
  130.  
  131.     // {{{ isType()
  132.     function isType() {
  133.         return isset($this->types[$this->token]);
  134.     }
  135.     // }}}
  136.  
  137.     // {{{ isVal()
  138.     function isVal() {
  139.        return (($this->token == 'real_val') ||
  140.                ($this->token == 'int_val') ||
  141.                ($this->token == 'text_val') ||
  142.                ($this->token == 'null'));
  143.     }
  144.     // }}}
  145.  
  146.     // {{{ isFunc()
  147.     function isFunc() {
  148.         return isset($this->functions[$this->token]);
  149.     }
  150.     // }}}
  151.  
  152.     // {{{ isCommand()
  153.     function isCommand() {
  154.         return isset($this->commands[$this->token]);
  155.     }
  156.     // }}}
  157.  
  158.     // {{{ isReserved()
  159.     function isReserved() {
  160.         return isset($this->symbols[$this->token]);
  161.     }
  162.     // }}}
  163.  
  164.     // {{{ isOperator()
  165.     function isOperator() {
  166.         return isset($this->operators[$this->token]);
  167.     }
  168.     // }}}
  169.  
  170.     // {{{ getTok()
  171.     function getTok() {
  172.         $this->token = $this->lexer->lex();
  173.         //echo $this->token."\t".$this->lexer->tokText."\n";
  174.     }
  175.     // }}}
  176.  
  177.     // {{{ &parseFieldOptions()
  178.     function parseFieldOptions()
  179.     {
  180.         // parse field options
  181.         $namedConstraint = false;
  182.         $options = array();
  183.         while (($this->token != ',') && ($this->token != ')') &&
  184.                 ($this->token != null)) {
  185.             $option = $this->token;
  186.             $haveValue = true;
  187.             switch ($option) {
  188.                 case 'constraint':
  189.                     $this->getTok();
  190.                     if ($this->token = 'ident') {
  191.                         $constraintName = $this->lexer->tokText;
  192.                         $namedConstraint = true;
  193.                         $haveValue = false;
  194.                     } else {
  195.                         return $this->raiseError('Expected a constraint name');
  196.                     }
  197.                     break;
  198.                 case 'default':
  199.                     $this->getTok();
  200.                     if ($this->isVal()) {
  201.                         $constraintOpts = array('type'=>'default_value',
  202.                                                 'value'=>$this->lexer->tokText);
  203.                     } elseif ($this->isFunc()) {
  204.                         $results = $this->parseFunctionOpts();
  205.                         if (PEAR::isError($results)) {
  206.                             return $results;
  207.                         }
  208.                         $results['type'] = 'default_function';
  209.                         $constraintOpts = $results;
  210.                     } else {
  211.                         return $this->raiseError('Expected default value');
  212.                     }
  213.                     break;
  214.                 case 'primary':
  215.                     $this->getTok();
  216.                     if ($this->token == 'key') {
  217.                         $constraintOpts = array('type'=>'primary_key',
  218.                                                 'value'=>true);
  219.                     } else {
  220.                         return $this->raiseError('Expected "key"');
  221.                     }
  222.                     break;
  223.                 case 'not':
  224.                     $this->getTok();
  225.                     if ($this->token == 'null') {
  226.                         $constraintOpts = array('type'=>'not_null',
  227.                                                 'value' => true);
  228.                     } else {
  229.                         return $this->raiseError('Expected "null"');
  230.                     }
  231.                     break;
  232.                 case 'check':
  233.                     $this->getTok();
  234.                     if ($this->token != '(') {
  235.                         return $this->raiseError('Expected (');
  236.                     }
  237.                     $results = $this->parseSearchClause();
  238.                     if (PEAR::isError($results)) {
  239.                         return $results;
  240.                     }
  241.                     $results['type'] = 'check';
  242.                     $constraintOpts = $results;
  243.                     if ($this->token != ')') {
  244.                         return $this->raiseError('Expected )');
  245.                     }
  246.                     break;
  247.                 case 'unique':
  248.                     $this->getTok();
  249.                     if ($this->token != '(') {
  250.                         return $this->raiseError('Expected (');
  251.                     }
  252.                     $constraintOpts = array('type'=>'unique');
  253.                     $this->getTok();
  254.                     while ($this->token != ')') {
  255.                         if ($this->token != 'ident') {
  256.                             return $this->raiseError('Expected an identifier');
  257.                         }
  258.                         $constraintOpts['column_names'][] = $this->lexer->tokText;
  259.                         $this->getTok();
  260.                         if (($this->token != ')') && ($this->token != ',')) {
  261.                             return $this->raiseError('Expected ) or ,');
  262.                         }
  263.                     }
  264.                     if ($this->token != ')') {
  265.                         return $this->raiseError('Expected )');
  266.                     }
  267.                     break;
  268.                 case 'month': case 'year': case 'day': case 'hour':
  269.                 case 'minute': case 'second':
  270.                     $intervals = array(
  271.                                     array('month'=>0,
  272.                                           'year'=>1),
  273.                                     array('second'=>0,
  274.                                           'minute'=>1,
  275.                                           'hour'=>2,
  276.                                           'day'=>3));
  277.                     foreach ($intervals as $class) {
  278.                         if (isset($class[$option])) {
  279.                             $constraintOpts = array('quantum_1'=>$this->token);
  280.                             $this->getTok();
  281.                             if ($this->token == 'to') {
  282.                                 $this->getTok();
  283.                                 if (!isset($class[$this->token])) {
  284.                                     return $this->raiseError(
  285.                                         'Expected interval quanta');
  286.                                 }
  287.                                 if ($class[$this->token] >=
  288.                                     $class[$constraintOpts['quantum_1']]) {
  289.                                     return $this->raiseError($this->token.
  290.                                         ' is not smaller than '.
  291.                                         $constraintOpts['quantum_1']);
  292.                                 } 
  293.                                 $constraintOpts['quantum_2'] = $this->token;
  294.                             } else {
  295.                                 $this->lexer->unget();
  296.                             }
  297.                             break;
  298.                         }
  299.                     }
  300.                     if (!isset($constraintOpts['quantum_1'])) {
  301.                         return $this->raiseError('Expected interval quanta');
  302.                     }
  303.                     $constraintOpts['type'] = 'values';
  304.                     break;
  305.                 case 'null':
  306.                     $haveValue = false;
  307.                     break;
  308.                 default:
  309.                     return $this->raiseError('Unexpected token '
  310.                                         .$this->lexer->tokText);
  311.             }
  312.             if ($haveValue) {
  313.                 if ($namedConstraint) {
  314.                     $options['constraints'][$constraintName] = $constraintOpts;
  315.                     $namedConstraint = false;
  316.                 } else {
  317.                     $options['constraints'][] = $constraintOpts;
  318.                 }
  319.             }
  320.             $this->getTok();
  321.         }
  322.         return $options;
  323.     }
  324.     // }}}
  325.  
  326.     // {{{ parseSearchClause()
  327.     function parseSearchClause($subSearch = false)
  328.     {
  329.         $clause = array();
  330.         // parse the first argument
  331.         $this->getTok();
  332.         if ($this->token == 'not') {
  333.             $clause['neg'] = true;
  334.             $this->getTok();
  335.         }
  336.  
  337.         $foundSubclause = false;
  338.         if ($this->token == '(') {
  339.             $clause['arg_1']['value'] = $this->parseSearchClause(true);
  340.             $clause['arg_1']['type'] = 'subclause';
  341.             if ($this->token != ')') {
  342.                 return $this->raiseError('Expected ")"');
  343.             }
  344.             $foundSubclause = true;
  345.         } else if ($this->isReserved()) {
  346.             return $this->raiseError('Expected a column name or value');
  347.         } else {
  348.             $clause['arg_1']['value'] = $this->lexer->tokText;
  349.             $clause['arg_1']['type'] = $this->token;
  350.         }
  351.  
  352.         // parse the operator
  353.         if (!$foundSubclause) {
  354.             $this->getTok();
  355.             if (!$this->isOperator()) {
  356.                 return $this->raiseError('Expected an operator');
  357.             }
  358.             $clause['op'] = $this->token;
  359.  
  360.             $this->getTok();
  361.             switch ($clause['op']) {
  362.                 case 'is':
  363.                     // parse for 'is' operator
  364.                     if ($this->token == 'not') {
  365.                         $clause['neg'] = true;
  366.                         $this->getTok();
  367.                     }
  368.                     if ($this->token != 'null') {
  369.                         return $this->raiseError('Expected "null"');
  370.                     }
  371.                     $clause['arg_2']['value'] = '';
  372.                     $clause['arg_2']['type'] = $this->token;
  373.                     break;
  374.                 case 'not':
  375.                     // parse for 'not in' operator
  376.                     if ($this->token != 'in') {
  377.                         return $this->raiseError('Expected "in"');
  378.                     }
  379.                     $clause['op'] = $this->token;
  380.                     $clause['neg'] = true;
  381.                     $this->getTok();
  382.                 case 'in':
  383.                     // parse for 'in' operator 
  384.                     if ($this->token != '(') {
  385.                         return $this->raiseError('Expected "("');
  386.                     }
  387.  
  388.                     // read the subset
  389.                     $this->getTok();
  390.                     // is this a subselect?
  391.                     if ($this->token == 'select') {
  392.                         $clause['arg_2']['value'] = $this->parseSelect(true);
  393.                         $clause['arg_2']['type'] = 'command';
  394.                     } else {
  395.                         $this->lexer->pushBack();
  396.                         // parse the set
  397.                         $result = $this->getParams($clause['arg_2']['value'],
  398.                                                 $clause['arg_2']['type']);
  399.                         if (PEAR::isError($result)) {
  400.                             return $result;
  401.                         }
  402.                     }
  403.                     if ($this->token != ')') {
  404.                         return $this->raiseError('Expected ")"');
  405.                     }
  406.                     break;
  407.                 case 'and': case 'or':
  408.                     $this->lexer->unget();
  409.                     break;
  410.                 default:
  411.                     // parse for in-fix binary operators
  412.                     if ($this->isReserved()) {
  413.                         return $this->raiseError('Expected a column name or value');
  414.                     }
  415.                     if ($this->token == '(') {
  416.                         $clause['arg_2']['value'] = $this->parseSearchClause(true);
  417.                         $clause['arg_2']['type'] = 'subclause';
  418.                         $this->getTok();
  419.                         if ($this->token != ')') {
  420.                             return $this->raiseError('Expected ")"');
  421.                         }
  422.                     } else {
  423.                         $clause['arg_2']['value'] = $this->lexer->tokText;
  424.                         $clause['arg_2']['type'] = $this->token;
  425.                     }
  426.             }
  427.         }
  428.  
  429.         $this->getTok();
  430.         if (($this->token == 'and') || ($this->token == 'or')) {
  431.             $op = $this->token;
  432.             $subClause = $this->parseSearchClause($subSearch);
  433.             if (PEAR::isError($subClause)) {
  434.                 return $subClause;
  435.             } else {
  436.                 $clause = array('arg_1' => $clause,
  437.                                 'op' => $op,
  438.                                 'arg_2' => $subClause);
  439.             }
  440.         } else {
  441.             $this->lexer->unget();
  442.         }
  443.         return $clause;
  444.     }
  445.     // }}}
  446.  
  447.     // {{{ parseFieldList()
  448.     function parseFieldList()
  449.     {
  450.         $this->getTok();
  451.         if ($this->token != '(') {
  452.             return $this->raiseError('Expected (');
  453.         }
  454.  
  455.         $fields = array();
  456.         while (1) {
  457.             // parse field identifier
  458.             $this->getTok();
  459.             if ($this->token == 'ident') {
  460.                 $name = $this->lexer->tokText;
  461.             } elseif ($this->token == ')') {
  462.                 return $fields;
  463.             } else {
  464.                 return $this->raiseError('Expected identifier');
  465.             }
  466.  
  467.             // parse field type
  468.             $this->getTok();
  469.             if ($this->isType($this->token)) {
  470.                 $type = $this->token;
  471.             } else {
  472.                 return $this->raiseError('Expected a valid type');
  473.             }
  474.  
  475.             $this->getTok();
  476.             // handle special case two-word types
  477.             if ($this->token == 'precision') {
  478.                 // double precision == double
  479.                 if ($type == 'double') {
  480.                     return $this->raiseError('Unexpected token');
  481.                 }
  482.                 $this->getTok();
  483.             } elseif ($this->token == 'varying') {
  484.                 // character varying() == varchar()
  485.                 if ($type == 'character') {
  486.                     $type == 'varchar';
  487.                     $this->getTok();
  488.                 } else {
  489.                     return $this->raiseError('Unexpected token');
  490.                 }
  491.             }
  492.             $fields[$name]['type'] = $this->synonyms[$type];
  493.             // parse type parameters
  494.             if ($this->token == '(') {
  495.                 $results = $this->getParams($values, $types);
  496.                 if (PEAR::isError($results)) {
  497.                     return $results;
  498.                 }
  499.                 switch ($fields[$name]['type']) {
  500.                     case 'numeric':
  501.                         if (isset($values[1])) {
  502.                             if ($types[1] != 'int_val') {
  503.                                 return $this->raiseError('Expected an integer');
  504.                             }
  505.                             $fields[$name]['decimals'] = $values[1];
  506.                         }
  507.                     case 'float':
  508.                         if ($types[0] != 'int_val') {
  509.                             return $this->raiseError('Expected an integer');
  510.                         }
  511.                         $fields[$name]['length'] = $values[0];
  512.                         break;
  513.                     case 'char': case 'varchar':
  514.                     case 'integer': case 'int':
  515.                         if (sizeof($values) != 1) {
  516.                             return $this->raiseError('Expected 1 parameter');
  517.                         }
  518.                         if ($types[0] != 'int_val') {
  519.                             return $this->raiseError('Expected an integer');
  520.                         }
  521.                         $fields[$name]['length'] = $values[0];
  522.                         break;
  523.                     case 'set': case 'enum':
  524.                         if (!sizeof($values)) {
  525.                             return $this->raiseError('Expected a domain');
  526.                         }
  527.                         $fields[$name]['domain'] = $values;
  528.                         break;
  529.                     default:
  530.                         if (sizeof($values)) {
  531.                             return $this->raiseError('Unexpected )');
  532.                         }
  533.                 }
  534.                 $this->getTok();
  535.             }
  536.  
  537.             $options = $this->parseFieldOptions();
  538.             if (PEAR::isError($options)) {
  539.                 return $options;
  540.             }
  541.  
  542.             $fields[$name] += $options;
  543.  
  544.             if ($this->token == ')') {
  545.                 return $fields;
  546.             } elseif (is_null($this->token)) {
  547.                 return $this->raiseError('Expected )');
  548.             }
  549.         }
  550.     }
  551.     // }}}
  552.  
  553.     // {{{ parseFunctionOpts()
  554.     function parseFunctionOpts()
  555.     {
  556.         $function = $this->token;
  557.         $opts['name'] = $function;
  558.         $this->getTok();
  559.         if ($this->token != '(') {
  560.             return $this->raiseError('Expected "("');
  561.         }
  562.         switch ($function) {
  563.             case 'count':
  564.                 $this->getTok();
  565.                 switch ($this->token) {
  566.                     case 'distinct':
  567.                         $opts['distinct'] = true;
  568.                         $this->getTok();
  569.                         if ($this->token != 'ident') {
  570.                             return $this->raiseError('Expected a column name');
  571.                         }
  572.                     case 'ident': case '*':
  573.                         $opts['arg'][] = $this->lexer->tokText;
  574.                         break;
  575.                     default:
  576.                         return $this->raiseError('Invalid argument');
  577.                 }
  578.                 break;
  579.             case 'concat':
  580.                 $this->getTok();
  581.                 while ($this->token != ')') {
  582.                     switch ($this->token) {
  583.                         case 'ident': case 'text_val':
  584.                             $opts['arg'][] = $this->lexer->tokText;
  585.                             break;
  586.                         case ',':
  587.                             // do nothing
  588.                             break;
  589.                         default:
  590.                             return $this->raiseError('Expected a string or a column name');
  591.                     }
  592.                     $this->getTok();
  593.                 }
  594.                 $this->lexer->pushBack();
  595.                 break;
  596.             case 'avg': case 'min': case 'max': case 'sum':
  597.             default:
  598.                 $this->getTok();
  599.                 $opts['arg'] = $this->lexer->tokText;
  600.                 break;
  601.         }
  602.         $this->getTok();
  603.         if ($this->token != ')') {
  604.             return $this->raiseError('Expected ")"');
  605.         }
  606.  
  607.         // check for an alias
  608.         $this->getTok();
  609.         if ($this->token == ',' || $this->token == 'from') {
  610.             $this->lexer->pushBack();
  611.         } elseif ($this->token == 'as') {
  612.             $this->getTok();
  613.             if ($this->token == 'ident' ) {
  614.                 $opts['alias'] = $this->lexer->tokText;
  615.             } else {
  616.                 return $this->raiseError('Expected column alias');
  617.             }
  618.         } else {
  619.             if ($this->token == 'ident' ) {
  620.                 $opts['alias'] = $this->lexer->tokText;
  621.             } else {
  622.                 return $this->raiseError('Expected column alias, from or comma');
  623.             }
  624.         }
  625.         return $opts;
  626.     }
  627.     // }}}
  628.  
  629.     // {{{ parseCreate()
  630.     function parseCreate() {
  631.         $this->getTok();
  632.         switch ($this->token) {
  633.             case 'table':
  634.                 $tree = array('command' => 'create_table');
  635.                 $this->getTok();
  636.                 if ($this->token == 'ident') {
  637.                     $tree['table_names'][] = $this->lexer->tokText;
  638.                     $fields = $this->parseFieldList();
  639.                     if (PEAR::isError($fields)) {
  640.                         return $fields;
  641.                     }
  642.                     $tree['column_defs'] = $fields;
  643. //                    $tree['column_names'] = array_keys($fields);
  644.                 } else {
  645.                     return $this->raiseError('Expected table name');
  646.                 }
  647.                 break;
  648.             case 'index':
  649.                 $tree = array('command' => 'create_index');
  650.                 break;
  651.             case 'constraint':
  652.                 $tree = array('command' => 'create_constraint');
  653.                 break;
  654.             case 'sequence':
  655.                 $tree = array('command' => 'create_sequence');
  656.                 break;
  657.             default:
  658.                 return $this->raiseError('Unknown object to create');
  659.         }
  660.         return $tree;
  661.     }
  662.     // }}}
  663.  
  664.     // {{{ parseInsert()
  665.     function parseInsert() {
  666.         $this->getTok();
  667.         if ($this->token == 'into') {
  668.             $tree = array('command' => 'insert');
  669.             $this->getTok();
  670.             if ($this->token == 'ident') {
  671.                 $tree['table_names'][] = $this->lexer->tokText;
  672.                 $this->getTok();
  673.             } else {
  674.                 return $this->raiseError('Expected table name');
  675.             }
  676.             if ($this->token == '(') {
  677.                 $results = $this->getParams($values, $types);
  678.                 if (PEAR::isError($results)) {
  679.                     return $results;
  680.                 } else {
  681.                     if (sizeof($values)) {
  682.                         $tree['column_names'] = $values;
  683.                     }
  684.                 }
  685.                 $this->getTok();
  686.             }
  687.             if ($this->token == 'values') {
  688.                 $this->getTok();
  689.                 $results = $this->getParams($values, $types);
  690.                 if (PEAR::isError($results)) {
  691.                     return $results;
  692.                 } else {
  693.                     if (isset($tree['column_defs']) && 
  694.                         (sizeof($tree['column_defs']) != sizeof($values))) {
  695.                         return $this->raiseError('field/value mismatch');
  696.                     }
  697.                     if (sizeof($values)) {
  698.                         foreach ($values as $key=>$value) {
  699.                             $values[$key] = array('value'=>$value,
  700.                                                     'type'=>$types[$key]);
  701.                         }
  702.                         $tree['values'] = $values;
  703.                     } else {
  704.                         return $this->raiseError('No fields to insert');
  705.                     }
  706.                 }
  707.             } else {
  708.                 return $this->raiseError('Expected "values"');
  709.             }
  710.         } else {
  711.             return $this->raiseError('Expected "into"');
  712.         }
  713.         return $tree;
  714.     }
  715.     // }}}
  716.  
  717.     // {{{ parseUpdate()
  718.     function parseUpdate() {
  719.         $this->getTok();
  720.         if ($this->token == 'ident') {
  721.             $tree = array('command' => 'update');
  722.             $tree['table_names'][] = $this->lexer->tokText;
  723.         } else {
  724.             return $this->raiseError('Expected table name');
  725.         }
  726.         $this->getTok();
  727.         if ($this->token != 'set') {
  728.             return $this->raiseError('Expected "set"');
  729.         }
  730.         while (true) {
  731.             $this->getTok();
  732.             if ($this->token != 'ident') {
  733.                 return $this->raiseError('Expected a column name');
  734.             }
  735.             $tree['column_names'][] = $this->lexer->tokText;
  736.             $this->getTok();
  737.             if ($this->token != '=') {
  738.                 return $this->raiseError('Expected =');
  739.             }
  740.             $this->getTok();
  741.             if (!$this->isVal($this->token)) {
  742.                 return $this->raiseError('Expected a value');
  743.             }
  744.             $tree['values'][] = array('value'=>$this->lexer->tokText,
  745.                                       'type'=>$this->token);
  746.             $this->getTok();
  747.             if ($this->token == 'where') {
  748.                 $clause = $this->parseSearchClause();
  749.                 if (PEAR::isError($clause)) {
  750.                     return $clause;
  751.                 }
  752.                 $tree['where_clause'] = $clause;
  753.                 break;
  754.             } elseif ($this->token != ',') {
  755.                 return $this->raiseError('Expected "where" or ","');
  756.             }
  757.         }
  758.         return $tree;
  759.     }
  760.     // }}}
  761.  
  762.     // {{{ parseDelete()
  763.     function parseDelete() {
  764.         $this->getTok();
  765.         if ($this->token != 'from') {
  766.             return $this->raiseError('Expected "from"');
  767.         }
  768.         $tree = array('command' => 'delete');
  769.         $this->getTok();
  770.         if ($this->token != 'ident') {
  771.             return $this->raiseError('Expected a table name');
  772.         }
  773.         $tree['table_names'][] = $this->lexer->tokText;
  774.         $this->getTok();
  775.         if ($this->token != 'where') {
  776.             return $this->raiseError('Expected "where"');
  777.         }
  778.         $clause = $this->parseSearchClause();
  779.         if (PEAR::isError($clause)) {
  780.             return $clause;
  781.         }
  782.         $tree['where_clause'] = $clause;
  783.         return $tree;
  784.     }
  785.     // }}}
  786.  
  787.     // {{{ parseDrop()
  788.     function parseDrop() {
  789.         $this->getTok();
  790.         switch ($this->token) {
  791.             case 'table':
  792.                 $tree = array('command' => 'drop_table');
  793.                 $this->getTok();
  794.                 if ($this->token != 'ident') {
  795.                     return $this->raiseError('Expected a table name');
  796.                 }
  797.                 $tree['table_names'][] = $this->lexer->tokText;
  798.                 $this->getTok();
  799.                 if (($this->token == 'restrict') ||
  800.                     ($this->token == 'cascade')) {
  801.                     $tree['drop_behavior'] = $this->token;
  802.                 }
  803.                 $this->getTok();
  804.                 if (!is_null($this->token)) {
  805.                     return $this->raiseError('Unexpected token');
  806.                 }
  807.                 return $tree;
  808.                 break;
  809.             case 'index':
  810.                 $tree = array('command' => 'drop_index');
  811.                 break;
  812.             case 'constraint':
  813.                 $tree = array('command' => 'drop_constraint');
  814.                 break;
  815.             case 'sequence':
  816.                 $tree = array('command' => 'drop_sequence');
  817.                 break;
  818.             default:
  819.                 return $this->raiseError('Unknown object to drop');
  820.         }
  821.         return $tree;
  822.     }
  823.     // }}}
  824.  
  825.     // {{{ parseSelect()
  826.     function parseSelect($subSelect = false) {
  827.         $tree = array('command' => 'select');
  828.         $this->getTok();
  829.         if (($this->token == 'distinct') || ($this->token == 'all')) {
  830.             $tree['set_quantifier'] = $this->token;
  831.             $this->getTok();
  832.         }
  833.         if ($this->token == '*') {
  834.             $tree['column_names'][] = '*';
  835.             $this->getTok();
  836.         } elseif ($this->token == 'ident' || $this->isFunc()) {
  837.             while ($this->token != 'from') {
  838.                 if ($this->token == 'ident') {
  839.                     $prevTok = $this->token;
  840.                     $prevTokText = $this->lexer->tokText;
  841.                     $this->getTok();
  842.                     if ($this->token == '.') {
  843.                         $columnTable = $prevTokText;
  844.                         $this->getTok();
  845.                         $prevTok = $this->token;
  846.                         $prevTokText = $this->lexer->tokText;
  847.                     } else {
  848.                         $columnTable = '';
  849.                     }
  850.  
  851.                     if ($prevTok == 'ident') {
  852.                         $columnName = $prevTokText;
  853.                     } else {
  854.                         return $this->raiseError('Expected column name');
  855.                     }
  856.  
  857.                     if ($this->token == 'as') {
  858.                         $this->getTok();
  859.                         if ($this->token == 'ident' ) {
  860.                             $columnAlias = $this->lexer->tokText;
  861.                         } else {
  862.                             return $this->raiseError('Expected column alias');
  863.                         }
  864.                     } elseif ($this->token == 'ident') {
  865.                         $columnAlias = $this->lexer->tokText;
  866.                     } else {
  867.                         $columnAlias = '';
  868.                     }
  869.  
  870.                     $tree['column_tables'][] = $columnTable;
  871.                     $tree['column_names'][] = $columnName;
  872.                     $tree['column_aliases'][] = $columnAlias;
  873.                     if ($this->token != 'from') {
  874.                         $this->getTok();
  875.                     }
  876.                     if ($this->token == ',') {
  877.                         $this->getTok();
  878.                     }
  879.                 } elseif ($this->isFunc()) {
  880.                     if (!isset($tree['set_quantifier'])) {
  881.                         $result = $this->parseFunctionOpts();
  882.                         if (PEAR::isError($result)) {
  883.                             return $result;
  884.                         }
  885.                         $tree['set_function'][] = $result;
  886.                         $this->getTok();
  887.  
  888.                         if ($this->token == 'as') {
  889.                             $this->getTok();
  890.                             if ($this->token == 'ident' ) {
  891.                                 $columnAlias = $this->lexer->tokText;
  892.                             } else {
  893.                                 return $this->raiseError('Expected column alias');
  894.                             }
  895.                         } else {
  896.                             $columnAlias = '';
  897.                         }
  898.                     } else {
  899.                         return $this->raiseError('Cannot use "'.
  900.                                 $tree['set_quantifier'].'" with '.$this->token);
  901.                     }
  902.                 } elseif ($this->token == ',') {
  903.                     $this->getTok();
  904.                 } else {
  905.                         return $this->raiseError('Unexpected token "'.$this->token.'"');
  906.                 }
  907.             }
  908.         } else {
  909.             return $this->raiseError('Expected columns or a set function');
  910.         }
  911.         if ($this->token != 'from') {
  912.             return $this->raiseError('Expected "from"');
  913.         }
  914.         $this->getTok();
  915.         while ($this->token == 'ident') {
  916.             $tree['table_names'][] = $this->lexer->tokText;
  917.             $this->getTok();
  918.             if ($this->token == 'ident') {
  919.                 $tree['table_aliases'][] = $this->lexer->tokText;
  920.                 $this->getTok();
  921.             } elseif ($this->token == 'as') {
  922.                 $this->getTok();
  923.                 if ($this->token == 'ident') {
  924.                     $tree['table_aliases'][] = $this->lexer->tokText;
  925.                 } else {
  926.                     return $this->raiseError('Expected table alias');
  927.                 }
  928.                 $this->getTok();
  929.             } else {
  930.                 $tree['table_aliases'][] = '';
  931.             }
  932.             if ($this->token == 'on') {
  933.                 $clause = $this->parseSearchClause();
  934.                 if (PEAR::isError($clause)) {
  935.                     return $clause;
  936.                 }
  937.                 $tree['table_join_clause'][] = $clause;
  938.             } else {
  939.                 $tree['table_join_clause'][] = '';
  940.             }
  941.             if ($this->token == ',') {
  942.                 $tree['table_join'][] = ',';
  943.                 $this->getTok();
  944.             } elseif ($this->token == 'join') {
  945.                 $tree['table_join'][] = 'join';
  946.                 $this->getTok();
  947.             } elseif (($this->token == 'cross') ||
  948.                         ($this->token == 'inner')) {
  949.                 $join = $this->lexer->tokText;
  950.                 $this->getTok();
  951.                 if ($this->token != 'join') {
  952.                     return $this->raiseError('Expected token "join"');
  953.                 }
  954.                 $tree['table_join'][] = $join.' join';
  955.                 $this->getTok();
  956.             } elseif (($this->token == 'left') ||
  957.                         ($this->token == 'right')) {
  958.                 $join = $this->lexer->tokText;
  959.                 $this->getTok();
  960.                 if ($this->token == 'join') {
  961.                     $tree['table_join'][] = $join.' join';
  962.                 } elseif ($this->token == 'outer') {
  963.                         $join .= ' outer';
  964.                     $this->getTok();
  965.                     if ($this->token == 'join') {
  966.                         $tree['table_join'][] = $join.' join';
  967.                     } else {
  968.                         return $this->raiseError('Expected token "join"');
  969.                     }
  970.                 } else {
  971.                     return $this->raiseError('Expected token "outer" or "join"');
  972.                 }
  973.                 $this->getTok();
  974.             } elseif ($this->token == 'natural') {
  975.                 $join = $this->lexer->tokText;
  976.                 $this->getTok();
  977.                 if ($this->token == 'join') {
  978.                     $tree['table_join'][] = $join.' join';
  979.                 } elseif (($this->token == 'left') ||
  980.                             ($this->token == 'right')) {
  981.                         $join .= ' '.$this->token;
  982.                     $this->getTok();
  983.                     if ($this->token == 'join') {
  984.                         $tree['table_join'][] = $join.' join';
  985.                     } elseif ($this->token == 'outer') {
  986.                         $join .= ' '.$this->token;
  987.                         $this->getTok();
  988.                         if ($this->token == 'join') {
  989.                             $tree['table_join'][] = $join.' join';
  990.                         } else {
  991.                             return $this->raiseError('Expected token "join" or "outer"');
  992.                         }
  993.                     } else {
  994.                         return $this->raiseError('Expected token "join" or "outer"');
  995.                     }
  996.                 } else {
  997.                     return $this->raiseError('Expected token "left", "right" or "join"');
  998.                 }
  999.                 $this->getTok();
  1000.             } elseif (($this->token == 'where') ||
  1001.                         ($this->token == 'order') ||
  1002.                         ($this->token == 'limit') ||
  1003.                         (is_null($this->token))) {
  1004.                 break;
  1005.             }
  1006.         }
  1007.         while (!is_null($this->token) && (!$subSelect || $this->token != ')')
  1008.                && $this->token != ')') {
  1009.             switch ($this->token) {
  1010.                 case 'where':
  1011.                     $clause = $this->parseSearchClause();
  1012.                     if (PEAR::isError($clause)) {
  1013.                         return $clause;
  1014.                     }
  1015.                     $tree['where_clause'] = $clause;
  1016.                     break;
  1017.                 case 'order':
  1018.                     $this->getTok();
  1019.                     if ($this->token != 'by') {
  1020.                         return $this->raiseError('Expected "by"');
  1021.                     }
  1022.                     $this->getTok();
  1023.                     while ($this->token == 'ident') {
  1024.                         $col = $this->lexer->tokText;
  1025.                         $this->getTok();
  1026.                         if (isset($this->synonyms[$this->token])) {
  1027.                             $order = $this->synonyms[$this->token];
  1028.                             if (($order != 'asc') && ($order != 'desc')) {
  1029.                                 return $this->raiseError('Unexpected token');
  1030.                             }
  1031.                             $this->getTok();
  1032.                         } else {
  1033.                             $order = 'asc';
  1034.                         }
  1035.                         if ($this->token == ',') {
  1036.                             $this->getTok();
  1037.                         }
  1038.                         $tree['sort_order'][$col] = $order;
  1039.                     }
  1040.                     break;
  1041.                 case 'limit':
  1042.                     $this->getTok();
  1043.                     if ($this->token != 'int_val') {
  1044.                         return $this->raiseError('Expected an integer value');
  1045.                     }
  1046.                     $length = $this->lexer->tokText;
  1047.                     $start = 0;
  1048.                     $this->getTok();
  1049.                     if ($this->token == ',') {
  1050.                         $this->getTok();
  1051.                         if ($this->token != 'int_val') {
  1052.                             return $this->raiseError('Expected an integer value');
  1053.                         }
  1054.                         $start = $length;
  1055.                         $length = $this->lexer->tokText;
  1056.                         $this->getTok();
  1057.                     }
  1058.                     $tree['limit_clause'] = array('start'=>$start,
  1059.                                                   'length'=>$length);
  1060.                     break;
  1061.                 case 'group':
  1062.                     $this->getTok();
  1063.                     if ($this->token != 'by') {
  1064.                         return $this->raiseError('Expected "by"');
  1065.                     }
  1066.                     $this->getTok();
  1067.                     while ($this->token == 'ident') {
  1068.                         $col = $this->lexer->tokText;
  1069.                         $this->getTok();
  1070.                         if ($this->token == ',') {
  1071.                             $this->getTok();
  1072.                         }
  1073.                         $tree['group_by'][] = $col;
  1074.                     }
  1075.                     break;
  1076.                 default:
  1077.                     return $this->raiseError('Unexpected clause');
  1078.             }
  1079.         }
  1080.         return $tree;
  1081.     }
  1082.     // }}}
  1083.  
  1084.     // {{{ parse($string)
  1085.     function parse($string = null)
  1086.     {
  1087.         if (is_string($string)) {
  1088.             // Initialize the Lexer with a 3-level look-back buffer
  1089.             $this->lexer = new Lexer($string, 3);
  1090.             $this->lexer->symbols =& $this->symbols;
  1091.         } else {
  1092.             if (!is_object($this->lexer)) {
  1093.                 return $this->raiseError('No initial string specified');
  1094.             }
  1095.         }
  1096.  
  1097.         // get query action
  1098.         $this->getTok();
  1099.         switch ($this->token) {
  1100.             case null:
  1101.                 // null == end of string
  1102.                 return $this->raiseError('Nothing to do');
  1103.             case 'select':
  1104.                 return $this->parseSelect();
  1105.             case 'update':
  1106.                 return $this->parseUpdate();
  1107.             case 'insert':
  1108.                 return $this->parseInsert();
  1109.             case 'delete':
  1110.                 return $this->parseDelete();
  1111.             case 'create':
  1112.                 return $this->parseCreate();
  1113.             case 'drop':
  1114.                 return $this->parseDrop();
  1115.             default:
  1116.                 return $this->raiseError('Unknown action :'.$this->token);
  1117.         }
  1118.     }
  1119.     // }}}
  1120. }
  1121. ?>
  1122.