Programma PHP che esegue backup e ripristino di database SQL

di il
9 risposte

Programma PHP che esegue backup e ripristino di database SQL

Ciao a tutti, spiego subito il problema che mi ritrovo: devo eseguire dei backup su un SQL SERVER 2000, copiarli su un nuovo server che supporta SQL 2008 ed eseguire quindi i ripristini uno alla volta.
Tenete presente che questo programma dovrebbe spostare circa un migliaio di database anche molto pesanti, relativi ad anni passati e, soprattutto, 146 database del corrente anno una volta al giorno.

Ho creato un programma che esegue i vari passaggi:
- Esegue i vari backup (uno alla volta) dei database che si trovano su un SQL SERVER 2000 con relativa query BACKUP DATABASE che si crea di volta in volta un file di backup in base al nome del database, alla data e all'ora in cui viene eseguito.
- Verifica il corretto backup con la query RESTORE VERIFYONLY.
- Copia il file backup appena creato sul nuovo server e cancella quello presente sul vecchio server.
- Fa il restore sul nuovo server (che come detto, è un SQL SQRVER 2008) con la query di RESTORE DATABASE.

Tutto questo in teoria... perchè il problema principale è che il comando che lancia la query (sqlsrv_query , preciso che il mio programma è lanciato da un terzo server che supporta le librerie PHP di SQL SERVER 2008) è come se venisse interrotto e la query va sempre in errore nonostante i backup o le verifiche o gli stessi ripristini siano andati a buon fine (e questo vuol dire che le query sono corrette anche perchè sono state verificate con il Management Studio).

Io penso che come accade sul Management Studio, dove all'esecuzione della query mi vengono restituiti dei messaggi di completamento delle varie operazioni (ad esempio quando viene eseguito il backup, Man Studio mi ritorna messaggio che mi dicono l'avanzamento della fase di backup, ad esempio "41 percent backed up.") e che questi messaggi vengano interpretati da sqlsrv_query come messaggi di errore e che quindi vada in errore.
Sul manuale ho trovato alcuni stati di errore SQLSTATE che potrebbero anche essere evitati ed infatti ho dovuto creare una funzione di rilevazione di questo tipo di stati e che quindi mi evita la rilevazione dell'errore, di seguito il codice di esempio.

sqlsrv_query($connect, $sql) or trigger_error("La query ha ritornato falso : ".erroriQuerySql2008($backup, $bkPathFile, "Errore durante il Backup del database", $bkDBName, $Log));


//funzione per segnalare più esplicitamente gli errori derivanti dalle query SQL 2008
function erroriQuerySql2008($result, $path, $errore, $bkDBName, $Log){
    $errText="";
    if($result===false){
        if(($errors=sqlsrv_errors())!=null){
            foreach($errors as $error){
                $errText.="SQLSTATE: ".$error[ 'SQLSTATE']."\n";
                $errText.="code: ".$error[ 'code']."\n";
                $errText.="message: ".$error[ 'message']."\n";
                if($error['SQLSTATE'] == '01000' || $error[ 'SQLSTATE'] == '01001' || $error[ 'SQLSTATE'] == '01003' || $error[ 'SQLSTATE'] == '01S02' ){
                    $errText = "ma non ci sono errori!!! \n";
                    return $errText;
                }else{
                    echo $errore;
                    $array = explode("\", $path);
                    $Log->BackupLog("Server: ".$array[2]." DB: ".$bkDBName, $errore, basename($_SERVER["PHP_SELF"]));
                    $GLOBALS['erroriBackup']++;
                    return $errText;
                }
            }
        }
    }
}
Il problema persiste nel momento in cui mi ritrovo di cmq che i restore non vengono effettuati in nessun caso.
In più nel momento in cui provo a fare anche solamente eseguire il tutto per i db del 2012 senza fare i restore molte volte durante la verifica del backup mi ritrovo errori di questo tipo:

SQLSTATE: 42000
code: 3201
message: [Microsoft][SQL SERVER Native Client 10.0][SQL server]Cannot open backup device <percorso del file di backup>. Device error or device off-line. See the SQL Server error log for more details.
 in <percorso del programma> on line <nr linea>
L'sql server non riesce ad aprire il backup il device, nonostante questo sia online e funzionante: il risultato di tutto ciò è che in genere di 146 db del 2012, circa 35/38 a seconda, e non sempre gli stessi, non vengono copiati nel nuovo server...

Qualche idea? Non so se sono stato abbastanza chiaro su quello che sto cercando di risolvere...

Tenete anche presente che ho provato ad effettuare anche con il Man Studio le copie dei database con il comando di Wizard, ma questo non parte nemmeno perchè mi dà errore durante la "configurazione del pacchetto di Integration Services" e quindi andando in errore subito senza nemmeno eseguire una sola azione.

9 Risposte

  • Re: Programma PHP che esegue backup e ripristino di database SQL

    Davvero nessuno ha qualche idea??

    Al momento siamo riusciti ad aggirare parzialmente il problema, creando poi un programmino che durante il fine settimana ha fatto il detach di alcuni database degli anni passati, li ha copiati sul nuovo server e ne ha fatto il reattach. Certo è che andando aventi così ci vorranno parecchi fine settimana.

    In più il problema persiste per i database del corrente anno dei quali si vorrebbe avere un backup giornaliero da spostare.

    HELP!!
  • Re: Programma PHP che esegue backup e ripristino di database SQL

    Forse ci siamo, come suggeritomi in altri forum, l'errore era proprio nella query!!!

    Eliminando le opzioni REWIND, NOUNLOAD e soprattutto STATS = 10 perlomeno i backup vengono eseguiti tutti correttamente. La nuova funzione è quindi la seguente con l'opzione RETAINDAYS = 3 per mantenere i db del corrente anno 3 giorni sul nuovo server:
    
    function backupDatabase($bkDBName, $bkPathFile, $bkSet, $Log){
        sqlsrv_configure("WarningsReturnAsErrors", 0);
        $dbSenzaParentesi = filtraParentesiDb($bkDBName);
        $serverName = <nome_server>;
        $connectionInfo = array("UID" => <user_name>, "PWD" => <password>, "Database" => $dbSenzaParentesi);
        $connect = sqlsrv_connect($serverName, $connectionInfo) or trigger_error('Impossibile collegarsi al database '.$dbSenzaParentesi);
        
        //$sql = "ALTER DATABASE ".$bkDBName." SET RECOVERY FULL";
        echo "\n Backup del database ".$bkDBName." in corso \n";
        $sql = "BACKUP DATABASE ".$bkDBName." 
                TO  DISK = N'".$bkPathFile."' 
                WITH  RETAINDAYS = 3, NOFORMAT, NOINIT,  NAME = N'".$bkSet."', SKIP";
        echo " Query sql per il backup del database ".$bkDBName." in corso \n";
        $backup = sqlsrv_query($connect, $sql) or trigger_error("La query ha ritornato falso : ".erroriQuerySql2008($backup, $bkPathFile, "Errore durante il Backup del database: ", $bkDBName, $Log));
        //sqlsrv_close($connect);
    }
    
    Resta il problema sul restore...

    La funzione di restore non ha mai funzionato e a questo punto mi viene il dubbio che possa essere ancora qualche opzione del comando RESTORE che mi ha sempre dato noie in quanto non ha mai funzionato e quando dico mai intendo che nemmeno per i db piccoli funziona.
    Cioè la chiamata della query non dà errori però se provo a visualizzare i db con Man Studio li vedo in costatnte ripristino e mi vengono creati dei file di checkRestore.
    Effettivamente poi questo ripristino non va mai a buon fine nel senso che non viene mai completato.
    Questa la funzione di restore:
    
    //Funzione per il restore del database
    function restoreDatabese($bkDBName, $bkNuovoPathFile, $Log){
        sqlsrv_configure("WarningsReturnAsErrors", 0);
        $dbSenzaParentesi = filtraParentesiDb('<nome_database_esistente>');
        $serverName = "<nome_server>";
        $connectionInfo = array("UID" => "<nome_user>", "PWD" => "<password>", "Database" => $dbSenzaParentesi);
        $connect = sqlsrv_connect($serverName, $connectionInfo) or trigger_error('Impossibile collegarsi al database '.$dbSenzaParentesi);
        
        echo "\n Restore del Backup del database ".$bkDBName." in corso sul nuovo server \n";
        $sql = "RESTORE DATABASE ".$bkDBName."
                FROM DISK = '".$bkNuovoPathFile."'
                WITH REPLACE, 
                MOVE '".$bkDBName."_Data' TO '<percorso>".$bkDBName."_Data.MDF',
                MOVE '".$bkDBName."_Log' TO '<percorso>".$bkDBName."_Log.LDF'";
        echo "\n Query sql per il restore del database ".$bkDBName." in corso \n";
        $restore = sqlsrv_query($connect, $sql) or trigger_error("La query ha ritornato falso : ".erroriQuerySql2008($restore, $bkNuovoPathFile, "Errore durante il Restore del database", $bkDBName, $Log));
        //sqlsrv_close($connect);
    }
    
    Che errore può essere dato che se la stessa query eseguita sul Man Studio va a buon fine ?
  • Re: Programma PHP che esegue backup e ripristino di database SQL

    Ciao php per procedure del genere mi sembra azzardato, troppe variabili in gioco (timeout, grant, ecc).
    Non facevi prima a fare un'applicazione consolle che fa i backup/restore lanciandoli con "sqlcmd"?
  • Re: Programma PHP che esegue backup e ripristino di database SQL

    SQLCMD non è supportato da SQL SERVER 2000... è entrato in vigore dalla versione SQL 2005.
  • Re: Programma PHP che esegue backup e ripristino di database SQL

    Il restore mi sembrava di aver capito che lo fai su sql 2008, ed è li che hai il problema grosso.
    Potresti usare sqlcmd in questo caso.
  • Re: Programma PHP che esegue backup e ripristino di database SQL

    Forse qui non l'ho menzionato, il mio programma viene eseguito da un terzo server WINDOWS SERVER 2003 che supporta tutte le librerie necessarie di PHP per poter eseguire le query SQL 2008 ma non supporta il comando sqlcmd.
  • Re: Programma PHP che esegue backup e ripristino di database SQL

    CI SIAMO RAGA!!!

    La soluzione potrebbe essere sostanzialmente la funzione sqlsrv_next_result() molto più semplice di quello che uno si possa aspettare:
    
    if ( ($restore = sqlsrv_query($connect, $sql)) ){
    	do{
    		$array = sqlsrv_errors();
    		echo " Risposta del server: ".$array[0]['message']."\n";
    	} while ( sqlsrv_next_result($restore) ) ;
    	sqlsrv_free_stmt($restore);
    }
    


    Successivamente si può anche chiudere la connessione senza problemi.
    Resta solo da testarla al meglio! Vi farò sapere con esattezza se è questa la risposta definitiva forse anche per i problemi del backup!

  • Re: Programma PHP che esegue backup e ripristino di database SQL

    In pratica con questo ciclo si prendono tutti i risultati che la query ritorna ed interpreta come errori e glieli si fa leggere tutti di modo che il restore alla fine venga portato effettivamente a termine.
    E' in pratica come avere una query select alla quale mi faccio restituire con il fetch array tutti i risultati, in questo caso mi faccio restituire tutti quelli che PHP interpreta come errori per far sì che la query SQL venga effettivamente portata a termine e che quindi venga effettuato il restore.

    Per il mio caso, l'interesse sarà che siano visualizzati i messaggi che SQL SERVER mi ritorna quindi mi farò leggere solo gli errori di sqlsrv_errors() che si trovano in 'message' anche solo per verificare che una volta portata a termine la query, come sul Man Studio, l'ultimo messaggio visualizzato alla fine di ogni ciclo, sarà quello di operazione avvenuta con successo.
    La nuova funzione di restore sarà quindi così composta:
    
    function restoreDatabese($bkDBName, $bkNuovoPathFile, $restorePath, $file, $Log){
        sqlsrv_configure("WarningsReturnAsErrors", 0);
        $dbSenzaParentesi = filtraParentesiDb('<database_esistente>');
        $serverName = "<nome_server>";
        $connectionInfo = array("UID" => "<user_name>", "PWD" => "<password>", "Database" => $dbSenzaParentesi);
        $connect = sqlsrv_connect($serverName, $connectionInfo) or trigger_error('Impossibile collegarsi al database '.$dbSenzaParentesi);
    
        if(strpos($file, "_Data")){
            $moveBkDBFile = $bkDBName."_Data";
            $moveBkDBFileLog = $bkDBName."_Log";
        }else if(strpos($file, "_data")){
            $moveBkDBFile = $bkDBName."_data";
            $moveBkDBFileLog = $bkDBName."_log";
        }else{
            $moveBkDBFile = $bkDBName;
            $moveBkDBFileLog = $bkDBName."_log";
        }
        echo "\n Restore del Backup del database ".$bkDBName." in corso sul nuovo server \n";
        $sql = "RESTORE DATABASE ".$bkDBName."
                FROM DISK = N'".$bkNuovoPathFile."'
                WITH REPLACE, RECOVERY, 
                MOVE N'".$moveBkDBFile."' TO N'".$restorePath."\\".$moveBkDBFile.".MDF',
                MOVE N'".$moveBkDBFileLog."' TO N'".$restorePath."\\".$moveBkDBFileLog.".LDF'";
        echo " Query sql per il restore del database ".$bkDBName." in corso \n";
        //$restore = sqlsrv_query($connect, $sql) or trigger_error("La query ha ritornato falso : ".erroriQuerySql2008($restore, $bkNuovoPathFile, "Errore durante il Restore del database: ", $bkDBName, $Log));
        if ( ($restore = sqlsrv_query($connect, $sql)) ){
            do{
                $array = sqlsrv_errors();
                echo " Risposta del server: ".$array[0]['message']."\n \n";
            } while ( sqlsrv_next_result($restore) ) ;
            sqlsrv_free_stmt($restore);
        }
        sqlsrv_close($connect);
    }
    
    Alla fine si riesce anche a chiudere la connessione senza problemi.

    Dovrà essere testata a fondo in settimana ma penso che questa sia la soluzione definitiva.
    Con lo stesso trucchetto potranno essere modificate anche le funzioni di Backup e di verifica del backup.
  • Re: Programma PHP che esegue backup e ripristino di database SQL

    Per dovere di completezza e per essere corretti fino in fondo nell'uso della programmazione, al posto dei cicli if per trovare il nome del file logico, la cosa migliore da fare è sostituirli con la chiamata SQL RESTORE FILELISTONLY.

    Quindi al posto di
         if(strpos($file, "_Data")){
             $moveBkDBFile = $bkDBName."_Data";
             $moveBkDBFileLog = $bkDBName."_Log";
         }else if(strpos($file, "_data")){
             $moveBkDBFile = $bkDBName."_data";
             $moveBkDBFileLog = $bkDBName."_log";
         }else{
             $moveBkDBFile = $bkDBName;
             $moveBkDBFileLog = $bkDBName."_log";
         }
    


    è più corretto e soprattutto più sicuro (possono capitare errori di riferimenti tra i vari file) usare:
         $sql = "RESTORE FILELISTONLY
                   FROM DISK = '".$bkNuovoPathFile."'";
         $restoreFileList = sqlsrv_query($connect, $sql) or trigger_error("La query RESTORE
             FILELISTONLY ha ritornato falso : ".erroriQuerySql2008($restoreFileList, $bkNuovoPathFile,
             "Errore durante la RESTORE FILELISTONLY: ", $bkDBName, $Log));
         while($logicalName = sqlsrv_fetch_array($restoreFileList)){
             if($logicalName['Type'] == 'D'){
                 $moveBkDBFile = $logicalName['LogicalName'];
             }else if ($logicalName['Type'] == 'L'){
                 $moveBkDBFileLog = $logicalName['LogicalName'];
             }
         }
         @sqlsrv_free_stmt($restoreFileList);
    


    e lasciare tutto il resto inalterato.
Devi accedere o registrarti per scrivere nel forum
9 risposte