Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F5971012
SchemaGenerator.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
4 KB
Referenced Files
None
Subscribers
None
SchemaGenerator.php
View Options
<?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\n
CREATE "
,
$sql
);
$sql
=
str_replace
(
";
\n\n
CREATE TABLE "
,
";
\n\n\n
CREATE 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
Details
Attached
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)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment