[DATABASE] Fix MariaDB schema verification
This commit is contained in:
parent
32a7cd6458
commit
eb993df072
@ -30,7 +30,7 @@ class Foreign_link extends Managed_DataObject
|
||||
return array(
|
||||
'fields' => array(
|
||||
'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'link to user on this system, if exists'),
|
||||
'foreign_id' => array('type' => 'int', 'size' => 'big', 'unsigned' => true, 'not null' => true, 'description' => 'link to user on foreign service, if exists'),
|
||||
'foreign_id' => array('type' => 'int', 'size' => 'big', 'not null' => true, 'description' => 'link to user on foreign service, if exists'),
|
||||
'service' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to service'),
|
||||
'credentials' => array('type' => 'varchar', 'length' => 191, 'description' => 'authc credentials, typically a password'),
|
||||
'noticesync' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 1, 'description' => 'notice synchronization, bit 1 = sync outgoing, bit 2 = sync incoming, bit 3 = filter local replies'),
|
||||
@ -53,7 +53,7 @@ class Foreign_link extends Managed_DataObject
|
||||
);
|
||||
}
|
||||
|
||||
static function getByUserID($user_id, $service)
|
||||
public static function getByUserID($user_id, $service)
|
||||
{
|
||||
if (empty($user_id) || empty($service)) {
|
||||
throw new ServerException('Empty user_id or service for Foreign_link::getByUserID');
|
||||
@ -71,7 +71,7 @@ class Foreign_link extends Managed_DataObject
|
||||
return $flink;
|
||||
}
|
||||
|
||||
static function getByForeignID($foreign_id, $service)
|
||||
public static function getByForeignID($foreign_id, $service)
|
||||
{
|
||||
if (empty($foreign_id) || empty($service)) {
|
||||
throw new ServerException('Empty foreign_id or service for Foreign_link::getByForeignID');
|
||||
@ -89,7 +89,7 @@ class Foreign_link extends Managed_DataObject
|
||||
return $flink;
|
||||
}
|
||||
|
||||
function set_flags($noticesend, $noticerecv, $replysync, $repeatsync, $friendsync)
|
||||
public function set_flags($noticesend, $noticerecv, $replysync, $repeatsync, $friendsync)
|
||||
{
|
||||
if ($noticesend) {
|
||||
$this->noticesync |= FOREIGN_NOTICE_SEND;
|
||||
@ -125,7 +125,7 @@ class Foreign_link extends Managed_DataObject
|
||||
}
|
||||
|
||||
// Convenience methods
|
||||
function getForeignUser()
|
||||
public function getForeignUser()
|
||||
{
|
||||
$fuser = new Foreign_user();
|
||||
$fuser->service = $this->service;
|
||||
@ -140,29 +140,30 @@ class Foreign_link extends Managed_DataObject
|
||||
return $fuser;
|
||||
}
|
||||
|
||||
function getUser()
|
||||
public function getUser()
|
||||
{
|
||||
return Profile::getByID($this->user_id)->getUser();
|
||||
}
|
||||
|
||||
function getProfile()
|
||||
public function getProfile()
|
||||
{
|
||||
return Profile::getByID($this->user_id);
|
||||
}
|
||||
|
||||
// Make sure we only ever delete one record at a time
|
||||
function safeDelete()
|
||||
public function safeDelete()
|
||||
{
|
||||
if (!empty($this->user_id)
|
||||
&& !empty($this->foreign_id)
|
||||
&& !empty($this->service))
|
||||
{
|
||||
&& !empty($this->service)) {
|
||||
return $this->delete();
|
||||
} else {
|
||||
common_debug(LOG_WARNING,
|
||||
common_debug(
|
||||
LOG_WARNING,
|
||||
'Foreign_link::safeDelete() tried to delete a '
|
||||
. 'Foreign_link without a fully specified compound key: '
|
||||
. var_export($this, true));
|
||||
. var_export($this, true)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -83,39 +83,53 @@ class MysqlSchema extends Schema
|
||||
$name = $row['COLUMN_NAME'];
|
||||
$field = [];
|
||||
|
||||
// warning -- 'unsigned' attr on numbers isn't given in DATA_TYPE and friends.
|
||||
// It is stuck in on COLUMN_TYPE though (eg 'bigint(20) unsigned')
|
||||
$field['type'] = $type = $row['DATA_TYPE'];
|
||||
$type = $field['type'] = $row['DATA_TYPE'];
|
||||
|
||||
if ($type == 'char' || $type == 'varchar') {
|
||||
if ($row['CHARACTER_MAXIMUM_LENGTH'] !== null) {
|
||||
$field['length'] = intval($row['CHARACTER_MAXIMUM_LENGTH']);
|
||||
}
|
||||
}
|
||||
if ($type == 'decimal') {
|
||||
// Other int types may report these values, but they're irrelevant.
|
||||
// Just ignore them!
|
||||
if ($row['NUMERIC_PRECISION'] !== null) {
|
||||
$field['precision'] = intval($row['NUMERIC_PRECISION']);
|
||||
}
|
||||
if ($row['NUMERIC_SCALE'] !== null) {
|
||||
$field['scale'] = intval($row['NUMERIC_SCALE']);
|
||||
}
|
||||
switch ($type) {
|
||||
case 'char':
|
||||
case 'varchar':
|
||||
if (!is_null($row['CHARACTER_MAXIMUM_LENGTH'])) {
|
||||
$field['length'] = (int) $row['CHARACTER_MAXIMUM_LENGTH'];
|
||||
}
|
||||
break;
|
||||
case 'decimal':
|
||||
// Other int types may report these values, but they're irrelevant.
|
||||
// Just ignore them!
|
||||
if (!is_null($row['NUMERIC_PRECISION'])) {
|
||||
$field['precision'] = (int) $row['NUMERIC_PRECISION'];
|
||||
}
|
||||
if (!is_null($row['NUMERIC_SCALE'])) {
|
||||
$field['scale'] = (int) $row['NUMERIC_SCALE'];
|
||||
}
|
||||
break;
|
||||
case 'enum':
|
||||
$enum = preg_replace("/^enum\('(.+)'\)$/", '\1', $row['COLUMN_TYPE']);
|
||||
$field['enum'] = explode("','", $enum);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if ($row['IS_NULLABLE'] == 'NO') {
|
||||
$field['not null'] = true;
|
||||
}
|
||||
if ($row['COLUMN_DEFAULT'] !== null) {
|
||||
// Hack for timestamp columns
|
||||
if ($row['COLUMN_DEFAULT'] === 'current_timestamp()') {
|
||||
// skip timestamp columns as they get a CURRENT_TIMESTAMP default implicitly
|
||||
$col_default = $row['COLUMN_DEFAULT'];
|
||||
if (!is_null($col_default) && $col_default !== 'NULL') {
|
||||
if ($this->isNumericType($field)) {
|
||||
$field['default'] = (int) $col_default;
|
||||
} elseif ($col_default === 'CURRENT_TIMESTAMP'
|
||||
|| $col_default === 'current_timestamp()') {
|
||||
// A hack for "datetime" fields
|
||||
// Skip "timestamp" as they get a CURRENT_TIMESTAMP default implicitly
|
||||
if ($type !== 'timestamp') {
|
||||
$field['default'] = 'CURRENT_TIMESTAMP';
|
||||
}
|
||||
} elseif ($this->isNumericType($type)) {
|
||||
$field['default'] = intval($row['COLUMN_DEFAULT']);
|
||||
} else {
|
||||
$field['default'] = $row['COLUMN_DEFAULT'];
|
||||
$match = "/^'(.*)'$/";
|
||||
if (preg_match($match, $col_default)) {
|
||||
$field['default'] = preg_replace($match, '\1', $col_default);
|
||||
} else {
|
||||
$field['default'] = $col_default;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($row['COLUMN_KEY'] !== null) {
|
||||
@ -135,11 +149,11 @@ class MysqlSchema extends Schema
|
||||
// ^ ...... how to specify?
|
||||
}
|
||||
|
||||
/* @fixme check against defaults?
|
||||
if ($row['CHARACTER_SET_NAME'] !== null) {
|
||||
$def['charset'] = $row['CHARACTER_SET_NAME'];
|
||||
$def['collate'] = $row['COLLATION_NAME'];
|
||||
}*/
|
||||
$table_props = $this->getTableProperties($table, ['TABLE_COLLATION']);
|
||||
$collate = $row['COLLATION_NAME'];
|
||||
if (!empty($collate) && $collate !== $table_props['TABLE_COLLATION']) {
|
||||
$field['collate'] = $collate;
|
||||
}
|
||||
|
||||
$def['fields'][$name] = $field;
|
||||
}
|
||||
@ -389,7 +403,7 @@ class MysqlSchema extends Schema
|
||||
public function appendAlterExtras(array &$phrase, $tableName, array $def)
|
||||
{
|
||||
// Check for table properties: make sure we're using a sane
|
||||
// engine type and charset/collation.
|
||||
// engine type and collation.
|
||||
// @fixme make the default engine configurable?
|
||||
$oldProps = $this->getTableProperties($tableName, ['ENGINE', 'TABLE_COLLATION']);
|
||||
$engine = $this->preferredEngine($def);
|
||||
@ -403,12 +417,24 @@ class MysqlSchema extends Schema
|
||||
}
|
||||
}
|
||||
|
||||
private function isNumericType(array $cd): bool
|
||||
{
|
||||
$ints = array_map(
|
||||
function ($s) {
|
||||
return $s . 'int';
|
||||
},
|
||||
['tiny', 'small', 'medium', 'big']
|
||||
);
|
||||
$ints = array_merge($ints, ['int', 'numeric', 'serial']);
|
||||
return in_array(strtolower($cd['type']), $ints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this column a string type?
|
||||
* @param array $cd
|
||||
* @return bool
|
||||
*/
|
||||
private function _isString(array $cd)
|
||||
private function isStringType(array $cd): bool
|
||||
{
|
||||
$strings = ['char', 'varchar', 'text'];
|
||||
return in_array(strtolower($cd['type']), $strings);
|
||||
@ -448,7 +474,6 @@ class MysqlSchema extends Schema
|
||||
{
|
||||
$map = [
|
||||
'integer' => 'int',
|
||||
'bool' => 'tinyint',
|
||||
'numeric' => 'decimal',
|
||||
];
|
||||
|
||||
@ -476,15 +501,13 @@ class MysqlSchema extends Schema
|
||||
public function typeAndSize(string $name, array $column)
|
||||
{
|
||||
if ($column['type'] === 'enum') {
|
||||
$vals = [];
|
||||
foreach ($column['enum'] as &$val) {
|
||||
$vals[] = "'" . $val . "'";
|
||||
$vals[] = "'{$val}'";
|
||||
}
|
||||
return 'enum(' . implode(',', $vals) . ')';
|
||||
} elseif ($this->_isString($column)) {
|
||||
} elseif ($this->isStringType($column)) {
|
||||
$col = parent::typeAndSize($name, $column);
|
||||
if (!empty($column['charset'])) {
|
||||
$col .= ' CHARSET ' . $column['charset'];
|
||||
}
|
||||
if (!empty($column['collate'])) {
|
||||
$col .= ' COLLATE ' . $column['collate'];
|
||||
}
|
||||
@ -501,16 +524,39 @@ class MysqlSchema extends Schema
|
||||
* This lets us strip out unsupported things like comments, foreign keys,
|
||||
* or type variants that we wouldn't get back from getTableDef().
|
||||
*
|
||||
* @param string $tableName
|
||||
* @param array $tableDef
|
||||
* @return array
|
||||
*/
|
||||
public function filterDef(array $tableDef)
|
||||
public function filterDef(string $tableName, array $tableDef)
|
||||
{
|
||||
$version = $this->conn->getVersion();
|
||||
$tableDef = parent::filterDef($tableName, $tableDef);
|
||||
|
||||
// Get existing table collation if the table exists.
|
||||
// To know if collation that's been set is unique for the table.
|
||||
try {
|
||||
$table_props = $this->getTableProperties($tableName, ['TABLE_COLLATION']);
|
||||
$table_collate = $table_props['TABLE_COLLATION'];
|
||||
} catch (SchemaTableMissingException $e) {
|
||||
$table_collate = null;
|
||||
}
|
||||
|
||||
foreach ($tableDef['fields'] as $name => &$col) {
|
||||
if ($col['type'] == 'serial') {
|
||||
$col['type'] = 'int';
|
||||
$col['auto_increment'] = true;
|
||||
switch ($col['type']) {
|
||||
case 'serial':
|
||||
$col['type'] = 'int';
|
||||
$col['auto_increment'] = true;
|
||||
break;
|
||||
case 'bool':
|
||||
$col['type'] = 'int';
|
||||
$col['size'] = 'tiny';
|
||||
$col['default'] = (int) $col['default'];
|
||||
break;
|
||||
}
|
||||
|
||||
if (!empty($col['collate'])
|
||||
&& $col['collate'] === $table_collate) {
|
||||
unset($col['collate']);
|
||||
}
|
||||
|
||||
$col['type'] = $this->mapType($col);
|
||||
|
@ -110,8 +110,8 @@ class PgsqlSchema extends Schema
|
||||
}
|
||||
if ($row['column_default'] !== null) {
|
||||
$field['default'] = $row['column_default'];
|
||||
if ($this->isNumericType($type)) {
|
||||
$field['default'] = intval($field['default']);
|
||||
if ($this->isNumericType($field)) {
|
||||
$field['default'] = (int) $field['default'];
|
||||
}
|
||||
}
|
||||
|
||||
@ -266,6 +266,12 @@ class PgsqlSchema extends Schema
|
||||
return $out;
|
||||
}
|
||||
|
||||
private function isNumericType(array $cd): bool
|
||||
{
|
||||
$ints = ['int', 'numeric', 'serial'];
|
||||
return in_array(strtolower($cd['type']), $ints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the proper SQL for creating or
|
||||
* altering a column.
|
||||
@ -395,11 +401,14 @@ class PgsqlSchema extends Schema
|
||||
* This lets us strip out unsupported things like comments, foreign keys,
|
||||
* or type variants that we wouldn't get back from getTableDef().
|
||||
*
|
||||
* @param string $tableName
|
||||
* @param array $tableDef
|
||||
* @return array
|
||||
*/
|
||||
public function filterDef(array $tableDef)
|
||||
public function filterDef(string $tableName, array $tableDef)
|
||||
{
|
||||
$tableDef = parent::filterDef($tableName, $tableDef);
|
||||
|
||||
foreach ($tableDef['fields'] as $name => &$col) {
|
||||
// No convenient support for field descriptions
|
||||
unset($col['description']);
|
||||
@ -409,22 +418,19 @@ class PgsqlSchema extends Schema
|
||||
$col['type'] = 'int';
|
||||
$col['auto_increment'] = true;
|
||||
break;
|
||||
case 'timestamp':
|
||||
// FIXME: ON UPDATE CURRENT_TIMESTAMP
|
||||
if (!array_key_exists('default', $col)) {
|
||||
$col['default'] = 'CURRENT_TIMESTAMP';
|
||||
}
|
||||
// no break
|
||||
case 'datetime':
|
||||
// Replace archaic MySQL-specific zero-dates with NULL
|
||||
// Replace archaic MySQL-specific zero dates with NULL
|
||||
if (($col['default'] ?? null) === '0000-00-00 00:00:00') {
|
||||
$col['default'] = null;
|
||||
$col['not null'] = false;
|
||||
}
|
||||
break;
|
||||
case 'timestamp':
|
||||
// In MariaDB: If the column does not permit NULL values,
|
||||
// assigning NULL (or not referencing the column at all
|
||||
// when inserting) will set the column to CURRENT_TIMESTAMP
|
||||
// FIXME: ON UPDATE CURRENT_TIMESTAMP
|
||||
if ($col['not null'] && !isset($col['default'])) {
|
||||
$col['default'] = 'CURRENT_TIMESTAMP';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$col['type'] = $this->mapType($col);
|
||||
|
@ -139,7 +139,7 @@ class Schema
|
||||
public function buildCreateTable($name, $def)
|
||||
{
|
||||
$def = $this->validateDef($name, $def);
|
||||
$def = $this->filterDef($def);
|
||||
$def = $this->filterDef($name, $def);
|
||||
$sql = [];
|
||||
|
||||
foreach ($def['fields'] as $col => $colDef) {
|
||||
@ -572,10 +572,10 @@ class Schema
|
||||
// Filter the DB-independent table definition to match the current
|
||||
// database engine's features and limitations.
|
||||
$def = $this->validateDef($tableName, $def);
|
||||
$def = $this->filterDef($def);
|
||||
$def = $this->filterDef($tableName, $def);
|
||||
|
||||
$statements = [];
|
||||
$fields = $this->diffArrays($old, $def, 'fields', [$this, 'columnsEqual']);
|
||||
$fields = $this->diffArrays($old, $def, 'fields');
|
||||
$uniques = $this->diffArrays($old, $def, 'unique keys');
|
||||
$indexes = $this->diffArrays($old, $def, 'indexes');
|
||||
$foreign = $this->diffArrays($old, $def, 'foreign keys');
|
||||
@ -813,20 +813,6 @@ class Schema
|
||||
return $this->conn->quoteSmart($val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if two column definitions are equivalent.
|
||||
* The default implementation checks _everything_ but in many cases
|
||||
* you may be able to discard a bunch of equivalencies.
|
||||
*
|
||||
* @param array $a
|
||||
* @param array $b
|
||||
* @return bool
|
||||
*/
|
||||
public function columnsEqual(array $a, array $b)
|
||||
{
|
||||
return !array_diff_assoc($a, $b) && !array_diff_assoc($b, $a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of names from an array of
|
||||
* ColumnDef objects.
|
||||
@ -886,9 +872,11 @@ class Schema
|
||||
if (isset($cd['default'])) {
|
||||
$line[] = 'default';
|
||||
$line[] = $this->quoteDefaultValue($cd);
|
||||
} elseif (!empty($cd['not null'])) {
|
||||
// Can't have both not null AND default!
|
||||
$line[] = 'not null';
|
||||
}
|
||||
if (!empty($cd['not null'])) {
|
||||
$line[] = 'NOT NULL';
|
||||
} else {
|
||||
$line[] = 'NULL';
|
||||
}
|
||||
|
||||
return implode(' ', $line);
|
||||
@ -1004,11 +992,21 @@ class Schema
|
||||
* This lets us strip out unsupported things like comments, foreign keys,
|
||||
* or type variants that we wouldn't get back from getTableDef().
|
||||
*
|
||||
* @param string $tableName
|
||||
* @param array $tableDef
|
||||
* @return array
|
||||
*/
|
||||
public function filterDef(array $tableDef)
|
||||
public function filterDef(string $tableName, array $tableDef)
|
||||
{
|
||||
foreach ($tableDef['fields'] as $name => &$col) {
|
||||
if (array_key_exists('default', $col) && is_null($col['default'])) {
|
||||
unset($col['default']);
|
||||
}
|
||||
if (array_key_exists('not null', $col) && $col['not null'] !== true) {
|
||||
unset($col['not null']);
|
||||
}
|
||||
}
|
||||
|
||||
return $tableDef;
|
||||
}
|
||||
|
||||
@ -1037,13 +1035,6 @@ class Schema
|
||||
return $def;
|
||||
}
|
||||
|
||||
public function isNumericType($type)
|
||||
{
|
||||
$type = strtolower($type);
|
||||
$known = ['int', 'serial', 'numeric'];
|
||||
return in_array($type, $known);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull info from the query into a fun-fun array of dooooom
|
||||
*
|
||||
|
@ -61,7 +61,7 @@ class Activitypub_rsa extends Managed_DataObject
|
||||
],
|
||||
'primary key' => ['profile_id'],
|
||||
'foreign keys' => [
|
||||
'activitypub_profile_profile_id_fkey' => ['profile', ['profile_id' => 'id']],
|
||||
'activitypub_rsa_profile_id_fkey' => ['profile', ['profile_id' => 'id']],
|
||||
],
|
||||
];
|
||||
}
|
||||
@ -182,7 +182,7 @@ class Activitypub_rsa extends Managed_DataObject
|
||||
$apRSA = new Activitypub_rsa();
|
||||
$apRSA->profile_id = $profile->getID();
|
||||
$apRSA->public_key = $public_key;
|
||||
$apRSA->modified = common_sql_now();
|
||||
$apRSA->created = common_sql_now();
|
||||
if (!$apRSA->update()) {
|
||||
$apRSA->insert();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user