Пример #1
0
 public function testPreset()
 {
     $prefixDb = new Database('test', '\\selective\\ORM\\Tests\\Mocks\\Driver', ['prefix' => 'testprefix_']);
     $this->assertEquals(count($prefixDb->getTables()), 1);
     $this->assertFalse($prefixDb->hasTable('Books'));
     $this->assertTrue($prefixDb->hasTable('Test'));
     $table = $prefixDb->{'Test'};
     $this->assertInstanceOf('selective\\ORM\\Table', $table);
 }
Пример #2
0
    /**
     * Get a Table object for the given name
     * TODO table/column properties should not be public
     * @param Database $database
     * @param String $name
     * @throws \Exception
     * @return Table
     */
    public function buildTable(Database $database, $name)
    {
        $objectInfo = $this->fetchAll("SELECT object_id FROM sys.objects WHERE type IN ('U ', 'V ') AND name = ?", [$name]);
        if (isset($objectInfo[0]['object_id'])) {
            $objectId = $objectInfo[0]['object_id'];
            $columns = $this->fetchAll(<<<SQL
SELECT
\tcolumns.name,
\ttypes.name AS type,
\tcolumns.max_length AS length,
\tcolumns.is_nullable AS allowNull,
\tdefault_constraints.definition AS [default],
\tcolumns.is_identity AS isAutoIncrement,
\tCOALESCE(indexes.is_primary_key, 0) AS isPrimaryKey
FROM
\tsys.columns
\tINNER JOIN sys.types ON columns.user_type_id = types.user_type_id
\tLEFT JOIN sys.default_constraints
\t\tON columns.default_object_id = default_constraints.object_id
\t\tAND columns.object_id = default_constraints.parent_object_id
\tLEFT JOIN sys.index_columns
\t\tON index_columns.column_id = columns.column_id
\t\tAND index_columns.object_id = columns.object_id
\tLEFT JOIN sys.indexes
\t\tON indexes.index_id = index_columns.index_id
\t\tAND indexes.object_id = columns.object_id
\t\tAND indexes.is_primary_key = 1
WHERE
\tcolumns.object_id = ?
SQL
, [$objectId]);
            $constraints = $this->fetchAll(<<<SQL
SELECT
\tforeign_keys.name constraintName,
\tlocalColumns.name AS localColumnName,
\tforeignTables.name AS foreignTableName,
\tforeignColumns.name AS foreignColumnName
FROM
\tsys.foreign_keys
\tINNER JOIN sys.foreign_key_columns ON foreign_keys.object_id = foreign_key_columns.constraint_object_id
\tINNER JOIN sys.objects AS foreignTables ON foreignTables.object_id = foreign_key_columns.referenced_object_id
\tINNER JOIN sys.columns AS localColumns
\t\tON localColumns.column_id = foreign_key_columns.parent_column_id
\t\tAND localColumns.object_id = foreign_key_columns.parent_object_id
\tINNER JOIN sys.columns AS foreignColumns
\t\tON foreignColumns.column_id = foreign_key_columns.referenced_column_id
\t\tAND foreignColumns.object_id = foreign_key_columns.referenced_object_id
WHERE
\tforeign_keys.parent_object_id = ?
ORDER BY
\tforeign_keys.parent_object_id,
\tforeign_keys.object_id
SQL
, [$objectId], null, 'constraintName');
            $tableClass = $database->getClassMapper()->getClassForTable($name);
            /** @var Table $table */
            $table = new $tableClass($name, $database);
            foreach ($columns as $ordinal => $columnInfo) {
                $column = new Column($table);
                $default = null;
                if ($columnInfo['default'] !== null) {
                    // we need to parse the SQL default value
                    $defaultExpression = $this->getSQLExpressionForType($columnInfo['default'], $columnInfo['type'], '[default]');
                    $defaultResult = $this->fetchAll('SELECT ' . $defaultExpression);
                    $default = current(current($defaultResult));
                }
                $column->setName($columnInfo['name'])->setOrdinal($ordinal)->setType($columnInfo['type'])->setDefault($default)->setAllowNull((bool) $columnInfo['allowNull'])->setPrimaryKey((bool) $columnInfo['isPrimaryKey'])->setAutoIncrement((bool) $columnInfo['isAutoIncrement']);
                if ($columnInfo['isPrimaryKey']) {
                    $table->primaryKeys[] = $columnInfo['name'];
                }
                $length = null;
                switch ($columnInfo['type']) {
                    case 'text':
                        $length = 2147483647;
                        break;
                    case 'ntext':
                        $length = 1073741823;
                        break;
                    case 'varchar':
                    case 'nvarchar':
                    case 'char':
                    case 'nchar':
                        $length = $columnInfo['length'];
                        break;
                }
                $column->setLength($length);
                $table->columns[$column->getName()] = $column;
            }
            // enumerate relationships
            $offset = strlen($database->getPrefix());
            foreach ($constraints as $constraintName => $mappings) {
                $localColumns = [];
                $relatedColumns = [];
                $mapping = null;
                foreach ($mappings as $mapping) {
                    $localColumns[] = $mapping['localColumnName'];
                    $relatedColumns[] = $mapping['foreignColumnName'];
                    if (!isset($table->foreignKeys[$mapping['localColumnName']])) {
                        // columns can have multiple foreign keys; we can only use one of them
                        $table->foreignKeys[$mapping['localColumnName']] = $constraintName;
                    }
                }
                $foreignTableName = substr($mapping['foreignTableName'], $offset);
                if (!isset($table->relatedTables[$foreignTableName])) {
                    // tables can be related to another table multiple times; we can only use one of them
                    $table->relatedTables[$foreignTableName] = $constraintName;
                }
                $table->constraints[$constraintName] = ['localColumns' => $localColumns, 'relatedTable' => $foreignTableName, 'relatedColumns' => $relatedColumns];
            }
            return $table;
        } else {
            throw new \Exception('Could not find table ' . $name);
        }
    }
Пример #3
0
    /**
     * @return Database
     */
    protected function getDB()
    {
        $parameters = $this->getDriverParameters();
        $pdo = new \PDO("sqlsrv:Server={$parameters['host']}", $parameters['username'], $parameters['password']);
        $stmt = $pdo->prepare("IF NOT EXISTS(SELECT * FROM sys.databases WHERE name = ?) CREATE DATABASE [{$GLOBALS['test_dbname']}]");
        $stmt->bindParam(1, $GLOBALS['test_dbname']);
        $stmt->execute();
        $pdo = null;
        $db = new Database($GLOBALS['test_dbname'], $this->getDriverClassName(), $parameters);
        $db->getDriver()->executeUpdate("IF EXISTS (SELECT * FROM sys.objects WHERE name = 'Books') DROP TABLE Books");
        $db->getDriver()->executeUpdate("IF EXISTS (SELECT * FROM sys.objects WHERE name = 'Authors') DROP TABLE Authors");
        $db->getDriver()->executeUpdate("IF EXISTS (SELECT * FROM sys.objects WHERE name = 'testprefix_Test') DROP TABLE testprefix_Test");
        $db->getDriver()->executeUpdate(<<<SQL
CREATE TABLE Authors (
  idAuthor INTEGER PRIMARY KEY NOT NULL IDENTITY(1,1),
  name NVARCHAR(100)
)
SQL
);
        $db->getDriver()->executeUpdate(<<<SQL
CREATE TABLE Books (
    idBook INTEGER PRIMARY KEY NOT NULL IDENTITY(1,1),
    title NVARCHAR(200) NOT NULL,
    idAuthor INTEGER NOT NULL,
    isbn NVARCHAR(50) NOT NULL,
    description NVARCHAR(MAX),
    dateCreated DATETIME NOT NULL DEFAULT GETDATE(),
    FOREIGN KEY(idAuthor) REFERENCES Authors(idAuthor)
)
SQL
);
        $db->getDriver()->executeUpdate(<<<SQL
CREATE TABLE testprefix_Test (
    test int DEFAULT NULL
)
SQL
);
        $db->getDriver()->executeUpdate("DELETE FROM Books");
        $db->getDriver()->executeUpdate("DELETE FROM Authors");
        $db->getDriver()->executeUpdate("INSERT INTO Authors (name) VALUES ('Author 1')");
        $db->getDriver()->executeUpdate("INSERT INTO Authors (name) VALUES ('Author 2')");
        $db->getDriver()->executeUpdate("INSERT INTO Books (title, idAuthor, isbn, description) VALUES ('My First Book', 1, '12345-6789', 'It wasn''t very good')");
        $db->getDriver()->executeUpdate("INSERT INTO Books (title, idAuthor, isbn, description) VALUES ('My Second Book', 1, '12345-6790', 'It wasn''t very good either')");
        $db->getDriver()->executeUpdate("INSERT INTO Books (title, idAuthor, isbn, description) VALUES ('My First Book', 2, '12345-6790', 'It was OK')");
        return $db;
    }
Пример #4
0
 /**
  * Get a Table object for the given name
  * TODO table/column properties should not be public
  * @param Database $database
  * @param string $name
  * @throws \Exception
  * @return Table
  */
 public function buildTable(Database $database, $name)
 {
     $createTableInfo = $this->fetchAll("SHOW CREATE TABLE `{$database->getPrefix()}{$name}`");
     if (!isset($createTableInfo[0]['Create Table'])) {
         throw new \Exception('Could not find table ' . $name);
     }
     $createTableSql = $createTableInfo[0]['Create Table'];
     $columns = [];
     $primaryKeys = [];
     $constraints = [];
     $lowerToRealCaseTableNames = [];
     $actualTableNames = [];
     foreach ($this->getTables($database) as $tableName) {
         $actualTableNames[$tableName] = true;
         $lowerToRealCaseTableNames[strtolower($tableName)] = $tableName;
     }
     // parse columns
     if (preg_match_all(self::CREATE_TABLE_SQL_COLUMNS_REGEX, $createTableSql, $columns, PREG_SET_ORDER)) {
         $tableClass = $database->getClassMapper()->getClassForTable($name);
         /** @var Table $table */
         $table = new $tableClass($name, $database);
         foreach ($columns as $ordinal => $columnInfo) {
             $column = new Column($table);
             $default = null;
             if (isset($columnInfo['default']) && $columnInfo['default'] !== 'NULL') {
                 if ($columnInfo['default'] === '') {
                     $default = '';
                 } else {
                     if ($columnInfo['default'] !== 'CURRENT_TIMESTAMP' && $columnInfo['default'] !== 'CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP') {
                         // we need to parse the SQL default value
                         $defaultResult = $this->fetchAll('SELECT ' . $columnInfo['default']);
                         $default = current(current($defaultResult));
                     }
                 }
             }
             $column->setName($columnInfo['name'])->setOrdinal($ordinal)->setType(strtolower($columnInfo['type']))->setDefault($default)->setAllowNull(!isset($columnInfo['allowNull']) || $columnInfo['allowNull'] === 'NULL')->setAutoIncrement(!empty($columnInfo['autoIncrement']));
             if ($column->getType() == 'set' || $column->getType() == 'enum') {
                 // we need to parse the SQL options
                 $optionsResult = $this->fetchAll('SELECT ' . $columnInfo['length']);
                 $options = [];
                 $i = 0;
                 foreach (current($optionsResult) as $option) {
                     $options[$column->getType() == 'set' ? pow(2, $i) : $i] = $option;
                     $i++;
                 }
                 $column->setOptions($options);
             } else {
                 $column->setLength(isset($columnInfo['length']) && $columnInfo['length'] !== '' ? $columnInfo['length'] : null);
             }
             $table->columns[$column->getName()] = $column;
         }
         // parse primary keys
         preg_match(self::CREATE_TABLE_SQL_PRIMARY_KEY_REGEX, $createTableSql, $primaryKeys);
         $primaryKeys = explode('`,`', trim($primaryKeys[1], '`'));
         foreach ($primaryKeys as $primaryKey) {
             $table->primaryKeys[] = $primaryKey;
             $table->columns[$primaryKey]->setPrimaryKey(true);
         }
         // parse relationships
         preg_match_all(self::CREATE_TABLE_SQL_CONSTRAINT_REGEX, $createTableSql, $constraints, PREG_SET_ORDER);
         $offset = strlen($database->getPrefix());
         foreach ($constraints as $constraint) {
             $localColumns = explode('`, `', trim($constraint['localColumns'], '`'));
             $relatedColumns = explode('`, `', trim($constraint['relatedColumns'], '`'));
             foreach ($localColumns as $localColumn) {
                 if (!isset($table->foreignKeys[$localColumn])) {
                     // columns can have multiple foreign keys; we can only use one of them
                     $table->foreignKeys[$localColumn] = $constraint['name'];
                 }
             }
             $foreignTableName = substr($constraint['relatedTable'], $offset);
             // workaround for http://bugs.mysql.com/bug.php?id=6555
             // map lower case table names to actual case table names
             if (!isset($actualTableNames[$foreignTableName]) && isset($lowerToRealCaseTableNames[$foreignTableName])) {
                 $foreignTableName = $lowerToRealCaseTableNames[$foreignTableName];
             }
             if (!isset($table->relatedTables[$foreignTableName])) {
                 // tables can be related to another table multiple times; we can only use one of them
                 $table->relatedTables[$foreignTableName] = $constraint['name'];
             }
             $table->constraints[$constraint['name']] = ['localColumns' => $localColumns, 'relatedTable' => $foreignTableName, 'relatedColumns' => $relatedColumns];
         }
         return $table;
     } else {
         throw new \Exception('Could not parse table definition');
     }
 }
Пример #5
0
    /**
     * @return Database
     */
    protected function getDB()
    {
        $db = new Database($GLOBALS['test_dbname'], $this->getDriverClassName(), $this->getDriverParameters());
        $db->getDriver()->executeUpdate('DROP TABLE IF EXISTS Books');
        $db->getDriver()->executeUpdate('DROP TABLE IF EXISTS Authors');
        $db->getDriver()->executeUpdate(<<<SQL
CREATE TABLE IF NOT EXISTS Authors (
  idAuthor INTEGER PRIMARY KEY NOT NULL AUTO_INCREMENT,
  name VARCHAR(100)
)
SQL
);
        $db->getDriver()->executeUpdate(<<<SQL
CREATE TABLE Books (
    idBook INTEGER PRIMARY KEY NOT NULL AUTO_INCREMENT,
    title VARCHAR(200) NOT NULL,
    idAuthor INTEGER NOT NULL,
    isbn VARCHAR(50) NOT NULL,
    description TEXT,
    dateCreated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY(idAuthor) REFERENCES Authors(idAuthor)
)
SQL
);
        $db->getDriver()->executeUpdate(<<<SQL
CREATE TABLE IF NOT EXISTS testprefix_Test (
    test int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
SQL
);
        $db->getDriver()->executeUpdate("DELETE FROM Books");
        $db->getDriver()->executeUpdate("DELETE FROM Authors");
        $db->getDriver()->executeUpdate("INSERT INTO Authors (idAuthor, name) VALUES (1, 'Author 1')");
        $db->getDriver()->executeUpdate("INSERT INTO Authors (idAuthor, name) VALUES (2, 'Author 2')");
        $db->getDriver()->executeUpdate("INSERT INTO Books (idBook, title, idAuthor, isbn, description) VALUES (1, 'My First Book', 1, '12345-6789', 'It wasn''t very good')");
        $db->getDriver()->executeUpdate("INSERT INTO Books (idBook, title, idAuthor, isbn, description) VALUES (2, 'My Second Book', 1, '12345-6790', 'It wasn''t very good either')");
        $db->getDriver()->executeUpdate("INSERT INTO Books (idBook, title, idAuthor, isbn, description) VALUES (3, 'My First Book', 2, '12345-6790', 'It was OK')");
        return $db;
    }
Пример #6
0
 /**
  * Get a Table object for the given name
  * TODO table/column properties should not be public
  * @param Database $database
  * @param String $name
  * @throws \Exception
  * @return Table
  */
 public function buildTable(Database $database, $name)
 {
     $columns = $this->fetchAll("PRAGMA table_info(`{$database->getPrefix()}{$name}`)");
     if ($columns) {
         $tableClass = $database->getClassMapper()->getClassForTable($name);
         /** @var Table $table */
         $table = new $tableClass($name, $database);
         foreach ($columns as $ordinal => $columnInfo) {
             $column = new Column($table);
             $column->setName($columnInfo['name'])->setOrdinal($ordinal)->setType(strtolower($columnInfo['type']))->setDefault($columnInfo['dflt_value'])->setAllowNull($columnInfo['notnull'] === '0')->setPrimaryKey($columnInfo['pk'] === '1')->setAutoIncrement(strpos($columnInfo['type'], 'INT') === 0 && $columnInfo['pk']);
             if ($columnInfo['pk']) {
                 $table->primaryKeys[] = $columnInfo['name'];
             }
             $table->columns[$column->getName()] = $column;
         }
         // enumerate relationships
         $offset = strlen($database->getPrefix());
         $foreignKeys = $this->fetchAll("PRAGMA foreign_key_list(`{$database->getPrefix()}{$name}`)", [], null, 'id');
         foreach ($foreignKeys as $mappings) {
             $localColumns = [];
             $relatedColumns = [];
             $constraintName = null;
             $mapping = null;
             foreach ($mappings as $mapping) {
                 $localColumns[] = $mapping['from'];
                 $relatedColumns[] = $mapping['to'];
                 $constraintName = 'fk_' . $mapping['id'];
                 if (!isset($table->foreignKeys[$mapping['from']])) {
                     // columns can have multiple foreign keys; we can only use one of them
                     $table->foreignKeys[$mapping['from']] = $constraintName;
                 }
             }
             $foreignTableName = substr($mapping['table'], $offset);
             if (!isset($table->relatedTables[$foreignTableName])) {
                 // tables can be related to another table multiple times; we can only use one of them
                 $table->relatedTables[$foreignTableName] = $constraintName;
             }
             $table->constraints[$constraintName] = ['localColumns' => $localColumns, 'relatedTable' => $foreignTableName, 'relatedColumns' => $relatedColumns];
         }
         return $table;
     } else {
         throw new \Exception('Could not parse table definition');
     }
 }