Skip to content

Commit

Permalink
Begin experimental support for PostgreSQL (#114)
Browse files Browse the repository at this point in the history
  • Loading branch information
csev authored Mar 22, 2021
1 parent 401febc commit 5dd2a0a
Show file tree
Hide file tree
Showing 12 changed files with 288 additions and 42 deletions.
4 changes: 3 additions & 1 deletion admin/blob/database.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
$DATABASE_INSTALL = array(
array( "{$CFG->dbprefix}blob_file",
"create table {$CFG->dbprefix}blob_file (
file_id INTEGER NOT NULL KEY AUTO_INCREMENT,
file_id INTEGER NOT NULL AUTO_INCREMENT,
file_sha256 CHAR(64) NOT NULL,
context_id INTEGER NULL,
Expand All @@ -28,6 +28,8 @@
INDEX `{$CFG->dbprefix}blob_indx_2` ( path (128) ),
INDEX `{$CFG->dbprefix}blob_indx_4` ( context_id ),
CONSTRAINT `{$CFG->dbprefix}lti_blob_file_pk` PRIMARY KEY (file_id),
CONSTRAINT `{$CFG->dbprefix}blob_ibfk_1`
FOREIGN KEY (`context_id`)
REFERENCES `{$CFG->dbprefix}lti_context` (`context_id`)
Expand Down
2 changes: 1 addition & 1 deletion admin/mail/database.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
) ENGINE = InnoDB DEFAULT CHARSET=utf8"),

array( "{$CFG->dbprefix}mail_sent",
"create table {$CFG->dbprefix}mail_sent(
"create table {$CFG->dbprefix}mail_sent (
sent_id INTEGER NOT NULL AUTO_INCREMENT,
context_id INTEGER NOT NULL,
Expand Down
12 changes: 6 additions & 6 deletions admin/migrate-run.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@
echo("-- Creating table ".$entry[0]."<br/>\n");
error_log("-- Creating table ".$entry[0]);
$q = $PDOX->queryReturnError($entry[1]);
if ( ! $q->success ) die("Unable to create ".$entry[1]." ".$q->errorImplode."<br/>".$entry[1] );
if ( ! $q->success ) die("Unable to create ".$entry[0]." ".$q->errorImplode."<br/>".$q->sqlQuery );
$OUTPUT->togglePre("-- Created table ".$entry[0], $entry[1]);
$sql = "INSERT INTO {$plugins}
( plugin_path, version, created_at, updated_at ) VALUES
( :plugin_path, :version, NOW(), NOW() )
ON DUPLICATE KEY
ON DUPLICATE KEY /* plugin_path */
UPDATE version = :version, updated_at = NOW()";
$values = array( ":plugin_path" => $path,
":version" => $CFG->dbversion);
$q = $PDOX->queryReturnError($sql, $values);
if ( ! $q->success ) die("Unable to set version for ".$path." ".$q->errorimplode."<br/>".$entry[1] );
if ( ! $q->success ) die("Unable to set version for ".$path." ".$q->errorImplode."<br/>".$q->sqlQuery );
// Do the POST-Create
if ( isset($DATABASE_POST_CREATE) && $DATABASE_POST_CREATE !== false ) {
$DATABASE_POST_CREATE($entry[0]);
Expand All @@ -48,7 +48,7 @@
$values = array( ":plugin_path" => $path,
":version" => $CFG->dbversion);
$q = $PDOX->queryReturnError($sql, $values);
if ( ! $q->success ) die("Unable to set version for ".$path." ".$q->errorimplode."<br/>".$entry[1] );
if ( ! $q->success ) die("Unable to set version for ".$path." ".$q->errorImplode."<br/>".$q->sqlQuery );
$delta = time() - $ticks;
if ( $delta > 1 ) echo("--- Ellapsed time=".$delta." seconds<br/>\n");
$ticks = time();
Expand Down Expand Up @@ -88,11 +88,11 @@
$sql = "INSERT INTO {$plugins}
( plugin_path, version, created_at, updated_at ) VALUES
( :plugin_path, :version, NOW(), NOW() )
ON DUPLICATE KEY
ON DUPLICATE KEY /* plugin_path */
UPDATE version = :version, updated_at = NOW()";
$values = array( ":version" => $newversion, ":plugin_path" => $path);
$q = $PDOX->queryReturnError($sql, $values);
if ( ! $q->success ) die("Unable to update version for ".$path." ".$q->errorimplode."<br/>".$entry[1] );
if ( ! $q->success ) die("Unable to update version for ".$path." ".$q->errorImplode."<br/>".$q->sqlQuery );
}

// Make sure these do not run twice
Expand Down
20 changes: 19 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,25 @@
"symfony/polyfill-php73": "v1.22.0",
"symfony/polyfill-php80": "v1.22.0",

"tsugi/lib": "dev-master#20f59d1ba8d8b558830df55497ba137a887bc504",
"guzzlehttp/promises": "1.4.0",
"guzzlehttp/psr7": "1.7.0",
"nesbot/carbon": "2.45.1",
"psr/container": "1.0.0",
"react/dns": "v1.4.0",
"symfony/console": "v5.2.3",
"symfony/error-handler": "v5.2.3",
"symfony/event-dispatcher": "v5.2.3",
"symfony/finder": "v5.2.3",
"symfony/http-foundation": "v5.2.3",
"symfony/http-kernel": "v5.2.3",
"symfony/mime": "v5.2.3",
"symfony/process": "v5.2.3",
"symfony/routing": "v5.2.3",
"symfony/string": "v5.2.3",
"symfony/translation": "v5.2.3",
"symfony/var-dumper": "v5.2.3",

"tsugi/lib": "dev-master#d7e042ccb6aac340f41726e502fe271dbaa30d8e",
"koseu/lib": "dev-master#5c5bcb32469977bea262b1900461c3f205adb899"
},
"config": {
Expand Down
10 changes: 5 additions & 5 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions vendor/composer/InstalledVersions.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class InstalledVersions
'aliases' =>
array (
),
'reference' => 'f1a39eac90658eef699859eb548b80a7230ac210',
'reference' => '401febcb05e0dd53adc06595f5e00ab5b0037515',
'name' => '__root__',
),
'versions' =>
Expand All @@ -41,7 +41,7 @@ class InstalledVersions
'aliases' =>
array (
),
'reference' => 'f1a39eac90658eef699859eb548b80a7230ac210',
'reference' => '401febcb05e0dd53adc06595f5e00ab5b0037515',
),
'aws/aws-sdk-php' =>
array (
Expand Down Expand Up @@ -930,7 +930,7 @@ class InstalledVersions
array (
0 => '9999999-dev',
),
'reference' => '20f59d1ba8d8b558830df55497ba137a887bc504',
'reference' => 'd7e042ccb6aac340f41726e502fe271dbaa30d8e',
),
'vlucas/phpdotenv' =>
array (
Expand Down
8 changes: 4 additions & 4 deletions vendor/composer/installed.json
Original file line number Diff line number Diff line change
Expand Up @@ -5941,12 +5941,12 @@
"source": {
"type": "git",
"url": "https://github.com/tsugiproject/tsugi-php.git",
"reference": "20f59d1ba8d8b558830df55497ba137a887bc504"
"reference": "d7e042ccb6aac340f41726e502fe271dbaa30d8e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/tsugiproject/tsugi-php/zipball/20f59d1ba8d8b558830df55497ba137a887bc504",
"reference": "20f59d1ba8d8b558830df55497ba137a887bc504",
"url": "https://api.github.com/repos/tsugiproject/tsugi-php/zipball/d7e042ccb6aac340f41726e502fe271dbaa30d8e",
"reference": "d7e042ccb6aac340f41726e502fe271dbaa30d8e",
"shasum": ""
},
"require": {
Expand All @@ -5959,7 +5959,7 @@
"phpunit/php-timer": ">=1.0.6",
"phpunit/phpunit": "8.*"
},
"time": "2021-02-28T03:45:33+00:00",
"time": "2021-03-21T18:58:10+00:00",
"default-branch": true,
"type": "library",
"installation-source": "dist",
Expand Down
6 changes: 3 additions & 3 deletions vendor/composer/installed.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
'aliases' =>
array (
),
'reference' => 'f1a39eac90658eef699859eb548b80a7230ac210',
'reference' => '401febcb05e0dd53adc06595f5e00ab5b0037515',
'name' => '__root__',
),
'versions' =>
Expand All @@ -18,7 +18,7 @@
'aliases' =>
array (
),
'reference' => 'f1a39eac90658eef699859eb548b80a7230ac210',
'reference' => '401febcb05e0dd53adc06595f5e00ab5b0037515',
),
'aws/aws-sdk-php' =>
array (
Expand Down Expand Up @@ -907,7 +907,7 @@
array (
0 => '9999999-dev',
),
'reference' => '20f59d1ba8d8b558830df55497ba137a887bc504',
'reference' => 'd7e042ccb6aac340f41726e502fe271dbaa30d8e',
),
'vlucas/phpdotenv' =>
array (
Expand Down
2 changes: 2 additions & 0 deletions vendor/tsugi/lib/src/Core/LTIX.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use \Tsugi\UI\Output;
use \Tsugi\Core\I18N;
use \Tsugi\Core\Settings;
use \Tsugi\Core\SQLDialect;
use \Tsugi\OAuth\OAuthUtil;
use \Tsugi\Crypt\SecureCookie;
use \Tsugi\Crypt\AesCtr;
Expand Down Expand Up @@ -74,6 +75,7 @@ public static function getConnection() {
}
}
if ( isset($CFG->slow_query) ) $PDOX->slow_query = $CFG->slow_query;
$PDOX->sqlPatch = function($PDOX, $sql) { return \Tsugi\Core\SQLDialect::sqlPatch($PDOX, $sql); } ;
return $PDOX;
}

Expand Down
151 changes: 151 additions & 0 deletions vendor/tsugi/lib/src/Core/SQLDialect.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<?php

namespace Tsugi\Core;

use \Tsugi\Util\PS;

/**
* This is a helper class to transform between SQL Dialects
*/

class SQLDialect
{
public static function sqlPatch($PDOX, $sql) {
// Our default dialect is MySQL
if ( $PDOX->isMySQL() ) {
return $sql;
}
if ( ! $PDOX->isPgSQL() ) {
die('Only MySQL and PostgreSQL are supported');
}

// echo("Dialect\n".$sql."\n");
$pieces = (new PS($sql))->split();
if ( count($pieces) < 1 ) return $sql;
if ( strcasecmp($pieces[0], "create") == 0 ) {
return self::sqlCreate2Postgres($PDOX, $sql);
} else if ( strcasecmp($pieces[0], "insert") == 0 ) {
return self::sqlInsert2Postgres($PDOX, $sql);
} else if ( strcasecmp($pieces[0], "alter") == 0 ) {
return self::sqlAlter2Postgres($PDOX, $sql);
}
return $sql;
}

public static function sqlCreate2Postgres($PDOX, $sql) {

$nsql = self::patchPostgresQuotes($PDOX, $sql);
$nsql = self::patchDataTypes($PDOX, $nsql);

// ) ENGINE = InnoDB DEFAULT CHARSET=utf8;";
// ) COLLATE utf8_bin, ENGINE = InnoDB;
$nsql = preg_replace('/\).*ENGINE\s*=\s*InnoDB.*$/i', ');', $nsql);
$nsql = preg_replace('/\s+USING\s+HASH\s+/i', ' ', $nsql);

return $nsql;
}

// ON DUPLICATE KEY /* plugin_path */ UPDATE
// ON CONFLICT (plugin_path) DO UPDATE SET
public static function sqlInsert2Postgres($PDOX, $sql) {
$nsql = self::patchPostgresQuotes($PDOX, $sql);

$matches = array();
preg_match('/ON\s+DUPLICATE\s+KEY\s\/\*\s+[^ *]*\s+\*\/\s+UPDATE\s+/i', $nsql, $matches, PREG_OFFSET_CAPTURE);
if ( count($matches) < 1 ) return $nsql;
$str = $matches[0][0];
$pos = $matches[0][1];
$len = strlen($str);
$str = preg_replace('/\sDUPLICATE\s+KEY\s/i', ' CONFLICT ', $str);
$str = preg_replace('/\/\*/i', '(', $str);
$str = preg_replace('/\*\//i', ')', $str);
$str = preg_replace('/\s+UPDATE\s+/i', " DO UPDATE SET\n", $str);

$newsql = substr($nsql,0,$pos) . $str . substr($nsql, $pos+$len);
return $newsql;
}

public static function sqlAlter2Postgres($PDOX, $sql) {

$nsql = self::patchPostgresQuotes($PDOX, $sql);
$nsql = self::patchDataTypes($PDOX, $nsql);

// ALTER TABLE lms_tools_status MODIFY commit_log MEDIUMTEXT NULL
// ALTER TABLE lms_tools_status ALTER COLUMN column_name TYPE new_data_type;
// ALTER TABLE lms_tools_status ALTER COLUMN column_name DROP NOT NULL;
$matches = array();
preg_match('/\s+MODIFY\s+[^\s]*\s+/i', $nsql, $matches, PREG_OFFSET_CAPTURE);
if ( count($matches) < 1 ) return $nsql;
$str = $matches[0][0];
$pos = $matches[0][1];
$len = strlen($str);
$str = preg_replace('/\sMODIFY\s/i', ' ALTER COLUMN ', $str);

$tail = substr($nsql, $pos+$len);
$pieces = (new PS($tail))->split();
if ( count($pieces) < 1 ) {
$newsql = substr($nsql,0,$pos) . $str . ' TYPE ' .substr($nsql, $pos+$len);
return $newsql;
}

// Normal flow
$retval = array();
$newsql = substr($nsql,0,$pos) . $str . ' TYPE ' . array_shift($pieces);
$retval[] = $newsql;

$alter = substr($nsql,0,$pos) . $str . ' ';
if ( count($pieces) == 1 && strcasecmp($pieces[0], "null") == 0 ) {
$newsql = $alter . "DROP NOT NULL;";
$retval[] = $newsql;
} else if (count($pieces) == 2 && strcasecmp($pieces[0], "NOT") == 0 &&
strcasecmp($pieces[0], "NULL") == 0 ) {
$newsql = $alter . "SET NOT NULL;";
$retval[] = $newsql;
}
if ( count($retval) == 1 ) {
return $retval[0];
}
return $retval;
}


public static function patchDataTypes($PDOX, $sql) {
// https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_serial
// https://www.postgresqltutorial.com/postgresql-identity-column/
// https://www.depesz.com/2017/04/10/waiting-for-postgresql-10-identity-columns/
// plugin_id INTEGER NOT NULL AUTO_INCREMENT,
// plugin_id INTEGER NOT NULL primary key generated always as identity
$nsql = preg_replace('/AUTO_INCREMENT/i', "GENERATED BY DEFAULT AS IDENTITY", $sql);

// created_at DATETIME NOT NULL,
// created_at TIMESTAMP(0) NOT NULL,
$nsql = preg_replace('/\sDATETIME([\s,])/i', ' TIMESTAMP(0)$1', $nsql);

// deleted TINYINT(1) NOT NULL DEFAULT 0,
// deleted SMALLINT NOT NULL DEFAULT 0,
$nsql = preg_replace('/\sTINYINT\(1\)([\s,])/i', ' SMALLINT$1', $nsql);

$nsql = preg_replace('/\sMEDIUMTEXT([\s,])/i', ' TEXT$1', $nsql);

$nsql = preg_replace('/\sMEDIUMINT([\s,])/i', ' INTEGER$1', $nsql);
$nsql = preg_replace('/\sTINYINT([\s,])/i', ' INTEGER$1', $nsql);

$nsql = preg_replace('/\sUNSIGNED([\s,])/i', '$1', $nsql);

$nsql = preg_replace('/\sVARBINARY\([0-9]+\)([\s,])/i', ' BYTEA$1', $nsql);

$nsql = preg_replace('/\sBINARY\([0-9]+\)([\s,])/i', ' BYTEA$1', $nsql);

$nsql = preg_replace('/\sDOUBLE([\s,])/i', ' DOUBLE PRECISION$1', $nsql);

$nsql = preg_replace('/\sBLOB([\s,])/i', ' BYTEA$1', $nsql);

return $nsql;
}

public static function patchPostgresQuotes($PDOX, $sql) {
$nsql = str_replace('`', '"', $sql);
return $nsql;
}

}
Loading

0 comments on commit 5dd2a0a

Please sign in to comment.