/** * @param AbstractTable $table * @param RecordSchema $record */ public function __construct(AbstractTable $table, RecordSchema $record) { $altered = []; foreach ($table->alteredColumns() as $column) { $altered[] = $column->getName(); } parent::__construct(\Spiral\interpolate('Passive table "{database}"."{table}" ({record}), were altered, columns: {columns}', ['database' => $record->getDatabase(), 'table' => $table->getName(), 'record' => $record, 'columns' => join(', ', $altered)])); }
/** * Cast default value based on column type. Required to prevent conflicts when not nullable * column added to existed table with data in. * * @param AbstractColumn $column * @return bool|float|int|mixed|string */ private function castDefault(AbstractColumn $column) { if ($column->abstractType() == 'timestamp' || $column->abstractType() == 'datetime') { $driver = $this->tableSchema->driver(); return $driver::DEFAULT_DATETIME; } if ($column->abstractType() == 'enum') { //We can use first enum value as default return $column->getEnumValues()[0]; } if ($column->abstractType() == 'json') { return '{}'; } switch ($column->phpType()) { case 'int': return 0; break; case 'float': return 0.0; break; case 'bool': return false; break; } return ''; }
/** * @param Driver $driver Parent driver. * @param AbstractCommander $commander * @param string $name Table name, must include table prefix. * @param string $prefix Database specific table prefix. */ public function __construct(Driver $driver, AbstractCommander $commander, $name, $prefix) { parent::__construct($driver, $commander, $name, $prefix); //Let's load table type, just for fun if ($this->exists()) { $query = $driver->query('SHOW TABLE STATUS WHERE Name = ?', [$name]); $this->engine = $query->fetch()['Engine']; } }
/** * Index sql creation syntax. * * @param bool $includeTable Include table ON statement (not required for inline index * creation). * @return string */ public function sqlStatement($includeTable = true) { $statement = []; $statement[] = $this->type . ($this->type == self::UNIQUE ? ' INDEX' : ''); $statement[] = $this->getName(true); if ($includeTable) { $statement[] = 'ON ' . $this->table->getName(true); } $statement[] = '(' . join(', ', array_map([$this->table->driver(), 'identifier'], $this->columns)) . ')'; return join(' ', $statement); }
/** * Foreign key creation syntax. * * @return string */ public function sqlStatement() { $statement = []; $statement[] = 'CONSTRAINT'; $statement[] = $this->getName(true); $statement[] = 'FOREIGN KEY'; $statement[] = '(' . $this->table->driver()->identifier($this->column) . ')'; $statement[] = 'REFERENCES ' . $this->table->driver()->identifier($this->foreignTable); $statement[] = '(' . $this->table->driver()->identifier($this->foreignKey) . ')'; $statement[] = "ON DELETE {$this->deleteRule}"; $statement[] = "ON UPDATE {$this->updateRule}"; return join(" ", $statement); }
/** * @param AbstractTable $schema * @return AbstractColumn * @throws ColumnException */ protected function declareColumn(AbstractTable $schema) { $column = $schema->column($this->name); //Type configuring if (method_exists($column, $this->type)) { $arguments = []; $method = new \ReflectionMethod($column, $this->type); foreach ($method->getParameters() as $parameter) { if ($this->hasOption($parameter->getName())) { $arguments[] = $this->getOption($parameter->getName()); } elseif (!$parameter->isOptional()) { throw new ColumnException("Option '{$parameter->getName()}' are required to define column with type '{$this->type}'"); } else { $arguments[] = $parameter->getDefaultValue(); } } call_user_func_array([$column, $this->type], $arguments); } else { $column->setType($this->type); } $column->nullable($this->getOption('nullable', false)); $column->defaultValue($this->getOption('default', null)); return $column; }
/** * {@inheritdoc} */ public function dropForeign(AbstractTable $table, AbstractReference $foreign) { $this->run("ALTER TABLE {$table->getName(true)} DROP FOREIGN KEY {$foreign->getName(true)}"); return $this; }
/** * Must return driver specific default value. * * @return string */ protected function prepareDefault() { if (($defaultValue = $this->getDefaultValue()) === null) { return 'NULL'; } if ($defaultValue instanceof SQLFragmentInterface) { return $defaultValue->sqlStatement(); } if ($this->phpType() == 'bool') { return $defaultValue ? 'TRUE' : 'FALSE'; } if ($this->phpType() == 'float') { return sprintf('%F', $defaultValue); } if ($this->phpType() == 'int') { return $defaultValue; } return $this->table->driver()->getPDO()->quote($defaultValue); }
/** * Copy table data to another location. * * @see http://stackoverflow.com/questions/4007014/alter-column-in-sqlite * @param AbstractTable $temporary * @param array $mapping Association between old and new columns (quoted). */ private function copyData(AbstractTable $temporary, array $mapping) { $this->logger()->debug("Copying table data from {source} to {table} using mapping ({columns}) => ({target}).", ['source' => $this->driver->identifier($this->initial->getName()), 'table' => $temporary->getName(true), 'columns' => join(', ', $mapping), 'target' => join(', ', array_keys($mapping))]); $query = \Spiral\interpolate("INSERT INTO {table} ({target}) SELECT {columns} FROM {source}", ['source' => $this->driver->identifier($this->initial->getName()), 'table' => $temporary->getName(true), 'columns' => join(', ', $mapping), 'target' => join(', ', array_keys($mapping))]); //Let's go $this->driver->statement($query); }
/** * {@inheritdoc} */ protected function createSchema($execute = true) { $statement = parent::createSchema(false); //Additional table options $options = "ENGINE = {engine}"; $statement = $statement . ' ' . \Spiral\interpolate($options, ['engine' => $this->engine]); if ($execute) { $this->driver->statement($statement); //Not all databases support adding index while table creation, so we can do it after foreach ($this->indexes as $index) { $this->doIndexAdd($index); } } return $statement; }
/** * Get statement needed to create table. * * @param AbstractTable $table * @return string */ protected function createStatement(AbstractTable $table) { $statement = ["CREATE TABLE {$table->getName(true)} ("]; $innerStatement = []; //Columns foreach ($table->getColumns() as $column) { $innerStatement[] = $column->sqlStatement(); } //Primary key if (!empty($table->getPrimaryKeys())) { $primaryKeys = array_map([$this, 'quote'], $table->getPrimaryKeys()); $innerStatement[] = 'PRIMARY KEY (' . join(', ', $primaryKeys) . ')'; } //Constraints and foreign keys foreach ($table->getForeigns() as $reference) { $innerStatement[] = $reference->sqlStatement(); } $statement[] = " " . join(",\n ", $innerStatement); $statement[] = ')'; return join("\n", $statement); }
/** * Table aliases associated with given table schema. * * @param AbstractTable $table * @return string * @throws SchemaException */ public function tableAlias(AbstractTable $table) { foreach ($this->aliases as $item) { if ($item['schema'] === $table) { return $item['table']; } } throw new SchemaException("Unable to resolve table alias for table '{$table->getName()}'"); }
/** * @param AbstractTable $table * @param ColumnSchema $initial * @param ColumnSchema $column */ private function renameColumn(AbstractTable $table, ColumnSchema $initial, ColumnSchema $column) { $statement = \Spiral\interpolate('ALTER TABLE {table} RENAME COLUMN {column} TO {name}', ['table' => $table->getName(true), 'column' => $initial->getName(true), 'name' => $column->getName(true)]); $this->run($statement); }