Ferreteria/v0.6/clade/Sys/Data/Engine/endpt/Client/MyMar

From Woozle Writes Code
< Ferreteria‎ | v0.6‎ | clade‎ | Sys‎ | Data‎ | Engine‎ | endpt/Client
Jump to navigation Jump to search
clade: Sys\Data\Engine\endpt\Client\MyMar
Clade Family
Oper MyMar
Clade Aliases
Alias Clade
ActionIface Sys\Events\ItWent
Base* [ca,i] Sys\Data\Engine\endpt\Client
BkupModeEnum Sys\Data\Engine\aux\ActionRq\Admin\aux\FileFormat
BufferClass IO\Aspect\Connx\Stream\Buffer
CmdLine* [c,i] Sys\Data\Codec\aux\CmdLine
CommOp* [c,i] IO\Aspect\Connx\aux\itWent\CommOp
DataReqIface Sys\Data\Engine\aux\CommRq
ExportClass Sys\Data\Engine\aux\Exporter\MyMar
PlayerIface Data\Base\Result\Player
QObj* [c,i] Data\Mem\QVar\Obj
SchemaClass Sys\Data\Engine\schema\Ops\MyMar
Schemas* [c,i] Sys\Data\aux\list\Schemas
ScribeClass Sys\Data\Xfer\Scribe\MySQL
Subpages

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
    static function FromSpecs(string $sAddr, string $sUser, string $sPass, ?string $sSchema, ?int $nzPort) : self;
    // 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; }
    protected function ExportClass() : string { return ExportClass::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 -- //
    // ++ STATE ++ //

    private $isOpen = FALSE;
    protected function IsOpen(?bool $b=NULL) : bool { return $this->isOpen ?? ($this->isOpen = $b); }

    // -- STATE -- //
    // ++ SETUP ++ //

    public static function FromSpecs(string $sAddr, string $sUser, string $sPass, ?string $sSchema, ?int $nzPort) : iMyMar {
        $oThis = new static;
        $oThis->WithSpecs($sAddr,$sUser,$sPass,$sSchema,$nsPort);
        return $oThis;
    }
    public function __destruct() { $this->Shut(); }

    // ++ SETUP: dynamic ++ //

    protected function WithSpecs(string $sAddr, string $sUser, string $sPass, ?string $sSchema, ?int $nzPort) : iMyMar {
        $oAct = $this->ItWentLike();   // get an ItWent object
        $oAct->Clear();

        #$this->AmHere("ADDR: [$sAddr] - USER: [$sUser] - PASS: [$sPass]");
        $onDB = new mysqli();
        $this->QNative()->SetIt($onDB);
        try {
            $ok = $onDB->connect($sAddr,$sUser,$sPass,$sSchema,$nzPort); // open the DB connection
        } catch(\Exception $e) {
            $ok = FALSE;
            #echo "EXCEPTION: ".$e.CRLF;
        }
        if ($ok) {
            $oAct->SetOkay(TRUE);
            $this->IsOpen(TRUE);
        } else {
            // 2022-01-26 probably want a method to do all this
            $oAct->SetOkay(FALSE);
            #echo $this->ReflectThis()->Report();
            $oAct->SetError($e->getCode());
            $oAct->AddMsgString($e->getMessage());
        }
        return $oAct;
    }

    // -- SETUP -- //
    // ++ OBJECTS ++ //

    private $oStatus = NULL;
    protected function CommOpStatus() : CommOpIface { return $this->oStatus ?? ($this->oStatus = new ($this->CommOpClass())); }

    // -- OBJECTS -- //
    // ++ LIFECYCLE ++ //

    public function Shut() : CommOpIface {
        $oAct = $this->ItWentLike();
        if ($this->IsOpen()) {
            // Did Engine actually get opened? (2025-12-06 We probably need a separate flag for this. TODO)
            // close endpoint-to-engine connection
            $onDB = $this->QNative()->GetIt();
            $ok = $onDB->close();
            // close code-to-endpoint connection
            $oAct->SetOkay($ok);
            $this->IsOpen(FALSE);
        }
        return $oAct;
    }

    // -- LIFECYCLE -- //
    // ++ ACTION ++ //

    // ++ 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 {

        // OPEN THINGS:
        $oServer = $this->OServer(); // get next-hop connection object
        $oServer->Open(); // make sure it's open

        $oCmd = CmdLineClass::FromString('SHOW SCHEMAS;');

        #echo $oServer->ReflectThis()->Report();
        $oActCmd = $oServer->DoCommand($oCmd);
        // NOTE: For remote ssh connections, this ultimately gets forwarded to [WFe]IO\Aspect\Connx\Plug\Shell\Remote\cSSH
        //  ... which then wraps it and punts it back to [WFe]IO\Aspect\Connx\Plug\caShell.
        #$this->AmHere('RESULTS from '.get_class($oConn).'->DoCommand(): '.$oActCmd->VIEW_AsBlock());

        // SHUT THINGS:
        #$oPlug->Shut();
        $oServer->Shut();
        #$oBuff->Shut();

        $oMsgs = $this->MsgsOp();
        $oMsgs->HandleResults($oActCmd);  // deal with standard errors/warnings


        $oActApp = $this->CommOpStatus();
        $oScrn = self::Screen();

        $oaSchemas = SchemasClass::AsNew();

        if ($oActCmd->GetOkay()) {
            #echo 'oActCmd: '.$oActCmd->VIEW_AsLine();
            #echo $oActCmd->ReflectThis()->Report();
            #$this->AmHere('LIST COMMAND RESULT: '.$oActCmd->VIEW_AsBlock());
            $sNames = $oActCmd->QData()->GetIt();
            $arNames = explode("\n",trim($sNames)); // we might want to ensure that CRLF, CR, and LF all work as EOL.

            $sFirst = array_shift($arNames);
            if ($sFirst == "Database") {
                $oActApp->SetOkay(TRUE);
                foreach ($arNames as $sName) {
                    $oSchema = $this->GetSchema($sName);
                    $oaSchemas->AddSchema($oSchema);
                }
            } 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.$sNames;
                $oActApp->AddMsgString($sMsg);
            }
        } else {
            $sMsg = self::Screen()->ErrorIt('Problem').': the connection does not seem to be working.';
            $oActApp->SetOkay(FALSE);
            $oActApp->AddMsgString($sMsg);
        }

        return $oaSchemas;
    }

    // ++ ACTION: UI request callbacks ++ //

    // 2026-02-08 TODO This is going to need updating.
    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";
        $sql = "ALTER USER '$sDUser'@$sDHost IDENTIFIED BY '$sDPass';";

        #$sCmd = "echo $sql | sudo mysql";
        $oCmd = CmdLineClass::FromArray(['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;

        #$oBuff = new BufferClass;
        echo 'Running the SQL to create the first user...'.CRLF;
        #$oAct = $oConnx->DoCommand($oCmd,$oBuff);
        $oAct = $oConnx->DoCommand($oCmd);

        $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";
                $oCmd = CmdLineClass::FromArray(['echo',$sql,'|','sudo','mysql']);
                #$sCmd = 'echo "$sql" | sudo mysql';
                #$oAct = $oConnx->DoCommand($oCmd,$oBuff);
                $oAct = $oConnx->DoCommand($oCmd);
                $ok = $oAct->GetOkay();
                if (!$ok) {
                    echo 'Meh, that failed too. Giving up for now.'.CRLF;
                }
            }
        }

        if ($ok) {
            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 -- //

}