1
0
mirror of synced 2025-01-18 22:41:43 +03:00

Lots of small fixes, driver improvments and more tests

This commit is contained in:
zYne 2006-11-28 23:26:44 +00:00
parent 9df8e4d0e3
commit 2241453570
14 changed files with 220 additions and 67 deletions

View File

@ -118,6 +118,7 @@ abstract class Doctrine_Configurable {
case Doctrine::ATTR_QUERY_LIMIT:
case Doctrine::ATTR_QUOTE_IDENTIFIER:
case Doctrine::ATTR_PORTABILITY:
case Doctrine::ATTR_DEFAULT_TABLE_TYPE:
break;
case Doctrine::ATTR_SEQCOL_NAME:

View File

@ -252,6 +252,29 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun
$this->supported[$feature] === 'emulated' ||
$this->supported[$feature]);
}
/**
* quote
*
* @param mixed $input
* @param string $type
* @return mixed
*/
public function quote($input, $type = null) {
switch($type) {
case 'integer':
return $input;
case 'string':
case 'char':
case 'varchar':
case 'text':
case 'gzip':
case 'blob':
case 'clob':
case 'array':
case 'object':
return $this->dbh->quote($input);
}
}
/**
* Removes any formatting in an sequence name using the 'seqname_format' option
*

View File

@ -43,7 +43,9 @@ class Doctrine_Connection_Mysql extends Doctrine_Connection_Common {
* @param PDO|Doctrine_Adapter $adapter database handler
*/
public function __construct(Doctrine_Manager $manager, $adapter) {
$adapter->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
$adapter->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
$this->setAttribute(Doctrine::ATTR_DEFAULT_TABLE_TYPE, 'INNODB');
$this->supported = array(
'sequences' => 'emulated',
@ -83,6 +85,7 @@ class Doctrine_Connection_Mysql extends Doctrine_Connection_Common {
$this->properties['varchar_max_length'] = 255;
parent::__construct($manager, $adapter);
}
/**

View File

@ -213,7 +213,7 @@ class Doctrine_DataDict_Firebird extends Doctrine_Connection_Module {
*/
public function listTableFields($table) {
$table = $db->quote(strtoupper($table), 'text');
$query = "SELECT RDB\$FIELD_NAME FROM RDB\$RELATION_FIELDS WHERE UPPER(RDB\$RELATION_NAME)=$table";
$query = 'SELECT RDB\$FIELD_NAME FROM RDB$RELATION_FIELDS WHERE UPPER(RDB$RELATION_NAME) = ' . $table;
return $this->conn->fetchColumn($query);
}
@ -242,9 +242,9 @@ class Doctrine_DataDict_Firebird extends Doctrine_Connection_Module {
* @return array data array containing all views for given table
*/
public function listTableViews($table) {
$query = 'SELECT DISTINCT RDB$VIEW_NAME FROM RDB$VIEW_RELATIONS';
$table = $db->quote(strtoupper($table), 'text');
$query .= "WHERE UPPER(RDB\$RELATION_NAME)=$table";
$query = 'SELECT DISTINCT RDB$VIEW_NAME FROM RDB$VIEW_RELATIONS';
$table = $db->quote(strtoupper($table), 'text');
$query .= 'WHERE UPPER(RDB\$RELATION_NAME) = ' . $table;
return $this->conn->fetchColumn($query);
}
@ -274,7 +274,7 @@ class Doctrine_DataDict_Firebird extends Doctrine_Connection_Module {
if( ! is_null($table)) {
$table = $db->quote(strtoupper($table), 'text');
$query .= "WHERE UPPER(RDB\$RELATION_NAME)=$table";
$query .= 'WHERE UPPER(RDB$RELATION_NAME) = ' . $table;
}
return $this->conn->fetchColumn($query);

View File

@ -64,7 +64,7 @@ class Doctrine_DataDict_Mysql extends Doctrine_Connection_Module {
case 'object':
case 'string':
if (empty($field['length']) && array_key_exists('default', $field)) {
$field['length'] = $this->dbh->varchar_max_length;
$field['length'] = $this->conn->varchar_max_length;
}
$length = (! empty($field['length'])) ? $field['length'] : false;
@ -124,7 +124,7 @@ class Doctrine_DataDict_Mysql extends Doctrine_Connection_Module {
return 'DOUBLE';
case 'decimal':
$length = !empty($field['length']) ? $field['length'] : 18;
return 'DECIMAL('.$length.','.$this->dbh->options['decimal_places'].')';
return 'DECIMAL(' . $length . ',' . 0 . ')'; //$this->dbh->options['decimal_places'] . ')';
}
return '';
}
@ -134,7 +134,7 @@ class Doctrine_DataDict_Mysql extends Doctrine_Connection_Module {
* @param array $field native field description
* @return array containing the various possible types, length, sign, fixed
*/
public function getDoctrineDeclaration($field) {
public function getPortableDeclaration($field) {
$db_type = strtolower($field['type']);
$db_type = strtok($db_type, '(), ');
if ($db_type == 'national') {

View File

@ -562,7 +562,7 @@ class Doctrine_DataDict_Pgsql extends Doctrine_Connection_Module {
if( ! empty($field['autoincrement'])) {
$name = $this->conn->quoteIdentifier($name, true);
return $name.' '.$this->getTypeDeclaration($field);
return $name.' '.$this->getNativeDeclaration($field);
}
$default = '';
@ -570,14 +570,18 @@ class Doctrine_DataDict_Pgsql extends Doctrine_Connection_Module {
if ($field['default'] === '') {
$field['default'] = empty($field['notnull']) ? null : 0;
}
$default = ' DEFAULT '.$this->quote($field['default'], 'integer');
} elseif (empty($field['notnull'])) {
$default = ' DEFAULT '.$this->conn->quote($field['default'], $field['type']);
}
/**
TODO: is this needed ?
elseif (empty($field['notnull'])) {
$default = ' DEFAULT NULL';
}
*/
$notnull = empty($field['notnull']) ? '' : ' NOT NULL';
$name = $this->conn->quoteIdentifier($name, true);
return $name . ' ' . $this->getTypeDeclaration($field) . $default . $notnull;
return $name . ' ' . $this->getNativeDeclaration($field) . $default . $notnull;
}
/**
* listDatabases

View File

@ -131,15 +131,15 @@ class Doctrine_Export extends Doctrine_Connection_Module {
if (empty($fields))
throw new Doctrine_Export_Exception('no fields specified for table '.$name);
$query_fields = $this->getFieldDeclarationList($fields);
$queryFields = $this->getFieldDeclarationList($fields);
if (!empty($options['primary'])) {
$query_fields.= ', PRIMARY KEY ('.implode(', ', array_keys($options['primary'])).')';
$queryFields.= ', PRIMARY KEY('.implode(', ', array_values($options['primary'])).')';
}
$name = $db->quoteIdentifier($name, true);
$query = "CREATE TABLE $name ($query_fields)";
return $db->exec($query);
$name = $this->conn->quoteIdentifier($name, true);
$query = 'CREATE TABLE ' . $name . ' (' . $queryFields . ')';
return $this->conn->getDbh()->exec($query);
}
/**
* create sequence
@ -385,7 +385,7 @@ class Doctrine_Export extends Doctrine_Connection_Module {
$default = '';
if(isset($field['default'])) {
if ($field['default'] === '') {
if($field['default'] === '') {
$field['default'] = empty($field['notnull'])
? null : $this->valid_default_values[$field['type']];
if ($field['default'] === ''
@ -394,7 +394,8 @@ class Doctrine_Export extends Doctrine_Connection_Module {
$field['default'] = ' ';
}
}
$default = ' DEFAULT ' . $this->conn->getDbh()->quote($field['default']);
$default = ' DEFAULT ' . $this->conn->quote($field['default'], $field['type']);
}
/**
TODO: is this really needed for portability?

View File

@ -99,20 +99,20 @@ class Doctrine_Export_Mysql extends Doctrine_Export {
$query_fields = $this->getFieldDeclarationList($fields);
if( ! empty($options['primary']))
$query_fields.= ', PRIMARY KEY (' . implode(', ', array_keys($options['primary'])) . ')';
$query_fields.= ', PRIMARY KEY(' . implode(', ', array_values($options['primary'])) . ')';
$name = $this->conn->quoteIdentifier($name, true);
$query = 'CREATE TABLE ' . $name . ' (' . $query_fields . ')';
$optionStrings = array();
if( ! empty($options['comment']))
if(isset($options['comment']))
$optionStrings['comment'] = 'COMMENT = '.$this->dbh->quote($options['comment'], 'text');
if( ! empty($options['charset'])) {
if(isset($options['charset'])) {
$optionsSting['charset'] = 'DEFAULT CHARACTER SET '.$options['charset'];
if( ! empty($options['collate'])) {
if(isset($options['collate'])) {
$optionStrings['charset'].= ' COLLATE '.$options['collate'];
}
}
@ -121,12 +121,12 @@ class Doctrine_Export_Mysql extends Doctrine_Export {
if (!empty($options['type'])) {
$type = $options['type'];
} elseif ($this->dbh->options['default_table_type']) {
$type = $this->dbh->options['default_table_type'];
} else {
$type = $this->conn->getAttribute(Doctrine::ATTR_DEFAULT_TABLE_TYPE);
}
if ($type) {
$optionStrings[] = "ENGINE = $type";
$optionStrings[] = 'ENGINE = ' . $type;
}
if (!empty($optionStrings)) {

View File

@ -98,11 +98,14 @@ class Doctrine_Export_Oracle extends Doctrine_Export {
'primary' => true,
'fields' => array($name => true),
);
$result = $db->manager->createConstraint($table, $index_name, $definition);
$result = $this->createConstraint($table, $index_name, $definition);
/**
if (PEAR::isError($result)) {
return $db->raiseError($result, null, null,
'primary key for autoincrement PK could not be created', __FUNCTION__);
}
*/
if (is_null($start)) {
$db->beginTransaction();
@ -112,21 +115,22 @@ class Doctrine_Export_Oracle extends Doctrine_Export {
return $start;
}
++$start;
$result = $db->manager->createSequence($table, $start);
$result = $this->createSequence($table, $start);
$db->commit();
} else {
$result = $db->manager->createSequence($table, $start);
$result = $this->createSequence($table, $start);
}
/**
if (PEAR::isError($result)) {
return $db->raiseError($result, null, null,
'sequence for autoincrement PK could not be created', __FUNCTION__);
}
$sequence_name = $db->getSequenceName($table);
$trigger_name = $db->quoteIdentifier($table . '_AI_PK', true);
$table = $db->quoteIdentifier($table, true);
$name = $db->quoteIdentifier($name, true);
$trigger_sql = '
CREATE TRIGGER '.$trigger_name.'
*/
$sequence_name = $this->conn->getSequenceName($table);
$trigger_name = $this->conn->quoteIdentifier($table . '_AI_PK', true);
$table = $this->conn->quoteIdentifier($table, true);
$name = $this->conn->quoteIdentifier($name, true);
$trigger_sql = 'CREATE TRIGGER '.$trigger_name.'
BEFORE INSERT
ON '.$table.'
FOR EACH ROW
@ -148,7 +152,7 @@ BEGIN
END IF;
END;
';
return $db->exec($trigger_sql);
return $this->conn->getDbh()->exec($trigger_sql);
}
/**
* drop an existing autoincrement sequence + trigger
@ -209,27 +213,28 @@ END;
* );
* @param array $options An associative array of table options:
*
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @return void
*/
public function createTable($name, $fields, $options = array()) {
//$db->beginNestedTransaction();
$this->conn->beginTransaction();
$result = parent::createTable($name, $fields, $options);
if (!PEAR::isError($result)) {
foreach ($fields as $field_name => $field) {
if (!empty($field['autoincrement'])) {
$result = $this->_makeAutoincrement($field_name, $name);
}
foreach($fields as $field_name => $field) {
if(isset($field['autoincrement']) && $field['autoincrement']) {
$result = $this->_makeAutoincrement($field_name, $name);
}
}
//$db->completeNestedTransaction();
$this->conn->commit();
return $result;
}
/**
* drop an existing table
*
* @param string $name name of the table that should be dropped
* @return mixed MDB2_OK on success, a MDB2 error on failure
* @access public
* @return void
*/
public function dropTable($name) {
//$db->beginNestedTransaction();
@ -337,7 +342,7 @@ END;
case 'rename':
break;
default:
return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null,
return $this->conn->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null,
'change type "'.$change_name.'" not yet supported', __FUNCTION__);
}
}
@ -351,9 +356,9 @@ END;
if (!empty($changes['add']) && is_array($changes['add'])) {
$fields = array();
foreach ($changes['add'] as $field_name => $field) {
$fields[] = $db->getDeclaration($field['type'], $field_name, $field);
$fields[] = $this->conn->getDeclaration($field['type'], $field_name, $field);
}
$result = $db->exec("ALTER TABLE $name ADD (". implode(', ', $fields).')');
$result = $this->conn->exec("ALTER TABLE $name ADD (". implode(', ', $fields).')');
if (PEAR::isError($result)) {
return $result;
}
@ -362,9 +367,9 @@ END;
if (!empty($changes['change']) && is_array($changes['change'])) {
$fields = array();
foreach ($changes['change'] as $field_name => $field) {
$fields[] = $field_name. ' ' . $db->getDeclaration($field['definition']['type'], '', $field['definition']);
$fields[] = $field_name. ' ' . $this->conn->getDeclaration($field['definition']['type'], '', $field['definition']);
}
$result = $db->exec("ALTER TABLE $name MODIFY (". implode(', ', $fields).')');
$result = $this->conn->exec("ALTER TABLE $name MODIFY (". implode(', ', $fields).')');
if (PEAR::isError($result)) {
return $result;
}
@ -372,9 +377,9 @@ END;
if (!empty($changes['rename']) && is_array($changes['rename'])) {
foreach ($changes['rename'] as $field_name => $field) {
$field_name = $db->quoteIdentifier($field_name, true);
$query = "ALTER TABLE $name RENAME COLUMN $field_name TO ".$db->quoteIdentifier($field['name']);
$result = $db->exec($query);
$field_name = $this->conn->quoteIdentifier($field_name, true);
$query = "ALTER TABLE $name RENAME COLUMN $field_name TO ".$this->conn->quoteIdentifier($field['name']);
$result = $this->conn->exec($query);
if (PEAR::isError($result)) {
return $result;
}
@ -384,17 +389,17 @@ END;
if (!empty($changes['remove']) && is_array($changes['remove'])) {
$fields = array();
foreach ($changes['remove'] as $field_name => $field) {
$fields[] = $db->quoteIdentifier($field_name, true);
$fields[] = $this->conn->quoteIdentifier($field_name, true);
}
$result = $db->exec("ALTER TABLE $name DROP COLUMN ". implode(', ', $fields));
$result = $this->conn->exec("ALTER TABLE $name DROP COLUMN ". implode(', ', $fields));
if (PEAR::isError($result)) {
return $result;
}
}
if (!empty($changes['name'])) {
$change_name = $db->quoteIdentifier($changes['name'], true);
$result = $db->exec("ALTER TABLE $name RENAME TO ".$change_name);
$change_name = $this->conn->quoteIdentifier($changes['name'], true);
$result = $this->conn->exec("ALTER TABLE $name RENAME TO ".$change_name);
if (PEAR::isError($result)) {
return $result;
}

View File

@ -23,7 +23,9 @@ class AdapterMock implements Doctrine_Adapter_Interface {
return new AdapterStatementMock;
}
public function quote($input){ }
public function quote($input) {
return "'" . addslashes($input) . "'";
}
public function exec($statement) {
$this->queries[] = $statement;

View File

@ -23,9 +23,29 @@ class Doctrine_Export_Mysql_TestCase extends Doctrine_Driver_UnitTestCase {
$this->assertEqual($this->adapter->pop(), 'CREATE TABLE mytable (id INT UNSIGNED) ENGINE = MYISAM');
}
public function testCreateTableSupportsAutoincPks() {
public function testCreateTableSupportsDefaultTableType() {
$name = 'mytable';
$fields = array('id' => array('type' => 'integer', 'unsigned' => 1));
$this->export->createTable($name, $fields);
// INNODB is the default type
$this->assertEqual($this->adapter->pop(), 'CREATE TABLE mytable (id INT UNSIGNED) ENGINE = INNODB');
}
public function testCreateTableSupportsMultiplePks() {
$name = 'mytable';
$fields = array('name' => array('type' => 'char', 'length' => 10),
'type' => array('type' => 'integer', 'length' => 3));
$options = array('primary' => array('name', 'type'));
$this->export->createTable($name, $fields, $options);
$this->assertEqual($this->adapter->pop(), 'CREATE TABLE mytable (name CHAR(10), type MEDIUMINT, PRIMARY KEY(name, type)) ENGINE = INNODB');
}
public function testCreateTableSupportsAutoincPks() {
$name = 'mytable';
$fields = array('id' => array('type' => 'integer', 'unsigned' => 1, 'autoincrement' => true));
$options = array('type' => 'INNODB');
@ -58,7 +78,7 @@ class Doctrine_Export_Mysql_TestCase extends Doctrine_Driver_UnitTestCase {
$fields = array('id' => array('type' => 'varchar', 'length' => '100'));
$options = array('type' => 'MYISAM');
$this->export->createTable($name, $fields, $options);
$this->assertEqual($this->adapter->pop(), 'CREATE TABLE mytable (id VARCHAR(100)) ENGINE = MYISAM');

View File

@ -6,7 +6,7 @@ class Doctrine_Export_Oracle_TestCase extends Doctrine_Export_TestCase {
public function testCreateSequenceExecutesSql() {
$sequenceName = 'sequence';
$start = 1;
$query = 'CREATE SEQUENCE ' . $sequenceName . ' START WITH ' . $start . ' INCREMENT BY 1 NOCACHE';
$query = 'CREATE SEQUENCE ' . $sequenceName . '_seq START WITH ' . $start . ' INCREMENT BY 1 NOCACHE';
$this->export->createSequence($sequenceName, $start);
@ -16,11 +16,76 @@ class Doctrine_Export_Oracle_TestCase extends Doctrine_Export_TestCase {
public function testDropSequenceExecutesSql() {
$sequenceName = 'sequence';
$query = 'DROP SEQUENCE ' . $sequenceName;;
$query = 'DROP SEQUENCE ' . $sequenceName;
$this->export->dropSequence($sequenceName);
$this->assertEqual($this->adapter->pop(), $query);
$this->assertEqual($this->adapter->pop(), $query . '_seq');
}
public function testCreateTableExecutesSql() {
$name = 'mytable';
$fields = array('id' => array('type' => 'integer'));
$options = array('type' => 'MYISAM');
$this->export->createTable($name, $fields);
$this->assertEqual($this->adapter->pop(), 'COMMIT');
$this->assertEqual($this->adapter->pop(), 'CREATE TABLE mytable (id INT)');
$this->assertEqual($this->adapter->pop(), 'BEGIN TRANSACTION');
}
public function testCreateTableSupportsDefaultAttribute() {
$name = 'mytable';
$fields = array('name' => array('type' => 'char', 'length' => 10, 'default' => 'def'),
'type' => array('type' => 'integer', 'length' => 3, 'default' => 12)
);
$options = array('primary' => array('name', 'type'));
$this->export->createTable($name, $fields, $options);
$this->assertEqual($this->adapter->pop(), 'COMMIT');
$this->assertEqual($this->adapter->pop(), 'CREATE TABLE mytable (name CHAR(10) DEFAULT \'def\', type NUMBER(3) DEFAULT 12, PRIMARY KEY(name, type))');
$this->assertEqual($this->adapter->pop(), 'BEGIN TRANSACTION');
}
public function testCreateTableSupportsMultiplePks() {
$name = 'mytable';
$fields = array('name' => array('type' => 'char', 'length' => 10),
'type' => array('type' => 'integer', 'length' => 3));
$options = array('primary' => array('name', 'type'));
$this->export->createTable($name, $fields, $options);
$this->assertEqual($this->adapter->pop(), 'COMMIT');
$this->assertEqual($this->adapter->pop(), 'CREATE TABLE mytable (name CHAR(10), type NUMBER(3), PRIMARY KEY(name, type))');
$this->assertEqual($this->adapter->pop(), 'BEGIN TRANSACTION');
}
public function testCreateTableSupportsAutoincPks() {
$name = 'mytable';
$fields = array('id' => array('type' => 'integer', 'autoincrement' => true));
$this->export->createTable($name, $fields);
$this->assertEqual($this->adapter->pop(), 'COMMIT');
$this->assertEqual(substr($this->adapter->pop(),0, 14), 'CREATE TRIGGER');
$this->assertEqual($this->adapter->pop(), 'CREATE SEQUENCE MYTABLE_seq START WITH 1 INCREMENT BY 1 NOCACHE');
$this->assertEqual($this->adapter->pop(), 'ALTER TABLE MYTABLE ADD CONSTRAINT MYTABLE_AI_PK_idx PRIMARY KEY (id)');
$this->assertEqual($this->adapter->pop(), 'CREATE TABLE mytable (id INT)');
$this->assertEqual($this->adapter->pop(), 'BEGIN TRANSACTION');
}
public function testCreateTableSupportsCharType() {
$name = 'mytable';
$fields = array('id' => array('type' => 'char', 'length' => 3));
$this->export->createTable($name, $fields);
$this->adapter->pop();
$this->assertEqual($this->adapter->pop(), 'CREATE TABLE mytable (id CHAR(3))');
}
}
?>

View File

@ -13,6 +13,35 @@ class Doctrine_Export_Pgsql_TestCase extends Doctrine_Export_TestCase {
$this->assertEqual($this->adapter->pop(), 'DROP DATABASE db');
}
public function testCreateTableSupportsAutoincPks() {
$name = 'mytable';
$fields = array('id' => array('type' => 'integer', 'unsigned' => 1, 'autoincrement' => true));
$this->export->createTable($name, $fields);
$this->assertEqual($this->adapter->pop(), 'CREATE TABLE mytable (id SERIAL PRIMARY KEY)');
}
public function testCreateTableSupportsDefaultAttribute() {
$name = 'mytable';
$fields = array('name' => array('type' => 'char', 'length' => 10, 'default' => 'def'),
'type' => array('type' => 'integer', 'length' => 3, 'default' => 12)
);
$options = array('primary' => array('name', 'type'));
$this->export->createTable($name, $fields, $options);
$this->assertEqual($this->adapter->pop(), 'CREATE TABLE mytable (name CHAR(10) DEFAULT \'def\', type INT DEFAULT 12, PRIMARY KEY(name, type))');
}
public function testCreateTableSupportsMultiplePks() {
$name = 'mytable';
$fields = array('name' => array('type' => 'char', 'length' => 10),
'type' => array('type' => 'integer', 'length' => 3));
$options = array('primary' => array('name', 'type'));
$this->export->createTable($name, $fields, $options);
$this->assertEqual($this->adapter->pop(), 'CREATE TABLE mytable (name CHAR(10), type INT, PRIMARY KEY(name, type))');
}
}
?>

View File

@ -23,7 +23,7 @@ class Doctrine_Export_TestCase extends Doctrine_Driver_UnitTestCase {
public function testDropConstraintExecutesSql() {
$this->export->dropConstraint('sometable', 'relevancy');
$this->assertEqual($this->adapter->pop(), 'ALTER TABLE sometable DROP CONSTRAINT relevancy');
$this->assertEqual($this->adapter->pop(), 'ALTER TABLE sometable DROP CONSTRAINT relevancy_idx');
}
public function testCreateIndexExecutesSql() {
$this->export->createIndex('sometable', 'relevancy', array('fields' => array('title' => array(), 'content' => array())));
@ -34,7 +34,7 @@ class Doctrine_Export_TestCase extends Doctrine_Driver_UnitTestCase {
public function testDropIndexExecutesSql() {
$this->export->dropIndex('sometable', 'relevancy');
$this->assertEqual($this->adapter->pop(), 'DROP INDEX relevancy');
$this->assertEqual($this->adapter->pop(), 'DROP INDEX relevancy_idx');
}
public function testDropTableExecutesSql() {
$this->export->dropTable('sometable');