summaryrefslogtreecommitdiff
path: root/framework
diff options
context:
space:
mode:
authorwei <>2007-04-12 08:05:03 +0000
committerwei <>2007-04-12 08:05:03 +0000
commitdc3bf922d9715bfd1b2105be04a9aabc84a1d7d4 (patch)
treeff97362f718b6ebb482128527402efe03fe1ac43 /framework
parent6178a99df0c722581bc61e35559ac5f8af1cc6e8 (diff)
Refactor and add TTableGateway, System.Data.Common, System.Data.DataGateway
Diffstat (limited to 'framework')
-rw-r--r--framework/Data/ActiveRecord/TActiveRecord.php27
-rw-r--r--framework/Data/ActiveRecord/TActiveRecordCriteria.php146
-rw-r--r--framework/Data/Common/Mysql/TMysqlMetaData.php251
-rw-r--r--framework/Data/Common/Mysql/TMysqlTableColumn.php69
-rw-r--r--framework/Data/Common/Mysql/TMysqlTableInfo.php46
-rw-r--r--framework/Data/Common/Pgsql/TPgsqlMetaData.php329
-rw-r--r--framework/Data/Common/Pgsql/TPgsqlTableColumn.php48
-rw-r--r--framework/Data/Common/Pgsql/TPgsqlTableInfo.php46
-rw-r--r--framework/Data/Common/TDbCommandBuilder.php320
-rw-r--r--framework/Data/Common/TDbMetaData.php104
-rw-r--r--framework/Data/Common/TDbTableColumn.php182
-rw-r--r--framework/Data/Common/TDbTableInfo.php167
-rw-r--r--framework/Exceptions/messages.txt11
13 files changed, 1582 insertions, 164 deletions
diff --git a/framework/Data/ActiveRecord/TActiveRecord.php b/framework/Data/ActiveRecord/TActiveRecord.php
index 109ae9a6..70fd7b23 100644
--- a/framework/Data/ActiveRecord/TActiveRecord.php
+++ b/framework/Data/ActiveRecord/TActiveRecord.php
@@ -250,10 +250,7 @@ abstract class TActiveRecord extends TComponent
if(is_string($criteria))
{
if(!is_array($parameters) && func_num_args() > 1)
- {
- $parameters = func_get_args();
- array_shift($parameters);
- }
+ $parameters = array_slice(func_get_args(),1);
$criteria=new TActiveRecordCriteria($criteria,$parameters);
}
$gateway = $this->getRecordManager()->getRecordGateway();
@@ -309,10 +306,7 @@ abstract class TActiveRecord extends TComponent
if(is_string($criteria))
{
if(!is_array($parameters) && func_num_args() > 1)
- {
- $parameters = func_get_args();
- array_shift($parameters);
- }
+ $parameters = array_slice(func_get_args(),1);
$criteria=new TActiveRecordCriteria($criteria,$parameters);
}
$gateway = $this->getRecordManager()->getRecordGateway();
@@ -332,10 +326,7 @@ abstract class TActiveRecord extends TComponent
if(is_string($criteria))
{
if(!is_array($parameters) && func_num_args() > 1)
- {
- $parameters = func_get_args();
- array_shift($parameters);
- }
+ $parameters = array_slice(func_get_args(),1);
$criteria=new TActiveRecordCriteria($criteria,$parameters);
}
$gateway = $this->getRecordManager()->getRecordGateway();
@@ -407,10 +398,7 @@ abstract class TActiveRecord extends TComponent
public function findBySql($sql,$parameters=array())
{
if(!is_array($parameters) && func_num_args() > 1)
- {
- $parameters = func_get_args();
- array_shift($parameters);
- }
+ $parameters = array_slice(func_get_args(),1);
$gateway = $this->getRecordManager()->getRecordGateway();
$data = $gateway->findRecordsBySql($this,$sql,$parameters);
$results = array();
@@ -430,11 +418,8 @@ abstract class TActiveRecord extends TComponent
{
if(is_string($criteria))
{
- if(!is_array($parameters) && func_num_args() > 1)
- {
- $parameters = func_get_args();
- array_shift($parameters);
- }
+ if(!is_array($parameters) && func_num_args() > 1)
+ $parameters = array_slice(func_get_args(),1);
$criteria=new TActiveRecordCriteria($criteria,$parameters);
}
$gateway = $this->getRecordManager()->getRecordGateway();
diff --git a/framework/Data/ActiveRecord/TActiveRecordCriteria.php b/framework/Data/ActiveRecord/TActiveRecordCriteria.php
index 134632e8..cc4da7c8 100644
--- a/framework/Data/ActiveRecord/TActiveRecordCriteria.php
+++ b/framework/Data/ActiveRecord/TActiveRecordCriteria.php
@@ -10,6 +10,8 @@
* @package System.Data.ActiveRecord
*/
+Prado::using('System.Data.DataGateway.TSqlCriteria');
+
/**
* Search criteria for Active Record.
*
@@ -30,125 +32,8 @@
* @package System.Data.ActiveRecord
* @since 3.1
*/
-class TActiveRecordCriteria extends TComponent
+class TActiveRecordCriteria extends TSqlCriteria
{
- private $_condition;
- private $_parameters;
- private $_ordersBy;
- private $_limit;
- private $_offset;
-
- /**
- * Creates a new criteria with given condition;
- * @param string sql string after the WHERE stanza
- * @param mixed named or indexed parameters, accepts as multiple arguments.
- */
- public function __construct($condition=null,$parameters=array())
- {
- if(!is_array($parameters) && func_num_args() > 2)
- {
- $parameters = func_get_args();
- array_shift($parameters);
- }
- $this->setCondition($condition);
- $this->_parameters=new TAttributeCollection((array)$parameters);
- $this->_ordersBy=new TAttributeCollection;
- }
-
- /**
- * @return TAttributeCollection list of named parameters and values.
- */
- public function getParameters()
- {
- return $this->_parameters;
- }
-
- /**
- * @param ArrayAccess named parameters.
- */
- public function setParameters($value)
- {
- if(!(is_array($value) || $value instanceof ArrayAccess))
- throw new TException('value must be array or ArrayAccess');
- $this->_parameters->copyFrom($value);
- }
-
- /**
- * @return boolean true if the parameter index are string base, false otherwise.
- */
- public function getIsNamedParameters()
- {
- foreach($this->getParameters() as $k=>$v)
- return is_string($k);
- }
-
- /**
- * @return TAttributeCollection ordering clause.
- */
- public function getOrdersBy()
- {
- return $this->_ordersBy;
- }
-
- /**
- * @param ArrayAccess ordering clause.
- */
- public function setOrdersBy($value)
- {
- if(!(is_array($value) || $value instanceof ArrayAccess))
- throw new TException('value must be array or ArrayAccess');
- $this->_ordersBy->copyFrom($value);
- }
-
- /**
- * @return string search conditions.
- */
- public function getCondition()
- {
- return $this->_condition;
- }
-
- /**
- * Sets the search conditions to be placed after the WHERE clause in the SQL.
- * @param string search conditions.
- */
- public function setCondition($value)
- {
- $this->_condition=$value;
- }
-
- /**
- * @return int maximum number of records to return.
- */
- public function getLimit()
- {
- return $this->_limit;
- }
-
- /**
- * @param int maximum number of records to return.
- */
- public function setLimit($value)
- {
- $this->_limit=$value;
- }
-
- /**
- * @return int record offset.
- */
- public function getOffset()
- {
- return $this->_offset;
- }
-
- /**
- * @param int record offset.
- */
- public function setOffset($value)
- {
- $this->_offset=$value;
- }
-
/**
* This method is invoked before the object is deleted from the database.
* The method raises 'OnDelete' event.
@@ -172,31 +57,6 @@ class TActiveRecordCriteria extends TComponent
{
$this->raiseEvent('OnSelect', $this, $param);
}
-
- /**
- * @return string string representation of the criteria. Useful for debugging.
- */
- public function __toString()
- {
- $str = '';
- if(strlen((string)$this->getCondition()) > 0)
- $str .= '"'.(string)$this->getCondition().'"';
- $params = array();
- foreach($this->getParameters() as $k=>$v)
- $params[] = "{$k} => ${v}";
- if(count($params) > 0)
- $str .= ', "'.implode(', ',$params).'"';
- $orders = array();
- foreach($this->getOrdersBy() as $k=>$v)
- $orders[] = "{$k} => ${v}";
- if(count($orders) > 0)
- $str .= ', "'.implode(', ',$orders).'"';
- if($this->_limit !==null)
- $str .= ', '.$this->_limit;
- if($this->_offset !== null)
- $str .= ', '.$this->_offset;
- return $str;
- }
}
?> \ No newline at end of file
diff --git a/framework/Data/Common/Mysql/TMysqlMetaData.php b/framework/Data/Common/Mysql/TMysqlMetaData.php
new file mode 100644
index 00000000..ee3bd2e0
--- /dev/null
+++ b/framework/Data/Common/Mysql/TMysqlMetaData.php
@@ -0,0 +1,251 @@
+<?php
+
+/**
+ * Load the base TDbMetaData class.
+ */
+Prado::using('System.Data.Common.TDbMetaData');
+Prado::using('System.Data.Common.Mysql.TMysqlTableInfo');
+
+class TMysqlMetaData extends TDbMetaData
+{
+ /**
+ * Get the column definitions for given table.
+ * @param string table name.
+ * @return TMysqlTableInfo table information.
+ */
+ protected function createTableInfo($table)
+ {
+ $this->getDbConnection()->setActive(true);
+ $sql = "SHOW FULL FIELDS FROM {$table}";
+ $command = $this->getDbConnection()->createCommand($sql);
+ $tableInfo = $this->createNewTableInfo($table);
+ $index=0;
+ foreach($command->query() as $col)
+ {
+ $col['index'] = $index++;
+ $this->processColumn($tableInfo,$col);
+ }
+ return $tableInfo;
+ }
+
+ /**
+ * @param TMysqlTableInfo table information.
+ * @param array column information.
+ */
+ protected function processColumn($tableInfo, $col)
+ {
+ $columnId = $col['Field'];
+
+ $info['ColumnName'] = "`$columnId`"; //quote the column names!
+ $info['ColumnIndex'] = $col['index'];
+ if($col['Null']!=='NO')
+ $info['AllowNull'] = true;
+ if(is_int(strpos(strtolower($col['Extra']), 'auto_increment')))
+ $info['AutoIncrement']=true;
+ if($col['Default']!=="")
+ $info['DefaultValue'] = $col['Default'];
+
+ if($col['Key']==='PRI' || in_array($columnId, $tableInfo->getPrimaryKeys()))
+ $info['IsPrimaryKey'] = true;
+ if($this->isForeignKeyColumn($columnId, $tableInfo))
+ $info['IsForeignKey'] = true;
+ if(in_array($columnId, $tableInfo->getUniqueKeys()))
+ $info['IsUnique'] = true;
+
+ $info['DbType'] = $col['Type'];
+ $match=array();
+ //find SET/ENUM values, column size, precision, and scale
+ if(preg_match('/\((.*)\)/', $col['Type'], $match))
+ {
+ $info['DbType']= preg_replace('/\(.*\)/', '', $col['Type']);
+
+ //find SET/ENUM values
+ if($this->isEnumSetType($info['DbType']))
+ $info['DbTypeValues'] = preg_split('/\s*,\s*|\s+/', preg_replace('/\'|"/', '', $match[1]));
+
+ //find column size, precision and scale
+ $pscale = array();
+ if(preg_match('/(\d+)(?:,(\d+))?+/', $match[1], $pscale))
+ {
+ if($this->isPrecisionType($info['DbType']))
+ {
+ $info['NumericPrecision'] = intval($pscale[1]);
+ if(count($pscale) > 2)
+ $info['NumericScale'] = intval($pscale[2]);
+ }
+ else
+ $info['ColumnSize'] = intval($pscale[1]);
+ }
+ }
+
+ $tableInfo->Columns[$columnId] = new TMysqlTableColumn($info);
+ }
+
+ /**
+ * @return boolean true if column type if "numeric", "interval" or begins with "time".
+ */
+ protected function isPrecisionType($type)
+ {
+ $type = strtolower(trim($type));
+ return $type==='decimal' || $type==='dec'
+ || $type==='float' || $type==='double'
+ || $type==='double precision' || $type==='real';
+ }
+
+ /**
+ * @return boolean true if column type if "enum" or "set".
+ */
+ protected function isEnumSetType($type)
+ {
+ $type = strtolower(trim($type));
+ return $type==='set' || $type==='enum';
+ }
+
+
+ /**
+ * @param string table name, may be quoted with back-ticks and may contain database name.
+ * @return array tuple ($schema,$table), $schema may be null.
+ * @throws TDbException when table name contains invalid identifier bytes.
+ */
+ protected function getSchemaTableName($table)
+ {
+ //remove the back ticks and separate out the "database.table"
+ $result = explode('.', str_replace('`', '', $table));
+ foreach($result as $name)
+ {
+ if(!$this->isValidIdentifier($name))
+ {
+ $ref = 'http://dev.mysql.com/doc/refman/5.0/en/identifiers.html';
+ throw new TDbException('dbcommon_invalid_identifier_name', $table, $ref);
+ }
+ }
+ return count($result) > 1 ? $result : array(null, $result[0]);
+ }
+
+ /**
+ * http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
+ * @param unknown_type $name
+ */
+ protected function isValidIdentifier($name)
+ {
+ return !preg_match('#/|\\|.|\x00|\xFF#', $name);
+ }
+
+ /**
+ * @param string table schema name
+ * @param string table name.
+ * @return TMysqlTableInfo
+ */
+ protected function createNewTableInfo($table)
+ {
+ list($schemaName,$tableName) = $this->getSchemaTableName($table);
+ $info['SchemaName'] = $schemaName;
+ $info['TableName'] = $tableName;
+ $info['IsView'] = $this->getIsView($schemaName,$tableName);
+ list($primary, $foreign, $unique) = $this->getConstraintKeys($schemaName, $tableName);
+ return new TMysqlTableInfo($info,$primary,$foreign, $unique);
+ }
+
+ /**
+ * @param string database name, null to use default connection database.
+ * @param string table or view name.
+ * @return boolean true if is view, false otherwise.
+ * @throws TDbException if table or view does not exist.
+ */
+ protected function getIsView($schemaName,$tableName)
+ {
+ if($schemaName!==null)
+ $sql = "SHOW FULL TABLES FROM `{$schemaName}` LIKE :table";
+ else
+ $sql = 'SHOW FULL TABLES LIKE :table';
+
+ $command = $this->getDbConnection()->createCommand($sql);
+ $command->bindValue(':table', $tableName);
+ try
+ {
+ return count($result = $command->queryRow()) > 0 && $result['Table_type']==='VIEW';
+ }
+ catch(TDbException $e)
+ {
+ $table = $schemaName===null?$tableName:$schemaName.'.'.$tableName;
+ throw new TDbException('dbcommon_invalid_table_name',$table,$e->getMessage());
+ }
+ }
+
+ /**
+ * Gets the primary, foreign key, and unique column details for the given table.
+ * @param string schema name
+ * @param string table name.
+ * @return array tuple ($primary, $foreign, $unique)
+ */
+ protected function getConstraintKeys($schemaName, $tableName)
+ {
+ $table = $schemaName===null ? "`{$tableName}`" : "`{$schemaName}`.`{$tableName}`";
+ $sql = "SHOW INDEX FROM {$table}";
+ $command = $this->getDbConnection()->createCommand($sql);
+ $primary = array();
+ $foreign = $this->getForeignConstraints($schemaName,$tableName);
+ $unique = array();
+ foreach($command->query() as $row)
+ {
+ if($row['Key_name']==='PRIMARY')
+ $primary[] = $row['Column_name'];
+ else if(intval($row['Non_unique'])===0)
+ $unique[] = $row['Column_name'];
+ }
+ return array($primary,$foreign,$unique);
+ }
+
+ /**
+ * Gets foreign relationship constraint keys and table name
+ * @param string database name
+ * @param string table name
+ * @return array foreign relationship table name and keys.
+ */
+ protected function getForeignConstraints($schemaName, $tableName)
+ {
+ $andSchema = $schemaName !== null ? 'AND TABLE_SCHEMA = :schema' : '';
+ $sql = <<<EOD
+ SELECT
+ CONSTRAINT_NAME as con,
+ COLUMN_NAME as col,
+ REFERENCED_TABLE_SCHEMA as fkschema,
+ REFERENCED_TABLE_NAME as fktable,
+ REFERENCED_COLUMN_NAME as fkcol
+ FROM
+ `INFORMATION_SCHEMA`.`KEY_COLUMN_USAGE`
+ WHERE
+ REFERENCED_TABLE_NAME IS NOT NULL
+ AND TABLE_NAME = :table
+ $andSchema
+EOD;
+ $command = $this->getDbConnection()->createCommand($sql);
+ $command->bindValue(':table', $tableName);
+ if($schemaName!==null)
+ $command->bindValue(':schema', $schemaName);
+ $fkeys=array();
+ foreach($command->query() as $col)
+ {
+ $fkeys[$col['con']]['keys'][$col['col']] = $col['fkcol'];
+ $fkeys[$col['con']]['table'] = "`{$col['fkschema']}`.`{$col['fktable']}`";
+ }
+ return array_values($fkeys);
+ }
+
+ /**
+ * @param string column name.
+ * @param TPgsqlTableInfo table information.
+ * @return boolean true if column is a foreign key.
+ */
+ protected function isForeignKeyColumn($columnId, $tableInfo)
+ {
+ foreach($tableInfo->getForeignKeys() as $fk)
+ {
+ if(in_array($columnId, array_keys($fk['keys'])))
+ return true;
+ }
+ return false;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/Common/Mysql/TMysqlTableColumn.php b/framework/Data/Common/Mysql/TMysqlTableColumn.php
new file mode 100644
index 00000000..0f013acd
--- /dev/null
+++ b/framework/Data/Common/Mysql/TMysqlTableColumn.php
@@ -0,0 +1,69 @@
+<?php
+/**
+ * TMysqlTableColumn class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.Common.Mysql
+ */
+
+/**
+ * Load common TDbTableCommon class.
+ */
+Prado::using('System.Data.Common.TDbTableColumn');
+
+/**
+ * Describes the column metadata of the schema for a Mysql database table.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.Common.Mysql
+ * @since 3.1
+ */
+class TMysqlTableColumn extends TDbTableColumn
+{
+ /**
+ * @return boolean true if column will auto-increment when the column value is inserted as null.
+ */
+ public function getAutoIncrement()
+ {
+ return $this->getInfo('AutoIncrement', false);
+ }
+
+ /**
+ * @return boolean true if auto increment is true.
+ */
+ public function getHasSequence()
+ {
+ return $this->getAutoIncrement();
+ }
+
+ public function getDbTypeValues()
+ {
+ return $this->getInfo('DbTypeValues');
+ }
+
+ /**
+ * Overrides parent implementation, returns PHP type from the db type.
+ * @return boolean derived PHP primitive type from the column db type.
+ */
+ public function getPHPType()
+ {
+ switch(strtolower($this->getDbType()))
+ {
+ case 'bit': case 'bit varying': case 'real': case 'serial': case 'int': case 'integer':
+ return 'integer';
+ case 'boolean':
+ return 'boolean';
+ case 'bigint': case 'bigserial': case 'double precision': case 'money': case 'numeric':
+ return 'float';
+ default:
+ return 'string';
+ }
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/Common/Mysql/TMysqlTableInfo.php b/framework/Data/Common/Mysql/TMysqlTableInfo.php
new file mode 100644
index 00000000..e8585730
--- /dev/null
+++ b/framework/Data/Common/Mysql/TMysqlTableInfo.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * TMysqlTableInfo class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.Common.Mysql
+ */
+
+/**
+ * Loads the base TDbTableInfo class and TMysqlTableColumn class.
+ */
+Prado::using('System.Data.Common.TDbTableInfo');
+Prado::using('System.Data.Common.Mysql.TMysqlTableColumn');
+
+/**
+ * TMysqlTableInfo class provides additional table information for MySQL database.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.Common.Mysql
+ * @since 3.1
+ */
+class TMysqlTableInfo extends TDbTableInfo
+{
+ /**
+ * @return string name of the schema this column belongs to.
+ */
+ public function getSchemaName()
+ {
+ return $this->getInfo('SchemaName');
+ }
+
+ /**
+ * @return string full name of the table, database dependent.
+ */
+ public function getTableFullName()
+ {
+ return '`'.$this->getSchemaName().'`.`'.$this->getTableName().'`';
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/Common/Pgsql/TPgsqlMetaData.php b/framework/Data/Common/Pgsql/TPgsqlMetaData.php
new file mode 100644
index 00000000..b789192f
--- /dev/null
+++ b/framework/Data/Common/Pgsql/TPgsqlMetaData.php
@@ -0,0 +1,329 @@
+<?php
+/**
+ * TPgsqlMetaData class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.Common.Pgsql
+ */
+
+/**
+ * Load the base TDbMetaData class.
+ */
+Prado::using('System.Data.Common.TDbMetaData');
+Prado::using('System.Data.Common.Pgsql.TPgsqlTableInfo');
+
+/**
+ * TPgsqlMetaData loads PostgreSQL database table and column information.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.Commom.Pgsql
+ * @since 3.1
+ */
+class TPgsqlMetaData extends TDbMetaData
+{
+ private $_defaultSchema = 'public';
+
+ /**
+ * @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('.', $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 TPgsqlTableInfo 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.attname,
+ pg_catalog.format_type(a.atttypid, a.atttypmod) as type,
+ a.atttypmod,
+ a.attnotnull, a.atthasdef, adef.adsrc,
+ (
+ SELECT 1 FROM pg_catalog.pg_depend pd, pg_catalog.pg_class pc
+ WHERE pd.objid=pc.oid
+ AND pd.classid=pc.tableoid
+ AND pd.refclassid=pc.tableoid
+ AND pd.refobjid=a.attrelid
+ AND pd.refobjsubid=a.attnum
+ AND pd.deptype='i'
+ AND pc.relkind='S'
+ ) IS NOT NULL AS attisserial
+
+ FROM
+ pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef
+ ON a.attrelid=adef.adrelid
+ AND a.attnum=adef.adnum
+ LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid
+ WHERE
+ a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname=:table
+ AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
+ nspname = :schema))
+ AND a.attnum > 0 AND NOT a.attisdropped
+ ORDER BY a.attnum
+EOD;
+ $this->getDbConnection()->setActive(true);
+ $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);
+ }
+ return $tableInfo;
+ }
+
+ /**
+ * @param string table schema name
+ * @param string table name.
+ * @return TPgsqlTableInfo
+ */
+ protected function createNewTableInfo($schemaName,$tableName)
+ {
+ $info['SchemaName'] = $schemaName;
+ $info['TableName'] = $tableName;
+ if($this->getIsView($schemaName,$tableName))
+ $info['IsView'] = true;
+ list($primary, $foreign, $unique) = $this->getConstraintKeys($schemaName, $tableName);
+ return new TPgsqlTableInfo($info,$primary,$foreign, $unique);
+ }
+
+ /**
+ * @param string table schema name
+ * @param string table name.
+ * @return boolean true if the table is a view.
+ */
+ protected function getIsView($schemaName,$tableName)
+ {
+ $sql =
+<<<EOD
+ SELECT count(c.relname) FROM pg_catalog.pg_class c
+ LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace)
+ WHERE (n.nspname=:schema) AND (c.relkind = 'v'::"char") AND c.relname = :table
+EOD;
+ $this->getDbConnection()->setActive(true);
+ $command = $this->getDbConnection()->createCommand($sql);
+ $command->bindValue(':schema',$schemaName);
+ $command->bindValue(':table', $tableName);
+ return intval($command->queryScalar()) === 1;
+ }
+
+ /**
+ * @param TPgsqlTableInfo table information.
+ * @param array column information.
+ */
+ protected function processColumn($tableInfo, $col)
+ {
+ $columnId = $col['attname']; //use column name as column Id
+
+ $info['ColumnName'] = '"'.$columnId.'"'; //quote the column names!
+ $info['ColumnIndex'] = $col['index'];
+ if(!$col['attnotnull'])
+ $info['AllowNull'] = true;
+ if(in_array($columnId, $tableInfo->getPrimaryKeys()))
+ $info['IsPrimaryKey'] = true;
+ if($this->isForeignKeyColumn($columnId, $tableInfo))
+ $info['IsForeignKey'] = true;
+ if(in_array($columnId, $tableInfo->getUniqueKeys()))
+ $info['IsUnique'] = true;
+
+ if($col['atttypmod'] > 0)
+ $info['ColumnSize'] = $col['atttypmod'] - 4;
+ if($col['atthasdef'])
+ $info['DefaultValue'] = $col['adsrc'];
+ if($col['attisserial'] || substr($col['adsrc'],0,8) === 'nextval(')
+ {
+ 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 TPgsqlTableColumn($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==='numeric' || $type==='interval' || strpos($type, 'time')===0;
+ }
+
+ /**
+ * Gets the primary, foreign key, and unique column details for the given table.
+ * @param string schema name
+ * @param string table name.
+ * @return array tuple ($primary, $foreign, $unique)
+ */
+ protected function getConstraintKeys($schemaName, $tableName)
+ {
+ $sql = 'SELECT
+ pg_catalog.pg_get_constraintdef(pc.oid, true) AS consrc,
+ pc.contype
+ FROM
+ pg_catalog.pg_constraint pc
+ WHERE
+ pc.conrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname=:table
+ AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace
+ WHERE nspname=:schema))
+ ';
+ $this->getDbConnection()->setActive(true);
+ $command = $this->getDbConnection()->createCommand($sql);
+ $command->bindValue(':table', $tableName);
+ $command->bindValue(':schema', $schemaName);
+ $primary = array();
+ $foreign = array();
+ $unique = array();
+ foreach($command->query() as $row)
+ {
+ switch($row['contype'])
+ {
+ case 'p':
+ $primary = $this->getPrimaryKeys($row['consrc']);
+ break;
+ case 'f':
+ if(($fkey = $this->getForeignKeys($row['consrc']))!==null)
+ $foreign[] = $fkey;
+ break;
+ case 'u':
+ if(($ukey = $this->getUniqueKey($row['consrc']))!==null)
+ $unique[] = $ukey;
+ break;
+ }
+ }
+ return array($primary,$foreign,$unique);
+ }
+
+ /**
+ * Gets the primary key field names
+ * @param string pgsql 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();
+ }
+
+ /**
+ * @param string pgsql unique constraint definition
+ * @return string column id if found, null otherwise.
+ */
+ protected function getUniqueKey($src)
+ {
+ $matches=array();
+ if(preg_match('/UNIQUE\s+\(([^\)]+)\)/i', $src, $matches))
+ return $matches[1];
+ }
+
+ /**
+ * Gets foreign relationship constraint keys and table name
+ * @param string pgsql 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' => $matches[2], 'keys' => $fkeys);
+ }
+ }
+
+ /**
+ * @param string column name.
+ * @param TPgsqlTableInfo table information.
+ * @return boolean true if column is a foreign key.
+ */
+ protected function isForeignKeyColumn($columnId, $tableInfo)
+ {
+ foreach($tableInfo->getForeignKeys() as $fk)
+ {
+ if(in_array($columnId, array_keys($fk['keys'])))
+ return true;
+ }
+ return false;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/Common/Pgsql/TPgsqlTableColumn.php b/framework/Data/Common/Pgsql/TPgsqlTableColumn.php
new file mode 100644
index 00000000..66053a63
--- /dev/null
+++ b/framework/Data/Common/Pgsql/TPgsqlTableColumn.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * TPgsqlTableColumn class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.Common.Pgsql
+ */
+
+/**
+ * Load common TDbTableCommon class.
+ */
+Prado::using('System.Data.Common.TDbTableColumn');
+
+/**
+ * Describes the column metadata of the schema for a PostgreSQL database table.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.Common.Pgsql
+ * @since 3.1
+ */
+class TPgsqlTableColumn extends TDbTableColumn
+{
+ /**
+ * Overrides parent implementation, returns PHP type from the db type.
+ * @return boolean derived PHP primitive type from the column db type.
+ */
+ public function getPHPType()
+ {
+ switch(strtolower($this->getDbType()))
+ {
+ case 'bit': case 'bit varying': case 'real': case 'serial': case 'int': case 'integer':
+ return 'integer';
+ case 'boolean':
+ return 'boolean';
+ case 'bigint': case 'bigserial': case 'double precision': case 'money': case 'numeric':
+ return 'float';
+ default:
+ return 'string';
+ }
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/Common/Pgsql/TPgsqlTableInfo.php b/framework/Data/Common/Pgsql/TPgsqlTableInfo.php
new file mode 100644
index 00000000..88a56635
--- /dev/null
+++ b/framework/Data/Common/Pgsql/TPgsqlTableInfo.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * TPgsqlTableInfo class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.Common.Pgsql
+ */
+
+/**
+ * Loads the base TDbTableInfo class and TPgsqlTableColumn class.
+ */
+Prado::using('System.Data.Common.TDbTableInfo');
+Prado::using('System.Data.Common.Pgsql.TPgsqlTableColumn');
+
+/**
+ * TPgsqlTableInfo class provides additional table information for PostgreSQL database.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.Common.Pgsql
+ * @since 3.1
+ */
+class TPgsqlTableInfo extends TDbTableInfo
+{
+ /**
+ * @return string name of the schema this column belongs to.
+ */
+ public function getSchemaName()
+ {
+ return $this->getInfo('SchemaName');
+ }
+
+ /**
+ * @return string full name of the table, database dependent.
+ */
+ public function getTableFullName()
+ {
+ return $this->getSchemaName().'.'.$this->getTableName();
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/Common/TDbCommandBuilder.php b/framework/Data/Common/TDbCommandBuilder.php
new file mode 100644
index 00000000..5238a045
--- /dev/null
+++ b/framework/Data/Common/TDbCommandBuilder.php
@@ -0,0 +1,320 @@
+<?php
+/**
+ * TDbCommandBuilder class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.Common
+ */
+
+/**
+ * TDbCommandBuilder provides basic methods to create query commands for tables
+ * giving by {@link setTableInfo TableInfo} the property.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.Common
+ * @since 3.1
+ */
+class TDbCommandBuilder extends TComponent
+{
+ private $_connection;
+ private $_tableInfo;
+
+ /**
+ * @param TDbConnection database connection.
+ * @param TDbTableInfo table information.
+ */
+ public function __construct($connection=null, $tableInfo=null)
+ {
+ $this->setDbConnection($connection);
+ $this->setTableInfo($tableInfo);
+ }
+
+ /**
+ * @return TDbConnection database connection.
+ */
+ public function getDbConnection()
+ {
+ return $this->_connection;
+ }
+
+ /**
+ * @param TDbConnection database connection.
+ */
+ public function setDbConnection($value)
+ {
+ $this->_connection=$value;
+ }
+
+ /**
+ * @param TDbTableInfo table information.
+ */
+ public function setTableInfo($value)
+ {
+ $this->_tableInfo=$value;
+ }
+
+ /**
+ * @param TDbTableInfo table information.
+ */
+ public function getTableInfo()
+ {
+ return $this->_tableInfo;
+ }
+
+ /**
+ * Alters the sql to apply $limit and $offset. Default implementation is applicable
+ * for PostgreSQL, MySQL and SQLite.
+ * @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.
+ */
+ public function applyLimitOffset($sql, $limit=-1, $offset=-1)
+ {
+ $limit = $limit!==null ? intval($limit) : -1;
+ $offset = $offset!==null ? intval($offset) : -1;
+ $limitStr = $limit >= 0 ? ' LIMIT '.$limit : '';
+ $offsetStr = $offset >= 0 ? ' OFFSET '.$offset : '';
+ return $sql.$limitStr.$offsetStr;
+ }
+
+ /**
+ * @param string SQL string without existing ordering.
+ * @param array pairs of column names as key and direction as value.
+ * @return string modified SQL applied with ORDER BY.
+ */
+ public function applyOrdering($sql, $ordering)
+ {
+ $orders=array();
+ foreach($ordering as $name=>$direction)
+ {
+ $direction = strtolower($direction) == 'desc' ? 'DESC' : 'ASC';
+ $column = $this->getTableInfo()->getColumn($name)->getColumnName();
+ $orders[] = $column.' '.$direction;
+ }
+ if(count($orders) > 0)
+ $sql .= ' ORDER BY '.implode(', ', $orders);
+ return $sql;
+ }
+
+ /**
+ * Computes the SQL condition for search a set of column using regular expression
+ * to match a string of keywords. The implementation should only uses columns
+ * that permit regular expression matching. This method should be implemented in
+ * database specific command builder classes.
+ * @param array list of column id for potential search condition.
+ * @param string string of keywords
+ * @return string SQL condition for regular expression matching on a set of columns.
+ */
+ //abstract public function createRegExpSearch($columnIds, $keywords);
+
+ /**
+ * Appends the $where condition to the string "SELECT * FROM tableName WHERE ".
+ * The tableName is obtained from the {@link setTableInfo TableInfo} property.
+ * @param string query condition
+ * @param array condition parameters.
+ * @return TDbCommand query command.
+ */
+ public function createFindCommand($where, $parameters=array(), $ordering=array(), $limit=-1, $offset=-1)
+ {
+ $table = $this->getTableInfo()->getTableFullName();
+ $sql = "SELECT * FROM {$table} WHERE {$where}";
+ if(count($ordering) > 0)
+ $sql = $this->applyOrdering($sql, $ordering);
+ if($limit>=0 || $offset>=0)
+ $sql = $this->applyLimitOffset($sql, $limit, $offset);
+ $command = $this->createCommand($sql);
+ $this->bindArrayValues($command, $parameters);
+ return $command;
+ }
+
+ /**
+ * Creates a count(*) command for the table described in {@link setTableInfo TableInfo}.
+ * @param string count condition.
+ * @param array binding parameters.
+ * @return TDbCommand count command.
+ */
+ public function createCountCommand($where='true', $parameters=array(),$ordering=array(), $limit=-1, $offset=-1)
+ {
+ $table = $this->getTableInfo()->getTableFullName();
+ $sql = "SELECT COUNT(*) FROM {$table} WHERE {$where}";
+ if(count($ordering) > 0)
+ $sql = $this->applyOrdering($sql, $ordering);
+ if($limit>=0 || $offset>=0)
+ $sql = $this->applyLimitOffset($sql, $limit, $offset);
+ $command = $this->createCommand($sql);
+ $this->bindArrayValues($command, $parameters);
+ return $command;
+ }
+
+ /**
+ * Creates a delete command for the table described in {@link setTableInfo TableInfo}.
+ * The conditions for delete is given by the $where argument and the parameters
+ * for the condition is given by $parameters.
+ * @param string delete condition.
+ * @param array delete parameters.
+ * @return TDbCommand delete command.
+ */
+ public function createDeleteCommand($where,$parameters=array())
+ {
+ $table = $this->getTableInfo()->getTableFullName();
+ $command = $this->createCommand("DELETE FROM {$table} WHERE {$where}");
+ $this->bindArrayValues($command, $parameters);
+ return $command;
+ }
+
+ /**
+ * Creates an insert command for the table described in {@link setTableInfo TableInfo} for the given data.
+ * Each array key in the $data array must correspond to the column name of the table
+ * (if a column allows to be null, it may be omitted) to be inserted with
+ * the corresponding array value.
+ * @param array name-value pairs of new data to be inserted.
+ * @return TDbCommand insert command
+ */
+ public function createInsertCommand($data)
+ {
+ $table = $this->getTableInfo()->getTableFullName();
+ list($fields, $bindings) = $this->getInsertFieldBindings($data);
+ $command = $this->createCommand("INSERT INTO {$table}({$fields}) VALUES ($bindings)");
+ $this->bindColumnValues($command, $data);
+ return $command;
+ }
+
+ /**
+ * Creates an update command for the table described in {@link setTableInfo TableInfo} for the given data.
+ * Each array key in the $data array must correspond to the column name to be updated with the corresponding array value.
+ * @param array name-value pairs of data to be updated.
+ * @param string update condition.
+ * @param array update parameters.
+ * @return TDbCommand update command.
+ */
+ public function createUpdateCommand($data, $where, $parameters=array())
+ {
+ $table = $this->getTableInfo()->getTableFullName();
+ if($this->hasIntegerKey($parameters))
+ $fields = implode(', ', $this->getColumnBindings($data, true));
+ else
+ $fields = implode(', ', $this->getColumnBindings($data));
+ $command = $this->createCommand("UPDATE {$table} SET {$fields} WHERE {$where}");
+ $this->bindArrayValues($command, array_merge($data, $parameters));
+ return $command;
+ }
+
+ /**
+ * Returns a list of insert field name and a list of binding names.
+ * @param object array or object to be inserted.
+ * @return array tuple ($fields, $bindings)
+ */
+ protected function getInsertFieldBindings($values)
+ {
+ $fields = array(); $bindings=array();
+ foreach(array_keys($values) as $name)
+ {
+ $fields[] = $this->getTableInfo()->getColumn($name)->getColumnName();
+ $bindings[] = ':'.$name;
+ }
+ return array(implode(', ',$fields), implode(', ', $bindings));
+ }
+
+ /**
+ * Create a name-value or position-value if $position=true binding strings.
+ * @param array data for binding.
+ * @param boolean true to bind as position values.
+ * @return string update column names with corresponding binding substrings.
+ */
+ protected function getColumnBindings($values, $position=false)
+ {
+ $bindings=array();
+ foreach(array_keys($values) as $name)
+ {
+ $column = $this->getTableInfo()->getColumn($name)->getColumnName();
+ $bindings[] = $position ? $column.' = ?' : $column.' = :'.$name;
+ }
+ return $bindings;
+ }
+
+ /**
+ * @param string SQL query string.
+ * @return TDbCommand corresponding database command.
+ */
+ public function createCommand($sql)
+ {
+ $this->getDbConnection()->setActive(true);
+ return $this->getDbConnection()->createCommand($sql);
+ }
+
+ /**
+ * Bind the name-value pairs of $values where the array keys correspond to column names.
+ * @param TDbCommand database command.
+ * @param array name-value pairs.
+ */
+ public function bindColumnValues($command, $values)
+ {
+ foreach($values as $name=>$value)
+ {
+ $column = $this->getTableInfo()->getColumn($name);
+ if($value === null && $column->getAllowNull())
+ $command->bindValue(':'.$name, null, PDO::PARAM_NULL);
+ else
+ $command->bindValue(':'.$name, $value, $column->getPdoType());
+ }
+ }
+
+ /**
+ * @param TDbCommand database command
+ * @param array values for binding.
+ */
+ public function bindArrayValues($command, $values)
+ {
+ if($this->hasIntegerKey($values))
+ {
+ $values = array_values($values);
+ for($i = 0, $max=count($values); $i<$max; $i++)
+ $command->bindValue($i+1, $values[$i], $this->getPdoType($value));
+ }
+ else
+ {
+ foreach($values as $name=>$value)
+ {
+ $prop = $name[0]===':' ? $name : ':'.$name;
+ $command->bindValue($prop, $value, $this->getPdoType($value));
+ }
+ }
+ }
+
+ /**
+ * @param mixed PHP value
+ * @return integer PDO parameter types.
+ */
+ protected function getPdoType($value)
+ {
+ switch(gettype($value))
+ {
+ case 'boolean': return PDO::PARAM_BOOL;
+ case 'integer': return PDO::PARAM_INT;
+ case 'string' : return PDO::PARAM_STR;
+ case 'NULL' : return PDO::PARAM_NULL;
+ }
+ }
+
+ /**
+ * @param array
+ * @return boolean true if any array key is an integer.
+ */
+ protected function hasIntegerKey($array)
+ {
+ foreach($array as $k=>$v)
+ {
+ if(gettype($k)==='integer')
+ return true;
+ }
+ return false;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/Common/TDbMetaData.php b/framework/Data/Common/TDbMetaData.php
new file mode 100644
index 00000000..477e2805
--- /dev/null
+++ b/framework/Data/Common/TDbMetaData.php
@@ -0,0 +1,104 @@
+<?php
+/**
+ * TDbMetaData class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.Common
+ */
+
+/**
+ * TDbMetaData is the base class for retrieving metadata information, such as
+ * table and columns information, from a database connection.
+ *
+ * Use the {@link getTableInfo} method to retrieve a table information.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.Common
+ * @since 3.1
+ */
+abstract class TDbMetaData extends TComponent
+{
+ private $_tableInfoCache=array();
+ private $_connection;
+
+ /**
+ * @param TDbConnection database connection.
+ */
+ public function __construct($conn)
+ {
+ $this->_connection=$conn;
+ }
+
+ /**
+ * @return TDbConnection database connection.
+ */
+ public function getDbConnection()
+ {
+ return $this->_connection;
+ }
+
+ /**
+ * Obtain database specific TDbMetaData class using the driver name of the database connection.
+ * @param TDbConnection database connection.
+ * @return TDbMetaData database specific TDbMetaData.
+ */
+ public static function getMetaData($conn)
+ {
+ $conn->setActive(true); //must be connected before retrieving driver name
+ $driver = $conn->getDriverName();
+ switch(strtolower($driver))
+ {
+ case 'pgsql':
+ Prado::using('System.Data.Common.Pgsql.TPgsqlMetaData');
+ return new TPgsqlMetaData($conn);
+ case 'mysqli':
+ case 'mysql':
+ Prado::using('System.Data.Common.Mysql.TMysqlMetaData');
+ return new TMysqlMetaData($conn);
+ case 'sqlite': //sqlite 3
+ case 'sqlite2': //sqlite 2
+ Prado::using('System.Data.Common.Sqlite.TSqliteMetaData');
+ return new TSqliteMetaData($conn);
+ case 'ibm':
+ Prado::using('System.Data.Common.IbmDb2.TIbmDb2MetaData');
+ return new TIbmDb2MetaData($conn);
+ default:
+ throw new TDbException('ar_invalid_database_driver',$driver);
+ }
+ }
+
+ /**
+ * Obtains table meta data information for the current connection and given table name.
+ * @param string table or view name
+ * @return TDbTableInfo table information.
+ */
+ public function getTableInfo($tableName)
+ {
+ if(!isset($this->_tableInfoCache[$tableName]))
+ $this->_tableInfoCache[$tableName] = $this->createTableInfo($tableName);
+ return $this->_tableInfoCache[$tableName];
+ }
+
+ /**
+ * Creates a command builder for a given table name.
+ * @param string table name.
+ * @return TDbCommandBuilder command builder instance for the given table.
+ */
+ public function createCommandBuilder($tableName)
+ {
+ return $this->getTableInfo($tableName)->createCommandBuilder($this->getDbConnection());
+ }
+
+ /**
+ * This method should be implemented by decendent classes.
+ * @return TDbTableInfo driver dependent create builder.
+ */
+ abstract protected function createTableInfo($tableName);
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/Common/TDbTableColumn.php b/framework/Data/Common/TDbTableColumn.php
new file mode 100644
index 00000000..4d9bd8a0
--- /dev/null
+++ b/framework/Data/Common/TDbTableColumn.php
@@ -0,0 +1,182 @@
+<?php
+/**
+ * TDbTableColumn class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.Common
+ */
+
+/**
+ * TDbTableColumn class describes the column meta data of the schema for a database table.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.Common
+ * @since 3.1
+ */
+class TDbTableColumn extends TComponent
+{
+ const UNDEFINED_VALUE= INF; //use infinity for undefined value
+
+ private $_info=array();
+
+ /**
+ * Sets the table column meta data.
+ * @param array table column information.
+ */
+ public function __construct($columnInfo)
+ {
+ $this->_info=$columnInfo;
+ }
+
+ /**
+ * @param string information array key name
+ * @param mixed default value if information array value is null
+ * @return mixed information array value.
+ */
+ protected 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;
+ }
+
+ /**
+ * Returns the derived PHP primitive type from the db type. Default returns 'string'.
+ * @return string derived PHP primitive type from the column db type.
+ */
+ public function getPHPType()
+ {
+ return 'string';
+ }
+
+ /**
+ * @param integer PDO bind param/value types.
+ */
+ public function getPdoType()
+ {
+ switch($this->getPHPType())
+ {
+ case 'boolean': return PDO::PARAM_BOOL;
+ case 'integer': return PDO::PARAM_INT;
+ case 'string' : return PDO::PARAM_STR;
+ }
+ }
+
+ /**
+ * @return string name of the column in the table (identifier quoted).
+ */
+ public function getColumnName()
+ {
+ return $this->getInfo('ColumnName');
+ }
+
+ /**
+ * @return string size of the column.
+ */
+ public function getColumnSize()
+ {
+ return $this->getInfo('ColumnSize');
+ }
+
+ /**
+ * @return integer zero-based ordinal position of the column in the table.
+ */
+ public function getColumnIndex()
+ {
+ return $this->getInfo('ColumnIndex');
+ }
+
+ /**
+ * @return string column type.
+ */
+ public function getDbType()
+ {
+ return $this->getInfo('DbType');
+ }
+
+ /**
+ * @return boolean specifies whether value Null is allowed, default is false.
+ */
+ public function getAllowNull()
+ {
+ return $this->getInfo('AllowNull',false);
+ }
+
+ /**
+ * @return mixed default column value if column value was null.
+ */
+ public function getDefaultValue()
+ {
+ return $this->getInfo('DefaultValue', self::UNDEFINED_VALUE);
+ }
+
+ /**
+ * @return string precision of the column data, if the data is numeric.
+ */
+ public function getNumericPrecision()
+ {
+ return $this->getInfo('NumericPrecision');
+ }
+
+ /**
+ * @return string scale of the column data, if the data is numeric.
+ */
+ public function getNumericScale()
+ {
+ return $this->getInfo('NumericScale');
+ }
+
+ /**
+ * @return boolean whether this column is a primary key for the table, default is false.
+ */
+ public function getIsPrimaryKey()
+ {
+ return $this->getInfo('IsPrimaryKey',false);
+ }
+
+ /**
+ * @return boolean whether this column is a foreign key, default is false.
+ */
+ public function getIsForeignKey()
+ {
+ return $this->getInfo('IsForeignKey',false);
+ }
+
+ /**
+ * @return boolean whether a unique constraint applies to this column, default is false.
+ */
+ public function getIsUnique()
+ {
+ return $this->getInfo('IsUnique', false);
+ }
+
+ /**
+ * @param string sequence name, only applicable if column is a sequence
+ */
+ public function getSequenceName()
+ {
+ return $this->getInfo('SequenceName');
+ }
+
+ /**
+ * @return boolean whether the column is a sequence.
+ */
+ public function hasSequence()
+ {
+ return $this->getSequenceName()!==null;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/Common/TDbTableInfo.php b/framework/Data/Common/TDbTableInfo.php
new file mode 100644
index 00000000..9b7f4392
--- /dev/null
+++ b/framework/Data/Common/TDbTableInfo.php
@@ -0,0 +1,167 @@
+<?php
+/**
+ * TDbTableInfo class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.Common
+ */
+
+/**
+ * TDbTableInfo class describes the meta data of a database table.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.Common
+ * @since 3.1
+ */
+class TDbTableInfo extends TComponent
+{
+ private $_info=array();
+
+ private $_primaryKeys;
+ private $_foreignKeys;
+ private $_uniqueKeys;
+
+ private $_columns;
+
+ private $_lowercase;
+
+ /**
+ * Sets the database table meta data information.
+ * @param array table column information.
+ */
+ public function __construct($tableInfo,$primary=array(),$foreign=array(), $unique=array())
+ {
+ $this->_info=$tableInfo;
+ $this->_primaryKeys=$primary;
+ $this->_foreignKeys=$foreign;
+ $this->_uniqueKeys=$unique;
+ $this->_columns=new TMap;
+ }
+
+ /**
+ * @param TDbConnection database connection.
+ * @return TDbCommandBuilder new command builder
+ */
+ public function createCommandBuilder($connection)
+ {
+ Prado::using('System.Data.Common.TDbCommandBuilder');
+ return new TDbCommandBuilder($connection,$this);
+ }
+
+ /**
+ * @param string information array key name
+ * @param mixed default value if information array value is null
+ * @return mixed information array value.
+ */
+ protected 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->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)
+ {
+ return $this->_columns->itemAt($name);
+ }
+
+ /**
+ * @param array list of column Id, empty to get all columns.
+ * @return array table column names (identifier quoted)
+ */
+ public function getColumnNames()
+ {
+ $names=array();
+ 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 unique column ids.
+ */
+ public function getUniqueKeys()
+ {
+ return $this->_uniqueKeys;
+ }
+
+ /**
+ * @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;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Exceptions/messages.txt b/framework/Exceptions/messages.txt
index 9072cbc3..0bd11361 100644
--- a/framework/Exceptions/messages.txt
+++ b/framework/Exceptions/messages.txt
@@ -366,6 +366,17 @@ dbcommand_column_empty = TDbCommand returned an empty result and could not o
dbdatareader_rewind_invalid = TDbDataReader is a forward-only stream. It can only be traversed once.
dbtransaction_transaction_inactive = TDbTransaction is inactive.
+dbcommandbuilder_value_must_not_be_null = Property {0} must not be null as defined by column '{2}' in table '{1}'.
+
+dbcommon_invalid_table_name = Database table '{0}' not found. Error Msg: {1}.
+dbcommon_invalid_identifier_name = Invalid database identifier name '{0}', see {1} for details.
+dbtablegateway_invalid_criteria = Invalid criteria object, must be a string or instance of TSqlCriteria.
+dbtablegateway_no_primary_key_found = Table '{0}' does not contain any primary key fields.
+dbtablegateway_missing_pk_values = Missing primary key values in forming IN(key1, key2, ...) for table '{0}'.
+dbtablegateway_pk_value_count_mismatch = Composite key value count mismatch in forming IN( (key1, key2, ..), (key3, key4, ..)) for table '{0}'.
+dbtablegateway_mismatch_args_exception = TTableGateway finder method '{0}' expects {1} parameters but found only {2} parameters instead.
+dbtablegateway_mismatch_column_name = In dynamic __call() method '{0}', no matching columns were found, valid columns for table '{2}' are '{1}'.
+
directorycachedependency_directory_invalid = TDirectoryCacheDependency.Directory {0} does not refer to a valid directory.
cachedependencylist_cachedependency_required = Only objects implementing ICacheDependency can be added into TCacheDependencyList.