Page MenuHomeWickedGov Phorge

SchemaGenerator.php
No OneTemporary

Size
4 KB
Referenced Files
None
Subscribers
None

SchemaGenerator.php

<?php
namespace MediaWiki\Maintenance;
use Doctrine\SqlFormatter\NullHighlighter;
use Doctrine\SqlFormatter\SqlFormatter;
use JsonException;
use MediaWiki\DB\AbstractSchemaValidationError;
use MediaWiki\DB\AbstractSchemaValidator;
use Wikimedia\Rdbms\DoctrineSchemaBuilderFactory;
/**
* Helper to generate abstract schema and schema changes in maintenance scripts.
*/
class SchemaGenerator {
/**
* Fetches the abstract schema.
*
* @param string $jsonPath
* @return array
* @throws AbstractSchemaValidationError
*/
public function validateAndGetSchema( string $jsonPath ): array {
$json = file_get_contents( $jsonPath );
if ( !$json ) {
throw new AbstractSchemaValidationError( "'$jsonPath' does not exist!" );
}
try {
$abstractSchema = json_decode( $json, true, 512, JSON_THROW_ON_ERROR );
} catch ( JsonException $e ) {
throw new AbstractSchemaValidationError( "Invalid JSON schema: " . $e->getMessage(), 0, $e );
}
$validator = new AbstractSchemaValidator();
$validator->validate( $jsonPath );
return $abstractSchema;
}
/**
* @throws AbstractSchemaValidationError
*/
public function generateSchema( string $platform, string $jsonPath ): string {
$abstractSchemaChange = $this->validateAndGetSchema( $jsonPath );
$sql = $this->makeSQLComment( 'generateSchemaSql.php', $jsonPath );
$schemaBuilder = ( new DoctrineSchemaBuilderFactory() )->getSchemaBuilder( $platform );
foreach ( $abstractSchemaChange as $table ) {
$schemaBuilder->addTable( $table );
}
$tableSqls = $schemaBuilder->getSql();
$sql .= $this->cleanupSqlArray( $platform, $tableSqls );
return $sql;
}
/**
* @throws AbstractSchemaValidationError
*/
public function generateSchemaChange( string $platform, string $jsonPath ): string {
$abstractSchemaChange = $this->validateAndGetSchema( $jsonPath );
$sql = $this->makeSQLComment( 'generateSchemaChangeSql.php', $jsonPath );
$schemaChangeBuilder = ( new DoctrineSchemaBuilderFactory() )->getSchemaChangeBuilder( $platform );
$schemaChangeSqls = $schemaChangeBuilder->getSchemaChangeSql( $abstractSchemaChange );
if ( !$schemaChangeSqls ) {
throw new AbstractSchemaValidationError( "No schema changes detected!" );
}
$sql .= $this->cleanupSqlArray( $platform, $schemaChangeSqls );
return $sql;
}
private function makeSQLComment( string $scriptName, string $jsonPath ): string {
global $IP;
$installPath = realpath( $IP );
$jsonPath = realpath( $jsonPath );
// For windows
if ( DIRECTORY_SEPARATOR === '\\' ) {
$installPath = strtr( $installPath, '\\', '/' );
$jsonPath = strtr( $jsonPath, '\\', '/' );
}
$canonicalJsonPath = str_replace( "$installPath/", '', $jsonPath );
$canonicalJsonPath = preg_replace( '!^extensions/[^/]+/!', '', $canonicalJsonPath );
return "-- This file is automatically generated using maintenance/$scriptName.\n" .
"-- Source: $canonicalJsonPath\n" .
"-- Do not modify this file directly.\n" .
"-- See https://www.mediawiki.org/wiki/Manual:Schema_changes\n";
}
/**
* Takes the output of DoctrineSchemaBuilder::getSql() or
* DoctrineSchemaChangeBuilder::getSchemaChangeSql() and applies presentational changes.
*
* @param string $platform DB engine identifier
* @param array $sqlArray Array of SQL statements
* @return string
*/
private function cleanupSqlArray( string $platform, array $sqlArray ): string {
if ( !$sqlArray ) {
return '';
}
// Temporary
$sql = implode( ";\n\n", $sqlArray ) . ';';
$sql = ( new SqlFormatter( new NullHighlighter() ) )->format( $sql );
// Postgres hacks
if ( $platform === 'postgres' ) {
// FIXME: Fix a lot of weird formatting issues caused by
// presence of partial index's WHERE clause, this should probably
// be done in some better way, but for now this can work temporarily
$sql = str_replace(
[ "WHERE\n ", "\n /*_*/\n ", " ", " );", "KEY(\n " ],
[ "WHERE", ' ', " ", ');', "KEY(\n " ],
$sql
);
}
// Temporary fixes until the linting issues are resolved upstream.
// https://github.com/doctrine/sql-formatter/issues/53
$sql = preg_replace( "!\s+/\*_\*/\s+!", " /*_*/", $sql );
$sql = preg_replace(
'!\s+/\*\$wgDBTableOptions\*/\s+;!',
' /*$wgDBTableOptions*/;',
$sql
);
$sql = str_replace( "; CREATE ", ";\n\nCREATE ", $sql );
$sql = str_replace( ";\n\nCREATE TABLE ", ";\n\n\nCREATE TABLE ", $sql );
$sql = preg_replace( '/^(CREATE|DROP|ALTER)\s+(TABLE|VIEW|INDEX)\s+/m', '$1 $2 ', $sql );
$sql = preg_replace( '/(?<!\s|;)\s+(ADD|DROP|ALTER|MODIFY|CHANGE|RENAME)\s+/', "\n \$1 ", $sql );
$sql = str_replace( "; ", ";\n", $sql );
if ( !str_ends_with( $sql, "\n" ) ) {
$sql .= "\n";
}
// Sqlite hacks
if ( $platform === 'sqlite' ) {
// Doctrine prepends __temp__ to the table name and we set the table with the schema prefix causing invalid
// sqlite.
$sql = preg_replace( '/__temp__\s*\/\*_\*\//', '/*_*/__temp__', $sql );
}
return $sql;
}
}

File Metadata

Mime Type
text/x-php
Expires
Sat, Oct 3, 14:53 (17 h, 58 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
2b/ed/7e61e52f49f1cc61a8f893ad3533
Default Alt Text
SchemaGenerator.php (4 KB)

Event Timeline