952 days continuous production uptime, 40k+ tp/s single node. Original corpo Bitbucket history not included — clean archive commit.
1064 lines
53 KiB
PHP
1064 lines
53 KiB
PHP
<?php
|
|
/**
|
|
* mysqlConfig.php -- mysql template deployment script
|
|
*
|
|
* Whenever you stage a new version of Namaste to an environment, you will need to run this script. This script
|
|
* will perform the following function:
|
|
*
|
|
* Rudimentary Template Validation
|
|
* -------------------------------
|
|
* The script will only process PDO templates (mariaDB) within the current environment. Within each template, the
|
|
* script will stop execution if certain fields are missing or in the incorrect format.
|
|
*
|
|
* Create New Tables
|
|
* -----------------
|
|
* When a new table is required, the script will create the table from the SQL stored in the template.
|
|
*
|
|
* Update Existing Tables
|
|
* ----------------------
|
|
* There is a separate update section for each table and this will always be executed.
|
|
*
|
|
* Install Database Objects
|
|
* ------------------------
|
|
* The script will install all database objects defined in the template. This includes views, stored procedures and
|
|
* functions, events and triggers.
|
|
*
|
|
* Because we're anticipating schema changes, all database objects will be dropped and re-created. However, SQL code
|
|
* in the PDO_SQL code blocks will only be executed when the defined release version (PDO_VERSION) matches the version
|
|
* value defined in the XML configuration file.
|
|
*
|
|
*
|
|
* Finally, this script can be run without the brokers being active in the current environment.
|
|
*
|
|
*
|
|
* PROGRAMMER'S NOTES:
|
|
* -------------------
|
|
* When defining a stored object, other than a view, that requires a DELIMITER statement, you must defines, separately,
|
|
* the DROP {$OBJECT} statement. Additionally, PHP does not support DELIMITER statements so your database object
|
|
* code cannot contain DELIMITER re-definitions.
|
|
*
|
|
* I made several comments about "current environment". As of this writing, PDO (mariaDB) is supported in both the
|
|
* namaste (Prime) and Segundo environment. That means you must run this script for each environment, in each
|
|
* environment, in order to correctly deploy.
|
|
*
|
|
* All non-standard functions referenced in this program are in this listing, included at the end of the file.
|
|
*
|
|
* Debug Run Options:
|
|
* ------------------
|
|
* Run -> Edit Configurations -> Arguments
|
|
*
|
|
* Put this in the text input: -k --env=dev --user=mshallop --password=einstein
|
|
*
|
|
* To debug w/out creating an application account: -k --env=dev
|
|
*
|
|
*
|
|
* @author mike@givingassistant.org
|
|
* @version 1.0
|
|
*
|
|
*
|
|
* HISTORY:
|
|
* ========
|
|
* 07-02-18 mks CORE-1045: original coding
|
|
* 05-21-19 mks DB-116: processing of templates updates s.t. template processing only happens in the
|
|
* environment for which it is declared
|
|
* 07-15-19 mks DB-124: support for arguments, limited app to cli, support for dynamically dropping indexes
|
|
* 08-26-19 mks DB-111: re-write to support creation of a new *database* and database user, support for
|
|
* environments hosted off localhost -> env-based processing, refactored output to consoleLog only
|
|
* 10-09-19 mks DB-136: fixed bug where db name was getting appended to previous db name on every iteration
|
|
* of the drop-index loop
|
|
*/
|
|
$_REDIRECT=false; // enable stdout
|
|
require_once(dirname(__DIR__) . '/config/sneakerstrap.inc'); // load env
|
|
global $topDir;
|
|
|
|
// exit with an error message if the app was launched by a browser
|
|
if (!empty($_REQUEST)) echo 'This app can only be run from the command line.' && exit;
|
|
|
|
if (empty($argv)) printHelp($argv[0]) && exit;
|
|
|
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
// lvar initialization
|
|
$shortOptions = 'e:t::k::u::p::h::'; // get the cli options
|
|
$longOptions = [ 'env:', 'template::', 'keep::', 'user::', 'password::', 'help::'];
|
|
$options = getopt($shortOptions, $longOptions);
|
|
$deleteDevTables = true;
|
|
$singleFile = false;
|
|
$couldBeADuplicateEnvUser = false;
|
|
$env = NONE;
|
|
$isNewInstallation = false;
|
|
$templateDirectory = $topDir . DIR_CLASSES . DIR_TEMPLATE . '/';
|
|
//var_export($options);exit;
|
|
$errors = null;
|
|
$res = 'PDOC: '; // PDO Config
|
|
/** @var PDO $resDB */
|
|
$resDB = null;
|
|
$processTemplate = false;
|
|
$error404 = ERROR_CONFIG_RESOURCE_404 . CONFIG_BROKER_SERVICES . ARROW;
|
|
$dbObjects = [ PDO_VIEWS => STRING_VIEW, PDO_PROCEDURES => STRING_PROCEDURE, PDO_FUNCTIONS => STRING_FUNCTION,
|
|
PDO_EVENTS => STRING_EVENT, PDO_TRIGGERS => STRING_TRIGGER ];
|
|
// generate a list of environments that are currently local and active
|
|
foreach (gasConfig::$settings[CONFIG_REGISTERED_SERVICES] as $service => $enabled)
|
|
if ($enabled)
|
|
$envList[] = $service;
|
|
|
|
$aryPDOData = [ ENV_ADMIN => null, ENV_APPSERVER => null, ENV_SEGUNDO => null, ENV_TERCERO => null];
|
|
$config = gasConfig::$settings[CONFIG_DATABASE][CONFIG_DATABASE_PDO];
|
|
if ($config[CONFIG_DATABASE_PDO_ENABLED] !== 1) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . 'PDO is disabled in the current Namaste deployment...');
|
|
exit(1);
|
|
}
|
|
consoleLog($res, CON_SUCCESS, 'Starting mysql template processing...');
|
|
$curSvcEnv = gasConfig::$settings[CONFIG_ID][CONFIG_ID_ENV];
|
|
$currentReleaseVersion = number_format((float) gasConfig::$settings[CONFIG_ID][CONFIG_ID_VER], 1);
|
|
|
|
// process the options:
|
|
// ensure we have the required parameter (-e/--env) and subsequent argument
|
|
if ((!isset($options['e']) and !isset($options['env'])) or (isset($options['h']) or isset($options['help']))) {
|
|
printHelp($argv[0]);
|
|
exit();
|
|
}
|
|
|
|
// display the disclaimer
|
|
printDisclaimer();
|
|
// todo -- uncomment the next line!
|
|
//readline(); // wait until the user presses the enter key - discard the input
|
|
|
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
// process the cli input params...
|
|
$env = (isset($options['e'])) ? $options['e'] : ((isset($options['env'])) ? $options['env'] : '');
|
|
if ($env == NONE or ($env != 'dev' and $env != 'prod')) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . 'Environment is invalid - must be either dev or prod.');
|
|
printHelp($argv[0]) && exit;
|
|
}
|
|
|
|
// check the template name if the -t option was specified
|
|
if (isset($options['t']) or isset($options['template'])) {
|
|
$file = (isset($options['t'])) ? $options['t'] : ((isset($options['template'])) ? $options['template'] : '');
|
|
if (empty($file)) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . 'Template file name is blank.');
|
|
printHelp($argv[0]) && exit;
|
|
}
|
|
// we have a file name - validate that the file exists
|
|
if (!file_exists($topDir . DIR_CLASSES . DIR_TEMPLATE . SLASH . $file)) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . $file . ' could not be found in the template directory.');
|
|
printHelp($argv[0]) && exit;
|
|
}
|
|
$singleFile = true;
|
|
}
|
|
|
|
if (isset($options['k']) or isset($options['keep'])) $deleteDevTables = false;
|
|
|
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
// extract the cli admin-account info from the command line parameters...
|
|
|
|
// this is the user account that will create the db and bind the new user to the db...
|
|
// so make sure that this user has db-level grant auths!
|
|
$adminUser = (isset($options[STRING_USER])) ? $options[STRING_USER] : '';
|
|
$adminPass = (isset($options[STRING_PASSWORD])) ? $options[STRING_PASSWORD] : '';
|
|
// connect to the PDO resources based on configured environments - note that this call is NOT using resource
|
|
// manager... the framework does not have to be running for this application to run
|
|
try {
|
|
if (!getPDOResources($adminUser, $adminPass)) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . ERROR_PDO_CONNECT);
|
|
exit;
|
|
}
|
|
} catch (TypeError $t) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . ERROR_RESOURCE_404 . RESOURCE_PDO_MASTER);
|
|
exit;
|
|
}
|
|
// create a new database and namaste user account if they do not already exist
|
|
if (isset($options['u']) or isset($options['user'])) {
|
|
if (!isset($options['p']) and !isset($options['password'])) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . 'You must specify a password when providing a super user account name.');
|
|
printHelp($argv[0]);
|
|
exit;
|
|
}
|
|
try {
|
|
foreach ($envList as $env) {
|
|
/** @var PDO $ptr */ // <-- so we don't get an IDE warning...
|
|
$ptr = $aryPDOData[$env][STRING_DBR];
|
|
if (is_null($ptr)) continue;
|
|
// if the request including the option to create a new Namaste (application) user account and password,
|
|
// then check to see if user account already exists and, if so, stop execution...
|
|
/** @noinspection SqlResolve */
|
|
$query = "SELECT Host FROM mysql.user WHERE User='" . $aryPDOData[$env][STRING_USER] . "'";
|
|
foreach ($ptr->query($query, PDO::FETCH_ASSOC) as $row)
|
|
$results[] = $row['Host'];
|
|
if (in_array($aryPDOData[$env][STRING_HOST], $results) or in_array(OPERATOR_REGEX, $results)) {
|
|
$host = (in_array($aryPDOData[$env][STRING_HOST], $results)) ? $aryPDOData[$env][STRING_HOST] : OPERATOR_REGEX;
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
$msg = sprintf(INFO_DB_DUP_ENV_USER, $aryPDOData[$env][STRING_USER]) . $host;
|
|
consoleLog($res, CON_ERROR, $hdr . $msg);
|
|
$couldBeADuplicateEnvUser = true;
|
|
}
|
|
$ptr->beginTransaction();
|
|
/*
|
|
* Namaste Super-User Account Creation
|
|
* ------------------------------------
|
|
* The following code creates the Namaste Super-User which the requesting user identified using the --user
|
|
* and --password input parameters. Because mySQL user management is a bit convoluted, here's how it's
|
|
* broken-down for this app:
|
|
*
|
|
* 1. Create the application user which is "{userName}@{host}" where userName and host is pulled from both
|
|
* the XML (host). This user is an application-user who's privileges are restricted to the named database
|
|
* only. The user account is created using the password passed in via the command line parameter.
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
* IF THE $couldBeADuplicateEnvUser variable is false:
|
|
* 2. Create the database user based off the Namaste application user but create the user using the "%"
|
|
* host designation - we're creating this database-level user using the same password passed via the
|
|
* XML that we used to create the application user.
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
* 3. Grant all privileges to the named (XML) Namaste database to the application user.
|
|
* 4. Grant replication-slave privileges to the database-user level account so that the application can
|
|
* access the read-slave for read requests.
|
|
* 5. Flush the privileges (because of the create command).
|
|
* 6. Use the targeted (switch to) database.
|
|
*/
|
|
$sql = "CREATE DATABASE " . $aryPDOData[$env][STRING_DBN] . ";";
|
|
if (!$couldBeADuplicateEnvUser)
|
|
$sql .= "CREATE USER '" . $aryPDOData[$env][STRING_USER] . "'@'" . $aryPDOData[$env][STRING_HOST] . "' IDENTIFIED BY '" . $aryPDOData[$env][STRING_PASS] . "';
|
|
CREATE USER '" . $aryPDOData[$env][STRING_USER] . "'@'%' IDENTIFIED BY '" . $aryPDOData[$env][STRING_PASS] . "';";
|
|
$sql .= "GRANT ALL PRIVILEGES ON " . $aryPDOData[$env][STRING_DBN] . ".* to '" . $aryPDOData[$env][STRING_USER] . "'@'" . $aryPDOData[$env][STRING_HOST] . "' IDENTIFIED BY '" . $aryPDOData[$env][STRING_PASS] . "';
|
|
GRANT REPLICATION SLAVE ON *.* to '" . $aryPDOData[$env][STRING_USER] . "'@'% IDENTIFIED BY '" . $aryPDOData[$env][STRING_PASS] . "';
|
|
FLUSH PRIVILEGES;
|
|
USE " . $aryPDOData[$env][STRING_DBN] . ";";
|
|
$ptr->exec($sql) or $ptr->rollBack() and die(print_r($ptr->errorInfo(), true));
|
|
$isNewInstallation = true;
|
|
$ptr->commit();
|
|
}
|
|
} catch (PDOException | Throwable $e) {
|
|
$ptr->rollBack();
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, ERROR_EXCEPTION);
|
|
consoleLog($res, $e->getMessage());
|
|
exit;
|
|
}
|
|
}
|
|
|
|
// generate the file list... honoring the user's request if they want to process a single template file
|
|
if ($singleFile) {
|
|
// single file
|
|
$fileList = [ $templateDirectory . $file ];
|
|
} else {
|
|
// all template files
|
|
$fileList = glob($templateDirectory . '*.class.inc');
|
|
}
|
|
|
|
// todo: check the $isNewInstallation var: if true, then we need to apply ALL of the version changes starting with 1.0
|
|
// I already created a JIRA ticket for the above issue: DB-132.
|
|
foreach ($envList as $environment) {
|
|
// if the current env is set and if that env has enabled PDO then...
|
|
if (!empty($aryPDOData[$environment])) {
|
|
foreach ($fileList as $filename) {
|
|
/** @var PDO $resDB */
|
|
$resDB = $aryPDOData[$environment][STRING_DBR];
|
|
$transactionStarted = false;
|
|
/** @var gatTestPDO $currentTemplate */
|
|
$currentTemplate = basename($filename);
|
|
$currentTemplate = preg_replace("/" . STRING_CLASS_FILE_EXT . "/", "", $currentTemplate);
|
|
$currentTemplate = new $currentTemplate();
|
|
// if not a PDO template, quietly move to next template
|
|
if ($currentTemplate->schema != TEMPLATE_DB_PDO) {
|
|
if (is_object($currentTemplate)) $currentTemplate->__destruct();
|
|
unset($currentTemplate);
|
|
continue;
|
|
} elseif (empty($currentTemplate->dbObjects) or !is_array($currentTemplate->dbObjects)) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
$msg = $hdr . ERROR_SQL_TEMPLATE_DBO_404;
|
|
consoleLog($res, CON_ERROR, $msg);
|
|
continue;
|
|
}
|
|
consoleLog($res, CON_SUCCESS, sprintf(INFO_PROCESSING_TEMPLATE, $currentTemplate->collection, $currentTemplate->service));
|
|
// set the correct PDO resource
|
|
if ($currentTemplate->service == $environment) {
|
|
$processTemplate = true;
|
|
$database = gasConfig::$settings[CONFIG_ID][CONFIG_ID_ENV] . UDASH;
|
|
$database .= $config[$environment][CONFIG_DATABASE_PDO_MASTER][CONFIG_DATABASE_PDO_DB];
|
|
} else {
|
|
continue;
|
|
}
|
|
consoleLog($res, CON_SUCCESS, 'Processing PDO templates for env: ' . $environment);
|
|
|
|
// ---------------------------------------------------------------------------------------------------------
|
|
// Step 1: check to see if the current table exists and START THE TRANSACTION! <------------ pay attention
|
|
// ---------------------------------------------------------------------------------------------------------
|
|
try {
|
|
// set attribute to allow multi-queries because transaction
|
|
$resDB->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0);
|
|
$query = 'SHOW TABLES LIKE "' . $currentTemplate->collection . $currentTemplate->extension . '"';
|
|
$tableExists = $resDB->query($query)->rowCount() > 0;
|
|
if (!$resDB->inTransaction()) $resDB->beginTransaction();
|
|
} catch (PDOException | Throwable $p) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . ERROR_EXCEPTION);
|
|
consoleLog($res, CON_ERROR, $hdr . $p->getMessage());
|
|
exit;
|
|
}
|
|
|
|
// Step 2: loop through PDO_SQL until we get to the current version - we'll only execute the SQL in this block!
|
|
foreach ($currentTemplate->dbObjects[PDO_SQL] as $sqlBlock) {
|
|
// check the release version and exit if the release version is missing from the template block
|
|
if (!isset($sqlBlock[PDO_VERSION])) {
|
|
// if there is no PDO_VERSION section set within *any* PDO_SQL block, it's a malformed template so exit...
|
|
$msg = sprintf(ERROR_SQL_TEMPLATE_DBO_VER_404, $filename);
|
|
try {
|
|
@doRollBack($resDB, $msg);
|
|
continue;
|
|
} catch (PDOException | TypeError $t) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . $t->getMessage());
|
|
@doRollBack($resDB, $hdr . $t->getMessage());
|
|
continue;
|
|
}
|
|
} else {
|
|
// compare the current version block against the current release version -- skip block if not equal
|
|
if (!empty($sqlBlock[PDO_VERSION])) {
|
|
$rcCompare = strcmp($currentReleaseVersion, number_format((float)$sqlBlock[PDO_VERSION], 1));
|
|
} else {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
$msg = sprintf(ERROR_TEMPLATE_MISSING_FIELD, $filename, PDO_VERSION);
|
|
consoleLog($res, CON_ERROR, $hdr . $msg);
|
|
continue;
|
|
}
|
|
if ($rcCompare !== 0) continue;
|
|
|
|
// pull the table name from the sqlBlock
|
|
$currentTable = $sqlBlock[PDO_TABLE];
|
|
|
|
// at this point, we have a PDO_SQL block that matches the current release version
|
|
|
|
/*
|
|
* Step 3: Start the transaction -- ALL SQL commands must process successfully or issue a rollback.
|
|
*
|
|
* However...
|
|
*
|
|
* Some databases, including MySQL, automatically issue an implicit COMMIT when a database
|
|
* definition language (DDL) statement such as DROP TABLE or CREATE TABLE is issued within a
|
|
* transaction. The implicit COMMIT will prevent you from rolling back any other changes within
|
|
* the transaction boundary.
|
|
*/
|
|
try {
|
|
if (!$resDB->inTransaction()) {
|
|
if (false === $resDB->beginTransaction()) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . ERROR_PDO_ST_FAIL);
|
|
continue;
|
|
} else {
|
|
$transactionStarted = true;
|
|
}
|
|
}
|
|
} catch (PDOException | Throwable $p) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . $p->getMessage());
|
|
continue;
|
|
}
|
|
|
|
// Step 4: If the current table DNE, then pull the FC query for the current release version and execute
|
|
if (!$tableExists) {
|
|
// if the table does not exist, check the template $dbObjects[PDO_SQL][PDO_VERSION] to see if there is a
|
|
// corresponding content for the PDO_SQL_FC declaration. If so, then this is the table CREATE syntax and
|
|
// we need to execute this SQL.
|
|
if (isset($sqlBlock[PDO_SQL_FC]) and !empty($sqlBlock[PDO_SQL_FC])) {
|
|
if (!loadQuery($sqlBlock[PDO_SQL_FC])) {
|
|
doRollBack($resDB, ERROR_PDO_FC_CREATE . COMMA . ERROR_PDO_CURRENT_TABLE . $currentTable);
|
|
continue;
|
|
}
|
|
consoleLog($res, CON_SUCCESS, sprintf(INFO_PDO_DEPLOY, PDO_TABLE, $sqlBlock[PDO_TABLE]));
|
|
} else {
|
|
$msg = ERROR_PDO_CREATE_404 . $currentTemplate->collection;
|
|
consoleLog($res, CON_ERROR, $msg);
|
|
}
|
|
} elseif (intval($currentReleaseVersion) == 1) {
|
|
// Step 5.1:
|
|
// we're installing version 1.0 of Namaste and the table exists... this means that the table
|
|
// does exist and needs to be dropped - which is common in development environments so, if
|
|
// there exists a drop component (and there should be) execute this first before executing the
|
|
// SQL create statement:
|
|
if (array_key_exists(STRING_DROP_CODE_DEV, $sqlBlock) and !empty($sqlBlock[STRING_DROP_CODE_DEV]) and
|
|
(isset($sqlBlock[PDO_SQL_FC]) and !empty($sqlBlock[PDO_SQL_FC]))) {
|
|
// do not delete the table if the user launched with -k|--keep option
|
|
if ($deleteDevTables) {
|
|
if (!loadQuery($sqlBlock[STRING_DROP_CODE_DEV])) {
|
|
doRollBack($resDB, ERROR_PDO_DROP_DEV . COMMA . ERROR_PDO_CURRENT_TABLE . $currentTable);
|
|
continue;
|
|
}
|
|
if (!loadQuery($sqlBlock[PDO_SQL_FC])) {
|
|
doRollBack($resDB, ERROR_PDO_FC_CREATE . COMMA . ERROR_PDO_CURRENT_TABLE . $currentTable);
|
|
continue;
|
|
}
|
|
if (!isset($sqlBlock[PDO_SQL_UPDATE]) or empty($sqlBlock[PDO_SQL_UPDATE])) {
|
|
doRollBack($resDB, ERROR_PDO_UPDATE_404 . COMMA . ERROR_PDO_CURRENT_TABLE . $currentTable);
|
|
continue;
|
|
}
|
|
consoleLog($res, CON_SUCCESS, sprintf(INFO_PDO_DEPLOY, PDO_TABLE, $sqlBlock[PDO_TABLE]));
|
|
} else {
|
|
// Step 5.2: Call the dropIndexes function to drop the table indexes and tweak the
|
|
// update query in the sql block to remove the add primary key directive
|
|
if (!dropIndexes($database, $currentTable, true)) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . ERROR_PDO_INDEX_DROP . $currentTable);
|
|
continue;
|
|
}
|
|
consoleLog($res, CON_SUCCESS, INFO_PDO_INDEXES_DROPPED . $currentTable);
|
|
}
|
|
} else {
|
|
// Step 5.2:
|
|
// actually, if the table exists and there's no drop command defined in the template, or
|
|
// there's no create-table command defined, we can't continue so error-out and quit.
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
$msg = $hdr . ERROR_PDO_DROP_404 . $currentTemplate->collection;
|
|
consoleLog($res, CON_ERROR, $msg);
|
|
@doRollBack($resDB, $msg);
|
|
continue;
|
|
}
|
|
}
|
|
// Step 6: exec the update block if it exists for the current release version
|
|
if (isset($sqlBlock[PDO_SQL_UPDATE]) and !empty($sqlBlock[PDO_SQL_UPDATE])) {
|
|
if (!loadQuery($sqlBlock[PDO_SQL_UPDATE])) {
|
|
doRollBack($resDB, sprintf(ERROR_PDO_UPDATE_FAIL, $currentTable, $currentReleaseVersion));
|
|
continue;
|
|
}
|
|
} // if there's no update block for the current release for the current template, that's ok!
|
|
}
|
|
}
|
|
|
|
// sanity check: if we've not started a transaction, continue to the next template file
|
|
if (!$resDB->inTransaction()) continue;
|
|
|
|
// Step 6: Execute all the create-objects SQL statements found in the template. The release version is immaterial.
|
|
// process views, procedures, functions, events and triggers (defined as an associative array above)
|
|
foreach ($dbObjects as $dbObjectName => $dbObjectLabel) {
|
|
if (isset($currentTemplate->dbObjects[$dbObjectName]) and is_array($currentTemplate->dbObjects[$dbObjectName]) and !empty($currentTemplate->dbObjects[$dbObjectName])) {
|
|
foreach ($currentTemplate->dbObjects[$dbObjectName] as $name => $data) {
|
|
// transaction-wrap all the function calls
|
|
try {
|
|
// it is possible to reach this point without having previously started a transaction so,
|
|
// pause to see if one has been started and, if not, start one...
|
|
if (!$resDB->inTransaction()) {
|
|
if (false === $resDB->beginTransaction()) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . ERROR_PDO_ST_FAIL);
|
|
} else {
|
|
$transactionStarted = true;
|
|
}
|
|
}
|
|
// stored procedures, et all, require separate drop statements b/c PHP doesn't support embedded delimiter statements
|
|
if (isset($data[STRING_DROP_CODE_DEV]) and is_string($data[STRING_DROP_CODE_DEV])) {
|
|
if (!loadQuery($data[STRING_DROP_CODE_DEV])) exit(1);
|
|
}
|
|
if (isset($data[$dbObjectLabel]) and is_string($data[$dbObjectLabel])) {
|
|
if (!loadQuery($data[$dbObjectLabel])) exit(1);
|
|
consoleLog($res, CON_SUCCESS, sprintf(INFO_PDO_DEPLOY, $dbObjectName, $name));
|
|
}
|
|
|
|
} catch (PDOException | Throwable $p) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . $p->getMessage());
|
|
}
|
|
}
|
|
} else {
|
|
consoleLog($res, CON_SUCCESS, sprintf(INFO_PDO_NO_DEPLOY, $currentTemplate->collection . $currentTemplate->extension, $dbObjectName));
|
|
}
|
|
}
|
|
|
|
// Step 7: commit all of the sql we just executed before proceeding to the next template file...
|
|
try {
|
|
$rc = $resDB->commit();
|
|
if (!$rc) {
|
|
consoleLog($res, CON_ERROR, ERROR_PDO_COMMIT);
|
|
@doRollBack($resDB, ERROR_PDO_COMMIT);
|
|
exit(1);
|
|
}
|
|
} catch (PDOException $p) {
|
|
consoleLog($res, CON_ERROR, $p->getMessage());
|
|
@doRollBack($resDB, $p->getMessage());
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
} // end env loop
|
|
|
|
// if we reach this point, all templates were processed successfully - log a termination message and exit
|
|
consoleLog($res, CON_SUCCESS, SUCCESS_PDO_TEMPLATE_PROCESSING . (string) $currentReleaseVersion);
|
|
exit(0);
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PROGRAM ENDS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// LOCAL FUNCTIONS
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
/**
|
|
* loadQuery() -- local function
|
|
*
|
|
* I call this code twice so I have it in a function -- there is one input parameter to the function - this is the
|
|
* query to be loaded and sent for execution.
|
|
*
|
|
* The function accesses most of it's data from the heap by declaring it's lvars as globals - mostly because there
|
|
* are five lvars and I didn't want to define them as a parameter list.
|
|
*
|
|
* The purpose, then, of this function is to execute the passed query that was extracted from the template file and
|
|
* used to drop/create the mysql table.
|
|
*
|
|
* The method requires that we be covered under a transaction before executing the passed query. If we're not under
|
|
* a transaction, as indicated by the appropriate and brilliantly-named global, then stop execution immediately and
|
|
* return a false status after making a console log entry.
|
|
*
|
|
* If the sql fails to exec, or if any error or exception is raised, then we call the rollback function and return
|
|
* a Boolean false back to the calling client. If the query executed successfully, a Boolean true is returned.
|
|
*
|
|
*
|
|
* @author mike@givingassistant.org
|
|
* @version 1.0
|
|
*
|
|
* @param string $_query -- the query to be executed
|
|
* @param bool $_needResults -- if this is a query that returns results, override the default
|
|
* @return bool
|
|
*
|
|
*
|
|
* HISTORY:
|
|
* ========
|
|
* 05-22-19 mks DB-122: original coding
|
|
* 07-18-19 mks DB-124: support for queries where result data is generated
|
|
* 08-07-19 mks DB-124: enforcing transaction state
|
|
*
|
|
*/
|
|
function loadQuery(string $_query, bool $_needResults = false): bool
|
|
{
|
|
global $res, $resDB;
|
|
|
|
if (!$resDB->inTransaction()) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . ERROR_PDO_NO_TRANS);
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
$rc = ($_needResults) ? runQuery($resDB, $_query) : execQuery($resDB, $_query);
|
|
if (!$rc) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
$msg = $hdr . ERROR_PDO_QUERY . $_query;
|
|
@doRollBack($resDB, $msg);
|
|
return false;
|
|
}
|
|
return true;
|
|
} catch (TypeError $t) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . $t->getMessage());
|
|
@doRollBack($resDB, $t->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* doRollBack() -- local function
|
|
*
|
|
* This function executes the transaction rollback command. Since there are so many places in the main processing
|
|
* where a transaction failure is trapped, it made sense to put the code that actually rolls back the transaction
|
|
* into it's own method.
|
|
*
|
|
* The method requires two input parameters:
|
|
*
|
|
* $_rc -- the PDO resource
|
|
* $_msg - the transaction msg (why we're rolling back)
|
|
*
|
|
* If the rollback command fails, we'll output additional diagnostics.
|
|
*
|
|
* On exit, it sets the global variable: $transactionStarted to false.
|
|
*
|
|
* PROGRAMMER'S NOTES:
|
|
* -------------------
|
|
* Some databases, including MySQL, automatically issue an implicit COMMIT when a database definition language
|
|
* (DDL) statement such as DROP TABLE or CREATE TABLE is issued within a transaction. The implicit COMMIT will
|
|
* prevent you from rolling back any other changes within the transaction boundary.
|
|
*
|
|
* The method is type void.
|
|
*
|
|
*
|
|
* @author mike@givingassistant.org
|
|
* @version 1.0
|
|
*
|
|
* @param PDO $_rc
|
|
* @param string $_msg
|
|
*
|
|
*
|
|
* HISTORY:
|
|
* ========
|
|
* 06-21-18 mks CORE-1045: original coding completed
|
|
*
|
|
*/
|
|
function doRollBack(PDO $_rc, string $_msg): void
|
|
{
|
|
global $res, $transactionStarted;
|
|
try {
|
|
if (false === $_rc->rollBack()) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . ERROR_PDO_ROLLBACK);
|
|
}
|
|
consoleLog($res, CON_ERROR, $_msg);
|
|
consoleLog($res, CON_ERROR, INFO_TRX_ROLLBACK);
|
|
} catch (PDOException $p) {
|
|
consoleLog($res, CON_ERROR, $p->getMessage());
|
|
}
|
|
$transactionStarted = false;
|
|
}
|
|
|
|
|
|
/**
|
|
* dropIndexes() -- local function
|
|
*
|
|
* This method requires two input parameters:
|
|
*
|
|
* $_database -- the name of the current database being processed
|
|
* $_currentTable -- omg! a self-documenting variable!
|
|
*
|
|
* We declare the $sqlBlock (pulled from the template) and the resource variable ($res) as global so that they can be
|
|
* accessed within the function. If there's a index block defined in the template, then we're going to build the drop
|
|
* index query by parsing out the database and tables names replacing them with the parameters passed into the function.
|
|
*
|
|
* We submit the query and, if the query failed to execute, return a boolean false. Otherwise, output a console
|
|
* message and return a boolean true to the caller.
|
|
*
|
|
* Programmer's Notes:
|
|
* -------------------
|
|
* This function was created b/c there are two conditions in which we'll eliminate indexes. We do NOT want to run the
|
|
* drop-indexes command when we're in a dev env and the user has not specified the --keep option.
|
|
*
|
|
* There's also place-holder code in this method for removing the auto-increment feature of the primary key (id_{ext})
|
|
* presumably so that all indexes can be dropped and restored. I'd assume that this would be necessary under the
|
|
* following conditions:
|
|
*
|
|
* 1. you're in a dev env
|
|
* 2. you used the --keep|-k option to keep the table data
|
|
* 3. you're on version 1.0
|
|
*
|
|
* If ALL of the above conditions are met, then we'll have to remove the auto-increment attribute from the primary
|
|
* key field and then drop the index. mariaDB does not allow you to drop the index unless this attribute has been
|
|
* removed first. I'd assume that I'd add new params to the method, a switch telling the function that I want to
|
|
* exec the drop-autoincrement attribute.
|
|
*
|
|
*
|
|
* @author mike@givingassistant.org
|
|
* @version 1.0
|
|
*
|
|
* @param string $_database -- string containing the current database name
|
|
* @param string $_currentTable -- string containing the current table name
|
|
* @param bool $_dropAI -- flag, defaults to false, indicating if we're dropping the AI attribute from the pKey
|
|
* @return bool
|
|
*
|
|
* HISTORY:
|
|
* ========
|
|
* 07-31-19 mks DB-124: original coding
|
|
*
|
|
*/
|
|
function dropIndexes(string $_database, string $_currentTable, bool $_dropAI = false): bool
|
|
{
|
|
/** var gatTestPDO $currentTemplate */
|
|
global $currentTemplate;
|
|
global $sqlBlock, $res;
|
|
|
|
if (!isset($sqlBlock[STRING_DROP_CODE_IDX]) or empty($sqlBlock[STRING_DROP_CODE_IDX])) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . ERROR_DATA_ARRAY_EMPTY . COLON . STRING_DROP_CODE_IDX);
|
|
return false;
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------------------------------------------
|
|
* the code in the next block is provided in-case there's every a time where we want to drop the primary key
|
|
* when doing a sql-update. The primary key cannot be dropped like a regular index because of the auto-increment
|
|
* attribute. So, this block executes a query that will drop the auto-increment attribute from the id_{ext}
|
|
* column.
|
|
-----------------------------------------------------------------------------------------------------------------*/
|
|
if ($_dropAI) {
|
|
// we're not dropping the column, just the auto-increment attribute...
|
|
$pkQuery = 'ALTER TABLE ' . STRING_TABLE_NAME_TAG . ' CHANGE XXXidXXX XXXidXXX int(11)';
|
|
// constant for the above place-holder is: STRING_TABLE_ID_TAG
|
|
$pKey = $currentTemplate->collection . $currentTemplate->extension;
|
|
if (empty($pKey)) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . ERROR_PKEY_ID . STRING_TEMPLATE);
|
|
return false;
|
|
}
|
|
// replace the table name with the current table name
|
|
$pkQuery = str_replace(STRING_TABLE_NAME_TAG, $pKey, $pkQuery);
|
|
// replace the id-field with the current table extension
|
|
$pkQuery = str_replace(STRING_TABLE_ID_TAG, (PKEY_ID . $currentTemplate->extension), $pkQuery);
|
|
if (empty($pkQuery)) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . ERROR_PDO_QUERY_BUILD);
|
|
return false;
|
|
} else {
|
|
// execute the query to drop the auto-increment attribute from the primary key
|
|
if (!loadQuery($pkQuery)) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . ERROR_PDO_DROP_AI . $_currentTable);
|
|
return false;
|
|
}
|
|
// if we're dropping the auto-increment, we need to remove the ADD PRIMARY KEY... text from the sql
|
|
// update string in the template so that we don't fail at adding the indexes
|
|
$key = 'id' . $currentTemplate->extension;
|
|
$targetString = 'ADD PRIMARY KEY (`' . $key . '`),';
|
|
$sqlBlock[PDO_SQL_UPDATE] = str_replace($targetString, '', $sqlBlock[PDO_SQL_UPDATE]);
|
|
consoleLog($res, CON_SUCCESS, INFO_PDO_AI_ATTR_DROPPED);
|
|
}
|
|
}
|
|
/* ----------------------------------END DROP AUTO-INCREMENT QUERY------------------------------------------------*/
|
|
|
|
// clear out the comment tag
|
|
$query = str_replace('--', '', $sqlBlock[STRING_DROP_CODE_IDX]);
|
|
// first, we need to parse and replace the placeholder strings with the actual values
|
|
$query = str_replace(STRING_DB_NAME_TAG, $_database, $query);
|
|
$query = str_replace(STRING_TABLE_NAME_TAG, $_currentTable, $query);
|
|
|
|
if (!loadQuery($query, true)) {
|
|
return false;
|
|
}
|
|
consoleLog($res, CON_SUCCESS, sprintf(INFO_PDO_DEPLOY, $query, $sqlBlock[PDO_TABLE]));
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* execQuery() -- local function
|
|
*
|
|
* this function handles all non-transaction and non-db-validation queries. All queries for creating and maintaining
|
|
* tables are static and do not require preparation.
|
|
*
|
|
* Since we're just executing DDL statements (CREATE, ALTER), then there's no return data, per se, only information
|
|
* is if the query successfully executed or not.
|
|
*
|
|
* Input parameters to the method are:
|
|
*
|
|
* $_rc -- this is the PDO resource (provisioned by the gasResourceManager)
|
|
* $_query -- this is the SQL query(ies) that will be executed
|
|
*
|
|
* If a query raises a PDO exception, we'll trap the exception and, if transactions are currently active, then
|
|
* we'll invoke the roll-back function (above).
|
|
*
|
|
* The method returns a BOOLEAN indicating if the query(ies) successfully executed to completion or not.
|
|
*
|
|
*
|
|
* @author mike@givingassistant.org
|
|
* @version 1.0
|
|
*
|
|
* @param PDO $_rc
|
|
* @param string $_query
|
|
* @return mixed
|
|
*
|
|
*
|
|
* HISTORY:
|
|
* ========
|
|
* 06-21-16 mks CORE-1045: original coding completed
|
|
* 07-18-19 mks DB-124: added ability to return query data for the drop-indexing command
|
|
*
|
|
*/
|
|
function execQuery(PDO $_rc, string $_query)
|
|
{
|
|
global $eos, $res, $transactionStarted;
|
|
try {
|
|
$results = $_rc->exec($_query);
|
|
return (!($results === false));
|
|
} catch (PDOException $p) {
|
|
if ($transactionStarted) {
|
|
try {
|
|
@doRollBack($_rc, $p->getMessage());
|
|
} catch (TypeError $t) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . $t->getMessage());
|
|
}
|
|
} else {
|
|
$msg = sprintf(ERROR_PDO_EXCEPTION, $_query);
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . $eos. $msg . $_query);
|
|
consoleLog($res, CON_ERROR, $p->getMessage());
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* runQuery() -- local function
|
|
*
|
|
* This method is similar to the execQuery() function except that we prepare the query, tokenizing it, before
|
|
* submitting the query to the sql engine. If you ever submit a query that requires params, you'll have to modify
|
|
* this function by passing the placement variable as an argument to execute() assuming you've also added the array
|
|
* variable (containing the parameter values) as an input parameter.
|
|
*
|
|
* The method currently requires two input parameters:
|
|
*
|
|
* $_rc - the database resource pointer
|
|
* $_query - the query to be tokenized and executed
|
|
*
|
|
* Since the query is going to be a query pulled from the template, we're going to first parse and remove all the
|
|
* new-line chars from the string. Next, we're going to build a system query that allows us to exec queries longer
|
|
* (in length) than 10K.
|
|
*
|
|
* Then we prepare and execute the query. We fetch and return the results and check the results for errors, reporting
|
|
* if same was found.
|
|
*
|
|
* The method returns a boolean value, true if everything was successful, otherwise false.
|
|
*
|
|
*
|
|
* @author mike@givingassistant.org
|
|
* @version 1.0
|
|
*
|
|
* @param PDO $_rc
|
|
* @param string $_query
|
|
* @return bool
|
|
*
|
|
*
|
|
* HISTORY:
|
|
* ========
|
|
* 07-31-19 mks DB-124: original coding
|
|
* 10-09-19 mks DB-136: fix for PHP warning
|
|
*
|
|
*/
|
|
function runQuery(PDO $_rc, string $_query): bool
|
|
{
|
|
global $eos, $res;
|
|
|
|
// clean-up the newline chars from the query string
|
|
$query = str_replace("\n","", $_query);
|
|
// remove multiple space chars from the query string
|
|
$query = preg_replace('/\s\s+/', ' ', $query);
|
|
if (empty($query)) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . ERROR_PDO_QUERY_BUILD);
|
|
return false;
|
|
}
|
|
|
|
// plan for overflow of the query if the table uses a lot of indexing, just in case...
|
|
$sysQ = 'SET SESSION group_concat_max_len=10240';
|
|
|
|
try {
|
|
if (false === $_rc->exec($sysQ)) {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . ERROR_PDO_QUERY . $sysQ);
|
|
return false;
|
|
}
|
|
$tmp = $_rc->prepare($query);
|
|
$tmp->execute();
|
|
$results = $tmp->fetchAll();
|
|
if ($results !== false) {
|
|
$dropQuery = (isset($results) and isset($results[0]) and is_array($results[0])) ? reset($results[0]) : null;
|
|
if (is_null($dropQuery)) return true; // no indexes to drop
|
|
$results = $_rc->query($dropQuery);
|
|
if (false === $results) return false;
|
|
// need to clean up any unique fields still hanging around, including the primary key
|
|
} else
|
|
return false;
|
|
} catch (PDOException | Throwable $e) {
|
|
$msg = sprintf(ERROR_PDO_EXCEPTION, $_query);
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . $eos. $msg . $_query);
|
|
consoleLog($res, CON_ERROR, $e->getMessage());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* getPDOResources() -- internal method
|
|
*
|
|
* This is a "new" function courtesy of DB-111 and requires two input parameters:
|
|
*
|
|
* $_admin -- this is a string containing the name of the admin user for the mysql env. Optimally, this should be a
|
|
* user account carrying global permissions allowing them to create new databases.
|
|
* $_pass -- this is a string containing the password for the global admin user.
|
|
*
|
|
* If nulls are passed (which is possible if the requesting user does not provide this information via argv), then
|
|
* we'll attempt to establish a database connection using the user/password account information stored in the XML
|
|
* for the service we're attempting the connection. We'll also connect to the destination resource specifying
|
|
* the database name for that resource.
|
|
*
|
|
* If the user/password is passed to the method, the assumption is that we need to create the database and the user
|
|
* on the destination resource. We'll connect to the remote mysql resource without specifying the db name assuming
|
|
* that will be created, along with the Namaste-admin-user account, post-invocation.
|
|
*
|
|
* This method uses a global array container - we loop through an array of valid envs and pull out the PDO host data
|
|
* for that env and, if the env is set/enabled, then assign the param values to self-descriptive keys in the container.
|
|
* This data includes the db resource pointer.
|
|
*
|
|
* If the connection attempt throws an exception, we process the exception and return a boolean(false) to the
|
|
* calling client.
|
|
*
|
|
* If an unknown env is in the environment list, we proc an error and return a boolean(false).
|
|
*
|
|
* Otherwise, we return a boolean true and implicitly return the PDO connectors and the array containers for each
|
|
* environment.
|
|
*
|
|
* Also, if Namaste and Admin are not enabled, generate an error and return a boolean(false) as these two envs are
|
|
* minimally required for Namaste.
|
|
*
|
|
*
|
|
* @author mike@givingassistant.org
|
|
* @version 1.0
|
|
*
|
|
* @param string $_admin
|
|
* @param string $_pass
|
|
* @return bool
|
|
*
|
|
*
|
|
* HISTORY:
|
|
* ========
|
|
* 08-29-19 mks DB-111: original coding
|
|
*
|
|
*/
|
|
function getPDOResources(string $_admin, string $_pass): bool
|
|
{
|
|
global $res, $envList, $aryPDOData;
|
|
|
|
$createDB = (empty($_admin) and empty($_pass)) ? false : true;
|
|
foreach ($envList as $env) {
|
|
if (isset(gasConfig::$settings[CONFIG_DATABASE][CONFIG_DATABASE_PDO][$env])) {
|
|
$cfgOld = gasConfig::$settings[CONFIG_DATABASE][CONFIG_DATABASE_PDO][$env];
|
|
$cfg = $cfgOld[CONFIG_DATABASE_PDO_MASTER];
|
|
$aryPDOData[$env][STRING_HOST] = $cfg[CONFIG_DATABASE_PDO_HOSTNAME];
|
|
$aryPDOData[$env][STRING_PORT] = $cfg[CONFIG_DATABASE_PDO_PORT];
|
|
$aryPDOData[$env][STRING_USER] = $cfg[CONFIG_DATABASE_PDO_USERNAME];
|
|
$aryPDOData[$env][STRING_PASS] = $cfg[CONFIG_DATABASE_PDO_PASSWORD];
|
|
$aryPDOData[$env][STRING_DBN] = gasConfig::$settings[CONFIG_ID][CONFIG_ID_ENV] . UDASH . $cfg[CONFIG_DATABASE_PDO_DB];
|
|
if ($createDB)
|
|
$dbs = 'mysql:host=' . $aryPDOData[$env][STRING_HOST] . COLON . $aryPDOData[$env][STRING_PORT];
|
|
else
|
|
$dbs = 'mysql:host=' . $aryPDOData[$env][STRING_HOST] . COLON . $aryPDOData[$env][STRING_PORT] . ';dbname=' . $aryPDOData[$env][STRING_DBN];
|
|
} else {
|
|
continue; // skip all processing for the current env b/c not configured
|
|
}
|
|
try {
|
|
switch ($env) {
|
|
case ENV_APPSERVER :
|
|
case ENV_SEGUNDO :
|
|
if ($cfgOld[CONFIG_DATABASE_PDO_ENABLED]) {
|
|
$aryPDOData[$env][STRING_DBR] = new PDO($dbs, (empty($_admin)) ? $aryPDOData[$env][STRING_USER] : $_admin, (empty($_pass)) ? $aryPDOData[$env][STRING_PASS] : $_pass);
|
|
} else {
|
|
$hdr = basename(__FILE__) . AT . __LINE__ . COLON;
|
|
consoleLog($res, CON_ERROR, $hdr . sprintf(ERROR_SQL_ENV_NOT_ENABLED, $env));
|
|
return false;
|
|
}
|
|
break;
|
|
case ENV_TERCERO :
|
|
case ENV_ADMIN :
|
|
if ($cfgOld[CONFIG_DATABASE_PDO_ENABLED]) {
|
|
$aryPDOData[$env][STRING_DBR] = new PDO($dbs, (empty($_admin)) ? $aryPDOData[$env][STRING_USER] : $_admin, (empty($_pass)) ? $aryPDOData[$env][STRING_PASS] : $_pass);
|
|
} // no error messages if not enabled as segundo/tercero configs are not required like namaste/admin
|
|
break;
|
|
default :
|
|
$hdr = basename(__METHOD__) . AT . __LINE__ . COLON;
|
|
$msg = $hdr . sprintf(ERROR_UNKNOWN_KEY, $env, 'envList');
|
|
consoleLog($res, CON_ERROR, $msg);
|
|
return false;
|
|
break;
|
|
}
|
|
} catch (PDOException | Throwable $p) {
|
|
$hdr = basename(__METHOD__) . AT . __LINE__ . COLON;
|
|
$msg = $hdr . sprintf(ERROR_ARRAY_KEY_UNK, $env);
|
|
consoleLog($res, CON_ERROR, $msg);
|
|
$msg = $hdr . $p->getMessage();
|
|
consoleLog($res, CON_ERROR, $msg);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* printHelp() -- internal function
|
|
*
|
|
* This function displays the help screen for this app. It prints the help message to stdout and exists returning void.
|
|
*
|
|
* The input parameter is meant to be $argv[0]: the name of the script file.
|
|
*
|
|
*
|
|
* @author mike@givingassistant.org
|
|
* @version 1.0
|
|
*
|
|
* @param string $_appName -- the name of the application, meant to be argv[0]
|
|
*
|
|
*
|
|
* HISTORY:
|
|
* ========
|
|
* 07-15-19 mks DB-124: original coding
|
|
*
|
|
*/
|
|
function printHelp(string $_appName): void
|
|
{
|
|
echo PHP_EOL;
|
|
echo "\e[1m" . $_appName . "\e[0m" . PHP_EOL . PHP_EOL;
|
|
echo "\e[1mUsage:\e[0m" . PHP_EOL;
|
|
echo '------' . PHP_EOL;
|
|
echo $_appName . ' is a mySQL/PDO deployment script that analyzes the data class templates and executes the' . PHP_EOL;
|
|
echo 'SQL code embedded in the templates to create (and/or drop) new tables, install indexes and database' . PHP_EOL;
|
|
echo 'objects (views, stored-procedures, functions, etc.).' . PHP_EOL . PHP_EOL;
|
|
echo 'The following are valid options for this program:' . PHP_EOL . PHP_EOL;
|
|
echo "\t" . ' --env={dev|prod} REQUIRED: deploy into a dev (tables are truncated) or prod (table data kept) environments' . PHP_EOL;
|
|
echo "\t" . ' --template={fileName} OPTIONAL: Deploy only for the template specified (e.g.: gatTestPDO.class.inc)' . PHP_EOL;
|
|
echo "\t" . ' --user={username} OPTIONAL: If you need to create a new database, specify the super user name' . PHP_EOL;
|
|
echo "\t" . ' --password={password} OPTIONAL: If you specify the super user name, specify the super user password' . PHP_EOL;
|
|
echo "\t" . ' -k | --keep OPTIONAL: Deploy into dev env but keep the table data (no table truncation)' . PHP_EOL;
|
|
echo "\t" . ' -h | --help Displays this help screen' . PHP_EOL . PHP_EOL . PHP_EOL;
|
|
echo "\e[91mWhen you deploy in a development capacity: tables are destroyed and re-created dropping all records.\e[0m" . PHP_EOL;
|
|
echo "\e[91mWhen you deploy in a production capacity: tables are retained but indexes are dropped and re-created.\e[0m" . PHP_EOL . PHP_EOL . PHP_EOL;
|
|
echo 'When specifying the super user data, note that this is NOT the same as the gaAdmin account user defined in the XML.' . PHP_EOL;
|
|
echo 'The super-user is a database account with admin privileges that can create new databases and users.' . PHP_EOL . PHP_EOL;
|
|
echo 'In both modes, all database objects (views, stored-procedures, etc.) are dropped and re-created.' . PHP_EOL . PHP_EOL;
|
|
echo 'All database modifications are done under the protection of a transaction. Either all of the table' . PHP_EOL;
|
|
echo 'changes will be saved and committed, or none will be. You will have to validate by reading the report' . PHP_EOL;
|
|
echo 'for each table that is processed by the program to determine success or failure.' . PHP_EOL . PHP_EOL;
|
|
echo "\e[1mExamples:\e[0m" . PHP_EOL;
|
|
echo '---------' . PHP_EOL;
|
|
echo "\e[37m" . $_appName . ' -e dev -t gatProductRegistrations.class.inc -k' . "\e[0m" . PHP_EOL;
|
|
echo 'This command will deploy into a dev environment but will limit the deployment to only processing the SQL directives' . PHP_EOL;
|
|
echo 'for the gatProductRegistrations.class.inc class. (You have to provide the fully-qualified template file name!)' . PHP_EOL;
|
|
echo 'The -k option tells the app to keep the table data; do not truncate the table.' . PHP_EOL . PHP_EOL;
|
|
echo "\e[37m" . $_appName . ' -e prod' . "\e[0m" . PHP_EOL;
|
|
echo 'Standard non-development deployment. All PDO (sql) tables will be updated but no data will be deleted.' . PHP_EOL . PHP_EOL;
|
|
echo "\e[37m" . $_appName . "\e[0m" . PHP_EOL;
|
|
echo 'Displays this message.' . PHP_EOL . PHP_EOL;
|
|
}
|
|
|
|
/**
|
|
* function printDisclaimer() -- internal function
|
|
*
|
|
* This function simply prints-out a disclaimer to the cli user stating that processing commands in this script is
|
|
* expected to take some reasonable amounts of time, depending on the amount of data in the table(s) being processed.
|
|
*
|
|
* The user is invited to display some patience during processing and is told to press the control-c button if they
|
|
* wish to stop the program.
|
|
*
|
|
*
|
|
* @author mike@givingassistant.org
|
|
* @version 1.0
|
|
*
|
|
*
|
|
* HISTORY:
|
|
* ========
|
|
* 08-07-19 mks DB-124: original coding
|
|
*
|
|
*/
|
|
function printDisclaimer()
|
|
{
|
|
echo <<<EOT
|
|
|
|
You are executing a script which will request several schema changes to your database tables. Some of these commands
|
|
will require several seconds, or minutes, PER TABLE, to successfully complete! Please do not abort execution of this
|
|
script. Monitor the console log output for status messages so that you know processing is on-going.
|
|
|
|
There will be no further output to the console -- MONITOR CONSOLE LOG for program output.
|
|
|
|
Press [Enter] to accept and begin processing. Press Ctrl+C to stop this program now.
|
|
|
|
EOT;
|
|
|
|
}
|