From 36b3e180a8acfa5206af56210d391eede2f8eed9 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 7 Apr 2010 20:35:33 +0200 Subject: [PATCH 01/13] Fix Testsuite to work with Git --- tests/.gitignore | 2 ++ .../ORM/Mapping/YamlMappingDriverTest.php | 4 ++++ .../ORM/Tools/ConvertDoctrine1SchemaTest.php | 4 ++++ .../Export/YamlClassMetadataExporterTest.php | 4 ++++ tests/Doctrine/Tests/TestInit.php | 21 +++++++++++++++++-- 5 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 tests/.gitignore diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 000000000..aab720af4 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,2 @@ +Doctrine/Tests/Proxies/ +Doctrine/Tests/ORM/Proxy/generated/ \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php index b9193d581..2aad38645 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/YamlMappingDriverTest.php @@ -12,6 +12,10 @@ class YamlMappingDriverTest extends AbstractMappingDriverTest { protected function _loadDriver() { + if (!class_exists('Symfony\Components\Yaml\Yaml', true)) { + $this->markTestSkipped('Please install Symfony YAML Component into the include path of your PHP installation.'); + } + return new YamlDriver(__DIR__ . DIRECTORY_SEPARATOR . 'yaml'); } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Tools/ConvertDoctrine1SchemaTest.php b/tests/Doctrine/Tests/ORM/Tools/ConvertDoctrine1SchemaTest.php index 088c871f7..f76f7baa7 100644 --- a/tests/Doctrine/Tests/ORM/Tools/ConvertDoctrine1SchemaTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/ConvertDoctrine1SchemaTest.php @@ -40,6 +40,10 @@ class ConvertDoctrine1SchemaTest extends \Doctrine\Tests\OrmTestCase { public function testTest() { + if (!class_exists('Symfony\Components\Yaml\Yaml', true)) { + $this->markTestSkipped('Please install Symfony YAML Component into the include path of your PHP installation.'); + } + $cme = new ClassMetadataExporter(); $converter = new ConvertDoctrine1Schema(__DIR__ . '/doctrine1schema'); diff --git a/tests/Doctrine/Tests/ORM/Tools/Export/YamlClassMetadataExporterTest.php b/tests/Doctrine/Tests/ORM/Tools/Export/YamlClassMetadataExporterTest.php index 03a246b40..d7258a561 100644 --- a/tests/Doctrine/Tests/ORM/Tools/Export/YamlClassMetadataExporterTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/Export/YamlClassMetadataExporterTest.php @@ -37,6 +37,10 @@ class YamlClassMetadataExporterTest extends AbstractClassMetadataExporterTest { protected function _getType() { + if (!class_exists('Symfony\Components\Yaml\Yaml', true)) { + $this->markTestSkipped('Please install Symfony YAML Component into the include path of your PHP installation.'); + } + return 'yaml'; } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/TestInit.php b/tests/Doctrine/Tests/TestInit.php index 0945ce50c..c6b0e29f8 100644 --- a/tests/Doctrine/Tests/TestInit.php +++ b/tests/Doctrine/Tests/TestInit.php @@ -13,8 +13,25 @@ require_once __DIR__ . '/../../../lib/Doctrine/Common/ClassLoader.php'; $classLoader = new \Doctrine\Common\ClassLoader('Doctrine'); $classLoader->register(); -$classLoader = new \Doctrine\Common\ClassLoader('Symfony', __DIR__ . '/../../../lib/vendor'); -$classLoader->register(); +if (!file_exists(__DIR__."/Proxies")) { + if (!mkdir(__DIR__."/Proxies")) { + throw new Exception("Could not create " . __DIR__."/Proxies Folder."); + } +} +if (!file_exists(__DIR__."/ORM/Proxy/generated")) { + if (!mkdir(__DIR__."/ORM/Proxy/generated")) { + throw new Exception("Could not create " . __DIR__."/ORM/Proxy/generated Folder."); + } +} + +spl_autoload_register(function($class) { + if (strpos($class, 'Symfony') === 0) { + $file = str_replace("\\", "/", $class); + if (@fopen($class, "r")) { + require_once ($file); + } + } +}); set_include_path( __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'lib' From ed9692187d0d43a546520a8f4b6edbfa386935ca Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 7 Apr 2010 20:38:13 +0200 Subject: [PATCH 02/13] Fix Testsuite to work with Git --- tests/.gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/.gitignore b/tests/.gitignore index aab720af4..721040526 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,2 +1,3 @@ Doctrine/Tests/Proxies/ -Doctrine/Tests/ORM/Proxy/generated/ \ No newline at end of file +Doctrine/Tests/ORM/Proxy/generated/ +Doctrine/Tests/ORM/Tools/Export/export From 7d179aaf9531d51b1748d030affb2106b817ae12 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 7 Apr 2010 20:39:34 +0200 Subject: [PATCH 03/13] Current snapshot of the IBM Db2 Driver Implementation --- .../DBAL/Driver/IbmDb2/Db2Connection.php | 119 ++++++++ lib/Doctrine/DBAL/Driver/IbmDb2/Db2Driver.php | 113 +++++++ .../DBAL/Driver/IbmDb2/Db2Exception.php | 27 ++ .../DBAL/Driver/IbmDb2/Db2Statement.php | 285 ++++++++++++++++++ .../DBAL/Platforms/AbstractPlatform.php | 10 + .../DBAL/Platforms/IbmDb2Platform.php | 222 ++++++++++++++ .../DBAL/Schema/IbmDb2SchemaManager.php | 83 +++++ .../Tests/DBAL/Functional/DataAccessTest.php | 61 ++++ .../Schema/Db2SchemaManagerTest.php | 12 + .../Doctrine/Tests/DbalFunctionalTestCase.php | 4 + tests/Doctrine/Tests/TestUtil.php | 40 +-- 11 files changed, 959 insertions(+), 17 deletions(-) create mode 100644 lib/Doctrine/DBAL/Driver/IbmDb2/Db2Connection.php create mode 100644 lib/Doctrine/DBAL/Driver/IbmDb2/Db2Driver.php create mode 100644 lib/Doctrine/DBAL/Driver/IbmDb2/Db2Exception.php create mode 100644 lib/Doctrine/DBAL/Driver/IbmDb2/Db2Statement.php create mode 100644 lib/Doctrine/DBAL/Platforms/IbmDb2Platform.php create mode 100644 lib/Doctrine/DBAL/Schema/IbmDb2SchemaManager.php create mode 100644 tests/Doctrine/Tests/DBAL/Functional/DataAccessTest.php create mode 100644 tests/Doctrine/Tests/DBAL/Functional/Schema/Db2SchemaManagerTest.php diff --git a/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Connection.php b/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Connection.php new file mode 100644 index 000000000..f6909a437 --- /dev/null +++ b/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Connection.php @@ -0,0 +1,119 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\IbmDb2; + +class Db2Connection implements \Doctrine\DBAL\Driver\Connection +{ + private $_conn = null; + + public function __construct($dbname, $username, $password, $driverOptions = array(), $isPersistant = false) + { + if ($isPersistant) { + $this->_conn = db2_pconnect($dbname, $username, $password, $driverOptions); + } else { + $this->_conn = db2_connect($dbname, $username, $password, $driverOptions); + } + if (!$this->_conn) { + throw new Db2Exception(db2_conn_errormsg()); + } + } + + function prepare($sql) + { + $stmt = @db2_prepare($this->_conn, $sql); + if (!$stmt) { + throw new Db2Exception(db2_stmt_errormsg()); + } + return new Db2Statement($stmt); + } + + function query() + { + $args = func_get_args(); + $sql = $args[0]; + $stmt = $this->prepare($sql); + $stmt->execute(); + return $stmt; + } + + function quote($input, $type=\PDO::PARAM_STR) + { + $input = db2_escape_string($input); + if ($type == \PDO::PARAM_INT ) { + return $input; + } else { + return "'".$input."'"; + } + } + + function exec($statement) + { + $stmt = $this->prepare($statement); + $stmt->execute(); + return $stmt; + } + + function lastInsertId($name = null) + { + $sql = 'SELECT IDENTITY_VAL_LOCAL() AS VAL FROM SYSIBM.SYSDUMMY1'; + if ($stmt = $this->query($sql)) { + if ($col = $stmt->fetchColumn()) { + return $col; + } + } + return false; + } + + function beginTransaction() + { + db2_autocommit($this->_conn, DB2_AUTOCOMMIT_OFF); + } + + function commit() + { + if (!db2_commit($this->_conn)) { + throw new Db2Exception(db2_conn_errormsg($this->_conn)); + } + db2_autocommit($this->_conn, DB2_AUTOCOMMIT_ON); + } + + function rollBack() + { + if (!db2_rollback($this->_conn)) { + throw new Db2Exception(db2_conn_errormsg($this->_conn)); + } + db2_autocommit($this->_conn, DB2_AUTOCOMMIT_ON); + } + + function errorCode() + { + return db2_conn_error($this->_conn); + } + + function errorInfo() + { + return array( + 0 => db2_conn_errormsg($this->_conn), + 1 => $this->errorCode(), + ); + } +} \ No newline at end of file diff --git a/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Driver.php b/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Driver.php new file mode 100644 index 000000000..5a88b488c --- /dev/null +++ b/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Driver.php @@ -0,0 +1,113 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\IbmDb2; + +use Doctrine\DBAL\Driver, + Doctrine\DBAL\Connection; + +/** + * IBM Db2 Driver + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class Db2Driver implements Driver +{ + /** + * Attempts to create a connection with the database. + * + * @param array $params All connection parameters passed by the user. + * @param string $username The username to use when connecting. + * @param string $password The password to use when connecting. + * @param array $driverOptions The driver options to use when connecting. + * @return Doctrine\DBAL\Driver\Connection The database connection. + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + if ($params['host'] !== 'localhost' && $params['host'] != '127.0.0.1') { + // if the host isn't localhost, use extended connection params + $dbname = 'DRIVER={IBM DB2 ODBC DRIVER}' . + ';DATABASE=' . $params['dbname'] . + ';HOSTNAME=' . $params['host'] . + ';PORT=' . $params['port'] . + ';PROTOCOL=' . $params['protocol'] . + ';UID=' . $username . + ';PWD=' . $password .';'; + $username = null; + $password = null; + } else { + $dbname = $params['dbname']; + } + + $isPersistant = (isset($params['persistent']) && $params['persistent'] == true); + + return new Db2Connection($dbname, $username, $password, $driverOptions, $isPersistant); + } + + /** + * Gets the DatabasePlatform instance that provides all the metadata about + * the platform this driver connects to. + * + * @return Doctrine\DBAL\Platforms\AbstractPlatform The database platform. + */ + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\IbmDb2Platform; + } + + /** + * Gets the SchemaManager that can be used to inspect and change the underlying + * database schema of the platform this driver connects to. + * + * @param Doctrine\DBAL\Connection $conn + * @return Doctrine\DBAL\SchemaManager + */ + public function getSchemaManager(Connection $conn) + { + return new \Doctrine\DBAL\Schema\IbmDb2SchemaManager($conn); + } + + /** + * Gets the name of the driver. + * + * @return string The name of the driver. + */ + public function getName() + { + return 'ibm_db2'; + } + + /** + * Get the name of the database connected to for this driver. + * + * @param Doctrine\DBAL\Connection $conn + * @return string $database + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['dbname']; + } +} diff --git a/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Exception.php b/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Exception.php new file mode 100644 index 000000000..76d992b50 --- /dev/null +++ b/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Exception.php @@ -0,0 +1,27 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\IbmDb2; + +class Db2Exception extends \Exception +{ + +} \ No newline at end of file diff --git a/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Statement.php b/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Statement.php new file mode 100644 index 000000000..c4594ff04 --- /dev/null +++ b/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Statement.php @@ -0,0 +1,285 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\IbmDb2; + +class Db2Statement implements \Doctrine\DBAL\Driver\Statement +{ + private $_stmt = null; + + /** + * DB2_BINARY, DB2_CHAR, DB2_DOUBLE, or DB2_LONG + * @var + */ + static private $_typeMap = array( + \PDO::PARAM_INT => DB2_LONG, + \PDO::PARAM_STR => DB2_CHAR, + ); + + public function __construct($stmt) + { + $this->_stmt = $stmt; + } + + /** + * Binds a value to a corresponding named or positional + * placeholder in the SQL statement that was used to prepare the statement. + * + * @param mixed $param Parameter identifier. For a prepared statement using named placeholders, + * this will be a parameter name of the form :name. For a prepared statement + * using question mark placeholders, this will be the 1-indexed position of the parameter + * + * @param mixed $value The value to bind to the parameter. + * @param integer $type Explicit data type for the parameter using the PDO::PARAM_* constants. + * + * @return boolean Returns TRUE on success or FALSE on failure. + */ + function bindValue($param, $value, $type = null) + { + return $this->bindParam($param, $variable, $type); + } + + /** + * Binds a PHP variable to a corresponding named or question mark placeholder in the + * SQL statement that was use to prepare the statement. Unlike PDOStatement->bindValue(), + * the variable is bound as a reference and will only be evaluated at the time + * that PDOStatement->execute() is called. + * + * Most parameters are input parameters, that is, parameters that are + * used in a read-only fashion to build up the query. Some drivers support the invocation + * of stored procedures that return data as output parameters, and some also as input/output + * parameters that both send in data and are updated to receive it. + * + * @param mixed $param Parameter identifier. For a prepared statement using named placeholders, + * this will be a parameter name of the form :name. For a prepared statement + * using question mark placeholders, this will be the 1-indexed position of the parameter + * + * @param mixed $variable Name of the PHP variable to bind to the SQL statement parameter. + * + * @param integer $type Explicit data type for the parameter using the PDO::PARAM_* constants. To return + * an INOUT parameter from a stored procedure, use the bitwise OR operator to set the + * PDO::PARAM_INPUT_OUTPUT bits for the data_type parameter. + * @return boolean Returns TRUE on success or FALSE on failure. + */ + function bindParam($column, &$variable, $type = null) + { + if (!$type && isset(self::$_typeMap[$type])) { + $type = self::$_typeMap[$type]; + } else { + $type = DB2_CHAR; + } + + if (!db2_bind_param($this->_stmt, $column, "variable", DB2_PARAM_IN, $type)) { + throw new Db2Exception(db2_stmt_errormsg()); + } + } + + /** + * Closes the cursor, enabling the statement to be executed again. + * + * @return boolean Returns TRUE on success or FALSE on failure. + */ + function closeCursor() + { + if (!$this->_stmt) { + return false; + } + + $ret = db2_free_stmt($this->_stmt); + $this->_stmt = false; + return $ret; + } + + /** + * columnCount + * Returns the number of columns in the result set + * + * @return integer Returns the number of columns in the result set represented + * by the PDOStatement object. If there is no result set, + * this method should return 0. + */ + function columnCount() + { + if (!$this->_stmt) { + return false; + } + return db2_num_fields($this->_stmt); + } + + /** + * errorCode + * Fetch the SQLSTATE associated with the last operation on the statement handle + * + * @see Doctrine_Adapter_Interface::errorCode() + * @return string error code string + */ + function errorCode() + { + return db2_stmt_error(); + } + + /** + * errorInfo + * Fetch extended error information associated with the last operation on the statement handle + * + * @see Doctrine_Adapter_Interface::errorInfo() + * @return array error info array + */ + function errorInfo() + { + return array( + 0 => db2_stmt_errormsg(), + 1 => db2_stmt_error(), + ); + } + + /** + * Executes a prepared statement + * + * If the prepared statement included parameter markers, you must either: + * call PDOStatement->bindParam() to bind PHP variables to the parameter markers: + * bound variables pass their value as input and receive the output value, + * if any, of their associated parameter markers or pass an array of input-only + * parameter values + * + * + * @param array $params An array of values with as many elements as there are + * bound parameters in the SQL statement being executed. + * @return boolean Returns TRUE on success or FALSE on failure. + */ + function execute($params = null) + { + if (!$this->_stmt) { + return false; + } + + $retval = true; + if ($params !== null) { + $retval = @db2_execute($this->_stmt, $params); + } else { + $retval = @db2_execute($this->_stmt); + } + + if ($retval === false) { + throw new Db2Exception(db2_stmt_errormsg()); + } + return $retval; + } + + /** + * fetch + * + * @see Query::HYDRATE_* constants + * @param integer $fetchStyle Controls how the next row will be returned to the caller. + * This value must be one of the Query::HYDRATE_* constants, + * defaulting to Query::HYDRATE_BOTH + * + * @param integer $cursorOrientation For a PDOStatement object representing a scrollable cursor, + * this value determines which row will be returned to the caller. + * This value must be one of the Query::HYDRATE_ORI_* constants, defaulting to + * Query::HYDRATE_ORI_NEXT. To request a scrollable cursor for your + * PDOStatement object, + * you must set the PDO::ATTR_CURSOR attribute to Doctrine::CURSOR_SCROLL when you + * prepare the SQL statement with Doctrine_Adapter_Interface->prepare(). + * + * @param integer $cursorOffset For a PDOStatement object representing a scrollable cursor for which the + * $cursorOrientation parameter is set to Query::HYDRATE_ORI_ABS, this value specifies + * the absolute number of the row in the result set that shall be fetched. + * + * For a PDOStatement object representing a scrollable cursor for + * which the $cursorOrientation parameter is set to Query::HYDRATE_ORI_REL, this value + * specifies the row to fetch relative to the cursor position before + * PDOStatement->fetch() was called. + * + * @return mixed + */ + function fetch($fetchStyle = \PDO::FETCH_BOTH) + { + switch ($fetchStyle) { + case \PDO::FETCH_BOTH: + return db2_fetch_both($this->_stmt); + case \PDO::FETCH_ASSOC: + return db2_fetch_assoc($this->_stmt); + case \PDO::FETCH_NUM: + return db2_fetch_array($this->_stmt); + default: + throw new Db2Exception("Given Fetch-Style " . $fetchStyle . " is not supported."); + } + } + + /** + * Returns an array containing all of the result set rows + * + * @param integer $fetchStyle Controls how the next row will be returned to the caller. + * This value must be one of the Query::HYDRATE_* constants, + * defaulting to Query::HYDRATE_BOTH + * + * @param integer $columnIndex Returns the indicated 0-indexed column when the value of $fetchStyle is + * Query::HYDRATE_COLUMN. Defaults to 0. + * + * @return array + */ + function fetchAll($fetchStyle = \PDO::FETCH_BOTH) + { + $rows = array(); + while ($row = $this->fetch($fetchStyle)) { + $rows[] = $row; + } + return $rows; + } + + /** + * fetchColumn + * Returns a single column from the next row of a + * result set or FALSE if there are no more rows. + * + * @param integer $columnIndex 0-indexed number of the column you wish to retrieve from the row. If no + * value is supplied, PDOStatement->fetchColumn() + * fetches the first column. + * + * @return string returns a single column in the next row of a result set. + */ + function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(\PDO::FETCH_NUM); + if (!$row && isset($row[$columnIndex])) { + return $row[$columnIndex]; + } + return false; + } + + /** + * rowCount + * rowCount() returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement + * executed by the corresponding object. + * + * If the last SQL statement executed by the associated Statement object was a SELECT statement, + * some databases may return the number of rows returned by that statement. However, + * this behaviour is not guaranteed for all databases and should not be + * relied on for portable applications. + * + * @return integer Returns the number of rows. + */ + function rowCount() + { + return (@db2_num_rows($this->_stmt))?:0; + } +} diff --git a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php index 961ca0b7d..e2d416dda 100644 --- a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php @@ -1690,6 +1690,16 @@ abstract class AbstractPlatform return false; } + /** + * Some databases don't allow to create and drop databases at all or only with certain tools. + * + * @return bool + */ + public function supportsCreateDropDatabase() + { + return true; + } + /** * @return bool */ diff --git a/lib/Doctrine/DBAL/Platforms/IbmDb2Platform.php b/lib/Doctrine/DBAL/Platforms/IbmDb2Platform.php new file mode 100644 index 000000000..09de9c746 --- /dev/null +++ b/lib/Doctrine/DBAL/Platforms/IbmDb2Platform.php @@ -0,0 +1,222 @@ +. +*/ + +namespace Doctrine\DBAL\Platforms; + +class IbmDb2Platform extends AbstractPlatform +{ + /** + * Gets the SQL snippet used to declare a VARCHAR column type. + * + * @param array $field + */ + public function getVarcharTypeDeclarationSQL(array $field) + { + if ( ! isset($field['length'])) { + if (array_key_exists('default', $field)) { + $field['length'] = $this->getVarcharMaxLength(); + } else { + $field['length'] = false; + } + } + + $length = ($field['length'] <= $this->getVarcharMaxLength()) ? $field['length'] : false; + $fixed = (isset($field['fixed'])) ? $field['fixed'] : false; + + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); + } + + /** + * Gets the SQL snippet used to declare a CLOB column type. + * + * @param array $field + */ + public function getClobTypeDeclarationSQL(array $field) + { + // todo clob(n) with $field['length']; + return 'CLOB(1M)'; + } + + /** + * Gets the name of the platform. + * + * @return string + */ + public function getName() + { + return 'db2'; + } + + + /** + * Gets the SQL snippet that declares a boolean column. + * + * @param array $columnDef + * @return string + */ + public function getBooleanTypeDeclarationSQL(array $columnDef) + { + return 'SMALLINT'; + } + + /** + * Gets the SQL snippet that declares a 4 byte integer column. + * + * @param array $columnDef + * @return string + */ + public function getIntegerTypeDeclarationSQL(array $columnDef) + { + return 'INTEGER'; + } + + /** + * Gets the SQL snippet that declares an 8 byte integer column. + * + * @param array $columnDef + * @return string + */ + public function getBigIntTypeDeclarationSQL(array $columnDef) + { + return 'BIGINT'; + } + + /** + * Gets the SQL snippet that declares a 2 byte integer column. + * + * @param array $columnDef + * @return string + */ + public function getSmallIntTypeDeclarationSQL(array $columnDef) + { + return 'SMALLINT'; + } + + /** + * Gets the SQL snippet that declares common properties of an integer column. + * + * @param array $columnDef + * @return string + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + + } + + public function getListDatabasesSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListSequencesSQL($database) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListTableConstraintsSQL($table) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListTableColumnsSQL($table) + { + return "SELECT DISTINCT c.tabschema, c.tabname, c.colname, c.colno, + c.typename, c.default, c.nulls, c.length, c.scale, + c.identity, tc.type AS tabconsttype, k.colseq + FROM syscat.columns c + LEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tc + ON (k.tabschema = tc.tabschema + AND k.tabname = tc.tabname + AND tc.type = 'P')) + ON (c.tabschema = k.tabschema + AND c.tabname = k.tabname + AND c.colname = k.colname) + WHERE UPPER(c.tabname) = UPPER('" . $table . "') ORDER BY c.colno"; + } + + public function getListTablesSQL() + { + return "SELECT 'NAME' FROM SYSIBM.TABLES"; + } + + public function getListUsersSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Get the SQL to list all views of a database or user. + * + * @param string $database + * @return string + */ + public function getListViewsSQL($database) + { + return "SELECT NAME, TEXT FROM SYSIBM.SYSVIEWS"; + } + + public function getListTableIndexesSQL($table) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListTableForeignKeysSQL($table) + { + return "SELECT TBNAME, RELNAME, REFTBNAME, 'DELETE_RULE', 'UPDATE_RULE', FKCOLNAMES, PKCOLNAMES ". + "FROM SYSIBM.SYSRELS WHERE TBNAME = '".$table."'"; + } + + public function getCreateViewSQL($name, $sql) + { + return "CREATE VIEW ".$name." AS ".$sql; + } + + public function getDropViewSQL($name) + { + return "DROP VIEW ".$name; + } + + public function getDropSequenceSQL($sequence) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getSequenceNextValSQL($sequenceName) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getCreateDatabaseSQL($database) + { + return "CREATE DATABASE ".$database; + } + + public function getDropDatabaseSQL($database) + { + return "DROP DATABASE ".$database.";"; + } + + public function supportsCreateDropDatabase() + { + return false; + } +} \ No newline at end of file diff --git a/lib/Doctrine/DBAL/Schema/IbmDb2SchemaManager.php b/lib/Doctrine/DBAL/Schema/IbmDb2SchemaManager.php new file mode 100644 index 000000000..78c07cf33 --- /dev/null +++ b/lib/Doctrine/DBAL/Schema/IbmDb2SchemaManager.php @@ -0,0 +1,83 @@ +. +*/ + +namespace Doctrine\DBAL\Schema; + +/** + * IBM Db2 Schema Manager + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class IbmDb2SchemaManager extends AbstractSchemaManager +{ + /** + * Get Table Column Definition + * + * @param array $tableColumn + * @return Column + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + + } + + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + $tableForeignKey = array_change_key_case($tableForeignKey, CASE_LOWER); + + $tableForeignKey['delete_rule'] = $this->_getPortableForeignKeyRuleDef($tableForeignKey['delete_rule']); + $tableForeignKey['update_rule'] = $this->_getPortableForeignKeyRuleDef($tableForeignKey['update_rule']); + + return new ForeignKeyConstraint( + (array)$tableForeignKey['pkcolnames'], + $tableForeignKey['referenced_table_name'], + (array)$tableForeignKey['fkcolnames'], + $tableForeignKey['relname'], + array( + 'onUpdate' => $tableForeignKey['update_rule'], + 'onDelete' => $tableForeignKey['delete_rule'], + ) + ); + } + + protected function _getPortableForeignKeyRuleDef($def) + { + if ($def == "C") { + return "CASCADE"; + } else if ($def == "N") { + return "SET NULL"; + } + return null; + } + + protected function _getPortableViewDefinition($view) + { + $view = array_change_key_case($view, \CASE_LOWER); + $pos = strpos($view['text'], ' AS '); + $sql = substr($view['text'], $pos+4); + + return new View($view['name'], $sql); + } +} \ No newline at end of file diff --git a/tests/Doctrine/Tests/DBAL/Functional/DataAccessTest.php b/tests/Doctrine/Tests/DBAL/Functional/DataAccessTest.php new file mode 100644 index 000000000..4190025cd --- /dev/null +++ b/tests/Doctrine/Tests/DBAL/Functional/DataAccessTest.php @@ -0,0 +1,61 @@ +addColumn('test_int', 'integer'); + $table->addColumn('test_string', 'string'); + + $sm = $this->_conn->getSchemaManager(); + $sm->createTable($table); + + $this->_conn->insert('fetch_table', array('test_int' => 1, 'test_string' => 'foo')); + } catch(\Exception $e) { + + } + } + + public function testFetchAll() + { + $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?"; + $data = $this->_conn->fetchAll($sql, array(1, 'foo')); + + $this->assertEquals(1, count($data)); + + $row = $data[0]; + $this->assertEquals(2, count($row)); + + $row = array_change_key_case($row, \CASE_LOWER); + $this->assertEquals(1, $row['test_int']); + $this->assertEquals('foo', $row['test_string']); + } + + public function testFetchRow() + { + $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?"; + $row = $this->_conn->fetchRow($sql, array(1, 'foo')); + + $row = array_change_key_case($row, \CASE_LOWER); + + $this->assertEquals(1, $row['test_int']); + $this->assertEquals('foo', $row['test_string']); + } + + public function testFetchArray() + { + $sql = "SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?"; + $row = $this->_conn->fetchArray($sql, array(1, 'foo')); + + $this->assertEquals(1, $row[0]); + $this->assertEquals('foo', $row[1]); + } + +} \ No newline at end of file diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/Db2SchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/Db2SchemaManagerTest.php new file mode 100644 index 000000000..a567900c9 --- /dev/null +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/Db2SchemaManagerTest.php @@ -0,0 +1,12 @@ +real database connection using the following parameters * of the $GLOBALS array: - * + * * 'db_type' : The name of the Doctrine DBAL database driver to use. * 'db_username' : The username to use for connecting. * 'db_password' : The password to use for connecting. * 'db_host' : The hostname of the database to connect to. * 'db_name' : The name of the database to connect to. * 'db_port' : The port of the database to connect to. - * + * * Usually these variables of the $GLOBALS array are filled by PHPUnit based * on an XML configuration file. If no such parameters exist, an SQLite * in-memory database is used. - * + * * IMPORTANT: * 1) Each invocation of this method returns a NEW database connection. * 2) The database is dropped and recreated to ensure it's clean. - * + * * @return Doctrine\DBAL\Connection The database connection instance. */ public static function getConnection() @@ -52,18 +52,24 @@ class TestUtil 'dbname' => $GLOBALS['tmpdb_name'], 'port' => $GLOBALS['tmpdb_port'] ); - - // Connect to tmpdb in order to drop and create the real test db. - $tmpConn = \Doctrine\DBAL\DriverManager::getConnection($tmpDbParams); + $realConn = \Doctrine\DBAL\DriverManager::getConnection($realDbParams); - $dbname = $realConn->getDatabase(); - $realConn->close(); - - $tmpConn->getSchemaManager()->dropDatabase($dbname); - $tmpConn->getSchemaManager()->createDatabase($dbname); - - $tmpConn->close(); + $platform = $realConn->getDatabasePlatform(); + + if ($platform->supportsCreateDropDatabase()) { + $dbname = $realConn->getDatabase(); + // Connect to tmpdb in order to drop and create the real test db. + $tmpConn = \Doctrine\DBAL\DriverManager::getConnection($tmpDbParams); + $realConn->close(); + + $tmpConn->getSchemaManager()->dropDatabase($dbname); + $tmpConn->getSchemaManager()->createDatabase($dbname); + + $tmpConn->close(); + } else { + // wipe everything? + } $eventManager = null; if (isset($GLOBALS['db_event_subscribers'])) { @@ -73,9 +79,9 @@ class TestUtil $eventManager->addEventSubscriber($subscriberInstance); } } - + $conn = \Doctrine\DBAL\DriverManager::getConnection($realDbParams, null, $eventManager); - + } else { $params = array( 'driver' => 'pdo_sqlite', From 5fd6e687ce85020beaca0cd860557305553f3a1c Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 8 Apr 2010 22:40:53 +0200 Subject: [PATCH 04/13] Commit current state of IBM DB2 driver, but it segfaults the hell out of the Doctrine Testsuite --- lib/Doctrine/DBAL/Connection.php | 2 +- .../DBAL/Driver/IbmDb2/Db2Connection.php | 8 +- lib/Doctrine/DBAL/Driver/IbmDb2/Db2Driver.php | 16 +- lib/Doctrine/DBAL/DriverManager.php | 3 +- .../DBAL/Platforms/AbstractPlatform.php | 2 +- .../{IbmDb2Platform.php => Db2Platform.php} | 188 ++++++++++++++++- lib/Doctrine/DBAL/Platforms/MySqlPlatform.php | 87 +------- lib/Doctrine/DBAL/Schema/Db2SchemaManager.php | 195 ++++++++++++++++++ .../DBAL/Schema/IbmDb2SchemaManager.php | 83 -------- .../SchemaManagerFunctionalTestCase.php | 26 ++- tests/Doctrine/Tests/TestUtil.php | 8 +- 11 files changed, 421 insertions(+), 197 deletions(-) rename lib/Doctrine/DBAL/Platforms/{IbmDb2Platform.php => Db2Platform.php} (52%) create mode 100644 lib/Doctrine/DBAL/Schema/Db2SchemaManager.php delete mode 100644 lib/Doctrine/DBAL/Schema/IbmDb2SchemaManager.php diff --git a/lib/Doctrine/DBAL/Connection.php b/lib/Doctrine/DBAL/Connection.php index 92a3480d9..eb16a6aac 100644 --- a/lib/Doctrine/DBAL/Connection.php +++ b/lib/Doctrine/DBAL/Connection.php @@ -791,7 +791,7 @@ class Connection implements DriverConnection * Gets the SchemaManager that can be used to inspect or change the * database schema through the connection. * - * @return Doctrine\DBAL\Schema\SchemaManager + * @return Doctrine\DBAL\Schema\AbstractSchemaManager */ public function getSchemaManager() { diff --git a/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Connection.php b/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Connection.php index f6909a437..bc180fac6 100644 --- a/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Connection.php +++ b/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Connection.php @@ -25,12 +25,14 @@ class Db2Connection implements \Doctrine\DBAL\Driver\Connection { private $_conn = null; - public function __construct($dbname, $username, $password, $driverOptions = array(), $isPersistant = false) + public function __construct(array $params, $username, $password, $driverOptions = array()) { + $isPersistant = (isset($params['persistent']) && $params['persistent'] == true); + if ($isPersistant) { - $this->_conn = db2_pconnect($dbname, $username, $password, $driverOptions); + $this->_conn = db2_pconnect($params['dbname'], $username, $password, $driverOptions); } else { - $this->_conn = db2_connect($dbname, $username, $password, $driverOptions); + $this->_conn = db2_connect($params['dbname'], $username, $password, $driverOptions); } if (!$this->_conn) { throw new Db2Exception(db2_conn_errormsg()); diff --git a/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Driver.php b/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Driver.php index 5a88b488c..3522d0e37 100644 --- a/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Driver.php +++ b/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Driver.php @@ -46,9 +46,13 @@ class Db2Driver implements Driver */ public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) { + if ( !isset($params['schema']) ) { + + } + if ($params['host'] !== 'localhost' && $params['host'] != '127.0.0.1') { // if the host isn't localhost, use extended connection params - $dbname = 'DRIVER={IBM DB2 ODBC DRIVER}' . + $params['dbname'] = 'DRIVER={IBM DB2 ODBC DRIVER}' . ';DATABASE=' . $params['dbname'] . ';HOSTNAME=' . $params['host'] . ';PORT=' . $params['port'] . @@ -57,13 +61,9 @@ class Db2Driver implements Driver ';PWD=' . $password .';'; $username = null; $password = null; - } else { - $dbname = $params['dbname']; } - $isPersistant = (isset($params['persistent']) && $params['persistent'] == true); - - return new Db2Connection($dbname, $username, $password, $driverOptions, $isPersistant); + return new Db2Connection($params, $username, $password, $driverOptions); } /** @@ -74,7 +74,7 @@ class Db2Driver implements Driver */ public function getDatabasePlatform() { - return new \Doctrine\DBAL\Platforms\IbmDb2Platform; + return new \Doctrine\DBAL\Platforms\Db2Platform; } /** @@ -86,7 +86,7 @@ class Db2Driver implements Driver */ public function getSchemaManager(Connection $conn) { - return new \Doctrine\DBAL\Schema\IbmDb2SchemaManager($conn); + return new \Doctrine\DBAL\Schema\Db2SchemaManager($conn); } /** diff --git a/lib/Doctrine/DBAL/DriverManager.php b/lib/Doctrine/DBAL/DriverManager.php index 3a99c0073..4ed2ac1f3 100644 --- a/lib/Doctrine/DBAL/DriverManager.php +++ b/lib/Doctrine/DBAL/DriverManager.php @@ -43,7 +43,8 @@ final class DriverManager 'pdo_pgsql' => 'Doctrine\DBAL\Driver\PDOPgSql\Driver', 'pdo_oci' => 'Doctrine\DBAL\Driver\PDOOracle\Driver', 'pdo_mssql' => 'Doctrine\DBAL\Driver\PDOMsSql\Driver', - 'oci8' => 'Doctrine\DBAL\Driver\OCI8\Driver' + 'oci8' => 'Doctrine\DBAL\Driver\OCI8\Driver', + 'ibm_db2' => 'Doctrine\DBAL\Driver\IbmDb2\Db2Driver', ); /** Private constructor. This class cannot be instantiated. */ diff --git a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php index e2d416dda..d991c4b1e 100644 --- a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php @@ -1095,7 +1095,7 @@ abstract class AbstractPlatform throw \InvalidArgumentException("Incomplete definition. 'columns' required."); } - return 'CONSTRAINT' . $name . ' UNIQUE (' + return 'CONSTRAINT ' . $name . ' UNIQUE (' . $this->getIndexFieldDeclarationListSQL($index->getColumns()) . ')'; } diff --git a/lib/Doctrine/DBAL/Platforms/IbmDb2Platform.php b/lib/Doctrine/DBAL/Platforms/Db2Platform.php similarity index 52% rename from lib/Doctrine/DBAL/Platforms/IbmDb2Platform.php rename to lib/Doctrine/DBAL/Platforms/Db2Platform.php index 09de9c746..955440f43 100644 --- a/lib/Doctrine/DBAL/Platforms/IbmDb2Platform.php +++ b/lib/Doctrine/DBAL/Platforms/Db2Platform.php @@ -21,7 +21,11 @@ namespace Doctrine\DBAL\Platforms; -class IbmDb2Platform extends AbstractPlatform +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\TableDiff; + +class Db2Platform extends AbstractPlatform { /** * Gets the SQL snippet used to declare a VARCHAR column type. @@ -122,6 +126,42 @@ class IbmDb2Platform extends AbstractPlatform } + /** + * Obtain DBMS specific SQL to be used to create datetime fields in + * statements like CREATE TABLE + * + * @param array $fieldDeclaration + * @return string + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP(0)'; + } + + /** + * Obtain DBMS specific SQL to be used to create date fields in statements + * like CREATE TABLE. + * + * @param array $fieldDeclaration + * @return string + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * Obtain DBMS specific SQL to be used to create time fields in statements + * like CREATE TABLE. + * + * @param array $fieldDeclaration + * @return string + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME'; + } + public function getListDatabasesSQL() { throw DBALException::notSupported(__METHOD__); @@ -137,6 +177,13 @@ class IbmDb2Platform extends AbstractPlatform throw DBALException::notSupported(__METHOD__); } + /** + * This code fragment is originally from the Zend_Db_Adapter_Db2 class. + * + * @license New BSD License + * @param string $table + * @return string + */ public function getListTableColumnsSQL($table) { return "SELECT DISTINCT c.tabschema, c.tabname, c.colname, c.colno, @@ -155,7 +202,7 @@ class IbmDb2Platform extends AbstractPlatform public function getListTablesSQL() { - return "SELECT 'NAME' FROM SYSIBM.TABLES"; + return "SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = 'T'"; } public function getListUsersSQL() @@ -176,13 +223,13 @@ class IbmDb2Platform extends AbstractPlatform public function getListTableIndexesSQL($table) { - throw DBALException::notSupported(__METHOD__); + return "SELECT NAME, COLNAMES, UNIQUERULE FROM SYSIBM.SYSINDEXES WHERE TBNAME = UPPER('" . $table . "')"; } public function getListTableForeignKeysSQL($table) { - return "SELECT TBNAME, RELNAME, REFTBNAME, 'DELETE_RULE', 'UPDATE_RULE', FKCOLNAMES, PKCOLNAMES ". - "FROM SYSIBM.SYSRELS WHERE TBNAME = '".$table."'"; + return "SELECT TBNAME, RELNAME, REFTBNAME, DELETERULE, UPDATERULE, FKCOLNAMES, PKCOLNAMES ". + "FROM SYSIBM.SYSRELS WHERE TBNAME = UPPER('".$table."')"; } public function getCreateViewSQL($name, $sql) @@ -219,4 +266,135 @@ class IbmDb2Platform extends AbstractPlatform { return false; } + + /** + * Gets the SQL specific for the platform to get the current date. + * + * @return string + */ + public function getCurrentDateSQL() + { + return 'current date'; + } + + /** + * Gets the SQL specific for the platform to get the current time. + * + * @return string + */ + public function getCurrentTimeSQL() + { + return 'current time'; + } + + /** + * Gets the SQL specific for the platform to get the current timestamp + * + * @return string + */ + public function getCurrentTimestampSQL() + { + return 'current timestamp'; + } + + /** + * Obtain DBMS specific SQL code portion needed to set an index + * declaration to be used in statements like CREATE TABLE. + * + * @param string $name name of the index + * @param Index $index index definition + * @return string DBMS specific SQL code portion needed to set an index + */ + public function getIndexDeclarationSQL($name, Index $index) + { + return $this->getUniqueConstraintDeclarationSQL($name, $index); + } + + /** + * @param string $tableName + * @param array $columns + * @param array $options + * @return array + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $indexes = array(); + if (isset($options['indexes'])) { + $indexes = $options['indexes']; + } + $options['indexes'] = array(); + + $sqls = parent::_getCreateTableSQL($tableName, $columns, $options); + + foreach ($indexes as $index => $definition) { + $sqls[] = $this->getCreateIndexSQL($definition, $tableName); + } + return $sqls; + } + + /** + * Gets the SQL to alter an existing table. + * + * @param TableDiff $diff + * @return array + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = array(); + + $queryParts = array(); + foreach ($diff->addedColumns AS $fieldName => $column) { + $queryParts[] = 'ADD COLUMN ' . $this->getColumnDeclarationSQL($column->getName(), $column->toArray()); + } + + foreach ($diff->removedColumns AS $column) { + $queryParts[] = 'DROP COLUMN ' . $column->getName(); + } + + foreach ($diff->changedColumns AS $columnDiff) { + /* @var $columnDiff Doctrine\DBAL\Schema\ColumnDiff */ + $column = $columnDiff->column; + $queryParts[] = 'ALTER ' . ($columnDiff->oldColumnName) . ' ' + . $this->getColumnDeclarationSQL($column->getName(), $column->toArray()); + } + + foreach ($diff->renamedColumns AS $oldColumnName => $column) { + $queryParts[] = 'RENAME ' . $oldColumnName . ' TO ' . $column->getName(); + } + + if (count($queryParts) > 0) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . implode(" ", $queryParts); + } + + $sql = array_merge($sql, $this->_getAlterTableIndexForeignKeySQL($diff)); + + if ($diff->newName !== false) { + $sql[] = 'RENAME TABLE TO ' . $diff->newName; + } + + return $sql; + } + + public function getDefaultValueDeclarationSQL($field) + { + if (isset($field['notnull']) && $field['notnull'] && !isset($field['default'])) { + if (in_array((string)$field['type'], array("Integer", "BigInteger", "SmallInteger"))) { + $field['default'] = 0; + } else { + $field['default'] = ''; + } + } + + return parent::getDefaultValueDeclarationSQL($field); + } + + public function supportsIdentityColumns() + { + return true; + } + + public function prefersIdentityColumns() + { + return true; + } } \ No newline at end of file diff --git a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php index 5bf9b84c2..c7793de95 100644 --- a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php @@ -412,91 +412,8 @@ class MySqlPlatform extends AbstractPlatform /** * Gets the SQL to alter an existing table. * - * @param string $name The name of the table that is intended to be changed. - * @param array $changes Associative array that contains the details of each type - * of change that is intended to be performed. The types of - * changes that are currently supported are defined as follows: - * - * name - * - * New name for the table. - * - * add - * - * Associative array with the names of fields to be added as - * indexes of the array. The value of each entry of the array - * should be set to another associative array with the properties - * of the fields to be added. The properties of the fields should - * be the same as defined by the Metabase parser. - * - * - * remove - * - * Associative array with the names of fields to be removed as indexes - * of the array. Currently the values assigned to each entry are ignored. - * An empty array should be used for future compatibility. - * - * rename - * - * Associative array with the names of fields to be renamed as indexes - * of the array. The value of each entry of the array should be set to - * another associative array with the entry named name with the new - * field name and the entry named Declaration that is expected to contain - * the portion of the field declaration already in DBMS specific SQL code - * as it is used in the CREATE TABLE statement. - * - * change - * - * Associative array with the names of the fields to be changed as indexes - * of the array. Keep in mind that if it is intended to change either the - * name of a field and any other properties, the change array entries - * should have the new names of the fields as array indexes. - * - * The value of each entry of the array should be set to another associative - * array with the properties of the fields to that are meant to be changed as - * array entries. These entries should be assigned to the new values of the - * respective properties. The properties of the fields should be the same - * as defined by the Metabase parser. - * - * Example - * array( - * 'name' => 'userlist', - * 'add' => array( - * 'quota' => array( - * 'type' => 'integer', - * 'unsigned' => 1 - * ) - * ), - * 'remove' => array( - * 'file_limit' => array(), - * 'time_limit' => array() - * ), - * 'change' => array( - * 'name' => array( - * 'length' => '20', - * 'definition' => array( - * 'type' => 'text', - * 'length' => 20, - * ), - * ) - * ), - * 'rename' => array( - * 'sex' => array( - * 'name' => 'gender', - * 'definition' => array( - * 'type' => 'text', - * 'length' => 1, - * 'default' => 'M', - * ), - * ) - * ) - * ) - * - * @param boolean $check indicates whether the function should just check if the DBMS driver - * can perform the requested table alterations if the value is true or - * actually perform them otherwise. - * @return boolean - * @override + * @param TableDiff $diff + * @return array */ public function getAlterTableSQL(TableDiff $diff) { diff --git a/lib/Doctrine/DBAL/Schema/Db2SchemaManager.php b/lib/Doctrine/DBAL/Schema/Db2SchemaManager.php new file mode 100644 index 000000000..b3d802bac --- /dev/null +++ b/lib/Doctrine/DBAL/Schema/Db2SchemaManager.php @@ -0,0 +1,195 @@ +. +*/ + +namespace Doctrine\DBAL\Schema; + +/** + * IBM Db2 Schema Manager + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class Db2SchemaManager extends AbstractSchemaManager +{ + /** + * Return a list of all tables in the current database + * + * Apparently creator is the schema not the user who created it: + * {@link http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=/com.ibm.db29.doc.sqlref/db2z_sysibmsystablestable.htm} + * + * @return array + */ + public function listTableNames() + { + + $sql = $this->_platform->getListTablesSQL(); + $sql .= " AND CREATOR = UPPER('".$this->_conn->getUsername()."')"; + + $tables = $this->_conn->fetchAll($sql); + + return $this->_getPortableTablesList($tables); + } + + + /** + * Get Table Column Definition + * + * @param array $tableColumn + * @return Column + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = array_change_key_case($tableColumn, \CASE_LOWER); + + $length = null; + $fixed = null; + $unsigned = false; + $scale = false; + $precision = false; + + switch (strtolower($tableColumn['typename'])) { + case 'smallint': + case 'bigint': + case 'integer': + case 'time': + case 'date': + $type = strtolower($tableColumn['typename']); + break; + case 'varchar': + $type = 'string'; + $length = $tableColumn['length']; + $fixed = false; + break; + case 'character': + $type = 'string'; + $length = $tableColumn['length']; + $fixed = true; + break; + case 'clob': + $type = 'text'; + $length = $tableColumn['length']; + break; + case 'decimal': + case 'double': + case 'real': + $type = 'decimal'; + $scale = $tableColumn['scale']; + $precision = $tableColumn['length']; + break; + case 'timestamp': + $type = 'datetime'; + break; + default: + throw new \Doctrine\DBAL\DBALException("Unknown Type: ".$tableColumn['typename']); + } + + $options = array( + 'length' => $length, + 'unsigned' => (bool)$unsigned, + 'fixed' => (bool)$fixed, + 'default' => ($tableColumn['default'] == "NULL") ? null : $tableColumn['default'], + 'notnull' => (bool) ($tableColumn['nulls'] == 'N'), + 'scale' => null, + 'precision' => null, + 'platformOptions' => array(), + ); + + if ($scale !== null && $precision !== null) { + $options['scale'] = $scale; + $options['precision'] = $precision; + } + + return new Column($tableColumn['colname'], \Doctrine\DBAL\Types\Type::getType($type), $options); + } + + protected function _getPortableTablesList($tables) + { + $tableNames = array(); + foreach ($tables AS $tableRow) { + $tableRow = array_change_key_case($tableRow, \CASE_LOWER); + $tableNames[] = $tableRow['name']; + } + return $tableNames; + } + + protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) + { + $tableIndexRows = array(); + $indexes = array(); + foreach($tableIndexes AS $indexKey => $data) { + $data = array_change_key_case($data, \CASE_LOWER); + $unique = ($data['uniquerule'] == "D") ? false : true; + $primary = ($data['uniquerule'] == "P"); + + $indexName = strtolower($data['name']); + if ($primary) { + $keyName = 'primary'; + } else { + $keyName = $indexName; + } + + $indexes[$keyName] = new Index($indexName, explode("+", ltrim($data['colnames'], '+')), $unique, $primary); + } + + return $indexes; + } + + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + $tableForeignKey = array_change_key_case($tableForeignKey, CASE_LOWER); + + $tableForeignKey['deleterule'] = $this->_getPortableForeignKeyRuleDef($tableForeignKey['deleterule']); + $tableForeignKey['updaterule'] = $this->_getPortableForeignKeyRuleDef($tableForeignKey['updaterule']); + + return new ForeignKeyConstraint( + array_map('trim', (array)$tableForeignKey['fkcolnames']), + $tableForeignKey['reftbname'], + array_map('trim', (array)$tableForeignKey['pkcolnames']), + $tableForeignKey['relname'], + array( + 'onUpdate' => $tableForeignKey['updaterule'], + 'onDelete' => $tableForeignKey['deleterule'], + ) + ); + } + + protected function _getPortableForeignKeyRuleDef($def) + { + if ($def == "C") { + return "CASCADE"; + } else if ($def == "N") { + return "SET NULL"; + } + return null; + } + + protected function _getPortableViewDefinition($view) + { + $view = array_change_key_case($view, \CASE_LOWER); + $pos = strpos($view['text'], ' AS '); + $sql = substr($view['text'], $pos+4); + + return new View($view['name'], $sql); + } +} \ No newline at end of file diff --git a/lib/Doctrine/DBAL/Schema/IbmDb2SchemaManager.php b/lib/Doctrine/DBAL/Schema/IbmDb2SchemaManager.php deleted file mode 100644 index 78c07cf33..000000000 --- a/lib/Doctrine/DBAL/Schema/IbmDb2SchemaManager.php +++ /dev/null @@ -1,83 +0,0 @@ -. -*/ - -namespace Doctrine\DBAL\Schema; - -/** - * IBM Db2 Schema Manager - * - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link www.doctrine-project.com - * @since 1.0 - * @version $Revision$ - * @author Benjamin Eberlei - */ -class IbmDb2SchemaManager extends AbstractSchemaManager -{ - /** - * Get Table Column Definition - * - * @param array $tableColumn - * @return Column - */ - protected function _getPortableTableColumnDefinition($tableColumn) - { - - } - - protected function _getPortableTableForeignKeyDefinition($tableForeignKey) - { - $tableForeignKey = array_change_key_case($tableForeignKey, CASE_LOWER); - - $tableForeignKey['delete_rule'] = $this->_getPortableForeignKeyRuleDef($tableForeignKey['delete_rule']); - $tableForeignKey['update_rule'] = $this->_getPortableForeignKeyRuleDef($tableForeignKey['update_rule']); - - return new ForeignKeyConstraint( - (array)$tableForeignKey['pkcolnames'], - $tableForeignKey['referenced_table_name'], - (array)$tableForeignKey['fkcolnames'], - $tableForeignKey['relname'], - array( - 'onUpdate' => $tableForeignKey['update_rule'], - 'onDelete' => $tableForeignKey['delete_rule'], - ) - ); - } - - protected function _getPortableForeignKeyRuleDef($def) - { - if ($def == "C") { - return "CASCADE"; - } else if ($def == "N") { - return "SET NULL"; - } - return null; - } - - protected function _getPortableViewDefinition($view) - { - $view = array_change_key_case($view, \CASE_LOWER); - $pos = strpos($view['text'], ' AS '); - $sql = substr($view['text'], $pos+4); - - return new View($view['name'], $sql); - } -} \ No newline at end of file diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php index ef770c756..167c9290b 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php @@ -28,6 +28,8 @@ class SchemaManagerFunctionalTestCase extends \Doctrine\Tests\DbalFunctionalTest $this->markTestSkipped('The ' . $testClass .' requires the use of ' . $dbms); } + #$this->_conn->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger); + $this->_sm = $this->_conn->getSchemaManager(); } @@ -59,6 +61,10 @@ class SchemaManagerFunctionalTestCase extends \Doctrine\Tests\DbalFunctionalTest public function testListDatabases() { + if (!$this->_sm->getDatabasePlatform()->supportsCreateDropDatabase()) { + $this->markTestSkipped('Cannot drop Database client side with this Driver.'); + } + $this->_sm->dropAndCreateDatabase('test_create_database'); $databases = $this->_sm->listDatabases(); @@ -73,12 +79,12 @@ class SchemaManagerFunctionalTestCase extends \Doctrine\Tests\DbalFunctionalTest $tables = $this->_sm->listTables(); $this->assertType('array', $tables); - $this->assertTrue(count($tables) > 0); + $this->assertTrue(count($tables) > 0, "List Tables has to find at least one table named 'list_tables_test'."); $foundTable = false; foreach ($tables AS $table) { $this->assertType('Doctrine\DBAL\Schema\Table', $table); - if ($table->getName() == 'list_tables_test') { + if (strtolower($table->getName()) == 'list_tables_test') { $foundTable = true; $this->assertTrue($table->hasColumn('id')); @@ -86,6 +92,8 @@ class SchemaManagerFunctionalTestCase extends \Doctrine\Tests\DbalFunctionalTest $this->assertTrue($table->hasColumn('foreign_key_test')); } } + + $this->assertTrue( $foundTable , "The 'list_tables_test' table has to be found."); } public function testListTableColumns() @@ -122,7 +130,6 @@ class SchemaManagerFunctionalTestCase extends \Doctrine\Tests\DbalFunctionalTest $this->assertEquals('foo', strtolower($columns['foo']->getname())); $this->assertType('Doctrine\DBAL\Types\TextType', $columns['foo']->gettype()); - $this->assertEquals(null, $columns['foo']->getlength()); $this->assertEquals(false, $columns['foo']->getunsigned()); $this->assertEquals(false, $columns['foo']->getfixed()); $this->assertEquals(true, $columns['foo']->getnotnull()); @@ -171,6 +178,7 @@ class SchemaManagerFunctionalTestCase extends \Doctrine\Tests\DbalFunctionalTest $this->assertEquals(3, count($tableIndexes)); + $this->assertArrayHasKey('primary', $tableIndexes, 'listTableIndexes() has to return a "primary" array key.'); $this->assertEquals(array('id'), array_map('strtolower', $tableIndexes['primary']->getColumns())); $this->assertTrue($tableIndexes['primary']->isUnique()); $this->assertTrue($tableIndexes['primary']->isPrimary()); @@ -218,7 +226,7 @@ class SchemaManagerFunctionalTestCase extends \Doctrine\Tests\DbalFunctionalTest $this->_sm->dropAndCreateTable($tableA); $fkConstraints = $this->_sm->listTableForeignKeys('test_create_fk'); - $this->assertEquals(1, count($fkConstraints)); + $this->assertEquals(1, count($fkConstraints), "Table 'test_create_fk1' has to have one foreign key."); $fkConstraint = current($fkConstraints); $this->assertType('\Doctrine\DBAL\Schema\ForeignKeyConstraint', $fkConstraint); @@ -237,22 +245,20 @@ class SchemaManagerFunctionalTestCase extends \Doctrine\Tests\DbalFunctionalTest $this->createTestTable('test_create_fk2'); $foreignKey = new \Doctrine\DBAL\Schema\ForeignKeyConstraint( - array('foreign_key_test'), 'test_create_fk2', array('id'), 'foreign_key_test_fk', array('onUpdate' => 'CASCADE', 'onDelete' => 'CASCADE') + array('foreign_key_test'), 'test_create_fk2', array('id'), 'foreign_key_test_fk', array('onDelete' => 'CASCADE') ); $this->_sm->createForeignKey($foreignKey, 'test_create_fk1'); $fkeys = $this->_sm->listTableForeignKeys('test_create_fk1'); - $this->assertEquals(1, count($fkeys)); + $this->assertEquals(1, count($fkeys), "Table 'test_create_fk1' has to have one foreign key."); + $this->assertType('Doctrine\DBAL\Schema\ForeignKeyConstraint', $fkeys[0]); $this->assertEquals(array('foreign_key_test'), array_map('strtolower', $fkeys[0]->getLocalColumns())); $this->assertEquals(array('id'), array_map('strtolower', $fkeys[0]->getForeignColumns())); $this->assertEquals('test_create_fk2', strtolower($fkeys[0]->getForeignTableName())); - if($fkeys[0]->hasOption('onUpdate')) { - $this->assertEquals('CASCADE', $fkeys[0]->getOption('onUpdate')); - } if($fkeys[0]->hasOption('onDelete')) { $this->assertEquals('CASCADE', $fkeys[0]->getOption('onDelete')); } @@ -278,6 +284,8 @@ class SchemaManagerFunctionalTestCase extends \Doctrine\Tests\DbalFunctionalTest $this->markTestSkipped('Alter Table is not supported by this platform.'); } + $this->_conn->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger); + $this->createTestTable('alter_table'); $this->createTestTable('alter_table_foreign'); diff --git a/tests/Doctrine/Tests/TestUtil.php b/tests/Doctrine/Tests/TestUtil.php index 2f266d6d9..185bb65a7 100644 --- a/tests/Doctrine/Tests/TestUtil.php +++ b/tests/Doctrine/Tests/TestUtil.php @@ -68,7 +68,13 @@ class TestUtil $tmpConn->close(); } else { - // wipe everything? + $sm = $realConn->getSchemaManager(); + + $tableNames = $sm->listTableNames(); + + foreach ($tableNames AS $tableName) { + $sm->dropTable($tableName); + } } $eventManager = null; From 024b2bab91ccf0c6ce9a0729b07762b6f9ee2593 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 18 Apr 2010 19:12:38 +0200 Subject: [PATCH 05/13] DDC-496 Finished first versions of platform and schema manager for DB2, DDC-528 Added support for PDO_IBM driver, passing all but 3 tests that are related to CLOB fields --- lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php | 126 +++++++++++++++++ lib/Doctrine/DBAL/DriverManager.php | 1 + .../DBAL/Platforms/AbstractPlatform.php | 11 ++ lib/Doctrine/DBAL/Platforms/Db2Platform.php | 132 +++++++++++++++++- lib/Doctrine/DBAL/Schema/Db2SchemaManager.php | 10 +- lib/Doctrine/DBAL/Types/ArrayType.php | 1 + lib/Doctrine/DBAL/Types/ObjectType.php | 1 + lib/Doctrine/DBAL/Types/TextType.php | 13 ++ .../Query/Exec/MultiTableDeleteExecutor.php | 5 +- .../Query/Exec/MultiTableUpdateExecutor.php | 5 +- .../Tests/DBAL/Functional/AllTests.php | 1 + .../SchemaManagerFunctionalTestCase.php | 2 - .../Tests/ORM/Functional/NativeQueryTest.php | 33 +++-- .../Doctrine/Tests/OrmFunctionalTestCase.php | 2 +- 14 files changed, 309 insertions(+), 34 deletions(-) create mode 100644 lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php diff --git a/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php b/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php new file mode 100644 index 000000000..3f05f5c75 --- /dev/null +++ b/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php @@ -0,0 +1,126 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\PDOIbm; + +use Doctrine\DBAL\Connection; + +/** + * Driver for the PDO IBM extension + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Driver implements \Doctrine\DBAL\Driver +{ + /** + * Attempts to establish a connection with the underlying driver. + * + * @param array $params + * @param string $username + * @param string $password + * @param array $driverOptions + * @return Doctrine\DBAL\Driver\Connection + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + $conn = new \Doctrine\DBAL\Driver\PDOConnection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + return $conn; + } + + /** + * Constructs the MySql PDO DSN. + * + * @return string The DSN. + */ + private function _constructPdoDsn(array $params) + { + $dsn = 'ibm:'; + if (isset($params['host'])) { + $dsn .= 'HOSTNAME=' . $params['host'] . ';'; + } + if (isset($params['port'])) { + $dsn .= 'PORT=' . $params['port'] . ';'; + } + $dsn .= 'PROTOCOL=TCPIP;'; + if (isset($params['dbname'])) { + $dsn .= 'DATABASE=' . $params['dbname'] . ';'; + } + + return $dsn; + } + + /** + * Gets the DatabasePlatform instance that provides all the metadata about + * the platform this driver connects to. + * + * @return Doctrine\DBAL\Platforms\AbstractPlatform The database platform. + */ + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\Db2Platform; + } + + /** + * Gets the SchemaManager that can be used to inspect and change the underlying + * database schema of the platform this driver connects to. + * + * @param Doctrine\DBAL\Connection $conn + * @return Doctrine\DBAL\SchemaManager + */ + public function getSchemaManager(Connection $conn) + { + return new \Doctrine\DBAL\Schema\Db2SchemaManager($conn); + } + + /** + * Gets the name of the driver. + * + * @return string The name of the driver. + */ + public function getName() + { + return 'pdo_ibm'; + } + + /** + * Get the name of the database connected to for this driver. + * + * @param Doctrine\DBAL\Connection $conn + * @return string $database + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['dbname']; + } +} \ No newline at end of file diff --git a/lib/Doctrine/DBAL/DriverManager.php b/lib/Doctrine/DBAL/DriverManager.php index 4ed2ac1f3..21a5c09d4 100644 --- a/lib/Doctrine/DBAL/DriverManager.php +++ b/lib/Doctrine/DBAL/DriverManager.php @@ -45,6 +45,7 @@ final class DriverManager 'pdo_mssql' => 'Doctrine\DBAL\Driver\PDOMsSql\Driver', 'oci8' => 'Doctrine\DBAL\Driver\OCI8\Driver', 'ibm_db2' => 'Doctrine\DBAL\Driver\IbmDb2\Db2Driver', + 'pdo_ibm' => 'Doctrine\DBAL\Driver\PDOIbm\Driver', ); /** Private constructor. This class cannot be instantiated. */ diff --git a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php index d991c4b1e..702e6a8dc 100644 --- a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php @@ -1177,6 +1177,17 @@ abstract class AbstractPlatform return 'TEMPORARY'; } + /** + * Some vendors require temporary table names to be qualified specially. + * + * @param string $tableName + * @return string + */ + public function getTemporaryTableName($tableName) + { + return $tableName; + } + /** * Get sql query to show a list of database. * diff --git a/lib/Doctrine/DBAL/Platforms/Db2Platform.php b/lib/Doctrine/DBAL/Platforms/Db2Platform.php index 955440f43..b02d2eade 100644 --- a/lib/Doctrine/DBAL/Platforms/Db2Platform.php +++ b/lib/Doctrine/DBAL/Platforms/Db2Platform.php @@ -90,7 +90,7 @@ class Db2Platform extends AbstractPlatform */ public function getIntegerTypeDeclarationSQL(array $columnDef) { - return 'INTEGER'; + return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef); } /** @@ -101,7 +101,7 @@ class Db2Platform extends AbstractPlatform */ public function getBigIntTypeDeclarationSQL(array $columnDef) { - return 'BIGINT'; + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef); } /** @@ -112,7 +112,7 @@ class Db2Platform extends AbstractPlatform */ public function getSmallIntTypeDeclarationSQL(array $columnDef) { - return 'SMALLINT'; + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef); } /** @@ -123,7 +123,11 @@ class Db2Platform extends AbstractPlatform */ protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) { - + $autoinc = ''; + if ( ! empty($columnDef['autoincrement'])) { + $autoinc = ' GENERATED BY DEFAULT AS IDENTITY'; + } + return $autoinc; } /** @@ -135,6 +139,10 @@ class Db2Platform extends AbstractPlatform */ public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) { + if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) { + return "TIMESTAMP(0) WITH DEFAULT"; + } + return 'TIMESTAMP(0)'; } @@ -292,10 +300,10 @@ class Db2Platform extends AbstractPlatform * * @return string */ - public function getCurrentTimestampSQL() + /*public function getCurrentTimestampSQL() { return 'current timestamp'; - } + }*/ /** * Obtain DBMS specific SQL code portion needed to set an index @@ -380,14 +388,113 @@ class Db2Platform extends AbstractPlatform if (isset($field['notnull']) && $field['notnull'] && !isset($field['default'])) { if (in_array((string)$field['type'], array("Integer", "BigInteger", "SmallInteger"))) { $field['default'] = 0; + } else if((string)$field['type'] == "DateTime") { + $field['default'] = "00-00-00 00:00:00"; + } else if ((string)$field['type'] == "Date") { + $field['default'] = "00-00-00"; + } else if((string)$field['type'] == "Time") { + $field['default'] = "00:00:00"; } else { $field['default'] = ''; } } + unset($field['default']); // @todo this needs fixing + if (isset($field['version']) && $field['version']) { + if ((string)$field['type'] != "DateTime") { + $field['default'] = "1"; + } + } + return parent::getDefaultValueDeclarationSQL($field); } + /** + * Get the insert sql for an empty insert statement + * + * @param string $tableName + * @param string $identifierColumnName + * @return string $sql + */ + public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName) + { + return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (DEFAULT)'; + } + + public function getCreateTemporaryTableSnippetSQL() + { + return "DECLARE GLOBAL TEMPORARY TABLE"; + } + + /** + * DB2 automatically moves temporary tables into the SESSION. schema. + * + * @param string $tableName + * @return string + */ + public function getTemporaryTableName($tableName) + { + return "SESSION." . $tableName; + } + + public function getCurrentTimestampSQL() + { + return "VALUES CURRENT TIMESTAMP"; + } + + public function modifyLimitQuery($query, $limit, $offset = null) + { + if ($limit === null && $offset === null) { + return $query; + } + + $limit = (int)$limit; + $offset = (int)(($offset)?:0); + + // Todo OVER() needs ORDER BY data! + $sql = 'SELECT db22.* FROM (SELECT ROW_NUMBER() OVER() AS DC_ROWNUM, db21.* '. + 'FROM (' . $query . ') db21) db22 WHERE db22.DC_ROWNUM BETWEEN ' . ($offset+1) .' AND ' . ($offset+$limit); + return $sql; + } + + /** + * returns the position of the first occurrence of substring $substr in string $str + * + * @param string $substr literal string to find + * @param string $str literal string + * @param int $pos position to start at, beginning of string by default + * @return integer + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'LOCATE(' . $substr . ', ' . $str . ')'; + } else { + return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')'; + } + } + + /** + * return string to call a function to get a substring inside an SQL statement + * + * Note: Not SQL92, but common functionality. + * + * SQLite only supports the 2 parameter variant of this function + * + * @param string $value an sql string literal or column name/alias + * @param integer $from where to start the substring portion + * @param integer $len the substring portion length + * @return string + */ + public function getSubstringExpression($value, $from, $len = null) + { + if ($len === null) + return 'SUBSTR(' . $value . ', ' . $from . ')'; + else { + return 'SUBSTR(' . $value . ', ' . $from . ', ' . $len . ')'; + } + } + public function supportsIdentityColumns() { return true; @@ -397,4 +504,17 @@ class Db2Platform extends AbstractPlatform { return true; } + + /** + * Gets the character casing of a column in an SQL result set of this platform. + * + * DB2 returns all column names in SQL result sets in uppercase. + * + * @param string $column The column name for which to get the correct character casing. + * @return string The column name in the character casing used in SQL result sets. + */ + public function getSQLResultCasing($column) + { + return strtoupper($column); + } } \ No newline at end of file diff --git a/lib/Doctrine/DBAL/Schema/Db2SchemaManager.php b/lib/Doctrine/DBAL/Schema/Db2SchemaManager.php index b3d802bac..656c9c00b 100644 --- a/lib/Doctrine/DBAL/Schema/Db2SchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/Db2SchemaManager.php @@ -187,8 +187,14 @@ class Db2SchemaManager extends AbstractSchemaManager protected function _getPortableViewDefinition($view) { $view = array_change_key_case($view, \CASE_LOWER); - $pos = strpos($view['text'], ' AS '); - $sql = substr($view['text'], $pos+4); + // sadly this still segfaults on PDO_IBM, see http://pecl.php.net/bugs/bug.php?id=17199 + //$view['text'] = (is_resource($view['text']) ? stream_get_contents($view['text']) : $view['text']); + if (!is_resource($view['text'])) { + $pos = strpos($view['text'], ' AS '); + $sql = substr($view['text'], $pos+4); + } else { + $sql = ''; + } return new View($view['name'], $sql); } diff --git a/lib/Doctrine/DBAL/Types/ArrayType.php b/lib/Doctrine/DBAL/Types/ArrayType.php index 4eb79af24..672710365 100644 --- a/lib/Doctrine/DBAL/Types/ArrayType.php +++ b/lib/Doctrine/DBAL/Types/ArrayType.php @@ -40,6 +40,7 @@ class ArrayType extends Type public function convertToPHPValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) { + $value = (is_resource($value)) ? stream_get_contents($value) : $value; return unserialize($value); } diff --git a/lib/Doctrine/DBAL/Types/ObjectType.php b/lib/Doctrine/DBAL/Types/ObjectType.php index 6b59f5757..668746f36 100644 --- a/lib/Doctrine/DBAL/Types/ObjectType.php +++ b/lib/Doctrine/DBAL/Types/ObjectType.php @@ -21,6 +21,7 @@ class ObjectType extends Type public function convertToPHPValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) { + $value = (is_resource($value)) ? stream_get_contents($value) : $value; return unserialize($value); } diff --git a/lib/Doctrine/DBAL/Types/TextType.php b/lib/Doctrine/DBAL/Types/TextType.php index 1ef008e04..4fcd87270 100644 --- a/lib/Doctrine/DBAL/Types/TextType.php +++ b/lib/Doctrine/DBAL/Types/TextType.php @@ -36,6 +36,19 @@ class TextType extends Type return $platform->getClobTypeDeclarationSQL($fieldDeclaration); } + /** + * Converts a value from its database representation to its PHP representation + * of this type. + * + * @param mixed $value The value to convert. + * @param AbstractPlatform $platform The currently used database platform. + * @return mixed The PHP representation of the value. + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (is_resource($value)) ? stream_get_contents($value) : $value; + } + public function getName() { return Type::TEXT; diff --git a/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php b/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php index 26453b2b9..8ddb78fff 100644 --- a/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php +++ b/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php @@ -58,7 +58,7 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor $primaryDqlAlias = $AST->deleteClause->aliasIdentificationVariable; $rootClass = $em->getClassMetadata($primaryClass->rootEntityName); - $tempTable = $rootClass->getTemporaryIdTableName(); + $tempTable = $platform->getTemporaryTableName($rootClass->getTemporaryIdTableName()); $idColumnNames = $rootClass->getIdentifierColumnNames(); $idColumnList = implode(', ', $idColumnNames); @@ -95,8 +95,7 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor ); } $this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' (' - . $platform->getColumnDeclarationListSQL($columnDefinitions) - . ', PRIMARY KEY(' . $idColumnList . '))'; + . $platform->getColumnDeclarationListSQL($columnDefinitions) . ')'; $this->_dropTempTableSql = 'DROP TABLE ' . $tempTable; } diff --git a/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php b/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php index add39dc83..902cb0251 100644 --- a/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php +++ b/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php @@ -64,7 +64,7 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor $updateItems = $updateClause->updateItems; - $tempTable = $rootClass->getTemporaryIdTableName(); + $tempTable = $platform->getTemporaryTableName($rootClass->getTemporaryIdTableName()); $idColumnNames = $rootClass->getIdentifierColumnNames(); $idColumnList = implode(', ', $idColumnNames); @@ -131,8 +131,7 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor ); } $this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' (' - . $platform->getColumnDeclarationListSQL($columnDefinitions) - . ', PRIMARY KEY(' . $idColumnList . '))'; + . $platform->getColumnDeclarationListSQL($columnDefinitions) . ')'; $this->_dropTempTableSql = 'DROP TABLE ' . $tempTable; } diff --git a/tests/Doctrine/Tests/DBAL/Functional/AllTests.php b/tests/Doctrine/Tests/DBAL/Functional/AllTests.php index 31719886b..2132fe5b4 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/AllTests.php +++ b/tests/Doctrine/Tests/DBAL/Functional/AllTests.php @@ -25,6 +25,7 @@ class AllTests $suite->addTestSuite('Doctrine\Tests\DBAL\Functional\Schema\MySqlSchemaManagerTest'); $suite->addTestSuite('Doctrine\Tests\DBAL\Functional\Schema\PostgreSqlSchemaManagerTest'); $suite->addTestSuite('Doctrine\Tests\DBAL\Functional\Schema\OracleSchemaManagerTest'); + $suite->addTestSuite('Doctrine\Tests\DBAL\Functional\Schema\Db2SchemaManagerTest'); $suite->addTestSuite('Doctrine\Tests\DBAL\Functional\ConnectionTest'); return $suite; diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php index 167c9290b..9b8d21bc9 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php @@ -284,8 +284,6 @@ class SchemaManagerFunctionalTestCase extends \Doctrine\Tests\DbalFunctionalTest $this->markTestSkipped('Alter Table is not supported by this platform.'); } - $this->_conn->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger); - $this->createTestTable('alter_table'); $this->createTestTable('alter_table_foreign'); diff --git a/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php b/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php index 07aa2edf8..23dfa3a73 100644 --- a/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php @@ -16,13 +16,12 @@ require_once __DIR__ . '/../../TestInit.php'; */ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase { + private $platform = null; + protected function setUp() { $this->useModelSet('cms'); parent::setUp(); - - if ($this->_em->getConnection()->getDatabasePlatform()->getName() == 'oracle') { - $this->markTestSkipped('The ' . __CLASS__ .' does not work with Oracle due to character casing.'); - } + $this->platform = $this->_em->getConnection()->getDatabasePlatform(); } public function testBasicNativeQuery() @@ -38,8 +37,8 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase $rsm = new ResultSetMapping; $rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u'); - $rsm->addFieldResult('u', 'id', 'id'); - $rsm->addFieldResult('u', 'name', 'name'); + $rsm->addFieldResult('u', $this->platform->getSQLResultCasing('id'), 'id'); + $rsm->addFieldResult('u', $this->platform->getSQLResultCasing('name'), 'name'); $query = $this->_em->createNativeQuery('SELECT id, name FROM cms_users WHERE username = ?', $rsm); $query->setParameter(1, 'romanb'); @@ -70,11 +69,11 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase $rsm = new ResultSetMapping; $rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u'); - $rsm->addFieldResult('u', 'id', 'id'); - $rsm->addFieldResult('u', 'name', 'name'); - $rsm->addFieldResult('u', 'status', 'status'); + $rsm->addFieldResult('u', $this->platform->getSQLResultCasing('id'), 'id'); + $rsm->addFieldResult('u', $this->platform->getSQLResultCasing('name'), 'name'); + $rsm->addFieldResult('u', $this->platform->getSQLResultCasing('status'), 'status'); $rsm->addJoinedEntityResult('Doctrine\Tests\Models\CMS\CmsPhonenumber', 'p', 'u', 'phonenumbers'); - $rsm->addFieldResult('p', 'phonenumber', 'phonenumber'); + $rsm->addFieldResult('p', $this->platform->getSQLResultCasing('phonenumber'), 'phonenumber'); $query = $this->_em->createNativeQuery('SELECT id, name, status, phonenumber FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username = ?', $rsm); $query->setParameter(1, 'romanb'); @@ -115,14 +114,14 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase $rsm = new ResultSetMapping; $rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u'); - $rsm->addFieldResult('u', 'id', 'id'); - $rsm->addFieldResult('u', 'name', 'name'); - $rsm->addFieldResult('u', 'status', 'status'); + $rsm->addFieldResult('u', $this->platform->getSQLResultCasing('id'), 'id'); + $rsm->addFieldResult('u', $this->platform->getSQLResultCasing('name'), 'name'); + $rsm->addFieldResult('u', $this->platform->getSQLResultCasing('status'), 'status'); $rsm->addJoinedEntityResult('Doctrine\Tests\Models\CMS\CmsAddress', 'a', 'u', 'address'); - $rsm->addFieldResult('a', 'a_id', 'id'); - $rsm->addFieldResult('a', 'country', 'country'); - $rsm->addFieldResult('a', 'zip', 'zip'); - $rsm->addFieldResult('a', 'city', 'city'); + $rsm->addFieldResult('a', $this->platform->getSQLResultCasing('a_id'), 'id'); + $rsm->addFieldResult('a', $this->platform->getSQLResultCasing('country'), 'country'); + $rsm->addFieldResult('a', $this->platform->getSQLResultCasing('zip'), 'zip'); + $rsm->addFieldResult('a', $this->platform->getSQLResultCasing('city'), 'city'); $query = $this->_em->createNativeQuery('SELECT u.id, u.name, u.status, a.id AS a_id, a.country, a.zip, a.city FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id WHERE u.username = ?', $rsm); $query->setParameter(1, 'romanb'); diff --git a/tests/Doctrine/Tests/OrmFunctionalTestCase.php b/tests/Doctrine/Tests/OrmFunctionalTestCase.php index 9e02cb285..2d1f370b0 100644 --- a/tests/Doctrine/Tests/OrmFunctionalTestCase.php +++ b/tests/Doctrine/Tests/OrmFunctionalTestCase.php @@ -251,7 +251,7 @@ abstract class OrmFunctionalTestCase extends OrmTestCase $queries = ""; for($i = count($this->_sqlLoggerStack->queries)-1; $i > max(count($this->_sqlLoggerStack->queries)-25, 0); $i--) { $query = $this->_sqlLoggerStack->queries[$i]; - $params = array_map(function($p) { return "'".$p."'"; }, $query['params'] ?: array()); + $params = array_map(function($p) { if (is_object($p)) return get_class($p); else return "'".$p."'"; }, $query['params'] ?: array()); $queries .= ($i+1).". SQL: '".$query['sql']."' Params: ".implode(", ", $params).PHP_EOL; } From b7cac8c310676c700a7cf406e9ca1fda0a4025e0 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 20 Apr 2010 23:20:42 +0200 Subject: [PATCH 06/13] Fixed pecl/ibm_db2 Driver and Connection to run smoothly against the complete test-suite (depending on a c-patch to the extension though) --- .../DBAL/Driver/IbmDb2/Db2Connection.php | 10 ++-------- .../DBAL/Driver/IbmDb2/Db2Statement.php | 20 +++++++++++++++---- lib/Doctrine/DBAL/Platforms/Db2Platform.php | 16 ++++++--------- .../ORM/Functional/AdvancedDqlQueryTest.php | 2 +- .../ORM/Functional/DefaultValuesTest.php | 2 +- .../ManyToManyBasicAssociationTest.php | 6 +++++- 6 files changed, 31 insertions(+), 25 deletions(-) diff --git a/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Connection.php b/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Connection.php index bc180fac6..2d13ce03f 100644 --- a/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Connection.php +++ b/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Connection.php @@ -71,18 +71,12 @@ class Db2Connection implements \Doctrine\DBAL\Driver\Connection { $stmt = $this->prepare($statement); $stmt->execute(); - return $stmt; + return $stmt->rowCount(); } function lastInsertId($name = null) { - $sql = 'SELECT IDENTITY_VAL_LOCAL() AS VAL FROM SYSIBM.SYSDUMMY1'; - if ($stmt = $this->query($sql)) { - if ($col = $stmt->fetchColumn()) { - return $col; - } - } - return false; + return db2_last_insert_id($this->_conn); } function beginTransaction() diff --git a/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Statement.php b/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Statement.php index c4594ff04..d25269cc3 100644 --- a/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Statement.php +++ b/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Statement.php @@ -25,6 +25,8 @@ class Db2Statement implements \Doctrine\DBAL\Driver\Statement { private $_stmt = null; + private $_bindParam = array(); + /** * DB2_BINARY, DB2_CHAR, DB2_DOUBLE, or DB2_LONG * @var @@ -54,7 +56,7 @@ class Db2Statement implements \Doctrine\DBAL\Driver\Statement */ function bindValue($param, $value, $type = null) { - return $this->bindParam($param, $variable, $type); + return $this->bindParam($param, $value, $type); } /** @@ -81,7 +83,9 @@ class Db2Statement implements \Doctrine\DBAL\Driver\Statement */ function bindParam($column, &$variable, $type = null) { - if (!$type && isset(self::$_typeMap[$type])) { + $this->_bindParam[$column] =& $variable; + + if ($type && isset(self::$_typeMap[$type])) { $type = self::$_typeMap[$type]; } else { $type = DB2_CHAR; @@ -90,6 +94,7 @@ class Db2Statement implements \Doctrine\DBAL\Driver\Statement if (!db2_bind_param($this->_stmt, $column, "variable", DB2_PARAM_IN, $type)) { throw new Db2Exception(db2_stmt_errormsg()); } + return true; } /** @@ -103,6 +108,8 @@ class Db2Statement implements \Doctrine\DBAL\Driver\Statement return false; } + $this->_bindParam = array(); + db2_free_result($this->_stmt); $ret = db2_free_stmt($this->_stmt); $this->_stmt = false; return $ret; @@ -171,12 +178,17 @@ class Db2Statement implements \Doctrine\DBAL\Driver\Statement return false; } - $retval = true; + /*$retval = true; if ($params !== null) { $retval = @db2_execute($this->_stmt, $params); } else { $retval = @db2_execute($this->_stmt); + }*/ + if ($params === null) { + ksort($this->_bindParam); + $params = array_values($this->_bindParam); } + $retval = @db2_execute($this->_stmt, $params); if ($retval === false) { throw new Db2Exception(db2_stmt_errormsg()); @@ -260,7 +272,7 @@ class Db2Statement implements \Doctrine\DBAL\Driver\Statement function fetchColumn($columnIndex = 0) { $row = $this->fetch(\PDO::FETCH_NUM); - if (!$row && isset($row[$columnIndex])) { + if ($row && isset($row[$columnIndex])) { return $row[$columnIndex]; } return false; diff --git a/lib/Doctrine/DBAL/Platforms/Db2Platform.php b/lib/Doctrine/DBAL/Platforms/Db2Platform.php index b02d2eade..96cb18870 100644 --- a/lib/Doctrine/DBAL/Platforms/Db2Platform.php +++ b/lib/Doctrine/DBAL/Platforms/Db2Platform.php @@ -282,7 +282,7 @@ class Db2Platform extends AbstractPlatform */ public function getCurrentDateSQL() { - return 'current date'; + return 'VALUES CURRENT DATE'; } /** @@ -292,7 +292,7 @@ class Db2Platform extends AbstractPlatform */ public function getCurrentTimeSQL() { - return 'current time'; + return 'VALUES CURRENT TIME'; } /** @@ -300,10 +300,11 @@ class Db2Platform extends AbstractPlatform * * @return string */ - /*public function getCurrentTimestampSQL() + + public function getCurrentTimestampSQL() { - return 'current timestamp'; - }*/ + return "VALUES CURRENT TIMESTAMP"; + } /** * Obtain DBMS specific SQL code portion needed to set an index @@ -437,11 +438,6 @@ class Db2Platform extends AbstractPlatform return "SESSION." . $tableName; } - public function getCurrentTimestampSQL() - { - return "VALUES CURRENT TIMESTAMP"; - } - public function modifyLimitQuery($query, $limit, $offset = null) { if ($limit === null && $offset === null) { diff --git a/tests/Doctrine/Tests/ORM/Functional/AdvancedDqlQueryTest.php b/tests/Doctrine/Tests/ORM/Functional/AdvancedDqlQueryTest.php index be75c2c1e..6a994c944 100644 --- a/tests/Doctrine/Tests/ORM/Functional/AdvancedDqlQueryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/AdvancedDqlQueryTest.php @@ -122,7 +122,7 @@ class AdvancedDqlQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase public function testUpdateAs() { $dql = 'UPDATE Doctrine\Tests\Models\Company\CompanyEmployee AS p SET p.salary = 1'; - $this->_em->createQuery($dql)->getResult(); + $this->_em->createQuery($dql)->execute(); $this->assertTrue(count($this->_em->createQuery( 'SELECT count(p.id) FROM Doctrine\Tests\Models\Company\CompanyEmployee p WHERE p.salary = 1')->getResult()) > 0); diff --git a/tests/Doctrine/Tests/ORM/Functional/DefaultValuesTest.php b/tests/Doctrine/Tests/ORM/Functional/DefaultValuesTest.php index 71edf4dc2..873f0d938 100644 --- a/tests/Doctrine/Tests/ORM/Functional/DefaultValuesTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/DefaultValuesTest.php @@ -28,7 +28,7 @@ class DefaultValuesTest extends \Doctrine\Tests\OrmFunctionalTestCase $user->name = 'romanb'; $this->_em->persist($user); $this->_em->flush(); - $this->_em->clear(); + $this->_em->clear(); $userId = $user->id; // e.g. from $_REQUEST $user2 = $this->_em->getReference(get_class($user), $userId); diff --git a/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php b/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php index bdabf6206..0ccb8db45 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php @@ -47,7 +47,9 @@ class ManyToManyBasicAssociationTest extends \Doctrine\Tests\OrmFunctionalTestCa // Get user $user = $uRep->findOneById($user->getId()); - + + $this->assertNotNull($user, "Has to return exactly one entry."); + $this->assertFalse($user->getGroups()->isInitialized()); // Check groups @@ -89,6 +91,8 @@ class ManyToManyBasicAssociationTest extends \Doctrine\Tests\OrmFunctionalTestCa // Association should not exist $user2 = $this->_em->find(get_class($user), $user->getId()); + + $this->assertNotNull($user2, "Has to return exactly one entry."); $this->assertEquals(0, $user2->getGroups()->count()); } From 1f656a16ac664210cdcc6d369cecdaac2f98e84b Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 21 Apr 2010 20:23:58 +0200 Subject: [PATCH 07/13] Renamed Db2 to DB2 --- .../Db2Connection.php => IBMDB2/DB2Connection.php} | 14 +++++++------- .../{IbmDb2/Db2Driver.php => IBMDB2/DB2Driver.php} | 10 +++++----- .../Db2Exception.php => IBMDB2/DB2Exception.php} | 4 ++-- .../Db2Statement.php => IBMDB2/DB2Statement.php} | 10 +++++----- lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php | 4 ++-- lib/Doctrine/DBAL/DriverManager.php | 2 +- .../Platforms/{Db2Platform.php => DB2Platform.php} | 0 .../{Db2SchemaManager.php => DB2SchemaManager.php} | 2 +- 8 files changed, 23 insertions(+), 23 deletions(-) rename lib/Doctrine/DBAL/Driver/{IbmDb2/Db2Connection.php => IBMDB2/DB2Connection.php} (88%) rename lib/Doctrine/DBAL/Driver/{IbmDb2/Db2Driver.php => IBMDB2/DB2Driver.php} (93%) rename lib/Doctrine/DBAL/Driver/{IbmDb2/Db2Exception.php => IBMDB2/DB2Exception.php} (92%) rename lib/Doctrine/DBAL/Driver/{IbmDb2/Db2Statement.php => IBMDB2/DB2Statement.php} (97%) rename lib/Doctrine/DBAL/Platforms/{Db2Platform.php => DB2Platform.php} (100%) rename lib/Doctrine/DBAL/Schema/{Db2SchemaManager.php => DB2SchemaManager.php} (99%) diff --git a/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Connection.php b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php similarity index 88% rename from lib/Doctrine/DBAL/Driver/IbmDb2/Db2Connection.php rename to lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php index 2d13ce03f..5d706de7b 100644 --- a/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Connection.php +++ b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php @@ -19,9 +19,9 @@ * . */ -namespace Doctrine\DBAL\Driver\IbmDb2; +namespace Doctrine\DBAL\Driver\IBMDB2; -class Db2Connection implements \Doctrine\DBAL\Driver\Connection +class DB2Connection implements \Doctrine\DBAL\Driver\Connection { private $_conn = null; @@ -35,7 +35,7 @@ class Db2Connection implements \Doctrine\DBAL\Driver\Connection $this->_conn = db2_connect($params['dbname'], $username, $password, $driverOptions); } if (!$this->_conn) { - throw new Db2Exception(db2_conn_errormsg()); + throw new DB2Exception(db2_conn_errormsg()); } } @@ -43,9 +43,9 @@ class Db2Connection implements \Doctrine\DBAL\Driver\Connection { $stmt = @db2_prepare($this->_conn, $sql); if (!$stmt) { - throw new Db2Exception(db2_stmt_errormsg()); + throw new DB2Exception(db2_stmt_errormsg()); } - return new Db2Statement($stmt); + return new DB2Statement($stmt); } function query() @@ -87,7 +87,7 @@ class Db2Connection implements \Doctrine\DBAL\Driver\Connection function commit() { if (!db2_commit($this->_conn)) { - throw new Db2Exception(db2_conn_errormsg($this->_conn)); + throw new DB2Exception(db2_conn_errormsg($this->_conn)); } db2_autocommit($this->_conn, DB2_AUTOCOMMIT_ON); } @@ -95,7 +95,7 @@ class Db2Connection implements \Doctrine\DBAL\Driver\Connection function rollBack() { if (!db2_rollback($this->_conn)) { - throw new Db2Exception(db2_conn_errormsg($this->_conn)); + throw new DB2Exception(db2_conn_errormsg($this->_conn)); } db2_autocommit($this->_conn, DB2_AUTOCOMMIT_ON); } diff --git a/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Driver.php b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php similarity index 93% rename from lib/Doctrine/DBAL/Driver/IbmDb2/Db2Driver.php rename to lib/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php index 3522d0e37..6209d5ffd 100644 --- a/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Driver.php +++ b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php @@ -19,7 +19,7 @@ * . */ -namespace Doctrine\DBAL\Driver\IbmDb2; +namespace Doctrine\DBAL\Driver\IBMDB2; use Doctrine\DBAL\Driver, Doctrine\DBAL\Connection; @@ -33,7 +33,7 @@ use Doctrine\DBAL\Driver, * @version $Revision$ * @author Benjamin Eberlei */ -class Db2Driver implements Driver +class DB2Driver implements Driver { /** * Attempts to create a connection with the database. @@ -63,7 +63,7 @@ class Db2Driver implements Driver $password = null; } - return new Db2Connection($params, $username, $password, $driverOptions); + return new DB2Connection($params, $username, $password, $driverOptions); } /** @@ -74,7 +74,7 @@ class Db2Driver implements Driver */ public function getDatabasePlatform() { - return new \Doctrine\DBAL\Platforms\Db2Platform; + return new \Doctrine\DBAL\Platforms\DB2Platform; } /** @@ -86,7 +86,7 @@ class Db2Driver implements Driver */ public function getSchemaManager(Connection $conn) { - return new \Doctrine\DBAL\Schema\Db2SchemaManager($conn); + return new \Doctrine\DBAL\Schema\DB2SchemaManager($conn); } /** diff --git a/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Exception.php b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php similarity index 92% rename from lib/Doctrine/DBAL/Driver/IbmDb2/Db2Exception.php rename to lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php index 76d992b50..b2a8de63a 100644 --- a/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Exception.php +++ b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php @@ -19,9 +19,9 @@ * . */ -namespace Doctrine\DBAL\Driver\IbmDb2; +namespace Doctrine\DBAL\Driver\IBMDB2; -class Db2Exception extends \Exception +class DB2Exception extends \Exception { } \ No newline at end of file diff --git a/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Statement.php b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php similarity index 97% rename from lib/Doctrine/DBAL/Driver/IbmDb2/Db2Statement.php rename to lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php index d25269cc3..41bff920e 100644 --- a/lib/Doctrine/DBAL/Driver/IbmDb2/Db2Statement.php +++ b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php @@ -19,9 +19,9 @@ * . */ -namespace Doctrine\DBAL\Driver\IbmDb2; +namespace Doctrine\DBAL\Driver\IBMDB2; -class Db2Statement implements \Doctrine\DBAL\Driver\Statement +class DB2Statement implements \Doctrine\DBAL\Driver\Statement { private $_stmt = null; @@ -92,7 +92,7 @@ class Db2Statement implements \Doctrine\DBAL\Driver\Statement } if (!db2_bind_param($this->_stmt, $column, "variable", DB2_PARAM_IN, $type)) { - throw new Db2Exception(db2_stmt_errormsg()); + throw new DB2Exception(db2_stmt_errormsg()); } return true; } @@ -191,7 +191,7 @@ class Db2Statement implements \Doctrine\DBAL\Driver\Statement $retval = @db2_execute($this->_stmt, $params); if ($retval === false) { - throw new Db2Exception(db2_stmt_errormsg()); + throw new DB2Exception(db2_stmt_errormsg()); } return $retval; } @@ -233,7 +233,7 @@ class Db2Statement implements \Doctrine\DBAL\Driver\Statement case \PDO::FETCH_NUM: return db2_fetch_array($this->_stmt); default: - throw new Db2Exception("Given Fetch-Style " . $fetchStyle . " is not supported."); + throw new DB2Exception("Given Fetch-Style " . $fetchStyle . " is not supported."); } } diff --git a/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php b/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php index 3f05f5c75..844f2ab3f 100644 --- a/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php +++ b/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php @@ -87,7 +87,7 @@ class Driver implements \Doctrine\DBAL\Driver */ public function getDatabasePlatform() { - return new \Doctrine\DBAL\Platforms\Db2Platform; + return new \Doctrine\DBAL\Platforms\DB2Platform; } /** @@ -99,7 +99,7 @@ class Driver implements \Doctrine\DBAL\Driver */ public function getSchemaManager(Connection $conn) { - return new \Doctrine\DBAL\Schema\Db2SchemaManager($conn); + return new \Doctrine\DBAL\Schema\DB2SchemaManager($conn); } /** diff --git a/lib/Doctrine/DBAL/DriverManager.php b/lib/Doctrine/DBAL/DriverManager.php index 21a5c09d4..39c88f1b8 100644 --- a/lib/Doctrine/DBAL/DriverManager.php +++ b/lib/Doctrine/DBAL/DriverManager.php @@ -44,7 +44,7 @@ final class DriverManager 'pdo_oci' => 'Doctrine\DBAL\Driver\PDOOracle\Driver', 'pdo_mssql' => 'Doctrine\DBAL\Driver\PDOMsSql\Driver', 'oci8' => 'Doctrine\DBAL\Driver\OCI8\Driver', - 'ibm_db2' => 'Doctrine\DBAL\Driver\IbmDb2\Db2Driver', + 'ibm_db2' => 'Doctrine\DBAL\Driver\IBMDB2\DB2Driver', 'pdo_ibm' => 'Doctrine\DBAL\Driver\PDOIbm\Driver', ); diff --git a/lib/Doctrine/DBAL/Platforms/Db2Platform.php b/lib/Doctrine/DBAL/Platforms/DB2Platform.php similarity index 100% rename from lib/Doctrine/DBAL/Platforms/Db2Platform.php rename to lib/Doctrine/DBAL/Platforms/DB2Platform.php diff --git a/lib/Doctrine/DBAL/Schema/Db2SchemaManager.php b/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php similarity index 99% rename from lib/Doctrine/DBAL/Schema/Db2SchemaManager.php rename to lib/Doctrine/DBAL/Schema/DB2SchemaManager.php index 656c9c00b..9c1466715 100644 --- a/lib/Doctrine/DBAL/Schema/Db2SchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php @@ -30,7 +30,7 @@ namespace Doctrine\DBAL\Schema; * @version $Revision$ * @author Benjamin Eberlei */ -class Db2SchemaManager extends AbstractSchemaManager +class DB2SchemaManager extends AbstractSchemaManager { /** * Return a list of all tables in the current database From f38584a51ef9bac77b1f91e968d9146d22ded11c Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 28 Apr 2010 20:27:53 +0200 Subject: [PATCH 08/13] DDC-545 - Add unittest for all drivers uniqueConstraint capabilities, fix bug in XML Driver that disallowed using them --- lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php | 16 ++++++++------ .../ORM/Mapping/AbstractMappingDriverTest.php | 22 ++++++++++++++++++- .../php/Doctrine.Tests.ORM.Mapping.User.php | 5 ++++- .../Doctrine.Tests.ORM.Mapping.User.dcm.xml | 4 ++++ .../Doctrine.Tests.ORM.Mapping.User.dcm.yml | 5 ++++- 5 files changed, 42 insertions(+), 10 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php index 40608d001..fd06dd07c 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php @@ -118,15 +118,17 @@ class XmlDriver extends AbstractFileDriver // Evaluate if (isset($xmlRoot->{'unique-constraints'})) { foreach ($xmlRoot->{'unique-constraints'}->{'unique-constraint'} as $unique) { - if (is_string($unique['columns'])) { - $columns = explode(',', $unique['columns']); + $columns = explode(',', (string)$unique['columns']); + + if (isset($unique['name'])) { + $metadata->table['uniqueConstraints'][(string)$unique['name']] = array( + 'columns' => $columns + ); } else { - $columns = $unique['columns']; + $metadata->table['uniqueConstraints'][] = array( + 'columns' => $columns + ); } - - $metadata->table['uniqueConstraints'][$unique['name']] = array( - 'columns' => $columns - ); } } diff --git a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php index 1fdf4a33c..741b35a6c 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php @@ -36,6 +36,23 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase return $class; } + /** + * @depends testEntityTableNameAndInheritance + * @param ClassMetadata $class + */ + public function testEntityUniqueConstraints($class) + { + $this->assertArrayHasKey('uniqueConstraints', $class->table, + 'ClassMetadata should have uniqueConstraints key in table property when Unique Constraints are set.'); + + $this->assertEquals(array( + "search_idx" => array("columns" => array("name", "user_email")) + ), $class->table['uniqueConstraints']); + + return $class; + } + + /** * @depends testEntityTableNameAndInheritance * @param ClassMetadata $class @@ -206,7 +223,7 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase /** * @Entity * @HasLifecycleCallbacks - * @Table(name="cms_users") + * @Table(name="cms_users", uniqueConstraints={@UniqueConstraint(name="search_idx", columns={"name", "user_email"})}) */ class User { @@ -369,5 +386,8 @@ class User ), 'orderBy' => NULL, )); + $metadata->table['uniqueConstraints'] = array( + 'search_idx' => array('columns' => array('name', 'user_email')), + ); } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.User.php b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.User.php index e2a057f14..a7a58c60d 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.User.php +++ b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.User.php @@ -102,4 +102,7 @@ $metadata->mapManyToMany(array( ), ), 'orderBy' => NULL, - )); \ No newline at end of file + )); +$metadata->table['uniqueConstraints'] = array( + 'search_idx' => array('columns' => array('name', 'user_email')), +); \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml index 65b71f04c..7372604bb 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml @@ -6,6 +6,10 @@ http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> + + + + diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.User.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.User.dcm.yml index 3cf687169..9017da012 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.User.dcm.yml +++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.User.dcm.yml @@ -51,4 +51,7 @@ Doctrine\Tests\ORM\Mapping\User: - all lifecycleCallbacks: prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ] - postPersist: [ doStuffOnPostPersist ] \ No newline at end of file + postPersist: [ doStuffOnPostPersist ] + uniqueConstraints: + search_idx: + columns: name,user_email \ No newline at end of file From 94928c5dd8e7c18ba7f09c6e650dc13b1d4a0733 Mon Sep 17 00:00:00 2001 From: "Jonathan H. Wage" Date: Fri, 30 Apr 2010 12:27:52 -0400 Subject: [PATCH 09/13] [DDC-552] Fixing issue with getReflectionClass() not existing on ClassMetadataInfo when it is required by AnnotationDriver --- lib/Doctrine/ORM/Mapping/ClassMetadata.php | 17 -------------- .../ORM/Mapping/ClassMetadataInfo.php | 22 +++++++++++++++++++ 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadata.php b/lib/Doctrine/ORM/Mapping/ClassMetadata.php index 18558c01a..48bbc996a 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadata.php @@ -41,13 +41,6 @@ use ReflectionClass, ReflectionProperty; */ class ClassMetadata extends ClassMetadataInfo { - /** - * The ReflectionClass instance of the mapped class. - * - * @var ReflectionClass - */ - public $reflClass; - /** * The ReflectionProperty instances of the mapped class. * @@ -76,16 +69,6 @@ class ClassMetadata extends ClassMetadataInfo $this->table['name'] = $this->reflClass->getShortName(); } - /** - * Gets the ReflectionClass instance of the mapped class. - * - * @return ReflectionClass - */ - public function getReflectionClass() - { - return $this->reflClass; - } - /** * Gets the ReflectionPropertys of the mapped class. * diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index ac481c48f..d1f13f002 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -19,6 +19,8 @@ namespace Doctrine\ORM\Mapping; +use ReflectionClass; + /** * A ClassMetadata instance holds all the object-relational mapping metadata * of an entity and it's associations. @@ -366,6 +368,13 @@ class ClassMetadataInfo */ public $versionField; + /** + * The ReflectionClass instance of the mapped class. + * + * @var ReflectionClass + */ + public $reflClass; + /** * Initializes a new ClassMetadata instance that will hold the object-relational mapping * metadata of the class with the given name. @@ -378,6 +387,19 @@ class ClassMetadataInfo $this->rootEntityName = $entityName; } + /** + * Gets the ReflectionClass instance of the mapped class. + * + * @return ReflectionClass + */ + public function getReflectionClass() + { + if ( ! $this->reflClass) { + $this->reflClass = new ReflectionClass($entityName); + } + return $this->reflClass; + } + /** * Sets the change tracking policy used by this class. * From 6c7aaa727c0b25f62078cb05f3bb861c57f962ed Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 1 May 2010 03:28:18 +0200 Subject: [PATCH 10/13] Added tests for 41e830ca68a8e3a46041a1a368fc6d408d58fa6a, thereby finding two issues with XML and YAML Driver handling of Sequence-Generator --- lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php | 2 +- .../ORM/Mapping/Driver/YamlDriver.php | 16 ++++++---- .../ORM/Mapping/AbstractMappingDriverTest.php | 29 ++++++++++++++++++- .../php/Doctrine.Tests.ORM.Mapping.User.php | 7 ++++- .../Doctrine.Tests.ORM.Mapping.User.dcm.xml | 1 + .../Doctrine.Tests.ORM.Mapping.User.dcm.yml | 4 +++ 6 files changed, 50 insertions(+), 9 deletions(-) diff --git a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php index 5eaa8897e..fe221c0d8 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php @@ -207,7 +207,7 @@ class XmlDriver extends AbstractFileDriver $metadata->setSequenceGeneratorDefinition(array( 'sequenceName' => (string)$seqGenerator['sequence-name'], 'allocationSize' => (string)$seqGenerator['allocation-size'], - 'initialValue' => (string)$seqGeneratorAnnot['initial-value'] + 'initialValue' => (string)$seqGenerator['initial-value'] )); } else if (isset($idElement->{'table-generator'})) { throw MappingException::tableIdGeneratorNotImplemented($className); diff --git a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php index f5448ff1f..873f9b21f 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php @@ -135,6 +135,10 @@ class YamlDriver extends AbstractFileDriver if (isset($element['id'])) { // Evaluate identifier settings foreach ($element['id'] as $name => $idElement) { + if (!isset($idElement['type'])) { + throw MappingException::propertyTypeIsRequired($className, $name); + } + $mapping = array( 'id' => true, 'fieldName' => $name, @@ -151,6 +155,12 @@ class YamlDriver extends AbstractFileDriver $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' . strtoupper($idElement['generator']['strategy']))); } + // Check for SequenceGenerator/TableGenerator definition + if (isset($idElement['sequenceGenerator'])) { + $metadata->setSequenceGeneratorDefinition($idElement['sequenceGenerator']); + } else if (isset($idElement['tableGenerator'])) { + throw MappingException::tableIdGeneratorNotImplemented($className); + } } } @@ -177,12 +187,6 @@ class YamlDriver extends AbstractFileDriver . strtoupper($fieldMapping['generator']['strategy']))); } } - // Check for SequenceGenerator/TableGenerator definition - if (isset($fieldMapping['sequenceGenerator'])) { - $metadata->setSequenceGeneratorDefinition($fieldMapping['sequenceGenerator']); - } else if (isset($fieldMapping['tableGenerator'])) { - throw MappingException::tableIdGeneratorNotImplemented($className); - } if (isset($fieldMapping['column'])) { $mapping['columnName'] = $fieldMapping['column']; } diff --git a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php index 741b35a6c..270e16234 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php @@ -52,6 +52,23 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase return $class; } + /** + * @depends testEntityTableNameAndInheritance + * @param ClassMetadata $class + */ + public function testEntitySequence($class) + { + $this->assertType('array', $class->sequenceGeneratorDefinition, 'No Sequence Definition set on this driver.'); + $this->assertEquals( + array( + 'sequenceName' => 'tablename_seq', + 'allocationSize' => 100, + 'initialValue' => 1, + ), + $class->sequenceGeneratorDefinition + ); + } + /** * @depends testEntityTableNameAndInheritance @@ -227,7 +244,12 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase */ class User { - /** @Id @Column(type="integer") @generatedValue(strategy="AUTO") */ + /** + * @Id + * @Column(type="integer") + * @generatedValue(strategy="AUTO") + * @SequenceGenerator(sequenceName="tablename_seq", initialValue=1, allocationSize=100) + **/ public $id; /** @@ -389,5 +411,10 @@ class User $metadata->table['uniqueConstraints'] = array( 'search_idx' => array('columns' => array('name', 'user_email')), ); + $metadata->setSequenceGeneratorDefinition(array( + 'sequenceName' => 'tablename_seq', + 'allocationSize' => 100, + 'initialValue' => 1, + )); } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.User.php b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.User.php index a7a58c60d..819a01109 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.User.php +++ b/tests/Doctrine/Tests/ORM/Mapping/php/Doctrine.Tests.ORM.Mapping.User.php @@ -105,4 +105,9 @@ $metadata->mapManyToMany(array( )); $metadata->table['uniqueConstraints'] = array( 'search_idx' => array('columns' => array('name', 'user_email')), -); \ No newline at end of file +); +$metadata->setSequenceGeneratorDefinition(array( + 'sequenceName' => 'tablename_seq', + 'allocationSize' => 100, + 'initialValue' => 1, + )); \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml index 7372604bb..793be0f06 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.User.dcm.xml @@ -18,6 +18,7 @@ + diff --git a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.User.dcm.yml b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.User.dcm.yml index 9017da012..7dd6bfaed 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.User.dcm.yml +++ b/tests/Doctrine/Tests/ORM/Mapping/yaml/Doctrine.Tests.ORM.Mapping.User.dcm.yml @@ -6,6 +6,10 @@ Doctrine\Tests\ORM\Mapping\User: type: integer generator: strategy: AUTO + sequenceGenerator: + sequenceName: tablename_seq + allocationSize: 100 + initialValue: 1 fields: name: type: string From 987fbee75862b9235292b0219fb9ffe7dfd0347a Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 1 May 2010 03:33:13 +0200 Subject: [PATCH 11/13] DDC-537 Add missing sequence-generator tag definition in doctrine-mapping.xsd --- doctrine-mapping.xsd | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doctrine-mapping.xsd b/doctrine-mapping.xsd index f3bea4f8e..e74010a8e 100644 --- a/doctrine-mapping.xsd +++ b/doctrine-mapping.xsd @@ -173,12 +173,19 @@ + + + + + + + From 427d4eed29e9c8893e33770492fcaee0100e50e6 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 1 May 2010 03:57:58 +0200 Subject: [PATCH 12/13] DDC-541 - Schema Table now return Pk, then Fk, then normal columns in that order --- lib/Doctrine/DBAL/Schema/Table.php | 19 ++++++++++++++++++- .../SchemaTool/MySqlSchemaToolTest.php | 2 +- .../SchemaTool/PostgreSqlSchemaToolTest.php | 2 +- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/DBAL/Schema/Table.php b/lib/Doctrine/DBAL/Schema/Table.php index c514f8f8f..7cf31fa8e 100644 --- a/lib/Doctrine/DBAL/Schema/Table.php +++ b/lib/Doctrine/DBAL/Schema/Table.php @@ -510,7 +510,24 @@ class Table extends AbstractAsset */ public function getColumns() { - return $this->_columns; + $columns = $this->_columns; + + $pkCols = array(); + $fkCols = array(); + + if ($this->hasIndex($this->_primaryKeyName)) { + $pkCols = $this->getPrimaryKey()->getColumns(); + } + foreach ($this->getForeignKeys() AS $fk) { + /* @var $fk ForeignKeyConstraint */ + $fkCols = array_merge($fkCols, $fk->getColumns()); + } + $colNames = array_unique(array_merge($pkCols, $fkCols, array_keys($columns))); + + uksort($columns, function($a, $b) use($colNames) { + return (array_search($a, $colNames) >= array_search($b, $colNames)); + }); + return $columns; } diff --git a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/MySqlSchemaToolTest.php b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/MySqlSchemaToolTest.php index 78977205c..194f7109e 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/MySqlSchemaToolTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/MySqlSchemaToolTest.php @@ -27,7 +27,7 @@ class MySqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase $tool = new SchemaTool($this->_em); $sql = $tool->getCreateSchemaSql($classes); $this->assertEquals(8, count($sql)); - $this->assertEquals("CREATE TABLE cms_addresses (id INT AUTO_INCREMENT NOT NULL, country VARCHAR(50) NOT NULL, zip VARCHAR(50) NOT NULL, city VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, PRIMARY KEY(id)) ENGINE = InnoDB", $sql[0]); + $this->assertEquals("CREATE TABLE cms_addresses (id INT AUTO_INCREMENT NOT NULL, user_id INT DEFAULT NULL, country VARCHAR(50) NOT NULL, zip VARCHAR(50) NOT NULL, city VARCHAR(50) NOT NULL, PRIMARY KEY(id)) ENGINE = InnoDB", $sql[0]); $this->assertEquals("CREATE TABLE cms_users (id INT AUTO_INCREMENT NOT NULL, status VARCHAR(50) NOT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, UNIQUE INDEX cms_users_username_uniq (username), PRIMARY KEY(id)) ENGINE = InnoDB", $sql[1]); $this->assertEquals("CREATE TABLE cms_users_groups (user_id INT NOT NULL, group_id INT NOT NULL, PRIMARY KEY(user_id, group_id)) ENGINE = InnoDB", $sql[2]); $this->assertEquals("CREATE TABLE cms_phonenumbers (phonenumber VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, PRIMARY KEY(phonenumber)) ENGINE = InnoDB", $sql[3]); diff --git a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php index 9210bbd3b..8646068f0 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SchemaTool/PostgreSqlSchemaToolTest.php @@ -34,7 +34,7 @@ class PostgreSqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase $sql = $tool->getCreateSchemaSql($classes); $this->assertEquals(count($sql), 11); - $this->assertEquals("CREATE TABLE cms_addresses (id INT NOT NULL, country VARCHAR(50) NOT NULL, zip VARCHAR(50) NOT NULL, city VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, PRIMARY KEY(id))", $sql[0]); + $this->assertEquals("CREATE TABLE cms_addresses (id INT NOT NULL, user_id INT DEFAULT NULL, country VARCHAR(50) NOT NULL, zip VARCHAR(50) NOT NULL, city VARCHAR(50) NOT NULL, PRIMARY KEY(id))", $sql[0]); $this->assertEquals("CREATE TABLE cms_users (id INT NOT NULL, status VARCHAR(50) NOT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id))", $sql[1]); $this->assertEquals("CREATE UNIQUE INDEX cms_users_username_uniq ON cms_users (username)", $sql[2]); $this->assertEquals("CREATE TABLE cms_users_groups (user_id INT NOT NULL, group_id INT NOT NULL, PRIMARY KEY(user_id, group_id))", $sql[3]); From 9277dba383d1d584594f1f3f9d02494b62675102 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 1 May 2010 04:31:10 +0200 Subject: [PATCH 13/13] Cleanup in DB2 Platform and SchemaManager --- lib/Doctrine/DBAL/Platforms/DB2Platform.php | 2 +- lib/Doctrine/DBAL/Schema/DB2SchemaManager.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/Doctrine/DBAL/Platforms/DB2Platform.php b/lib/Doctrine/DBAL/Platforms/DB2Platform.php index 96cb18870..9cc04840d 100644 --- a/lib/Doctrine/DBAL/Platforms/DB2Platform.php +++ b/lib/Doctrine/DBAL/Platforms/DB2Platform.php @@ -25,7 +25,7 @@ use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\TableDiff; -class Db2Platform extends AbstractPlatform +class DB2Platform extends AbstractPlatform { /** * Gets the SQL snippet used to declare a VARCHAR column type. diff --git a/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php b/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php index 9c1466715..e5d87148b 100644 --- a/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php @@ -42,7 +42,6 @@ class DB2SchemaManager extends AbstractSchemaManager */ public function listTableNames() { - $sql = $this->_platform->getListTablesSQL(); $sql .= " AND CREATOR = UPPER('".$this->_conn->getUsername()."')";