diff --git a/Relational.php b/Relational.php index 4a3ff52..de52a3b 100644 --- a/Relational.php +++ b/Relational.php @@ -524,6 +524,122 @@ function select($tableName, $query='*', $rows=null) } // }}} + //{{{ &join($tableA,$tableB,$option=array()) + /** + * Makes a new table joining 2 existing tables + * + * @access public + * @param $tableA mixed table name or object to join + * @param $tableB mixed table name or object to join + * @param string $query query expression for performing the select + * @param $type string 'inner','outer','right','left' + * @param array $option join options + * @return object PEAR_Error on failure, DBA_TempTable on success + */ + function &joinTables($tableA, $tableB, $clause=true, $type='inner') + { + if (!DBA_TempTable::isTempTable($tableA)) { + // Open table + $result = $this->_openTable($tableA, 'r'); + if (PEAR::isError($result)) + return $result; + + // Makes a DBA_TempTable object + $objectA = new DBA_TempTable($this->_tables[$tableA], $option['alias'][0]); + } else { + $objectA = &$tableA; + } + + if (!DBA_TempTable::isTempTable($tableB)) { + // Open table + $result = $this->_openTable($tableB, 'r'); + if (PEAR::isError($result)) + return $result; + + // Makes a DBA_TempTable object + $objectB = new DBA_TempTable($this->_tables[$tableB], $option['alias'][1]); + } else { + $objectB = &$tableB; + } + + if (isset($option['on'])) { + $onJoin = $this->_parsePHPQuery($option['on']); + if (PEAR::isError($onJoin)) + return $onJoin; + } else { + $onJoin = 1; + } + if (isset($option['using'])) { + + //not supported yet... + } + + $PHPeval = ' + // get each row from each table object and make a new row + $keyA = $objectA->firstRow(); + while($keyA !== false) { + $rowA = $objectA->getRow($keyA); + $keyB = $objectB->firstRow(); + while($keyB !== false) { + $rowB = $objectB->getRow($keyB); + + //make a new row + $row = $rowA+$rowB; + + if ('.$onJoin.') { + $rows[] = $row; + } + $keyB = $objectB->nextRow(); + } + $keyA = $objectA->nextRow(); + } + + // If join type is outer + if ($type != "inner") { + // Makes blank rows; + $blankA = $objectA->blankRow(); + $blankB = $objectB->blankRow(); + + if ($type != "right") { + $keyA = $objectA->firstRow(); + while ($keyA !== false) { + $rowA = $objectA->getRow($keyA); + + $row = $rowA + $blankB; + + if ('.$onJoin.') { + $rows[] = $row; + } + $keyA = $objectA->nextRow(); + } + } + + if ($type != "left") { + $keyB = $objectB->firstRow(); + while ($keyB !== false) { + $rowB = $objectB->getRow($keyB); + + $row = $blankA + $rowB; + + if ('.$onJoin.') { + $rows[] = $row; + } + $keyB = $objectB->nextRow(); + } + } + } + '; + eval($PHPeval); + + // set rows to newly created object + $tblObject = new DBA_TempTable(); + $tblObject->set($rows); + + // return table object + return $tblObject; + } + // }}} + // {{{ join($tableA, $tableB, $rawQuery) /** * Joins rows between two tables based on a query. diff --git a/Table.php b/Table.php index 1bbeca8..9b4444a 100644 --- a/Table.php +++ b/Table.php @@ -40,6 +40,10 @@ function floatval( $strValue ) { '*','/','+','-','%',',')); } +if (!isset($_dba_keywords)) { + $_dba_keywords = array_flip(array('and','or','null','false','true')); +} + // {{{ constants /** * Reserved key used to store the schema record @@ -305,7 +309,7 @@ function create($tableName, $schema, $driver, $format='php') * Validates a DBA schema * * @access private - * @returns the validated schema, PEARError + * @returns the validated schema, PEAR_Error */ function _validateSchema($schema) { foreach ($schema as $field=>$meta) { @@ -329,6 +333,11 @@ function _validateSchema($schema) { ($meta['default'] != 'time()')) { return $this->raiseError($meta['default'].' is not a valid function'); } + if (!isset($meta['default_type'])) { + $meta['default_type'] = $meta['type']; + $meta['default'] = "\x00"; + } + $schema[$field] = $meta; } return $schema; @@ -709,7 +718,7 @@ function _packRow($data, &$key) } elseif (isset($fieldMeta['not_null'])) { return $this->raiseError("$fieldName cannot be null"); - } elseif (is_null($data[$fieldName])) { + } elseif (!isset($data[$fieldName]) || is_null($data[$fieldName])) { $c_value = "\x00"; // \x00 is the null value placeholder } else { @@ -724,7 +733,11 @@ function _packRow($data, &$key) $buffer[] = $c_value; ++$i; } - $key = implode(DBA_KEY_SEPARATOR, $key); + if (sizeof($key) > 1) { + $key = implode(DBA_KEY_SEPARATOR, $key); + } else { + $key = $key[0]; + } return implode(DBA_FIELD_SEPARATOR, $buffer); } // }}} @@ -785,7 +798,7 @@ function insert($data) // {{{ replace($rawQuery, $data, $rows=null) /** - * Replaces rows that match $rawQuery with $ + * Replaces rows that match $rawQuery with $data * * @access public * @param string $rawQuery query expression for performing the replace @@ -850,11 +863,11 @@ function replaceKey($key, $data) */ function remove($rawQuery, $rows=null) { - $rows =& $this->select($rawQuery, $rows); + $removableRows =& $this->select($rawQuery, $rows); if (PEAR::isError($rows)) { return $rows; } - foreach (array_keys($rows) as $rowKey) { + foreach (array_keys($removableRows) as $rowKey) { $result = $this->_dba->remove($rowKey); if (PEAR::isError($result)) { return $result; @@ -1006,40 +1019,42 @@ function getFieldNames() } // }}} + // {{{ function _cookToken($token, &$isField) + function _cookToken($token, &$isField) + { + global $_dba_functions, $_dba_keywords; + $isField = false; + + // a quoted string + if ($token{0} == '"' || $token{0} == "'" + || isset($_dba_keywords[$token])) { + return $token; + + // a function + } elseif (isset($_dba_functions[$token])) { + return 'DBA_Function::'.$token; + + // table field + } elseif (!is_numeric($token)) { + $isField = true; + return '$row[\''.$token.'\']'; + + // something else + } else { + return $token; + } + } + // }}} + // {{{ function _parsePHPQuery($rawQuery) - function _parsePHPQuery($rawQuery) + function _parsePHPQuery($rawQuery, &$fields) { global $_dba_functions, $_dba_operators; - if (!function_exists(_cookIdentToken)) { - - // {{{ function _cookIdentToken($token) - function _cookIdentToken($token) - { - global $_dba_functions; - - // quoted string - if ($token{0} == "'" || $token{0} == '"') { - $cookedToken .= $token; - } elseif ($token == 'null' || $token == 'and' || $token == 'or' || - $token == 'false' || $token == 'true' || - function_exists($token)) { - $cookedToken .= $token; - } elseif (isset($_dba_functions[$token])) { - $cookedToken .= 'DBA_Function::'.$token; - } elseif (!is_numeric($token)) { - $cookedToken .= '$row[\''.$token.'\']'; - } else { - $cookedToken .= $token; - } - return $cookedToken; - } - // }}} - } - $inQuote = false; $PHPQuery = ''; $token = ''; + $fields = array(); for($i=0; $i < strlen($rawQuery); $i++) { $c = $rawQuery{$i}; @@ -1054,7 +1069,10 @@ function_exists($token)) { $token .= $c; } elseif (isset($_dba_operators[$c]) || $c == ' ') { if (!$inQuote && strlen($token)) { - $PHPQuery .= _cookIdentToken($token); + $PHPQuery .= DBA_Table::_cookToken($token, $isField); + if ($isField) { + $fields[] = $token; + } $PHPQuery .= $c; $token = ''; } elseif ($inQuote) { @@ -1064,7 +1082,10 @@ function_exists($token)) { } } elseif ($c == "\t" || $c == "\n" || $c == "\r") { if (!$inQuote && strlen($token)) { - $PHPQuery .= _cookToken($token); + $PHPQuery .= DBA_Table::_cookToken($token, $isField); + if ($isField) { + $fields.push($token); + } $PHPQuery .= $c; $token = ''; } elseif ($inQuote) { @@ -1078,7 +1099,7 @@ function_exists($token)) { } if (strlen($token)) { - $PHPQuery .= _cookIdentToken($token); + $PHPQuery .= DBA_Table::_cookToken($token, $isField); } return $PHPQuery; } @@ -1113,12 +1134,21 @@ function &select($rawQuery, $rows=null) } // convert the query into a php statement - $PHPQuery = $this->_parsePHPQuery($rawQuery); + $PHPQuery = $this->_parsePHPQuery($rawQuery, $fields); + + // validate field names + foreach ($fields as $field) { + if (!$this->fieldExists($field)) { + return $this->raiseError($field . ' is not a field name'); + } + } + $PHPSelect = 'foreach ($rows as $key=>$row) if ('. $PHPQuery.') $results[$key] = $row;'; // perform the select - $results = null; + $results = array(); + echo $PHPSelect; eval ($PHPSelect); return $results; @@ -1246,9 +1276,11 @@ function project($fields, $rows) { if (is_array($rows)) { $projectFields = array(); - if (is_string($fields)) { + $projectRows = array(); + + if (is_string($fields)) { $projectFields = DBA_Table::_parseFieldString($fields, - reset($rows)); + reset($rows)); } else { if (is_array($fields)) { // we already have an array of fields diff --git a/TempTable.php b/TempTable.php index 62ce442..3c56b6f 100644 --- a/TempTable.php +++ b/TempTable.php @@ -34,7 +34,7 @@ class DBA_TempTable function DBA_TempTable($rows=null, $alias=null) { if (!is_null($rows) && is_array($rows)) - $this->setRows(&$rows, $alias); + $this->setRows($rows, $alias); } // }}} @@ -48,7 +48,7 @@ function isTempTable($tTable) // }}} // {{{ function setRows($rows, $alias=null) - function setRows($rows, $alias=null) + function setRows(&$rows, $alias=null) { if (is_array($rows)) { $v_rows = array(); diff --git a/Toolbox.php b/Toolbox.php index 5fe3f22..d88f270 100644 --- a/Toolbox.php +++ b/Toolbox.php @@ -105,6 +105,8 @@ function formatTextTable($rows, $fields = null, $style = 'oracle') { $corner = ($style == 'oracle') ? ' ' : '+'; $wall = ($style == 'oracle') ? ' ' : '|'; + $separator = ''; + $buffer = ''; if (is_array($rows) && sizeof($rows)) { diff --git a/tests/test_relational.php b/tests/test_relational.php index 00a106c..34f8aa3 100644 --- a/tests/test_relational.php +++ b/tests/test_relational.php @@ -68,6 +68,7 @@ '$db->select("nothere", "pigs == \'fly\'")', '$db->select("emp", "salary >= 1500")', +/* '$db->sort("empname", "a", $db->select("emp", "(job != \'analyst\') and (job != \'intern\')") )', @@ -103,6 +104,7 @@ ) ) )' +*/ ); $sql_queries = array(