diff options
author | emkael <emkael@tlen.pl> | 2016-02-24 23:18:07 +0100 |
---|---|---|
committer | emkael <emkael@tlen.pl> | 2016-02-24 23:18:07 +0100 |
commit | 6f7fdef0f500cd4bb540affd3bc1482243f337c1 (patch) | |
tree | 4853eecd0769a903e6130c1896e1d070848150dd /lib/prado/framework/Data/Common/Oracle | |
parent | 61f2ea48a4e11cb5fb941b3783e19c9e9ef38a45 (diff) |
* Prado 3.3.0
Diffstat (limited to 'lib/prado/framework/Data/Common/Oracle')
4 files changed, 731 insertions, 0 deletions
diff --git a/lib/prado/framework/Data/Common/Oracle/TOracleCommandBuilder.php b/lib/prado/framework/Data/Common/Oracle/TOracleCommandBuilder.php new file mode 100644 index 0000000..ad4c9f4 --- /dev/null +++ b/lib/prado/framework/Data/Common/Oracle/TOracleCommandBuilder.php @@ -0,0 +1,153 @@ +<?php + +/** + * TOracleCommandBuilder class file. + * + * @author Marcos Nobre <marconobre[at]gmail[dot]com> + * @link https://github.com/pradosoft/prado + * @copyright Copyright © 2005-2015 The PRADO Group + * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT + * @package System.Data.Common + */ + +Prado :: using('System.Data.Common.TDbCommandBuilder'); + +/** + * TOracleCommandBuilder provides specifics methods to create limit/offset query commands + * for Oracle database. + * + * @author Marcos Nobre <marconobre[at]gmail[dot]com> + * @package System.Data.Common + * @since 3.1 + */ +class TOracleCommandBuilder extends TDbCommandBuilder { + + /** + * Overrides parent implementation. Only column of type text or character (and its variants) + * accepts the LIKE criteria. + * @param array list of column id for potential search condition. + * @param string string of keywords + * @return string SQL search condition matching on a set of columns. + */ + public function getSearchExpression($fields, $keywords) { + $columns = array (); + foreach ($fields as $field) { + if ($this->isSearchableColumn($this->getTableInfo()->getColumn($field))) + $columns[] = $field; + } + return parent :: getSearchExpression($columns, $keywords); + } + /** + * + * @return boolean true if column can be used for LIKE searching. + */ + protected function isSearchableColumn($column) { + $type = strtolower($column->getDbType()); + return $type === 'character varying' || $type === 'varchar2' || $type === 'character' || $type === 'char' || $type === 'text'; + } + + /** + * Overrides parent implementation to use PostgreSQL's ILIKE instead of LIKE (case-sensitive). + * @param string column name. + * @param array keywords + * @return string search condition for all words in one column. + */ + /* + * + * how Oracle don't implements ILIKE, this method won't be overrided + * + protected function getSearchCondition($column, $words) + { + $conditions=array(); + foreach($words as $word) + $conditions[] = $column.' LIKE '.$this->getDbConnection()->quoteString('%'.$word.'%'); + return '('.implode(' AND ', $conditions).')'; + } + */ + + /** + * Overrides parent implementation to use Oracle way of get paginated RecordSet instead of using LIMIT sql clause. + * @param string SQL query string. + * @param integer maximum number of rows, -1 to ignore limit. + * @param integer row offset, -1 to ignore offset. + * @return string SQL with limit and offset in Oracle way. + */ + public function applyLimitOffset($sql, $limit = -1, $offset = -1) { + if ((int) $limit <= 0 && (int) $offset <= 0) + return $sql; + + $pradoNUMLIN = 'pradoNUMLIN'; + $fieldsALIAS = 'xyz'; + + $nfimDaSQL = strlen($sql); + $nfimDoWhere = (strpos($sql, 'ORDER') !== false ? strpos($sql, 'ORDER') : $nfimDaSQL); + $niniDoSelect = strpos($sql, 'SELECT') + 6; + $nfimDoSelect = (strpos($sql, 'FROM') !== false ? strpos($sql, 'FROM') : $nfimDaSQL); + + $WhereInSubSelect=""; + if(strpos($sql, 'WHERE')!==false) + $WhereInSubSelect = "WHERE " .substr($sql, strpos($sql, 'WHERE')+5, $nfimDoWhere - $niniDoWhere); + + $sORDERBY = ''; + if (stripos($sql, 'ORDER') !== false) { + $p = stripos($sql, 'ORDER'); + $sORDERBY = substr($sql, $p +8); + } + + $fields = substr($sql, 0, $nfimDoSelect); + $fields = trim(substr($fields, $niniDoSelect)); + $aliasedFields = ', '; + + if (trim($fields) == '*') { + $aliasedFields = ", {$fieldsALIAS}.{$fields}"; + $fields = ''; + $arr = $this->getTableInfo()->getColumns(); + foreach ($arr as $field) { + $fields .= strtolower($field->getColumnName()) . ', '; + } + $fields = str_replace('"', '', $fields); + $fields = trim($fields); + $fields = substr($fields, 0, strlen($fields) - 1); + } else { + if (strpos($fields, ',') !== false) { + $arr = $this->getTableInfo()->getColumns(); + foreach ($arr as $field) { + $field = strtolower($field); + $existAS = str_ireplace(' as ', '-as-', $field); + if (strpos($existAS, '-as-') === false) + $aliasedFields .= "{$fieldsALIAS}." . trim($field) . ", "; + else + $aliasedFields .= "{$field}, "; + } + $aliasedFields = trim($aliasedFields); + $aliasedFields = substr($aliasedFields, 0, strlen($aliasedFields) - 1); + } + } + if ($aliasedFields == ', ') + $aliasedFields = " , $fieldsALIAS.* "; + + /* ************************ + $newSql = " SELECT $fields FROM ". + "( ". + " SELECT rownum as {$pradoNUMLIN} {$aliasedFields} FROM ". + " ($sql) {$fieldsALIAS} WHERE rownum <= {$limit} ". + ") WHERE {$pradoNUMLIN} >= {$offset} "; + + ************************* */ + $offset=(int)$offset; + $toReg = $offset + $limit ; + $fullTableName = $this->getTableInfo()->getTableFullName(); + if (empty ($sORDERBY)) + $sORDERBY="ROWNUM"; + + $newSql = " SELECT $fields FROM " . + "( " . + " SELECT ROW_NUMBER() OVER ( ORDER BY {$sORDERBY} ) -1 as {$pradoNUMLIN} {$aliasedFields} " . + " FROM {$fullTableName} {$fieldsALIAS} $WhereInSubSelect" . + ") nn " . + " WHERE nn.{$pradoNUMLIN} >= {$offset} AND nn.{$pradoNUMLIN} < {$toReg} "; + //echo $newSql."\n<br>\n"; + return $newSql; + } + +} diff --git a/lib/prado/framework/Data/Common/Oracle/TOracleMetaData.php b/lib/prado/framework/Data/Common/Oracle/TOracleMetaData.php new file mode 100644 index 0000000..7fd3d1f --- /dev/null +++ b/lib/prado/framework/Data/Common/Oracle/TOracleMetaData.php @@ -0,0 +1,374 @@ +<?php +/** + * TOracleMetaData class file. + * + * @author Marcos Nobre <marconobre[at]gmail[dot]com> + * @link https://github.com/pradosoft/prado + * @copyright Copyright © 2005-2015 The PRADO Group + * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT + * @package System.Data.Common.Oracle + */ + +/** + * Load the base TDbMetaData class. + */ +Prado::using('System.Data.Common.TDbMetaData'); +Prado::using('System.Data.Common.Oracle.TOracleTableInfo'); +Prado::using('System.Data.Common.Oracle.TOracleTableColumn'); + +/** + * TOracleMetaData loads Oracle database table and column information. + * + * @author Marcos Nobre <marconobre[at]gmail[dot]com> + * @package System.Data.Common.Oracle + * @since 3.1 + */ +class TOracleMetaData extends TDbMetaData +{ + private $_defaultSchema = 'system'; + + + /** + * @return string TDbTableInfo class name. + */ + protected function getTableInfoClass() + { + return 'TOracleTableInfo'; + } + + /** + * @param string default schema. + */ + public function setDefaultSchema($schema) + { + $this->_defaultSchema=$schema; + } + + /** + * @return string default schema. + */ + public function getDefaultSchema() + { + return $this->_defaultSchema; + } + + /** + * @param string table name with optional schema name prefix, uses default schema name prefix is not provided. + * @return array tuple as ($schemaName,$tableName) + */ + protected function getSchemaTableName($table) + { + if(count($parts= explode('.', str_replace('"','',$table))) > 1) + return array($parts[0], $parts[1]); + else + return array($this->getDefaultSchema(),$parts[0]); + } + + /** + * Get the column definitions for given table. + * @param string table name. + * @return TOracleTableInfo table information. + */ + protected function createTableInfo($table) + { + list($schemaName,$tableName) = $this->getSchemaTableName($table); + + // This query is made much more complex by the addition of the 'attisserial' field. + // The subquery to get that field checks to see if there is an internally dependent + // sequence on the field. + $sql = +<<<EOD + SELECT + a.COLUMN_ID, + LOWER(a.COLUMN_NAME) as attname, + a.DATA_TYPE || DECODE( a.DATA_TYPE, 'NUMBER', '('||a.DATA_PRECISION||','||DATA_SCALE||')' , '') as type, + a.DATA_LENGTH as atttypmod, + DECODE(a.NULLABLE, 'Y', '0', '1') as attnotnull, + DECODE(a.DEFAULT_LENGTH, NULL, '0', '1') as atthasdef, + DATA_DEFAULT as adsrc, + '0' AS attisserial + FROM + ALL_TAB_COLUMNS a + WHERE + TABLE_NAME = '{$tableName}' + AND OWNER = '{$schemaName}' + ORDER BY a.COLUMN_ID +EOD; + $this->getDbConnection()->setActive(true); + $this->getDbConnection()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + $command = $this->getDbConnection()->createCommand($sql); + //$command->bindValue(':table', $tableName); + //$command->bindValue(':schema', $schemaName); + $tableInfo = $this->createNewTableInfo($schemaName, $tableName); + $index=0; + foreach($command->query() as $col) + { + $col['index'] = $index++; + $this->processColumn($tableInfo, $col); + } + if($index===0) + throw new TDbException('dbmetadata_invalid_table_view', $table); + return $tableInfo; + } + + /** + * @param string table schema name + * @param string table name. + * @return TOracleTableInfo + */ + protected function createNewTableInfo($schemaName,$tableName) + { + $info['SchemaName'] = $this->assertIdentifier($schemaName); + $info['TableName'] = $this->assertIdentifier($tableName); + $info['IsView'] = false; + if($this->getIsView($schemaName,$tableName)) $info['IsView'] = true; + list($primary, $foreign) = $this->getConstraintKeys($schemaName, $tableName); + $class = $this->getTableInfoClass(); + return new $class($info,$primary,$foreign); + } + + /** + * @param string table name, schema name or column name. + * @return string a valid identifier. + * @throws TDbException when table name contains a double quote ("). + */ + protected function assertIdentifier($name) + { + if(strpos($name, '"')!==false) + { + $ref = 'http://www.oracle.com'; + throw new TDbException('dbcommon_invalid_identifier_name', $name, $ref); + } + return $name; + } + + /** + * @param string table schema name + * @param string table name. + * @return boolean true if the table is a view. + */ + protected function getIsView($schemaName,$tableName) + { + $this->getDbConnection()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + $sql = +<<<EOD + select OBJECT_TYPE + from ALL_OBJECTS + where OBJECT_NAME = '{$tableName}' + and OWNER = '{$schemaName}' +EOD; + $this->getDbConnection()->setActive(true); + $command = $this->getDbConnection()->createCommand($sql); + //$command->bindValue(':schema',$schemaName); + //$command->bindValue(':table', $tableName); + return intval($command->queryScalar() === 'VIEW'); + } + + /** + * @param TOracleTableInfo table information. + * @param array column information. + */ + protected function processColumn($tableInfo, $col) + { + $columnId = strtolower($col['attname']); //use column name as column Id + + //$info['ColumnName'] = '"'.$columnId.'"'; //quote the column names! + $info['ColumnName'] = $columnId; //NOT quote the column names! + $info['ColumnId'] = $columnId; + $info['ColumnIndex'] = $col['index']; + if(! (bool)$col['attnotnull'] ) $info['AllowNull'] = true; + if(in_array($columnId, $tableInfo->getPrimaryKeys())) $info['IsPrimaryKey'] = true; + if($this->isForeignKeyColumn($columnId, $tableInfo)) $info['IsForeignKey'] = true; + if( (int)$col['atttypmod'] > 0 ) $info['ColumnSize'] = $col['atttypmod']; // - 4; + if( (bool)$col['atthasdef'] ) $info['DefaultValue'] = $col['adsrc']; + // + // For a while Oracle Tables has no associated AutoIncrement Triggers + // + /* + if( $col['attisserial'] ) + { + if(($sequence = $this->getSequenceName($tableInfo, $col['adsrc']))!==null) + { + $info['SequenceName'] = $sequence; + unset($info['DefaultValue']); + } + } + */ + $matches = array(); + if(preg_match('/\((\d+)(?:,(\d+))?+\)/', $col['type'], $matches)) + { + $info['DbType'] = preg_replace('/\(\d+(?:,\d+)?\)/','',$col['type']); + if($this->isPrecisionType($info['DbType'])) + { + $info['NumericPrecision'] = intval($matches[1]); + if(count($matches) > 2) + $info['NumericScale'] = intval($matches[2]); + } + else + $info['ColumnSize'] = intval($matches[1]); + } + else + $info['DbType'] = $col['type']; + $tableInfo->Columns[$columnId] = new TOracleTableColumn($info); + } + + /** + * @return string serial name if found, null otherwise. + */ + protected function getSequenceName($tableInfo,$src) + { + $matches = array(); + if(preg_match('/nextval\([^\']*\'([^\']+)\'[^\)]*\)/i',$src,$matches)) + { + if(is_int(strpos($matches[1], '.'))) + return $matches[1]; + else + return $tableInfo->getSchemaName().'.'.$matches[1]; + } + } + + /** + * @return boolean true if column type if "numeric", "interval" or begins with "time". + */ + protected function isPrecisionType($type) + { + $type = strtolower(trim($type)); + return $type==='number'; // || $type==='interval' || strpos($type, 'time')===0; + } + + /** + * Gets the primary and foreign key column details for the given table. + * @param string schema name + * @param string table name. + * @return array tuple ($primary, $foreign) + */ + protected function getConstraintKeys($schemaName, $tableName) + { + $this->getDbConnection()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); +// select decode( a.CONSTRAINT_TYPE, 'P', 'PRIMARY KEY (', 'FOREIGN KEY (' )||b.COLUMN_NAME||')' as consrc, + $sql = +<<<EOD + select b.COLUMN_NAME as consrc, + a.CONSTRAINT_TYPE as contype + from ALL_CONSTRAINTS a, ALL_CONS_COLUMNS b + where (a.constraint_name = b.constraint_name AND a.table_name = b.table_name AND a.owner = b.owner) + and a.TABLE_NAME = '{$tableName}' + and a.OWNER = '{$schemaName}' + and a.CONSTRAINT_TYPE in ('P','R') +EOD; + $this->getDbConnection()->setActive(true); + $command = $this->getDbConnection()->createCommand($sql); + //$command->bindValue(':table', $tableName); + //$command->bindValue(':schema', $schemaName); + $primary = array(); + $foreign = array(); + foreach($command->query() as $row) + { + switch( strtolower( $row['contype'] ) ) + { + case 'p': + $primary = array_merge( $primary, array(strtolower( $row['consrc'] )) ); + /* + $arr = $this->getPrimaryKeys($row['consrc']); + $primary = array_merge( $primary, array(strtolower( $arr[0] )) ); + */ + break; + case 'r': + $foreign = array_merge( $foreign, array(strtolower( $row['consrc'] )) ); + /* + // if(($fkey = $this->getForeignKeys($row['consrc']))!==null) + $fkey = $this->getForeignKeys( $row['consrc'] ); + $foreign = array_merge( $foreign, array(strtolower( $fkey )) ); + */ + break; + } + } + return array($primary,$foreign); + } + + /** + * Gets the primary key field names + * @param string Oracle primary key definition + * @return array primary key field names. + */ + protected function getPrimaryKeys($src) + { + $matches = array(); + if(preg_match('/PRIMARY\s+KEY\s+\(([^\)]+)\)/i', $src, $matches)) + return preg_split('/,\s+/',$matches[1]); + return array(); + } + + /** + * Gets foreign relationship constraint keys and table name + * @param string Oracle foreign key definition + * @return array foreign relationship table name and keys, null otherwise + */ + protected function getForeignKeys($src) + { + $matches = array(); + $brackets = '\(([^\)]+)\)'; + $find = "/FOREIGN\s+KEY\s+{$brackets}\s+REFERENCES\s+([^\(]+){$brackets}/i"; + if(preg_match($find, $src, $matches)) + { + $keys = preg_split('/,\s+/', $matches[1]); + $fkeys = array(); + foreach(preg_split('/,\s+/', $matches[3]) as $i => $fkey) + $fkeys[$keys[$i]] = $fkey; + return array('table' => str_replace('"','',$matches[2]), 'keys' => $fkeys); + } + } + + /** + * @param string column name. + * @param TOracleTableInfo table information. + * @return boolean true if column is a foreign key. + */ + protected function isForeignKeyColumn($columnId, $tableInfo) + { + foreach($tableInfo->getForeignKeys() as $fk) + { + if( $fk==$columnId ) + //if(in_array($columnId, array_keys($fk['keys']))) + return true; + } + return false; + } + + /** + * Returns all table names in the database. + * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema. + * If not empty, the returned table names will be prefixed with the schema name. + * @return array all table names in the database. + */ + public function findTableNames($schema='') + { + if($schema==='') + { + $sql=<<<EOD +SELECT table_name, '{$schema}' as table_schema FROM user_tables +EOD; + $command=$this->getDbConnection()->createCommand($sql); + } + else + { + $sql=<<<EOD +SELECT object_name as table_name, owner as table_schema FROM all_objects +WHERE object_type = 'TABLE' AND owner=:schema +EOD; + $command=$this->getDbConnection()->createCommand($sql); + $command->bindParam(':schema',$schema); + } + + $rows=$command->queryAll(); + $names=array(); + foreach($rows as $row) + { + if($schema===$this->getDefaultSchema() || $schema==='') + $names[]=$row['TABLE_NAME']; + else + $names[]=$row['TABLE_SCHEMA'].'.'.$row['TABLE_NAME']; + } + return $names; + } +}
\ No newline at end of file diff --git a/lib/prado/framework/Data/Common/Oracle/TOracleTableColumn.php b/lib/prado/framework/Data/Common/Oracle/TOracleTableColumn.php new file mode 100644 index 0000000..31a5e6c --- /dev/null +++ b/lib/prado/framework/Data/Common/Oracle/TOracleTableColumn.php @@ -0,0 +1,48 @@ +<?php +/** + * TOracleTableColumn class file. + * + * @author Marcos Nobre <marconobre[at]gmail[dot]com> + * @link https://github.com/pradosoft/prado + * @copyright Copyright © 2005-2015 The PRADO Group + * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT + * @package System.Data.Common.Oracle + */ + +/** + * Load common TDbTableCommon class. + */ +Prado::using('System.Data.Common.TDbTableColumn'); + +/** + * Describes the column metadata of the schema for a PostgreSQL database table. + * + * @author Marcos Nobre <marconobre[at]gmail[dot]com> + * @package System.Data.Common.Oracle + * @since 3.1 + */ +class TOracleTableColumn extends TDbTableColumn +{ + private static $types=array( + 'numeric' => array( 'numeric' ) +// 'integer' => array('bit', 'bit varying', 'real', 'serial', 'int', 'integer'), +// 'boolean' => array('boolean'), +// 'float' => array('bigint', 'bigserial', 'double precision', 'money', 'numeric') + ); + + /** + * Overrides parent implementation, returns PHP type from the db type. + * @return boolean derived PHP primitive type from the column db type. + */ + public function getPHPType() + { + $dbtype = strtolower($this->getDbType()); + foreach(self::$types as $type => $dbtypes) + { + if(in_array($dbtype, $dbtypes)) + return $type; + } + return 'string'; + } +} + diff --git a/lib/prado/framework/Data/Common/Oracle/TOracleTableInfo.php b/lib/prado/framework/Data/Common/Oracle/TOracleTableInfo.php new file mode 100644 index 0000000..f226562 --- /dev/null +++ b/lib/prado/framework/Data/Common/Oracle/TOracleTableInfo.php @@ -0,0 +1,156 @@ +<?php + +/** + * TOracleTableInfo class file. + * + * @author Marcos Nobre <marconobre[at]gmail[dot]com> + * @link https://github.com/pradosoft/prado + * @copyright Copyright © 2005-2015 The PRADO Group + * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT + * @package System.Data.Common + */ + +/** + * TDbTableInfo class describes the meta data of a database table. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @package System.Data.Common + * @since 3.1 + */ +class TOracleTableInfo extends TComponent +{ + private $_info=array(); + + private $_primaryKeys; + private $_foreignKeys; + + private $_columns; + + private $_lowercase; + + /** + * Sets the database table meta data information. + * @param array table column information. + */ + public function __construct($tableInfo=array(),$primary=array(),$foreign=array()) + { + $this->_info=$tableInfo; + $this->_primaryKeys=$primary; + $this->_foreignKeys=$foreign; + $this->_columns=new TMap; + } + + /** + * @param TDbConnection database connection. + * @return TDbCommandBuilder new command builder + */ + public function createCommandBuilder($connection) + { + Prado::using('System.Data.Common.Oracle.TOracleCommandBuilder'); + return new TOracleCommandBuilder($connection,$this); + } + + /** + * @param string information array key name + * @param mixed default value if information array value is null + * @return mixed information array value. + */ + public function getInfo($name,$default=null) + { + return isset($this->_info[$name]) ? $this->_info[$name] : $default; + } + + /** + * @param string information array key name + * @param mixed new information array value. + */ + protected function setInfo($name,$value) + { + $this->_info[$name]=$value; + } + + /** + * @return string name of the table this column belongs to. + */ + public function getTableName() + { + return $this->getInfo('TableName'); + } + + /** + * @return string full name of the table, database dependent. + */ + public function getTableFullName() + { + return $this->_info['SchemaName'].'.'.$this->getTableName(); + } + + /** + * @return boolean whether the table is a view, default is false. + */ + public function getIsView() + { + return $this->getInfo('IsView',false); + } + + /** + * @return TMap TDbTableColumn column meta data. + */ + public function getColumns() + { + return $this->_columns; + } + + /** + * @param string column id + * @return TDbTableColumn column information. + */ + public function getColumn($name) + { + if(($column = $this->_columns->itemAt($name))!==null) + return $column; + throw new TDbException('dbtableinfo_invalid_column_name', $name, $this->getTableFullName()); + } + + /** + * @param array list of column Id, empty to get all columns. + * @return array table column names (identifier quoted) + */ + public function getColumnNames() + { + foreach($this->getColumns() as $column) + $names[] = $column->getColumnName(); + return $names; + } + + /** + * @return string[] names of primary key columns. + */ + public function getPrimaryKeys() + { + return $this->_primaryKeys; + } + + /** + * @return array tuples of foreign table and column name. + */ + public function getForeignKeys() + { + return $this->_foreignKeys; + } + + /** + * @return array lowercased column key names mapped to normal column ids. + */ + public function getLowerCaseColumnNames() + { + if($this->_lowercase===null) + { + $this->_lowercase=array(); + foreach($this->getColumns()->getKeys() as $key) + $this->_lowercase[strtolower($key)] = $key; + } + return $this->_lowercase; + } +} + |