Una libreria per MySql - parte seconda
a cura di Lino Aiello (requisiti: conoscenza generica di .Net e di MySql)

Premessa
Dopo aver trattato, nella prima parte di questa esposizione della libreria per MySql, circa le intestazioni, le proprietà, la connessione, le varie query sia di selezione e modifica, e da ultimo, delle stored procedures, ci occuperemo, in questa seconda ed ultima parte, ci occuperemo dei seguenti argomenti:

  1. Funzioni
  2. Utilità
  3. Backup e restore
  4. Transazioni

Inoltre, alla fine dell'articolo, verrà riportato un semplicissimo programmino che utilizza alcune delle funzioni di base della libreria, il cui scopo è quello di familiarizzarsi con essa.
In aggiunta, verrà proposta - in formato installabile dato il numero elevato di righe di codice e di form - una shell di gestione per MySql (MySqlAdmin.msi), costruita intorno alla libreria di cui discutiamo, che ne illustra meglio le potenzialità.

Funzioni

  Public Function MySqlFunction(ByVal Testo As String, ByVal Dati As ArrayList) As String

    Dim i As Integer = 0

    Dim q As String = ""
    Dim qTemp As String = ""

    If Not Flag Then
      Return Nothing
      Exit Function
    End If

    Try
      If Dati(0) Is Nothing Then
        Return Nothing
        Exit Function
      End If

      For i = 0 To Dati.Count - 1
        If IsDBNull(MySqlPar(Dati(i))) Then
          qTemp = "NULL"
        Else
          qTemp = MySqlPar(Dati(i)).ToString
        End If
        q = q + qTemp + ","
      Next
      q = Mid(q, 1, q.Length - 1)
      q = "SELECT " + Testo + "(" + q + ");"

      Dim ret As Object = Nothing
      myCommand.CommandText = q
      ret = myCommand.ExecuteScalar()

      If ret Is DBNull.Value Then
        Return ""
      Else
        Return ret.ToString
      End If

    Catch ex As MySqlException
      Throw
    End Try

  End Function

Tutti i databases relazionali hanno la possibilità di creare e utilizzare funzioni.
Ma cosa sono le funzioni? Sono particolari procedure (assimilabili alle stored) che permettono di ampliare le potenzialità del linguaggio SQL, il quale, com'è noto, non possiede in modo nativo molte funzioni che sono proprie dei linguaggi ad oggetti (VB, C#, ecc.).
Per realizzare questi costrutti si ha necessità di conoscere il linguaggio proprio del motore che si sta usando, per cui si rinvia alla documentazione tecnica di MySql per i dettagli.
Le funzioni restituiscono un unico valore, che rappresenta il risultato delle operazioni fatte nel corpo della funzione medesima, le quali possono essere anche molto complesse al loro interno.

Qualunque sia il tipo restituito, il metodo MySqlFunction restituisce sempre un valore di tipo String, per cui sarà cura dello sviluppatore operare le opportune conversioni.
Il metodo effettua alcuni controlli per verificare che la connessione sia attiva (campo Flag) e che siano stati trasferiti i parametri (obbligatori).
Successivamente sottopone i parametri alle opportune conversioni tramite la funzione di servizio MySqlPar già vista, costruisce la stringa da passare alla linea di comando del motore di MySql (parametro q), e raccoglie il valore di ritorno (scalare, trattandosi di un solo parametro) in ret.

  Sintassi: string = Classe.MySqlFunction(string, arraylist)

Esempio:
Sia Moltiplica il nome di una banalissima funzione che restituisce il prodotto di due parametri interi.
Siano i due parametri 4 e 5.

    Dim cl As New MySqlDataConnect.MyClient
    Dim w As New arraylist
    w.Add(4)
    w.Add(5)
    Dim s As String = cl.MySqlFunction("Moltiplica", w)

Dopo l'esecuzione del codice, s conterrà il valore "20"

Utility

  Public Sub MySqlSender(ByVal Testo As String)

    If Not Flag Then
      Exit Sub
    End If
    Try
      myCommand.CommandType = CommandType.Text
      myCommand.CommandText = Trim(Testo)
      myCommand.ExecuteNonQuery()

    Catch ex As MySqlException
      Throw
    End Try
  End Sub
 
  Public Function MySqlShowCreateTable(ByVal Testo As String) As String
    '//L'unico modo per leggere il campo che contiene la definizione della tabella, in 
    '//modalità 'Option strict on', è quello di utilizzare un dataread ( e non un 
    '//datatable che non sembra in grado di leggere un campo LONGTEXT che viene
    '//immagazzinato da MySql come System.Byte[])
    Try
      Dim dr As MySqlDataReader
      Dim s As String = ""

      myCommand.CommandType = CommandType.Text
      myCommand.CommandText = "SHOW CREATE TABLE " + Testo + " "
      dr = myCommand.ExecuteReader

      dr.Read()
      s = dr.GetString(1)
      dr.Close()
      s = Replace(s, Chr(10), ControlChars.CrLf)

      Return s
    Catch ex As MySqlException
      Throw
    End Try
  End Function
 
  Public Function MySqlShowCreateStored(ByVal Testo As String) As String
    '//L'unico modo per leggere il campo che contiene la definizione della stored, in 
    '//modalità 'Option strict on', è quello di utilizzare un dataread ( e non un 
    '//datatable che non sembra in grado di leggere un campo LONGTEXT che viene
    '//immagazzinato da MySql come System.Byte[])
    Try
      Dim dr As MySqlDataReader
      Dim s As String = ""

      myCommand.CommandType = CommandType.Text
      myCommand.CommandText = "SHOW CREATE PROCEDURE " + Testo + " "
      dr = myCommand.ExecuteReader

      dr.Read()
      s = dr.GetString(2)
      dr.Close()
      s = Replace(s, Chr(10), ControlChars.CrLf)

      Return s
    Catch ex As MySqlException
      Throw
    End Try
  End Function

  Public Function MySqlShowCreateFunction(ByVal Testo As String) As String
    '//Esattamente uguale a MySqlShowCreateStored, ma per le funzioni
    Try
      Dim dr As MySqlDataReader
      Dim s As String = ""

      myCommand.CommandType = CommandType.Text
      myCommand.CommandText = "SHOW CREATE FUNCTION " + Testo + " "
      dr = myCommand.ExecuteReader

      dr.Read()
      s = dr.GetString(2)
      dr.Close()
      s = Replace(s, Chr(10), ControlChars.CrLf)

      Return s
    Catch ex As MySqlException
      Throw
    End Try
  End Function
 
  Public Function MySqlShowCreateTrigger(ByVal Testo As String) As String
    '//Esattamente uguale a MySqlShowCreateStored, ma per i triggers
    '//Questa funzione è stata introdotta a partire dalla versione 5.1.21 di
    '//MySql. Se usata con versioni inferiori, ritorna un errore di sintassi.
    '//Per le versioni anteriori alla 5.1.21, è possibile ottenere lo schema del trigger
    '/ricostruendolo dalla tabella "Triggers" di information_schema
    Try
      Dim dr As MySqlDataReader
      Dim s As String = ""

      myCommand.CommandType = CommandType.Text
      myCommand.CommandText = "SHOW CREATE TRIGGER " + Testo + " "
      dr = myCommand.ExecuteReader

      dr.Read()
      s = dr.GetString(2)
      dr.Close()
      s = Replace(s, Chr(10), ControlChars.CrLf)

      Return s
    Catch ex As MySqlException
      Throw
    End Try
  End Function
 
  Public Sub MySqlChangeDatabase(ByVal NomeDb As String)
    Try
      If Flag Then
        Me.myConnection.ChangeDatabase(NomeDb)
      End If
    Catch ex As MySqlException
      Throw
    End Try
  End Sub

Ciascun metodo di questo gruppo ha una sua specifica utilità.
Alcuni sono più utili di altri; alcuni sono necessari per cui vanno assolutamente inclusi in una procedura gestionale, ad esempio, altre meno. Il più importante di tutti è sicuramente .

La struttura del metodo MySqlSender è quanto di più semplice si possa pensare: esso infatti è costituito da un semplice Command, il cui testo si limita a trasmettere delle istruzioni a MySql, agendo in tal modo come linea di comando del database (per intenderci, come fosse l'utente a trasmettere comandi a mezzo tastiera).
Poiché l'invio è da considerare unidirezionale, viene utilizzato ExecuteNonQuery.
Per mezzo di MySqlSender è possibile creare o distruggere oggetti come tabelle, stored, viste, funzioni; inviare comandi di settaggio (se non tutti, un gran numero); creare ed eliminare utenti; assegnare o modificare password; gestire i privilegi sugli oggetti dei databases; bloccare o sbloccare tabelle; modificare il comportamento di MySql ecc.

Il motivo per cui il metodo MySqlSender dovrebbe essere sempre incluso in un eseguibile è ben spiegato nel caso di tabelle temporanee.
Le tabelle temporanee sono quegli oggetti del database che hanno vita solo per la durata della connessione; spariscono alla chiusura della stessa.
Spesso vengono utilizzate per creare tabelle di appoggio che servono come fonte dati per altre operazioni. Non possono essere create in sede di pianificazione del database perché, come detto, sono distrutte alla chiusura della connessione.
Sebbene sia possibile ricorrere a stored procedure deputate alla creazione di tabelle temporanee, spesso è molto più comodo costruirle 'al volo' all'interno del codice, al momento del loro utilizzo.
Dovendo quindi creare una tabella temporanea, è sufficiente scrivere il seguente codice:

  Classe.MySqlSender("CREATE TABLE #Tabella(......)")

La tabella è creata, potrà essere manipolata con le usuali istruzioni e verrà distrutta al termine della connessione.

I tre metodi "Show" e cioè:

  MySqlShowCreateTable(string)
  MySqlShowCreateStored(string)
  MySqlShowCreateFunction(string)

sono simili tra loro e restituiscono in una stringa lo script dell'oggetto relativo, invocando l'omonimo metodo di MySql.
Essi raccolgono il risultato della chiamata in un DataReader, e restituiscono un valore String.
Questi metodi sono utilizzati principalmente nella procedura di backup (vedere nel seguito), e sono veramente necessari solo nel caso di applicativi prettamente dedicati alla gestione di MySql (come, ad esempio, la shell che viene qui proposta).
L'ultimo metodo presente in questo gruppo, MySqlChangeDatabase(string), come si intuisce, serve a cambiare il database corrente, qualora ve ne fosse bisogno.
Ovviamente, l'operazione avrà successo solo nel caso si posseggano i privilegi relativi.

Backup

  Public Sub MySqlBackupSingleDB(ByVal Testo As String)
    '//Testo = percorso completo del file di backup
    '// es.  c:\MiaDir\miobackup[.estensione]
    '//E' compito de programma chiamante, costruire la stringa esatta.

    Const MSG_00 As String = "Autore: P.Aiello - 2007"
    Const MSG_01 As String = "Utente: "
    Const MSG_02 As String = "Server: "
    Const MSG_11 As String = "Applicazione: "
    Const MSG_12 As String = "Database: "
    Const MSG_15 As String = "DBMS: MySql v. "
    Const MSG_16 As String = "Struttura tabella: "
    Const MSG_17 As String = "Dati tabella: "
    Const MSG_18 As String = "Fine Backup: "


    Dim i As Integer = 0
    Dim q As Integer = 0
    Dim j As Integer = 0
    Dim y As Integer = 0

    Dim nameApp As String = ""
    Dim nameTableArray As New ArrayList
    Dim nameViewArray As New ArrayList
    Dim nameStoredArray As New ArrayList
    Dim nameFuncArray As New ArrayList
    Dim totRecords As Long = 0
    Dim numCurrentRecords As Long = 0
    Dim overLongProgress As Long = 0
    Dim sCarrier As New System.Text.StringBuilder("")
    Dim mysqlversion As String = ""
    Dim InsertGroup As String = ""
    Dim RecCounter As Integer = 0
    Dim strFlush As String = ""
    Dim strColumnsName As String = ""
    Dim recFlag As Boolean = False



    Dim dt As New DataTable
    Dim rows() As DataRow

    Dim dColumnsName As New DataTable
    Dim rowsColumnsName() As DataRow

    Try
      Testo = Testo.Trim

      If My.Computer.FileSystem.FileExists(Testo) = True Then
        My.Computer.FileSystem.DeleteFile(Testo, FileIO.UIOption.OnlyErrorDialogs, _
                                          FileIO.RecycleOption.SendToRecycleBin, _
                                          FileIO.UICancelOption.ThrowException)
      End If

      nameApp = My.Application.Info.AssemblyName + " v: " + My.Application.Info.Version.ToString

      dt = Me.MySqlDirectQuery("SHOW VARIABLES LIKE 'version'")
      rows = dt.Select()
      mysqlversion = CStr(rows(0).Item("value"))



      sCarrier.Append("" + ControlChars.CrLf)
      sCarrier.Append("-- " + MSG_00 + ControlChars.CrLf)
      sCarrier.Append("-- " + ControlChars.CrLf)
      sCarrier.Append("--  " + MSG_01 + Me.MySqlUID + ControlChars.CrLf)
      sCarrier.Append("--  " + MSG_02 + Me.MySqlServer + ControlChars.CrLf)
      sCarrier.Append("--  " + MSG_11 + nameApp + ControlChars.CrLf)
      sCarrier.Append("--  " + MSG_12 + Me.MySqlDataBase + ControlChars.CrLf)
      sCarrier.Append("--  " + MSG_15 + mysqlversion + ControlChars.CrLf)
      sCarrier.Append(ControlChars.CrLf)
      sCarrier.Append(ControlChars.CrLf)
      sCarrier.Append(ControlChars.CrLf)


      '//Prevenzione degli errori per violazione delle foreign key durate il processo di restore



      sCarrier.Append("-- " + ControlChars.CrLf)
      sCarrier.Append("" + ControlChars.CrLf)
      sCarrier.Append("SET FOREIGN_KEY_CHECKS=0;" + ControlChars.CrLf)
      sCarrier.Append("" + ControlChars.CrLf)
      sCarrier.Append("CREATE DATABASE IF NOT EXISTS " + Me.MySqlDataBase + ";" + ControlChars.CrLf)
      sCarrier.Append("USE " + Me.MySqlDataBase + ";" + ControlChars.CrLf)
      sCarrier.Append(ControlChars.CrLf)
      sCarrier.Append(ControlChars.CrLf)
      sCarrier.Append(ControlChars.CrLf)


      '/Cicla nel database ed assume il nome di tutte le tabelle in un arraylist
      dt = Me.MySqlDirectQuery("SHOW TABLE STATUS WHERE Engine IS NOT NULL")
      rows = dt.Select()
      For i = 0 To rows.GetUpperBound(0)
        If Not rows(i) Is DBNull.Value Then
          nameTableArray.Add(rows(i).Item("Name"))
          totRecords += CLng(rows(i).Item("Rows"))
        End If
      Next

      '//Per ogni elemento dell'arraylist, legge la struttura
      For i = 0 To nameTableArray.Count - 1

        sCarrier.Append("" + ControlChars.CrLf)
        sCarrier.Append("-- " + ControlChars.CrLf)
        sCarrier.Append("--  " + MSG_16 + nameTableArray(i).ToString + ControlChars.CrLf)
        sCarrier.Append("-- " + ControlChars.CrLf)
        sCarrier.Append("DROP TABLE IF EXISTS " + nameTableArray(i).ToString + ";" + ControlChars.CrLf)
        sCarrier.Append(Me.MySqlShowCreateTable(nameTableArray(i).ToString) + ";" + ControlChars.CrLf)
        sCarrier.Append(ControlChars.CrLf)

        Dim currentTable As String = nameTableArray(i).ToString
        Dim strBuffer As String = ""
        Dim strCurLine As String = ""

        '//Legge i nomi delle colonne dalla tabella
        dColumnsName = Me.MySqlDirectQuery("SHOW COLUMNS FROM " + Me.MySqlDataBase + "." + currentTable)
        rowsColumnsName = dColumnsName.Select
        For q = 0 To rowsColumnsName.GetUpperBound(0)
          If Not rowsColumnsName(q) Is DBNull.Value Then
            strColumnsName += rowsColumnsName(q)(0).ToString + ","
          End If
        Next
        strColumnsName = Mid(strColumnsName, 1, strColumnsName.Length - 1)

        '//Legge tutti i campi della tabella
        dt = Me.MySqlDirectQuery("SELECT * FROM " + currentTable + " ")

        sCarrier.Append("-- " + ControlChars.CrLf)
        sCarrier.Append("-- " + MSG_17 + currentTable + ControlChars.CrLf)
        sCarrier.Append("-- " + ControlChars.CrLf)
        sCarrier.Append("ALTER TABLE " + currentTable + " DISABLE KEYS;" + ControlChars.CrLf)
        sCarrier.Append("LOCK TABLES " + currentTable + " WRITE;" + ControlChars.CrLf)
        My.Computer.FileSystem.WriteAllText(Testo, sCarrier.ToString, True)

        '///////////////LETTURA RECORDS INIZIO
        'Azzera le variabili di gruppo
        sCarrier.Length = 0
        InsertGroup = ""
        RecCounter = 0
        'Se esistono righe nella tabella corrente...
        rows = dt.Select
        If rows.Length > 0 Then



          InsertGroup = "INSERT INTO " + currentTable + "(" + strColumnsName + ") VALUES " + _
                        ControlChars.CrLf
          sCarrier.Append(InsertGroup)
          'Per ogni riga (j) della tabella corrente, legge i campi (y)...
          For j = 0 To rows.GetUpperBound(0)
            For y = 0 To rows(j).ItemArray.GetUpperBound(0)
              strBuffer = ParBackup(rows(j).ItemArray(y))
              If strBuffer.Length <> 0 Then
                strCurLine += strBuffer + ","
              Else
                strCurLine += "NULL" + ","
              End If
            Next y
            If strCurLine.Length <> 0 Then
              strCurLine = Mid(strCurLine, 1, strCurLine.Length - 1)
              strCurLine = "(" + strCurLine + ")," + ControlChars.CrLf
              sCarrier.Append(strCurLine)
              recFlag = True
            End If
            strBuffer = ""
            strCurLine = ""

            numCurrentRecords += 1
            overLongProgress = CLng(numCurrentRecords * 100 / totRecords)

            If overLongProgress > 100 Then
              overLongProgress = 100
            End If
            RaiseEvent SendProgressBackup(overLongProgress)
            RecCounter += 1


            'Scrive i records a gruppi pari al valore di RecCounter
            If RecCounter >= 20 Then
              strFlush = Mid(sCarrier.ToString, 1, sCarrier.ToString.Length - 3) + ";" + _
                         ControlChars.CrLf
              My.Computer.FileSystem.WriteAllText(Testo, strFlush, True)
              sCarrier.Length = 0
              sCarrier.Append(InsertGroup)
              RecCounter = 0
              recFlag = False
            End If
          Next j
        End If

        'Prima di passare alla tabella successiva, verifica se ci sono record da scrivere nel buffer 
        'di(sCarrier)
        If Not recFlag Then
          sCarrier.Length = 0
        Else
          '// Ci sono records da scrivere
          strFlush = Mid(sCarrier.ToString, 1, sCarrier.ToString.Length - 3) + ";" + ControlChars.CrLf
          My.Computer.FileSystem.WriteAllText(Testo, strFlush, True)
          sCarrier.Length = 0
          recFlag = False
        End If

        sCarrier.Append("UNLOCK TABLES;" + ControlChars.CrLf)
        sCarrier.Append("ALTER TABLE " + currentTable + " ENABLE KEYS;" + ControlChars.CrLf)
        My.Computer.FileSystem.WriteAllText(Testo, sCarrier.ToString, True)
        sCarrier.Length = 0
        strColumnsName = ""
      Next i
      '/////////////// LETTURA RECORDS FINE

      '//Se esistono View nel database, ne scrive la struttura 
      dt = Me.MySqlDirectQuery("SHOW TABLE STATUS WHERE Engine IS NULL")
      rows = dt.Select()
      If rows.Length > 0 Then
        sCarrier.Append("-- " + ControlChars.CrLf)
        sCarrier.Append("-- Definizione delle viste" + ControlChars.CrLf)
        sCarrier.Append("-- " + ControlChars.CrLf)
        sCarrier.Append("" + ControlChars.CrLf)
        For i = 0 To rows.GetUpperBound(0)
          If Not rows(i) Is DBNull.Value Then
            nameViewArray.Add(rows(i).Item("Name"))
            sCarrier.Append("" + ControlChars.CrLf)
            sCarrier.Append("-- Struttura della vista " + nameViewArray(i).ToString + ControlChars.CrLf)
            sCarrier.Append("" + ControlChars.CrLf)
            sCarrier.Append("DROP VIEW IF EXISTS " + nameViewArray(i).ToString + ";" + ControlChars.CrLf)
            sCarrier.Append(Me.MySqlShowCreateTable(nameViewArray(i).ToString) + ";" + ControlChars.CrLf)
            My.Computer.FileSystem.WriteAllText(Testo, sCarrier.ToString, True)
            sCarrier.Length = 0
          End If
        Next
      End If

      '//Se esistono Stored procedures nel database, ne scrive la struttura
      dt = Me.MySqlDirectQuery("SHOW PROCEDURE STATUS WHERE DB='" + Me.MySqlDataBase + "'")
      rows = dt.Select()
      If rows.Length > 0 Then
        sCarrier.Append("" + ControlChars.CrLf)
        sCarrier.Append("" + ControlChars.CrLf)
        sCarrier.Append("-- " + ControlChars.CrLf)
        sCarrier.Append("-- Definizione delle stored procedures" + ControlChars.CrLf)
        sCarrier.Append("-- " + ControlChars.CrLf)
        sCarrier.Append("" + ControlChars.CrLf)
        sCarrier.Append("" + ControlChars.CrLf)
        For i = 0 To rows.GetUpperBound(0)
          If Not rows(i) Is DBNull.Value Then
            nameStoredArray.Add(rows(i).Item("Name"))
            sCarrier.Append("" + ControlChars.CrLf)
            sCarrier.Append("-- Struttura della stored procedure " + nameStoredArray(i).ToString + _
                            ControlChars.CrLf)
            sCarrier.Append("" + ControlChars.CrLf)
            sCarrier.Append("DROP PROCEDURE IF EXISTS " + nameStoredArray(i).ToString + ";" + _
                            ControlChars.CrLf)
            sCarrier.Append(Me.MySqlShowCreateStored(nameStoredArray(i).ToString) + ";" + _
                            ControlChars.CrLf)
            My.Computer.FileSystem.WriteAllText(Testo, sCarrier.ToString, True)
            sCarrier.Length = 0
          End If
        Next
      End If

      '//Se esistono funzioni nel database, ne scrive la struttura
      dt = Me.MySqlDirectQuery("SHOW FUNCTION STATUS WHERE DB='" + Me.MySqlDataBase + "'")
      rows = dt.Select()
      If rows.Length > 0 Then
        sCarrier.Append("" + ControlChars.CrLf)
        sCarrier.Append("" + ControlChars.CrLf)
        sCarrier.Append("-- " + ControlChars.CrLf)
        sCarrier.Append("-- Definizione delle funzioni" + ControlChars.CrLf)
        sCarrier.Append("-- " + ControlChars.CrLf)
        sCarrier.Append("" + ControlChars.CrLf)
        sCarrier.Append("" + ControlChars.CrLf)
        For i = 0 To rows.GetUpperBound(0)
          If Not rows(i) Is DBNull.Value Then
            nameFuncArray.Add(rows(i).Item("Name"))
            sCarrier.Append("" + ControlChars.CrLf)
            sCarrier.Append("-- Struttura della funzione " + nameFuncArray(i).ToString + _
                            ControlChars.CrLf)
            sCarrier.Append("" + ControlChars.CrLf)
            sCarrier.Append("DROP FUNCTION IF EXISTS " + nameFuncArray(i).ToString + ";" + _
                            ControlChars.CrLf)
            sCarrier.Append(Me.MySqlShowCreateFunction(nameFuncArray(i).ToString) + ";" + _
                            ControlChars.CrLf)
            My.Computer.FileSystem.WriteAllText(Testo, sCarrier.ToString, True)
            sCarrier.Length = 0
          End If
        Next
      End If
      '//Riallinea le foreign key e termina
      sCarrier.Length = 0
      sCarrier.Append("" + ControlChars.CrLf)
      sCarrier.Append("SET FOREIGN_KEY_CHECKS=1;" + ControlChars.CrLf)
      sCarrier.Append("--" + ControlChars.CrLf)
      sCarrier.Append("--" + ControlChars.CrLf)
      sCarrier.Append("" + ControlChars.CrLf)
      sCarrier.Append("-- " + MSG_18 + Format(Today, "dd/MM/yyyy") + " " + _
                      Format(TimeOfDay, "HH:mm:ss") + ControlChars.CrLf)

      My.Computer.FileSystem.WriteAllText(Testo, sCarrier.ToString, True)
      RaiseEvent SendProgressBackup(100)


    Catch ex As OperationCanceledException
      Exit Sub
    Catch ex As MySqlException
      Throw
    Finally
      If Not dt Is Nothing Then dt.Dispose()
    End Try
  End Sub

Perché la necessità di un una funzione di backup? MySql non è fornito di una procedura all'uopo destinata?
Sì, esiste una procedura nativa in MySql che si chiama mysqldump. Ma tale programma non è perfettamente integrato nel motore principale, bensì risiede in una propria locazione della quale bisogna preliminarmente conoscere il percorso. Funziona unicamente dalla linea di comando e non può essere raggiunto tramite la connessione. E', in pratica, un programma a parte, indipendente dal resto. Invece, normalmente, nei programmi che si occupano di gestione delle basi di dati, non dovrebbe essere richiesto di uscire dalla propria procedura per effettuare la copia degli archivi, ma il tutto dovrebbe essere perfettamente integrato nel software che si sta usando.
Questo è il motivo per cui, in MySql, si rende necessario scrivere una routine di backup.

Per fortuna il backup di MySql è costituito da un semplice file di testo nel quale sono riportate le strutture degli oggetti ed i dati in essi contenuti.

Il metodo di libreria MySqlBackupSingleDB fa proprio questo: legge il database e scrive in un file di testo, gestito tramite l'oggetto My, sia la struttura delle tabelle, viste, stored ecc, ed i dati in tali oggetti contenuti.
Tale metodo è in grado di eseguire il backup singolo dei database operativi.
Ciò vuol dire che non sarà possibile ottere il backup del motore mysql, né il backup complessivo di tutto l'ambiente.
Nonostante l'apparente complessità del tutto, non bisogna lasciarsi ingannare dalla lunghezza del codice. Infatti, tutte le righe di codice che iniziano con "- -" sono dei commenti che potrebbero essere omessi ai fini della funzionalità della routine.
Al metodo va passato il nome che si vuole dare al file di backup (compreso di percorso). Il codice, dopo aver aperto in scrittura un file di testo (tramite l'oggetto My), scrive in esso una serie di intestazioni (in forma di commenti, quindi omissibili).
Tutte le operazioni di scrittura vengono effettuate ricorrendo all'oggetto StringBuilder, che risulta più adatto, allo scopo, della semplice String.
Togliendo il superfluo dal metodo, il funzionamento, in sintesi, è il seguente, avendo cura di notare che ogni operazione viene scritta nel file di testo creato dall'oggetto My:

Dopo aver salvato tutte le tabelle, nella loro struttura e dati, analizza il database per verificare la presenza di altri oggetti quali viste, stored e funzioni.
Se tali oggetti sono presenti, ne crea un elenco, attiva un ciclo basato sul nome, scrive nel file una direttiva di cancellazione (DROP VIEW|PROCEDURE|FUNCTION IF EXISTS...) e ne riscrive la struttura (solamente, perché questi oggetti non contengono dati) mediante i metodi rispettivi (MySqlShowCreateTable, MySqlShowCreateStored, MySqlShowCreateFunction).
Giunti a questo punto, non rimane che riattivare le FOREIGN_KEY e scrivere alcuni commenti di chiusura (per esempio, la data del backup).

Esempio d'uso di backup:

' VB
Option Strict On

Public Class Form1
  '// Aprire un riferimento a MySqlDataConnect.dll
  Dim cl As New MySqlDataConnect.MyClient

  Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    '//Aprire la connessione (cambiare la password, se necessario)

    AddHandler cl.SendProgressBackup, AddressOf VediEventoB

    Try
      cl.MySqlServer = "localhost"
      cl.MySqlUID = "root"
      cl.MySqlPWD = "root"
      cl.MySqlDataBase = "prova"
      cl.MySqlConnect()

    Catch ex As Exception
      MessageBox.Show("Errore", ex.Message)
    End Try
  End Sub '//Fine connessione

  Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
    Try
      cl.MySqlBackupSingleDB("C:\backup\B1.sql")
            MessageBox.Show("Backup effettuato con successo", "Fine backup", MessageBoxButtons.OK,
            MessageBoxIcon.Information)
    Catch ex As Exception
      MessageBox.Show(ex.Message)
    End Try

  End Sub

  '//PB1 è una ProgressBar pilotata dall'evento SendProgressBackup
  Private Sub VediEventoB(ByVal barValue As Long)
    PB1.Value = CInt(barValue)
  End Sub
End Class
//C#
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace backup
{
  // Aprire un riferimento a MySqlDataConnect.dll
  public partial class Form1 : Form
  {
    //Creare un riferimento alla libreria
    MySqlDataConnect.MyClient cl = new MySqlDataConnect.MyClient();

    public Form1()
    {
      //Associazione evento al gestore eventi
      cl.SendProgressBackup += new
      MySqlDataConnect.MyClient.SendProgressBackupEventHandler(VediEventoB);
      InitializeComponent();
      init();

    }
    //Aprire la connessione (cambiare la password, se necessario)
    private void init()
    {
      try
      {
        cl.MySqlServer = "localhost";
        cl.MySqlUID = "root";
        cl.MySqlPWD = "root";
        cl.MySqlDataBase = "prova";
        cl.MySqlConnect();
      }
      catch (System.Exception e)
      {
        MessageBox.Show("Errore" + e.Message);
      }
    }//Fine connessione

    private void button1_Click_1(object sender, EventArgs e)
    {
      try
      {
        cl.MySqlBackupSingleDB(@"c:\backup\B2.sql");
        MessageBox.Show("Backup effettuato con successo", "Fine backup", MessageBoxButtons.OK,
                        MessageBoxIcon.Information);

      }
      catch (System.Exception ex)
      {
        MessageBox.Show("Errore" + ex.Message);
      }
    }

    //PB1 è una ProgressBar pilotata dall'evento SendProgressBackup
    private void VediEventoB(long barValue)
    {
      PB1.Value = (int)barValue;
    }

  }
}

Restore

  Public Sub MySqlRestoreSingleDB(ByVal Testo As String)
    Try
      Dim sr As StreamReader = New StreamReader(Testo)
      Dim lines As String = ""
      Dim s As New System.Text.StringBuilder("")
      Dim t As New System.Text.StringBuilder("")
      Dim infoReader As System.IO.FileInfo
      Dim fileLen As Long = 0
      Dim lenLine As Long = 0
      Dim isStored As Boolean = False


      '//Ottiene la lunghezza in byte del file
      infoReader = My.Computer.FileSystem.GetFileInfo(Testo)
      fileLen = infoReader.Length


      Do
        lines = sr.ReadLine()
        Select Case Mid(lines, 1, 1)
          Case Is = ""
            Continue Do
          Case Is = "-"
            Continue Do
          Case Is = "/"
            Continue Do
          Case Else

            If lines.StartsWith("CREATE DEFINER") Then
              isStored = True
            End If

            Select Case isStored
              Case False
                s.Append(lines)
                If lines.EndsWith(";") Then
                  lenLine += s.ToString.Length
                  Me.MySqlSender(s.ToString)
                  s.Length = 0
                  RaiseEvent SendProgressRestore(CLng(lenLine * 100 / fileLen))
                End If
              Case True
                t.Append(lines + ControlChars.CrLf)
                If lines.ToUpper = "END;" Then
                  lenLine += t.ToString.Length
                  Me.MySqlSender(Mid(t.ToString, 1, t.ToString.Length - 1))
                  t.Length = 0
                  isStored = False
                  RaiseEvent SendProgressRestore(CLng(lenLine * 100 / fileLen))
                End If
            End Select

        End Select
      Loop Until lines Is Nothing
      RaiseEvent SendProgressRestore(100)
      sr.Close()

    Catch ex As MySqlException
      Throw
    End Try
  End Sub

Il restore è molto più semplice, e si basa sulle seguenti considerazioni.
Tutte le righe nel file di testo create con il backup, se iniziano con "CREATE DEFINER" terminano con "END;" e sono relative alle stored ed alle funzioni. Altrimenti, terminano con il solo carattere ";" e sono relative alle tabelle, ai dati delle stesse ed alle viste.
Nel primo caso, dopo aver aperto uno Stream sul file di testo, si analizza l'inizio della linea.
Se inizia con "CREATE DEFINER" è una procedura (stored o funzione), per cui si continua a leggere fino a che non si incontra un "END;". Una variabile di tipo booleano (isStored) si occupa di operare la discriminazione.
Uno StringBuilder viene caricato con il blocco di istruzioni fino al termine delle stesse ("END;") e viene inviato, tramite MySqlSender, alla linea di comando per la scrittura.
Nel contempo viene attivato il conteggio delle linee, il cui valore viene recuperato dall'evento SendProgessRestore, per il pilotaggio della barra di avanzamento.
Se invece la linea prelevata dallo Stream non inizia con "CREATE DEFINER", essa è relativa ad una tabella, vista o dati di tabella create con "INSERT INTO..." e terminerà con un ";".
Di nuovo, viene caricata uno StringBuilder con un blocco di dati terminati da un ";" che verranno inviati al motore MySql, sempre tramite MySqlSender, per la scrittura.
Anche in questo caso, si attiverà l'evento SendProgressRestore, per l'avanzamento della barra.
E così si proseguirà fino alla fine del file.

Esempio di restore (speculare a quello del backup mostrato sopra):

' VB
Option Strict On

Public Class Form1
  '// Aprire un riferimento a MySqlDataConnect.dll
  Dim cl As New MySqlDataConnect.MyClient

  Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    '//Aprire la connessione (cambiare la password, se necessario)
    '//Associazione dell'evento al gestore eventi
    AddHandler cl.SendProgressRestore, AddressOf VediEventoR

    Try
      cl.MySqlServer = "localhost"
      cl.MySqlUID = "root"
      cl.MySqlPWD = "root"
      cl.MySqlDataBase = "prova"
      cl.MySqlConnect()

    Catch ex As Exception
      MessageBox.Show("Errore", ex.Message)
    End Try
  End Sub '//Fine connessione

  Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
    Try
      cl.MySqlRestoreSingleDB("C:\backup\B1.sql")
      MessageBox.Show("Restore effettuato con successo", "Fine restore", MessageBoxButtons.OK, _
                      MessageBoxIcon.Information)
    Catch ex As Exception
      MessageBox.Show(ex.Message)
    End Try

  End Sub

  '//PB1 è una ProgressBar pilotata dall'evento SendProgressRestore
  Private Sub VediEventoR(ByVal barValue As Long)
    PB1.Value = CInt(barValue)
  End Sub
End Class

Data l'identità con il backup, si omette il codice in C#

Transazioni

Prima di affrontare il tema delle transazioni, sono necessarie alcune precisazioni.
Lo storage engine di default di MySql è MyISAM. Questo motore di database non supporta le transazioni (almeno per il momento) ma soltanto il LOCK sulle tabelle.
Affinché sia possibile usufruire delle transazioni, è necessario l'uso dello storage engine InnoDB o BDB. Vi sono pure altri motori utilizzabili con MySql, ma tutto ciò esula dalla presente trattazione, per cui si rinvia alla letteratura tecnica specifica coloro i quali volessero approfondire l'argomento. Sebbene sia possibile utilizzare, nello stesso database, tabelle impostate su diversi storage engine, si sconsiglia l'uso di questa pratica.
Nell'illustrare l'uso delle transazioni, si farà riferimento alle tabelle InnoDB, sicuramente le più usate a tale scopo in ambiente MySql.
Per creare tabelle InnoDB, è sufficiente aggiungere la stringa "ENGINE=InnoDB;" dopo la parentesi di chiusura della definizione dello script di tabella.
Per utilizzare le transazioni, faremo riferimento a due proprietà (MySqlIsolation, MySqlTransIsPending) e a tre metodi (MySqlBeginTrans, MySqlCommit, MySqlRollback).

MySqlIsolation
Imposta e restituisce il valore dello stato di isolamento della transazione
I valori che può assumere la proprietà, sono i seguenti:

REPETEABLE READ Specifica che le istruzioni non possono leggere dati modificati da altre transazioni di cui non è ancora stato eseguito il commit e che nessun'altra transazione può modificare i dati letti dalla transazione corrente, fino al completamento della transazione stessa.
READ COMMITTED Specifica che le istruzioni non possono leggere le righe modificate da altre transazioni ma di cui non è ancora stato eseguito il commit. In questo modo si evitano letture dirty. Altre transazioni possono modificare i dati nell'intervallo tra le singole istruzioni della transazione corrente, con conseguenti letture irripetibili e la presenza di dati fantasma.
READ UNCOMMITTED Specifica che le istruzioni possono leggere le righe modificate da altre transazioni ma di cui non è ancora stato eseguito il commit.
SERIALIZABLE E' il caso più restrittivo. La lettura di un dato provoca il blocco totale degli aggiornamenti fino alla chiusura della transazione.

Il valore di default della proprietà, cioè il valore che assume la transazione in atto, in assenza di attivazione della proprietà stessa, è REPETEABLE READ.
I rimanenti valori che può assumere la proprietà, e cioè Chaos, Snapshot e Unspecified, poichè non supportati da MySql, sono settati automaticamente al valore di default.
Una volta impostata la proprietà a un valore specifico, essa rimane per tutta la durata della connessione, salvo nuovo assegnamento in prossimità di altra transazione.

  Sintassi: Classe.MySqlIsolation = IsolationLevel.[valore]

Esempi:

    'VB
    Dim cl As New MyClient
    '...
    cl.MySqlIsolation = IsolationLevel.Serializable
    cl.MySqlBeginTrans()
    '...
    '    (varie istruzioni)
    '...
    cl.MySqlCommit()
      //C#
      //...
      cl.MySqlIsolation = IsolationLevel.Serializable;
      cl.MySqlBeginTrans();
      //...
      //    (varie istruzioni)
      //...
      cl.MySqlCommit();

MySqlBeginTrans

  Public Sub MySqlBeginTrans()

    Try
      If IsPending Then Exit Sub
      Select Case Me.m_MySqlIsolation.ToString
        Case "16"  'Chaos
          Me.MySqlSender("SET TRANSACTION ISOLATION LEVEL REPEATABLE READ")
        Case "16777216" 'Snapshot
          Me.MySqlSender("SET TRANSACTION ISOLATION LEVEL REPEATABLE READ")
        Case "-1" 'Unspecified
          Me.MySqlSender("SET TRANSACTION ISOLATION LEVEL REPEATABLE READ")
        Case "4096" ' ReadCommitted
          Me.MySqlSender("SET TRANSACTION ISOLATION LEVEL READ COMMITTED")
        Case "256" ' ReadUncommitted
          Me.MySqlSender("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED")
        Case "65536" ' RepeatableRead ---Default
          Me.MySqlSender("SET TRANSACTION ISOLATION LEVEL REPEATABLE READ")
        Case "1048576" ' Serializable
          Me.MySqlSender("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE")
      End Select

      Me.MySqlSender("START TRANSACTION")
      IsPending = True
    Catch ex As MySqlException
      Throw
    End Try
  End Sub

Il metodo inizia una nuova transazione.
All'inizio del codice viene analizzata la variabile IsPending, impostata dalla proprietà MySqlTransIsPending. Se vale True, significa che è già attiva un'altra transazione, per cui impedisce l'avvio della nuova e abbandona. Altrimenti, recupera il valore del livello di isolamento corrente dalla proprietà MySqlIsolation e la trasmette al server (tramite MySqlSender), subito dopo attiva la transazione propriamente detta, tramite la trasmissione al server di "START TRANSACTION" (sempre tramite MySqlSender). IsPending viene settata a True, a indicare una transazione in corso.
Per gli esempi, si fa riferimento a quanto riportato sopra per MySqlIsolation.

MySqlCommit e MySqlRollback

  Public Sub MySqlCommit()
    If Not IsPending Then Exit Sub
    Try
      Me.MySqlSender("COMMIT")
    Catch ex As MySqlException
      Me.MySqlRollback()
      Throw
    Finally
      IsPending = False
    End Try
  End Sub
 
  Public Sub MySqlRollback()
    If Not IsPending Then Exit Sub
    Try
      Me.MySqlSender("ROLLBACK")
    Catch ex As MySqlException
      Throw
    Finally
      IsPending = False
    End Try
  End Sub

Le due routine sono identiche, per cui verranno trattate insieme.
La prima delle due, MySqlCommit, consolida la transazione, mentre MySqlRollback l'annulla.
Ambedue agiscono se è attiva una transazione, cioè se IsPending vale True. Una volta eseguito il comando, IsPending viene di nuovo posta a False, ad indicare la conclusione della transazione corrente.

Esempio applicativo
L'esempio che segue - che utilizza solo alcune delle funzioni basilari della libreria - sia per VB che per C#, presuppone l'utilizzo della libreria MySqlDataConnect.dll, da scaricare e già pronta per l'uso.
E' sufficiente aprire un riferimento alla detta libreria, in quanto la stessa ricomprende al suo interno la libreria MySql MySql.Data.Dll.
Qualora invece, si volesse utilizzare la classe MyClient in sorgente, è necessario scaricare la libreria MySql.Data.Dll dal sito di MySql (utilizzare almeno la versione 5.1.3.0), impostare una direttiva Imports (o using per C#) a MySql.Data.MySqlClient e referenziare la classe MyClient.
In ambedue i casi, creare un form con una casella di testo multilinea da chiamare txtTesto, una DataGridView da chiamare Grid1 ed un bottone da chiamare cmdEsegui. Quindi incollare il codice ed avviare il programma, dopo aver preso nota dei commenti esplicativi.

'VB
Option Strict On
Public Class Form1
  '/* Apre un riferimento a MySqlDataConnect.dll */
  Dim cl As New MySqlDataConnect.MyClient

  Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    '//Aprire la connessione (cambiare la password, se necessario)
    Try
      cl.MySqlServer = "localhost"
      cl.MySqlUID = "root"
      cl.MySqlPWD = "root"
      cl.MySqlDataBase = "mysql"
      cl.MySqlConnect()
      demo()

    Catch ex As Exception
      MessageBox.Show("Errore", ex.Message)
    End Try
  End Sub '//Fine connessione

  Private Sub demo()
    '/* Crea un nuovo database, chiamato 'DBTest', con una tabella chiamata 'tab1'
    ' * che contiene quattro campi: stringa, data, double e un campo contatore */
    Try
      cl.MySqlSender("CREATE DATABASE IF NOT EXISTS DBTest")
      '//Si crea la tabella, se non esiste, con Engine InnoDb
      Dim s As String = "CREATE TABLE IF NOT EXISTS DBTest.tab1" + _
                  "(nome VARCHAR(25) default NULL," + _
                  "data DATE default NULL," + _
                  "importo DOUBLE default NULL," + _
                  "id INT NOT NULL AUTO_INCREMENT," + _
                  "PRIMARY KEY(id)) ENGINE=InnoDB"
      cl.MySqlSender(s)
      '/* Si crea ora, un utente del database 'DBTest', a cui si assegnano tutti
      '* i privilegi sul database 'DBTest', ad eccezione della possibilità di trasferire
      '* ad altri i propri privilegi. L'utente ha nome 'vbuser' e password 'vb2007'
      '* Notare che il server viene definito come '%' (wildcard), ciò significa
      '* che l 'accesso per vbuser potrà avvenire da qualsiasi client in rete */
      s = "GRANT ALL PRIVILEGES ON DBTest.* TO 'vbuser'@'%' IDENTIFIED BY 'vb2007'"
      cl.MySqlSender(s)
      '/*A questo punto, è stato creato il database operativo, la tabella ed un utente
      ' * abilitato ad operare su di esso. La connessione all'utente 'root' non è più necessaria
      ' * quindi può essere chiusa ed aperta sul nuovo utente creato.
      ' * (Ovviamente, la vecchia connessione potrebbe essere conservata sull'utente
      ' * 'root', ed aprire una nuova connessione sul nuovo utente) */
      cl.MySqlConnectionClose()

      '/* A titolo di esempio, si utilizzi un metodo alternativo per aprire la nuova
      '* connessione. Se l'accesso non avviene da 'localhost', indicare il nome
      '* del server */
      cl.MySqlConnect("Persist Security Info=false;server=localhost;user id=vbuser;" + _
                                     "password=vb2007;database=DBTest")
      ' /*La connessione è aperta sul nuovo utente e sul nuovo database.
      ' * E' ora possibile operare sulla tabella tab1, inserendo i comandi
      ' * nella casella di testo, ed inviandoli tramite il pulsante "cmdEsegui" */
    Catch ex As Exception
      MessageBox.Show("Errore" + ex.Message)
    End Try
  End Sub  ' //Fine creazione oggetti database

  '/*Si inseriranno dei dati nella tabella utilizzando una query parametrica.
  '* Nella istruzione INSERT, da scrivere nella casella di testo,
  '* indicare i parametri con il segnaposto "?".
  '* Es. INSERT INTO tab1(nome,data,importo) values(?,?,?).
  '* I parametri verranno trasportati da un ArrayList.
  '* Se la prima lettera della stringa nella casella di testo è una 'S' o 's',
  '* si tratterà di una query di selezione, altrimenti sarà di modifica.
  '* Se si tratta di una query di selezione, a titolo di esempio, verrà
  '* utilizzata una query diretta (senza parametri), e quindi scrivere nella casella di testo
  '* qualcosa come "SELECT * FROM Tab1" oppure "SELECT data FROM tab1
  '* WHERE nome='D'Azeglio'" ecc.
  '*  SIA CHE SI TRATTI DI SELECT O INSERT, UPDATE ecc., I PARAMETRI
  '* SE PRESENTI DOVRANNO ESSERE TUTTI UTILIZZATI E NELLA SEQUENZA
  '* 1 A 1 */
    Private Sub cmdEsegui_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
                                Handles cmdEsegui.Click()
    Try
      Grid1.AutoGenerateColumns = True
      Dim dt As DataTable
      Dim s As String = txtTesto.Text
      If (s.StartsWith("S", StringComparison.InvariantCultureIgnoreCase)) Then
        dt = cl.MySqlDirectQuery(s)
        Grid1.DataSource = dt
      Else
        Dim w As New ArrayList()
        w.Add("Rossini")
        w.Add(System.Convert.ToDateTime("29/02/1792"))
        w.Add(126598.52)
        '//modifica la tabella e successivamente effettua una select generale
        cl.MySqlDirectMod(s, w)
        dt = cl.MySqlDirectQuery("select * from tab1")
        Grid1.DataSource = dt
        w.Clear()
      End If

    Catch ex As Exception
      MessageBox.Show("Errore", ex.Message)
    End Try
  End Sub

  Private Sub Form1_FormClosed(ByVal sender As Object, _
                               ByVal e As System.Windows.Forms.FormClosedEventArgs) _
                               Handles Me.FormClosed
    cl.MySqlConnectionClose()
  End Sub
End Class
//C#
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace WindowsApplication1
{
  /* Aprire un riferimento a MySqlDataConnect.dll */
  public partial class Form1 : Form
  {
    //Crea un riferimento alla libreria
    MySqlDataConnect.MyClient cl = new MySqlDataConnect.MyClient();
    public Form1()
    {
      InitializeComponent();
      init();
      demo();
    }
    //Aprire la connessione (cambiare la password, se necessario)
    private void init()
    {
      try
      {
        cl.MySqlServer = "localhost";
        cl.MySqlUID = "root";
        cl.MySqlPWD = "root";
        cl.MySqlDataBase = "mysql";
        cl.MySqlConnect();
      }
      catch (System.Exception e)
      {
        MessageBox.Show("Errore" + e.Message);
      }
    }     //Fine connessione

    /* Crea un nuovo database, chiamato 'DBTest', con una tabella chiamata 'tab1'
        che contiene quattro campi: stringa, data, double e un campo contatore */
    private void demo()
    {
      try
      {
        cl.MySqlSender("CREATE DATABASE IF NOT EXISTS DBTest");
        //Si crea la tabella, se non esiste, con Engine InnoDb
        string s = "CREATE TABLE IF NOT EXISTS DBTest.tab1" +
                    "(nome VARCHAR(25) default NULL," +
                    "data DATE default NULL," +
                    "importo DOUBLE default NULL," +
                    "id INT NOT NULL AUTO_INCREMENT," +
                    "PRIMARY KEY(id)) ENGINE=InnoDB";
        cl.MySqlSender(s);
        /* Si crea ora, un utente del database 'DBTest', a cui si assegnano tutti
            i privilegi sul database 'DBTest', ad eccezione della possibilità di trasferire
            ad altri i propri privilegi. L'utente ha none 'c#user' e password 'c#2007'
            Notare che il server viene definito come '%' (wildcard), ciò significa
           che l'accesso per c#user potrà avvenire da qualsiasi client in rete */
        s = "GRANT ALL PRIVILEGES ON DBTest.* TO 'c#user'@'%' IDENTIFIED BY 'c#2007'";
        cl.MySqlSender(s);

        /*A questo punto, è stato creato il database operativo, la tabella ed un utente
         * abilitato ad operare su di esso. La connessione all'utente 'root' non è più
         necessaria
         * quindi può essere chiusa ed aperta sul nuovo utente creato.
         * (Ovviamente, la vecchia connessione potrebbe essere conservata sull'utente
         * 'root', ed aprire una nuova connessione sul nuovo utente) */
        cl.MySqlConnectionClose();
        /* A titolo di esempio, si utilizzi un metodo alternativo per aprire la nuova
         * connessione. Se l'accesso non avviene da 'localhost', indicare il nome
         * del server */
        cl.MySqlConnect("Persist Security Info=false;server=localhost;user id=c#user;" +
                               "password=c#2007;database=DBTest");
        /*La connessione è aperta sul nuovo utente e sul nuovo database.
         * E' ora possibile operare sulla tabella tab1, inserendo i comandi
         * nella casella di testo, ed inviandoli tramite il pulsante "cmdEsegui" */
      }
      catch (System.Exception e)
      {
        MessageBox.Show("Errore" + e.Message);
      }
    }     //Fine creazione oggetti database

    /*Si inseriranno dei dati nella tabella utilizzando una query parametrica.
     * Nella istruzione INSERT, da scrivere nella casella di testo,
     * indicare i parametri con il segnaposto "?".
     * Es. INSERT INTO tab1(nome,data,importo) values(?,?,?).
     * I parametri verranno trasportati da un ArrayList.
     * Se la prima lettera della stringa nella casella di testo è una 'S' o 's',
     * si tratterà di una query di selezione, altrimenti sarà di modifica.
     * Se si tratta di una query di selezione, a titolo di esempio, verrà
     * utilizzata una query diretta (senza parametri), e quindi scrivere nella casella di testo
     * qualcosa come "SELECT * FROM Tab1" oppure "SELECT data FROM tab1
     * WHERE nome='D'Azeglio'" ecc.
     *  SIA CHE SI TRATTI DI SELECT O INSERT, UPDATE ecc., I PARAMETRI
     * SE PRESENTI DOVRANNO ESSERE TUTTI UTILIZZATI E NELLA SEQUENZA
     * 1 A 1 */

    private void cmdEsegui_Click_1(object sender, EventArgs e)
    {
      try
      {
        Grid1.AutoGenerateColumns = true;
        DataTable dt = new DataTable();
        string s = txtTesto.Text;
        if (s.StartsWith("S", StringComparison.InvariantCultureIgnoreCase))
        {
          dt = cl.MySqlDirectQuery(s);
          Grid1.DataSource = dt;
        }
        else
        {
          ArrayList w = new ArrayList();
          w.Add("D'Azeglio");
          w.Add(System.Convert.ToDateTime("10/10/1813"));
          w.Add(126598.52);
          //modifica la tabella e successivamente effettua una select generale
          cl.MySqlDirectMod(s, w);
          dt = cl.MySqlDirectQuery("select * from tab1");
          Grid1.DataSource = dt;
          w.Clear();
        }
      }
      catch (System.Exception ex)
      {
        MessageBox.Show("Errore" + ex.Message);
      }
    }
    private void Form1_FormClosed(object sender, FormClosedEventArgs e)
    {
      cl.MySqlConnectionClose();
    }
  }
}

Conclusioni
La trattazione della libreria MyClient è così terminata.
Nel file.zip a corredo dell'articolo, scaricabile dall'area download, è compreso non solo il sorgente della classe MyClient (MyClient.vb), ma anche il file di setup per la shell per MySql (MySqlAdmin.msi), che consiglio di installare, concepita quale test della libreria, anch'essa fornita (MySqlDataConnect.dll), onde valutarne le possibilità complessive.
Nella speranza di aver dato un piccolo contributo alla comunità degli sviluppatori, resto a disposizione all'indirizzo email lino.aiello@gmail.com per chiarimenti, consigli e quant'altro (non dimenticando gli errori!).

Note sull'Autore
Pasquale Aiello (detto Lino) vive e lavora a Pescara, dove è nato molto tempo fa (1947), e dove svolge la professione di Dottore Commercialista - Revisore dei Conti.
Si occupa di informatica fin dai primi anni 80', e ha utilizzato quasi tutti gli ambienti di programmazione, a partire da MSBasic, Clipper, C/C++ ed altri. A causa del suo lavoro, è stato impegnato principalmente in ambito gestionale. Ha realizzato procedure informatiche per Comuni ed altri Enti Pubblici. Attualmente ha focalizzato la sua attività lavorativa, come CTU/CTP, nel campo del contenzioso bancario (Anatocismo, reverse banking, modifica condizioni di utilizzo e ricalcolo su C/C, ecc.ecc.) utilizzando procedure proprietarie.
Nel poco tempo libero, coltiva come hobbies la lettura (saggistica e varia umanità) e la musica (preferibilmente classica).