Ferreteria/v0.6/clade/Sys/Data/Engine/Oper/MyMar
Jump to navigation
Jump to search
| ||||||||||||||||||||||||||||||||||||
About
- Purpose: class for ops that are done the same/identically in MariaDB and MySQL (and any other compatible engines that turn up).
History
- 2025-05-30 started
- 2025-06-01 moved all of xMaria up here, though some of it may be Maria-tuned (suboptimal for MySQL).
- This stuff can be fine-tuned later.
- 2025-06-10 adding
EngineOpen()/EngineShut()
Functions
Code
interface iMyMar extends BaseIface {
// LIFECYCLE
function EngineOpen(string $sAddr, string $sPass, ?string $sSchema, ?int $nPort) : ActionIface;
function EngineShut() : ActionIface;
// INFO
function SchemaExists(string $sName) : bool; // 2025-05-30 Eventually, will want a base-class for all multi-schema engines.
}
abstract class caMyMar extends BaseClass implements iMyMar {
// ++ CONFIG ++ //
protected function CommOpClass() : string { return CommOpClass::class; } // specialize later
protected function SchemaClass() : string { return SchemaClass::class; }
protected function ScribeClass() : string { return ScribeClass::class; }
// -- CONFIG -- //
// ++ SETTINGS ++ //
private $eFmt = BkupModeEnum::USE_SQL; // DEFAULT
public function DataFormat(?BkupModeEnum $eMode=NULL) : BkupModeEnum { return is_object($eMode) ? ($this->eFmt = $eMode) : $this->eFmt; }
// -- SETTINGS -- //
// ++ LIFECYCLE ++ //
public function EngineOpen(string $sAddr, string $sPass, ?string $sSchema, ?int $nPort) : ActionIface {
#$oAct = new ($this->ActionClass()); // result object
$oAct = $this->NewAction();
$onDB = new mysqli($sAddr,$sUser,$sPass,$sSchema,$nPort); // open the DB connection
if (is_object($onDB)) {
$oAct->SetOkay(TRUE);
$this->QNative()->SetIt($onDB);
} else {
// 2022-01-26 probably want a method to do all this
$oAct->SetOkay(FALSE);
$oAct->SetNumber($this->ErrorNumber());
$oAct->AddMsgString($this->ErrorMessage());
}
return $oAct;
}
public function EngineShut() : ActionIface {
// close endpoint-to-engine connection
$ok = $this->QNative()->GetIt()->close();
// close code-to-endpoint connection
#$oAct = new ($this->ActionClass());
$oAct = $this->NewAction();
$oAct->SetOkay($ok);
return $oAct;
}
// -- LIFECYCLE -- //
// ++ ACTION: UI requests ++ //
public function DoDataRequest(DataReqIface $oReq) : PlayerIface {
$this->Open();
$sql = $oReq->SQL();
#echo "SQL: $sql".CRLF;
$onRes = $this->QNative()->GetIt()->query($sql);
$oReq->Action()->SetOkay(is_object($onRes));
$scScribe = $this->ScribeClass();
# echo $oReq->ReflectThis()->Report();
$oScribe = $scScribe::FromRequestAndNative($oReq,$onRes);
$oPlay = $oReq->MakePlayer($oScribe);
$this->Shut();
return $oPlay;
}
// ++ ACTION: Schema info ++ //
private $oaSchemas = NULL;
public function SchemaList() : SchemasIface { return $this->oaSchemas ?? ($this->oaSchemas = $this->NewSchemaList()); }
public function SchemaExists(string $sName) : bool { return $this->SchemaList()->HasIt($sName); }
protected function NewSchemaList() : SchemasIface {
#$this->AmHere("GENERATING SCHEMA LIST");
$oConn = $this->OConn(); // get (remote or local) connection object
$sqCmd = '"SHOW SCHEMAS;"';
#$this->AmHere("DB COMMAND: $sqCmd");
$oBuff = new MemBufferClass;
#echo $oBuff->ReflectThis()->Report();
$oBuff->Open();
$oPlug = $oConn->OSock()->OPlug();
$oPlug->Open();
$oActConn = $oConn->DoCommand($sqCmd,$oBuff,new ($this->CommOpClass()));
$oActConn->HandleResults(); // deal with standard errors/warnings
$oPlug->Shut();
$oActApp = $this->CommOpStatus();
$oScrn = self::Screen();
$oaSchemas = SchemasClass::AsNew();
if ($oBuff->IsMore()) {
#$this->AmHere('GOT SCHEMA LIST');
$sNames = $oBuff->RemoveBytes();
$arNames = explode("\n",trim($sNames)); // we might want to ensure that CRLF, CR, and LF all work as EOL.
#$this->AmHere(' - ELEMENTS: '.count($arNames));
$sFirst = array_shift($arNames);
if ($sFirst == "Database") {
$oActApp->SetOkay(TRUE);
foreach ($arNames as $sName) {
$oSchema = $this->GetSchema($sName);
$oaSchemas->AddSchema($oSchema);
#$this->AmHere(" -- SCHEMA: [$sName]");
}
} else {
$oActApp->SetOkay(FALSE);
// "Database" is the standard header for list of databases (schemas).
// If we don't see this, then something isn't right.
$sMsg = $oScrn->ErrorIt('Problem').": Something's not right with the db list. Here's what we got:".CRLF.$sList;
$oActApp->AddMsgString($sMsg);
}
} else {
$sMsg = self::Screen()->ErrorIt('Problem').': the connection does not seem to be working.';
$oActApp->SetOkay(FALSE);
$oActApp->AddMsgString($sMsg);
}
$oConn->Shut();
return $oaSchemas;
}
// TODO: MySQL/Maria -specific versions of this
// NOTE: This and Engine\Schema\MyMar::DoExport() are both used..
public function DoExport(string $sSchema, RecvBuffClass $oInBuff) : CommOpIface {
$oConnDB = $this->OConn(); // DB connection
$oSockDB = $oConnDB->OSock();
#echo $oSockDB->ReflectThis()->Report();
$oConnSh = $oSockDB->OPlug();
#$oSockSh = $oConnSh->OSock();
#$oConn3 = $oSockSh->OPlug();
#echo 'oConn3 is: '.get_class($oConn3).CRLF; die();
$sCred = $oConnDB->CredsForCmd(); // DB connection string from DB-local shell
$sCmd = "mysqldump $sCred --quick $sSchema"; // "--quick" does line-at-a-time rather than buffering entire table
// should we also try "--single-transaction"? "To dump large tables, you should combine the --single-transaction option with --quick."
// Send dump command directly to shell, not to DB engine:
#echo $$oConnSh->ReflectThis()->Report(); die();
$oAct = $oConnSh->DoCommand($sCmd,$oInBuff,new CommOpClass);
return $oAct;
}
// ++ ACTION: UI request callbacks ++ //
// SEE DOCS
public function DoTestAccess() {
$ok = $this->TestUserAccess();
if (!$ok) {
echo "(Since that didn't work, let's try unconfigured root.)".CRLF;
$ok = $this->TestRootAccess();
}
}
// SEE DOCS
public function DoUserSetup() {
$oScrn = self::Screen();
// 2025-03-30 has worked at least once now
#echo $oScrn->YellowIt('Warning').': This functionality has not been tested to the point of success yet. It may not work.'.CRLF;
$ok = $this->TestRootAccess();
if ($ok) {
echo $oScrn->GreenIt('Ok').': we seem to have unconfigured root access. Proceeding...'.CRLF;
} else {
echo $oScrn->ErrorIt('Error').': This DB engine is configured, and can only be accessed with credentials.'.CRLF;
exit;
}
$oCreds = $this->Creds();
$sDUser = $oCreds->QSUser()->GetIt();
$sDPass = $oCreds->QSPass()->GetIt();
$sDHost = $oCreds->QSHost()->GetIt();
// IN-SHELL COMMAND: sudo mysql "CREATE USER '<USERNAME>'@<HOST> IDENTIFIED BY '<PASSWORD>'"
$q = '"';
$sql = $q."ALTER USER '$sDUser'@$sDHost IDENTIFIED BY '$sDPass';$q";
$sCmd = "echo $sql | sudo mysql";
// DEBUGGING ONLY (shows password)
#echo $oScrn->BoldIt('Command').': '.$oScrn->BlueIt($sCmd).CRLF;
$oConnx = $this->Connx(); // shell (outside) connection details
echo "Adding initial user '$sDUser' on db server '$sDHost'...".CRLF;
// DEBUGGING ONLY
#echo "INSIDE COMMAND: $sCmd".CRLF; die();
#echo $oConnx->ReflectThis()->Report(); die();
$oBuff = new MemBufferClass;
echo 'Running the SQL to create the first user...'.CRLF;
$oAct = $oConnx->DoCommand($sCmd,$oBuff);
$ok = $oAct->GetOkay();
if (!$ok) {
// See if we can handle this condition...
$sMsg = $oAct->GetMessage();
if (str_contains($sMsg,'server is running with the --skip-grant-tables option')) {
// NOTE: It may be that what actually needs to happen is just "FLUSH PRIVILEGES;" and try again.
echo 'Apparently the server is in permissionless mode, so using alternative SQL.'.CRLF;
// SET PASSWORD FOR 'root'@'localhost' = PASSWORD('new_password');
echo "(Full message: $sMsg)".CRLF;
// FOR DEBUGGING ONLY (shows password):
$sql = "ALTER USER '$sDUser'@'$sDHost' IDENTIFIED BY '$sDPass';";
echo "SQL: $sql".CRLF;
#$sCmd = "echo $sql | sudo mysql";
$sCmd = 'echo "$sql" | sudo mysql';
$oAct = $oConnx->DoCommand($sCmd,$oBuff);
$ok = $oAct->GetOkay();
if (!$ok) {
echo 'Meh, that failed too. Giving up for now.'.CRLF;
}
}
}
if ($ok) {
$oAct = $oConnx->DoCommand($sCmd,$oBuff);
$oConnx->Shut();
echo ' - ' . (($oAct->GetOkay() ? $oScrn->GreenIt('Done.') : ($oScrn->ErrorIt('Error: ').$oAct->GetMessage()))) . CRLF;
} else {
echo $oScrn->ErrorIt('Error: ')
.$oScrn->YellowIt($sMsg)
.' - The setup failed for some reason.'
.CRLF;
}
$oAct = $oBuff->Shut();
}
// ++ ACTION: UI pieces ++ //
// SEE DOCS
protected function TestUserAccess() {
$oScrn = self::Screen();
#echo $this->ReflectThis()->Report();
$sName = $this->ConnSlug();
$ftName = $oScrn->GreenIt($sName);
echo "[$ftName]: Testing configured credentials... ";
$sCreds = $this->OConn()->CredsForCmd(); // 2025-06-05 get this from the Creds object now.
return $this->TestAccess($sCreds);
}
// SEE DOCS
protected function TestRootAccess() : bool {
$oScrn = self::Screen();
$sName = $this->ObjectSlug();
$ftName = $oScrn->GreenIt($sName);
echo "Testing unconfigured root access on [$ftName]... ";
return $this->TestAccess('');
}
protected function TestAccess(string $sCreds) : bool {
$oScrn = self::Screen();
$sSQL = '"SHOW SCHEMAS;"';
$bRoot = ($sCreds === '');
if ($bRoot) {
$sCmd = "echo $sSQL | sudo mysql";
$sMode = 'unconfigured root';
} else {
$sCmd = "echo $sSQL | mysql $sCreds";
$sMode = 'credentialed user';
}
$oConnx = $this->OConn(); // shell (outside) connection details
// DEBUGGING ONLY:
#echo 'SQL COMMAND: '.$sCmd.CRLF;
$oBuff = new MemBufferClass;
#$oConnx->CommOpClass(CommOpClass::class); // tell oConnx what class to use for Action
$oBuff->Open(); // keep buffer open for reading data
$oConnx->Open();
$oAct = $oConnx->DoCommand($sCmd,$oBuff,new CommOpClass);
$oAct->HandleResults();
$oConnx->Shut();
$ok = $oAct->GetOkay();
if ($ok) {
#echo $oScrn->GreenIt('Done.').CRLF;
$sList = $oBuff->RemoveBytes();
$arList = explode("\n",trim($sList)); // we might want to ensure that CRLF, CR, and LF all work as EOL.
// "Database" is the standard header for list of databases.
// If we don't see this, then something isn't right.
$sFirst = array_shift($arList);
if ($sFirst != "Database") {
self::GotToHere("Something's not right with the db list. Here's what we got:".CRLF.$sList); die();
}
$ftMode = $oScrn->BoldIt($sMode);
$ftStatus = $oScrn->GreenIt('Success');
$sSuggest = $bRoot ? ' ("setup" should work)' : '';
echo "$ftStatus: Access in $ftMode mode seems to be ok$sSuggest. Here's a list of schemas found:".CRLF;
$oList = $oScrn->NewList();
foreach ($arList as $sSchema) {
$oList->AddLine($sSchema);
}
echo $oList->Render();
} else {
$ftStatus = $oScrn->ErrorIt('Error');
$ftMsg = $oScrn->YellowIt($oAct->GetMessage());
$sSuggest = $bRoot ? ' (Try "setup"?)' : '';
echo "$ftStatus: We do not seem to have access.$sSuggest Error received was: ".CRLF.$ftMsg.CRLF;
}
$oBuff->Shut();
return $ok;
}
// ++ ACTION: internal ++ //
private $oStatus = NULL;
protected function CommOpStatus() : CommOpIface { return $this->oStatus ?? ($this->oStatus = new CommOpClass); }
// -- ACTION -- //
// ++ OBJECTS ++ //
private $osNative;
protected function QNative() : QObjIface { return $this->osNative ?? ($this->osNative = QObjClass::AsNew()); }
// -- OBJECTS -- //
}