/** * {@inheritdoc} */ public function alterColumn(AbstractTable $table, AbstractColumn $initial, AbstractColumn $column) { $query = "ALTER TABLE {table} CHANGE {column} {statement}"; $query = \Spiral\interpolate($query, ['table' => $table->getName(true), 'column' => $initial->getName(true), 'statement' => $column->sqlStatement()]); $this->run($query); return $this; }
/** * {@inheritdoc} */ public function alterColumn(AbstractTable $table, AbstractColumn $initial, AbstractColumn $column) { if (!$initial instanceof ColumnSchema || !$column instanceof ColumnSchema) { throw new SchemaException("Postgres commander can work only with Postgres columns."); } //Rename is separate operation if ($column->getName() != $initial->getName()) { $this->renameColumn($table, $initial, $column); //This call is required to correctly built set of alter operations $initial->setName($column->getName()); } //Postgres columns should be altered using set of operations if (!($operations = $column->alteringOperations($initial))) { return $this; } //Postgres columns should be altered using set of operations $query = \Spiral\interpolate('ALTER TABLE {table} {operations}', ['table' => $table->getName(true), 'operations' => trim(join(', ', $operations), ', ')]); $this->run($query); return $this; }
/** * Cast (specify) column schema based on provided column definition. Column definition are * compatible with database Migrations, AbstractColumn types and Record schema. * * @param AbstractTable $table * @param AbstractColumn $column * @param string $definition * @return AbstractColumn * @throws DefinitionException * @throws \Spiral\Database\Exceptions\SchemaException */ private function castColumn(AbstractTable $table, AbstractColumn $column, $definition) { //Expression used to declare column type, easy to read $pattern = '/(?P<type>[a-z]+)(?: *\\((?P<options>[^\\)]+)\\))?(?: *, *(?P<nullable>null(?:able)?))?/i'; if (!preg_match($pattern, $definition, $type)) { throw new DefinitionException("Invalid column type definition in '{$this}'.'{$column->getName()}'."); } if (!empty($type['options'])) { //Exporting and trimming $type['options'] = array_map('trim', explode(',', $type['options'])); } //We are forcing every column to be NOT NULL by default, DEFAULT value should fix potential //problems, nullable flag must be applied before type was set (some types do not want //null values to be allowed) $column->nullable(!empty($type['nullable'])); //Bypassing call to AbstractColumn->__call method (or specialized column method) call_user_func_array([$column, $type['type']], !empty($type['options']) ? $type['options'] : []); //Default value if (!$column->hasDefaultValue() && !$column->isNullable()) { //Ouch, columns like that can break synchronization! $column->defaultValue($this->castDefault($table, $column)); } return $column; }
/** * Export default value from column schema into scalar form (which we can store in cache). * * @param AbstractColumn $column * @return mixed|null */ private function exportDefault(AbstractColumn $column) { if (in_array($column->getName(), $this->tableSchema->getPrimaryKeys())) { //Column declared as primary key, nothing to do with default values return null; } $defaultValue = $column->getDefaultValue(); if ($defaultValue instanceof FragmentInterface) { //We can't cache values like that return null; } if (is_null($defaultValue) && !$column->isNullable()) { return $this->castDefault($column); } return $defaultValue; }
/** * {@inheritdoc} */ protected function doColumnChange(AbstractColumn $column, AbstractColumn $dbColumn) { $query = \Spiral\interpolate("ALTER TABLE {table} CHANGE {column} {statement}", ['table' => $this->getName(true), 'column' => $dbColumn->getName(true), 'statement' => $column->sqlStatement()]); $this->driver->statement($query); }
/** * {@inheritdoc} */ protected function doColumnChange(AbstractColumn $column, AbstractColumn $dbColumn) { /** * @var ColumnSchema $column */ //Rename is separate operation if ($column->getName() != $dbColumn->getName()) { $this->driver->statement(\Spiral\interpolate('ALTER TABLE {table} RENAME COLUMN {original} TO {column}', ['table' => $this->getName(true), 'column' => $column->getName(true), 'original' => $dbColumn->getName(true)])); $column->setName($dbColumn->getName()); } //Postgres columns should be altered using set of operations if (!($operations = $column->alterOperations($dbColumn))) { return; } //Postgres columns should be altered using set of operations $query = \Spiral\interpolate('ALTER TABLE {table} {operations}', ['table' => $this->getName(true), 'operations' => trim(join(', ', $operations), ', ')]); $this->driver->statement($query); }
/** * Driver specific column remove (drop) command. * * @param AbstractColumn $column */ protected function doColumnDrop(AbstractColumn $column) { //We have to erase all associated constraints foreach ($column->getConstraints() as $constraint) { $this->doConstraintDrop($constraint); } if ($this->hasForeign($column->getName())) { $this->doForeignDrop($this->foreign($column->getName())); } $this->driver->statement("ALTER TABLE {$this->getName(true)} DROP COLUMN {$column->getName(true)}"); }
/** * {@inheritdoc} */ protected function doColumnChange(AbstractColumn $column, AbstractColumn $dbColumn) { /** * @var ColumnSchema $column * @var ColumnSchema $dbColumn */ //Renaming is separate operation if ($column->getName() != $dbColumn->getName()) { $this->driver->statement("sp_rename ?, ?, 'COLUMN'", [$this->getName() . '.' . $dbColumn->getName(), $column->getName()]); $column->setName($dbColumn->getName()); } //In SQLServer we have to drop ALL related indexes and foreign keys while //applying type change... yeah... $indexesBackup = []; $foreignBackup = []; foreach ($this->indexes as $index) { if (in_array($column->getName(), $index->getColumns())) { $indexesBackup[] = $index; $this->doIndexDrop($index); } } foreach ($this->references as $foreign) { if ($foreign->getColumn() == $column->getName()) { $foreignBackup[] = $foreign; $this->doForeignDrop($foreign); } } //Column will recreate needed constraints foreach ($column->getConstraints() as $constraint) { $this->doConstraintDrop($constraint); } foreach ($column->alterOperations($dbColumn) as $operation) { $query = \Spiral\interpolate('ALTER TABLE {table} {operation}', ['table' => $this->getName(true), 'operation' => $operation]); $this->driver->statement($query); } //Recreating indexes foreach ($indexesBackup as $index) { $this->doIndexAdd($index); } foreach ($foreignBackup as $foreign) { $this->doForeignAdd($foreign); } }
/** * Driver specific column remove (drop) command. * * @param AbstractTable $table * @param AbstractColumn $column * @return self */ public function dropColumn(AbstractTable $table, AbstractColumn $column) { foreach ($column->getConstraints() as $constraint) { //We have to erase all associated constraints $this->dropConstrain($table, $constraint); } $this->run("ALTER TABLE {$table->getName(true)} DROP COLUMN {$column->getName(true)}"); return $this; }
/** * Register new column element. * * @param AbstractColumn $column * @return AbstractColumn */ protected function registerColumn(AbstractColumn $column) { $this->columns[$column->getName()] = $column; return $column; }