<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------+
// | Copyright (c) 2003-2004 John Griffin                                 |
// +----------------------------------------------------------------------+
// | This library is free software; you can redistribute it and/or        |
// | modify it under the terms of the GNU Lesser General Public           |
// | License as published by the Free Software Foundation; either         |
// | version 2.1 of the License, or (at your option) any later version.   |
// |                                                                      |
// | This library is distributed in the hope that it will be useful,      |
// | but WITHOUT ANY WARRANTY; without even the implied warranty of       |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    |
// | Lesser General Public License for more details.                      |
// |                                                                      |
// | You should have received a copy of the GNU Lesser General Public     |
// | License along with this library; if not, write to the Free Software  |
// | Foundation, Inc., 59 Temple Place, Suite 330,Boston,MA 02111-1307 USA|
// +----------------------------------------------------------------------+
// | Authors: John Griffin <jgriffin316@netscape.net>                     |
// +----------------------------------------------------------------------+
//
// $Id: Compiler.php,v 1.1 2004/05/07 12:33:35 busterb Exp $
//

require_once PEAR_DIR . 'PEAR.php';

/**
 * A SQL parse tree compiler.
 *
 * @author  John Griffin <jgriffin316@netscape.net>
 * @version 0.1
 * @access  public
 * @package SQL_Parser
 */
class SQL_Compiler {
    var $tree;

// {{{ function SQL_Compiler($array = null)
    function SQL_Compiler($array = null)
    {
        $this->tree = $array;
    }
// }}}

//    {{{ function getWhereValue ($arg)
    function getWhereValue ($arg)
    {
        switch ($arg['type']) {
            case 'ident':
            case 'real_val':
            case 'int_val':
                $value = $arg['value'];
                break;
            case 'text_val':
                $value = '\''.$arg['value'].'\'';
                break;
            case 'subclause':
                $value = '('.$this->compileSearchClause($arg['value']).')';
                break;
            default:
                return PEAR::raiseError('Unknown type: '.$arg['type']);
        }
        return $value;
    }
//    }}}

//    {{{ function getParams($arg)
    function getParams($arg)
    {
        for ($i = 0; $i < sizeof ($arg['type']); $i++) {
            switch ($arg['type'][$i]) {
                case 'ident':
                case 'real_val':
                case 'int_val':
                    $value[] = $arg['value'][$i];
                    break;
                case 'text_val':
                    $value[] = '\''.$arg['value'][$i].'\'';
                    break;
                default:
                    return PEAR::raiseError('Unknown type: '.$arg['type']);
            }
        }
        $value ='('.implode(', ', $value).')';
        return $value;
    }
//    }}}

//    {{{ function compileSearchClause
    function compileSearchClause($where_clause)
    {
        $value = '';
        if (isset ($where_clause['arg_1']['value'])) {
            $value = $this->getWhereValue ($where_clause['arg_1']);
            if (PEAR::isError($value)) {
                return $value;
            }
            $sql = $value;
        } else {
            $value = $this->compileSearchClause($where_clause['arg_1']);
            if (PEAR::isError($value)) {
                return $value;
            }
            $sql = $value;
        }
        if (isset ($where_clause['op'])) {
            if ($where_clause['op'] == 'in') {
                $value = $this->getParams($where_clause['arg_2']);
                if (PEAR::isError($value)) {
                    return $value;
                }
                $sql .= ' '.$where_clause['op'].' '.$value;
            } elseif ($where_clause['op'] == 'is') {
                if (isset ($where_clause['neg'])) {
                    $value = 'not null';
                } else {
                    $value = 'null';
                }
                $sql .= ' is '.$value;
            } else {
                $sql .= ' '.$where_clause['op'].' ';
                if (isset ($where_clause['arg_2']['value'])) {
                    $value = $this->getWhereValue ($where_clause['arg_2']);
                    if (PEAR::isError($value)) {
                        return $value;
                    }
                    $sql .= $value;
                } else {
                    $value = $this->compileSearchClause($where_clause['arg_2']);
                    if (PEAR::isError($value)) {
                        return $value;
                    }
                    $sql .= $value;
                }
            }
        }
        return $sql;
    }
//    }}}

//    {{{ function compileSelect()
    function compileSelect()
    {
        // save the command and set quantifiers
        $sql = 'select ';
        if (isset($this->tree['set_quantifier'])) {
            $sql .= $this->tree['set_quantifier'].' ';
        }
        
        // save the column names and set functions
        for ($i = 0; $i < sizeof ($this->tree['column_names']); $i++) {
            $column = $this->tree['column_names'][$i];
            if ($this->tree['column_aliases'][$i] != '') {
                $column .= ' as '.$this->tree['column_aliases'][$i];
            }
            $column_names[] = $column;
        }
        for ($i = 0; $i < sizeof ($this->tree['set_function']); $i++) {
            $column = $this->tree['set_function'][$i]['name'].'(';
            if (isset ($this->tree['set_function'][$i]['distinct'])) {
                $column .= 'distinct ';
            }
            if (isset ($this->tree['set_function'][$i]['arg'])) {
                $column .= implode (',', $this->tree['set_function'][$i]['arg']);
            }
            $column .= ')';
            if ($this->tree['set_function'][$i]['alias'] != '') {
                $column .= ' as '.$this->tree['set_function'][$i]['alias'];
            }
            $column_names[] = $column;
        }
        if (isset($column_names)) {
            $sql .= implode (", ", $column_names);
        }
        
        // save the tables
        $sql .= ' from ';
        for ($i = 0; $i < sizeof ($this->tree['table_names']); $i++) {
            $sql .= $this->tree['table_names'][$i];
            if ($this->tree['table_aliases'][$i] != '') {
                $sql .= ' as '.$this->tree['table_aliases'][$i];
            }
            if ($this->tree['table_join_clause'][$i] != '') {
                $search_string = $this->compileSearchClause ($this->tree['table_join_clause'][$i]);
                if (PEAR::isError($search_string)) {
                    return $search_string;
                }
                $sql .= ' on '.$search_string;
            }
            if (isset($this->tree['table_join'][$i])) {
                $sql .= ' '.$this->tree['table_join'][$i].' ';
            }
        }
        
        // save the where clause
        if (isset($this->tree['where_clause'])) {
            $search_string = $this->compileSearchClause ($this->tree['where_clause']);
            if (PEAR::isError($search_string)) {
                return $search_string;
            }
            $sql .= ' where '.$search_string;
        }
        
        // save the group by clause
        if (isset ($this->tree['group_by'])) {
            $sql .= ' group by '.implode(', ', $this->tree['group_by']);
        }

        // save the order by clause
        if (isset ($this->tree['sort_order'])) {
            foreach ($this->tree['sort_order'] as $key => $value) {
                $sort_order[] = $key.' '.$value;
            }
            $sql .= ' order by '.implode(', ', $sort_order);
        }
        
        // save the limit clause
        if (isset ($this->tree['limit_clause'])) {
            $sql .= ' limit '.$this->tree['limit_clause']['start'].','.$this->tree['limit_clause']['length'];
        }
        
        return $sql;
    }
//    }}}

//    {{{ function compileUpdate()
    function compileUpdate()
    {
        $sql = 'update '.implode(', ', $this->tree['table_names']);
        
        // save the set clause
        for ($i = 0; $i < sizeof ($this->tree['column_names']); $i++) {
            $set_columns[] = $this->tree['column_names'][$i].' = '.$this->getWhereValue($this->tree['values'][$i]);
        }
        $sql .= ' set '.implode (', ', $set_columns);
        
        // save the where clause
        if (isset($this->tree['where_clause'])) {
            $search_string = $this->compileSearchClause ($this->tree['where_clause']);
            if (PEAR::isError($search_string)) {
                return $search_string;
            }
            $sql .= ' where '.$search_string;
        }
        return $sql;
    }
//    }}}

//    {{{ function compileDelete()
    function compileDelete()
    {
        $sql = 'delete from '.implode(', ', $this->tree['table_names']);
        
        // save the where clause
        if (isset($this->tree['where_clause'])) {
            $search_string = $this->compileSearchClause ($this->tree['where_clause']);
            if (PEAR::isError($search_string)) {
                return $search_string;
            }
            $sql .= ' where '.$search_string;
        }
        return $sql;
    }
//    }}}

//    {{{ function compileInsert()
    function compileInsert()
    {
        $sql = 'insert into '.$this->tree['table_names'][0].' ('.
                implode(', ', $this->tree['column_names']).') values (';
        for ($i = 0; $i < sizeof ($this->tree['values']); $i++) {
            $value = $this->getWhereValue ($this->tree['values'][$i]);
            if (PEAR::isError($value)) {
                return $value;
            }
            $value_array[] = $value;
        }
        $sql .= implode(', ', $value_array).')';
        return $sql;
    }
//    }}}

//    {{{ function compile($array = null)
    function compile($array = null)
    {
        $this->tree = $array;
        
        switch ($this->tree['command']) {
            case 'select':
                return $this->compileSelect();
                break;
            case 'update':
                return $this->compileUpdate();
                break;
            case 'delete':
                return $this->compileDelete();
                break;
            case 'insert':
                return $this->compileInsert();
                break;
            case 'create':
            case 'drop':
            case 'modify':
            default:
                return PEAR::raiseError('Unknown action: '.$this->tree['command']);
        }    // switch ($this->_tree["command"])

    }
//    }}}
}
?>

