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

Premessa
Dopo anni di utilizzo di SQLServer/MSDE, mi sono imbattuto nel motore MySql, disponibile in ambiente Windows.
Erano allora gli anni di COM, l'ambiente .NET non esisteva ancora, per cui scrissi un accenno di libreria che potesse interfacciarsi a MySql tramite ADO e ODBC.
Rimasi subito favorevolmente sorpreso dell'efficacia di questo motore per database, per quanto fosse necessario interporre ODBC per la gestione.
In breve, potei notare quanto segue:

  1. è di facile installazione, occupa poco spazio, impiega poche risorse;
  2. è di semplice ed efficiente gestione in tutti i suoi aspetti, non esclusa la parte relativa ai privilegi
  3. è performante. E' uno dei più rapidi database esistenti
  4. è ottimamente documetato, sia dalla casa produttrice che a livello di utenza in generale.
  5. è pressochè gratuito. Infatti, atteso che l'impiego non commerciale è completamete gratuito, quello commerciale è enormemente più economico degli altri RDBMS presenti sul mercato (si pensi al costo licenza di SQL Server!)
  6. il prodotto, gratuito o meno, non soffre di limitazioni all'uso, come limiti agli accessi concorrenti, dimensione dei database ecc. (anche in questo caso, si pensi alle limitazioni SQLEXPRESS!)
  7. non ha bisogno di sistemi operativi server dedicati
  8. gira indifferentemente sia su Windows che su Linux, e considerando che Linux avrà sicuramente un grande futuro almeno come sistema server (fra l'altro, ancora una volta, trattasi di un sistema gratuito), sarà sempre più conveniente localizzare il server dei dati su una macchina Linux.
  9. ci sono tanti altri motivi per cui conviene adottare MySql...

Recentemente, la casa di MySql ha pubblicato un connettore per l'ambiente .NET che permette di collegarsi a MySql senza ODBC. Tale connettore, reso disponibile gratuitamente, come è peraltro nella filosofia della casa, viene distribuito in licenza GPL, reca il nome di MySql.Data.Dll, è giunto ormai alla versione 5.2 ed è liberamente scaricabile dal sito MySql.

Questo articolo presenta la versione .Net della mia libreria.

MyClient
La mia libreria, che ho chiamato MyClient, si appoggia a MySql.Data.Dll, ma ne utilizza, essenzialmente, solo i due oggetti di base, cioè la connessione ed il command. Per il resto, tramite funzioni e proprietà originali, gestisce MySql dalla linea di comando, semplificandone, estendendone e, in alcuni casi, modificandone le attività di base, allo scopo di rendere più agevole il suo uso.

In sede di implementazione, consiglio l'utilizzo della versione 5.1.4 o superiore di MySql.Data.Dll. e la versione 5.0.22 o 5.0.53 di MySql.

La libreria non implementa la gestione dei trigger, pur potendolo fare in linea di principio.
Infatti, la completa ed efficiente implementazione dei trigger, è stata ottenuta solo a partire dalla versione 5.1.21 di MySql, attualmente in fase di test, e non è ancora destinata alla produzione. Tali oggetti saranno inclusi in una versione successiva di MyClient.

Struttura di MyClient
Seguendo il codice della libreria, la si può dividere nelle seguenti macroaree, corrispondenti alle "Region" del codice:

  1. Intestazione e costruttore
  2. Proprietà
  3. Connessione
  4. Query di selezione (SELECT)
  5. Query di modifica (INSERT, UPDATE, DELETE, REPLACE)
  6. Stored procedure
  7. Funzioni
  8. Utility
  9. Backup e restore
  10. Transazioni

Procederemo, ora, ad illustrare la libreria, secondo l'ordine logico sopra indicato.

Intestazione e costruttore

Option Strict On
Imports MySql.Data.MySqlClient
Imports System.Text.RegularExpressions
Imports System.IO
Public Class MyClient
#Region "Intestazioni e costruttore"
  Private myConnection As New MySqlConnection ' La connessione è privata
  Private myCommand As New MySqlCommand

  Private m_MySqlServer As String = ""
  Private m_MySqlUID As String = ""
  Private m_MySqlPWD As String = ""
  Private m_MySqlDataBase As String = ""
  Private m_MySqlConnectionString As String = ""
  Private m_MySqlIsolation As System.Data.IsolationLevel
  Private m_MySqlServerVersion As String = ""

  Private Flag As Boolean = False 'Vale TRUE se la connessione è attiva
  Private IsPending As Boolean = False 'Se TRUE, indica che una transazione è pendente

  Public Event SendProgressBackup(ByVal barValue As Long)
  Public Event SendProgressRestore(ByVal barValue As Long)

  Sub New()
    m_MySqlIsolation = IsolationLevel.RepeatableRead
    m_MySqlServer = "localhost"
    m_MySqlUID = "root"
    m_MySqlPWD = ""
    m_MySqlDataBase = "mysql"
  End Sub
#End Region

Il codice della libreria si apre con l'importazione dei namespace necessari al suo funzionamento ed alle dichiarazioni delle variabili e proprietà necessarie.
Si può notare che le due variabili private, myConnection e myCommand, sono gli unici riferimenti a MySql.Data.Dll; dopo di ciò la dll di base non è più chiamata, fatta eccezione per gli oggetti System.Exception e Reader, che però potrebbero essere agevolmente sostituiti, con poco lavoro aggiuntivo, con quelli dell'ambiente di sviluppo.

Vengono inoltre dichiarate le proprietà riferentesi alla connessione (server, utente, password, livello della transazione, versione del server), la variabile Flag, usata internamente, che indica se la connessione corrente è attiva, e un'altra variabile logica che verrà meglio definita in seguito.

L'intestazione si chiude con la definizione di due eventi che verranno utilizzati per il backup ed il restore.

Il costruttore assegna semplicemente i valori di default alle proprietà di connessione, come facilmente si evince dal codice.

Proprietà
Per le proprietà c'è da dire soltanto che MySqlPWD, che trasporta la password, è (ovviamente) in sola scrittura, mentre le altre sono unicamente delle stringhe di testo, a eccezione del livello della transazione.

Connessione

#Region "Connessione"
  '//Primo costruttore - Richiede, per la connessione, il nome del server, dell'utente,
  '// della password e del database, e costruisce la stringa di connessione
  Public Sub MySqlConnect()

    Try
      If myConnection.State = ConnectionState.Open Then
        Me.myConnection.Close()
        Flag = False
      End If
      m_MySqlConnectionString = ""
      m_MySqlConnectionString += "Persist Security Info=false;"
      m_MySqlConnectionString += "SERVER=" + m_MySqlServer + ";"
      m_MySqlConnectionString += "USER ID=" + m_MySqlUID + ";"
      m_MySqlConnectionString += "PASSWORD=" + m_MySqlPWD + ";"
      m_MySqlConnectionString += "DATABASE=" + m_MySqlDataBase + ";"
      'Apre la connessione (privata)
      myConnection.ConnectionString = m_MySqlConnectionString
      myConnection.Open()
      'Assegna la connessione al command
      myCommand.Connection = myConnection
      'La connessione è validamente aperta

      Flag = True
    Catch ex As MySqlException
      Throw
    End Try
  End Sub

  '//Secondo costruttore - deve essere passata solo la stringa di connessione già costruita
  '// Ricava da essa il nome utente, il server ed il database
  Public Sub MySqlConnect(ByVal Testo As String)
    Try
      If myConnection.State = ConnectionState.Open Then
        Me.myConnection.Close()
        Flag = False
      End If

      m_MySqlConnectionString = ""

      'Apre la connessione (privata)
      m_MySqlConnectionString = Trim(Testo)
      myConnection.ConnectionString = m_MySqlConnectionString
      myConnection.Open()
      'Assegna la connessione al command
      myCommand.Connection = myConnection
      'La connessione è validamente aperta

      Flag = True

      '//Estrae tutti gli elementi necessari dalla stringa di connessione
      Dim dr As MySqlDataReader
      Dim UserServer As String = ""
      Dim curDataBase As String = ""
      myCommand.CommandType = CommandType.Text
      myCommand.CommandText = "SELECT User()"
      dr = myCommand.ExecuteReader
      While (dr.Read())
        If dr(0) Is DBNull.Value Then
          UserServer = ""
        Else
          UserServer = dr(0).ToString
        End If
      End While
      dr.Close()

      If UserServer.Length <> 0 Then
        myCommand.CommandText = "SELECT Database()"
        dr = myCommand.ExecuteReader
        While (dr.Read())
          If dr(0) Is DBNull.Value Then
            curDataBase = ""
          Else
            curDataBase = dr(0).ToString
          End If
        End While
        dr.Close()
        m_MySqlUID = Mid(UserServer, 1, InStr(UserServer, "@") - 1)
        m_MySqlServer = Mid(UserServer, InStr(UserServer, "@") + 1)
        m_MySqlDataBase = curDataBase
      End If

    Catch ex As MySqlException
      Throw
    End Try
  End Sub

  Public Sub MySqlConnectionClose()
    If Flag Then
      myConnection.Close()
      myConnection.Dispose()
    End If
  End Sub
#End Region

Il metodo MySqlConnect effettua la connessione al motore MySql.
Si tratta di un metodo sovraccaricato, in quanto opera sia a livello di granularità, sia a livello di stringa di connessione.
Nel primo caso, apre la connessione qualora siano forniti dall'utente i valori delle relative proprietà (nome del server, dell'utente, della password e del database).
Nel secondo caso, agisce qualora sia fornita una stringa di connessione. In questo secondo caso, poiché gli elementi costitutivi della connessione sono annegati nella relativa stringa, sarà necessario estrapolarli per assegnarli alle relative proprietà. Allo scopo, viene interrogato il server, il quale restituisce in un datareader il nome dell'utente che sta aprendo la connessione, nel formato tipico di MySql ('username@servername'). Allo stesso modo, viene reperito il nome del database.

Il metodo MySqlConnectionClose chiude la connessione corrente, se la variabile globale Flag vale True.
Come si evince dal codice, la proprietà MySqlConnectionString riporterà, in ogni caso, la stringa di connessione.
Si riportano alcuni esempi di uso:

  Sintassi: Classe.MySqlConnect([String])
    Dim cl As New MyClient
    cl.MySqlServer = "localhost"
    cl.MysqlUID = "CurUser"
    cl.MySqlPWD = "pwd"
    cl.MySqlDatabase = "MioDB"
    cl.MySqlConnect()
      MyClient cl = New MyClient
      cl.MySqlServer = "localhost";
      cl.MysqlUID = "CurUser";
      cl.MySqlPWD = "pwd";
      cl.MySqlDatabase = "MioDB";
      cl.MySqlConnect();

Oppure:

    cl.MySqlConnectionString = _
      "Persist Security Info=False;SERVER=localhost;USER ID=CurUser;PASSWORD=""pwd"";DATABASE=MioDB"
    cl.MySqlConnect(cl.MySqlConnectionString)
      cl.MySqlConnectionString =
        "Persist Security Info=False;SERVER=localhost;USER ID=CurUser;PASSWORD=""pwd"";"
        + "DATABASE=MioDB";
      cl.MySqlConnect(cl.MySqlConnectionString);

Oppure

    cl.MySqlConnect( _
      "Persist Security Info=False;SERVER=localhost;USER ID=CurUser;PASSWORD=""pwd"";DATABASE=MioDB")
      cl.MySqlConnect("Persist Security Info=False;SERVER=localhost;USER ID=CurUser;"
        + "PASSWORD=""pwd"";DATABASE=MioDB");

Query di selezione

#Region "DirectQuery"
  '//MySqlDirectQuery - Primo costruttore - Senza parametri
  Public Function MySqlDirectQuery(ByVal Testo As String) As DataTable
    Dim adapter As New MySqlDataAdapter
    Dim oTable As New DataTable

    If Not Flag Then
      Return Nothing
      Exit Function
    End If

    Try
      Testo = CheckOriginalString(Testo)
      myCommand.CommandType = CommandType.Text
      myCommand.CommandText = Testo
      adapter.SelectCommand = myCommand
      oTable.Clear()
      adapter.Fill(oTable)
      Return oTable

    Catch ex As MySqlException
      Throw
    Finally
      If Not adapter Is Nothing Then adapter.Dispose()
      If Not oTable Is Nothing Then oTable.Dispose()
    End Try
  End Function

  '//MySqlDirectQuery (Overload) - Secondo costruttore - Con parametri
  Public Function MySqlDirectQuery(ByVal Testo As String, ByVal Dati As ArrayList) As DataTable
    Dim adapter As New MySqlDataAdapter
    Dim oTable As New DataTable
    Dim i As Integer = 0
    Dim MySqlQuery As String = ""
    Dim Sep() As String
    Dim strTemp As String = ""

    If Not Flag Then
      Return Nothing
      Exit Function
    End If

    Try
      Testo = CheckOriginalString(Testo)

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

      Sep = Split(Testo, "?")
      For i = 0 To UBound(Sep) - 1
        If IsDBNull(MySqlPar(Dati(i))) Then
          strTemp = "NULL"
        Else
          strTemp = CStr(MySqlPar(Dati(i)))
        End If
        MySqlQuery = MySqlQuery + Sep(i) + strTemp
      Next
      MySqlQuery = MySqlQuery + Sep(UBound(Sep))

      myCommand.CommandType = CommandType.Text
      myCommand.CommandText = MySqlQuery
      adapter.SelectCommand = myCommand
      oTable.Clear()
      adapter.Fill(oTable)
      Return oTable
    Catch ex As MySqlException
      Throw
    Finally
      If Not adapter Is Nothing Then adapter.Dispose()
      If Not oTable Is Nothing Then oTable.Dispose()
    End Try
  End Function
#End Region

MySqlDirectQuery è il primo metodo operativo, per così dire, della classe.
Esso si occupa di inviare le istruzioni Select al server, con o senza trasmissione di parametri in ingresso.
Ovviamente è un metodo che lavora in overload, dovendo modificare la sua firma a seconda della chiamata.

La prima versione - senza parametri - permette di inviare una stringa letterale che costituisce il corpo della select (vedere esempio successivo).
In questo caso, si pone il problema degli apostrofi, problema particolarmente sentito dalla lingua italiana. Per questo motivo il testo viene preliminarmente analizzato dalla funzione CheckOriginalString, inclusa fra le funzioni si servizio, che raddoppia gli apici, se presenti nella stringa passata al metodo. Questa funzione opera con una RegulaExpression, per cui la libreria necessita della referenza al relativo namespace.
Una volta analizzato il testo, esso viene passato ad un oggetto command.
Tale oggetto carica il set di righe richieste in un adapter, che a sua volta riempie un oggetto datatable. Quest'ultimo può essere, ad esempio, assegnato direttamente alla DataSource di una griglia, oppure sottoposto a manipolazione, allo scopo di utilizzarne singolarmente i componenti.

La seconda versione, con parametri, richiede nella firma del metodo, oltre alla stringa di testo, anche un arraylist che conterrà i parametri.
La stringa di testo usa come segnaposto per i parametri dei "?", i quali devono essere in sequenza corrispettivamente con i dati contentuti nell'arraylist (vedere esempio successivo).
I parametri trasmessi a mezzo dell'arraylist vengono analizzati dalla funzione MySqlPar, collocata anch'essa tra le funzioni di servizio, la quale, parametro per parametro, restituisce un valore congruo e in accordo con il tipo indicato nell'arraylist.
La stringa definitiva da passare al server viene costruita separando il testo originale in riferimento a ciascun "?" e concatenando ogni elemento della matrice risultante con il valore del parametro di volta in volta restituito da MySqlPar.
La nuova versione della stringa di testo, così ottenuta, viene associata al command, e il resto procederà come nel caso precedente.

  Sintassi: DataTable = cl.MySqlDirectQuery(String[,arraylist])

Query di selezione con stringa diretta e uscita su di una griglia:

    Dim od As DataTable = cl.MySqlDirectQuery( _
      "Select * From Tabella1 Where Cognome='D''Azeglio' And Data='1801-10-05'")
    Grid1.DataSource = od
      DataTable od = new DataTable();
      od = cl.MySqlDirectQuery(
        "Select * From Tabella1 Where Cognome='D''Azeglio' And Data='1801-10-05'");
      Grid1.DataSource = od;

Query di selezione con parametri e uscita su di una griglia:

    Dim w As New ArrayList
    Dim s As String = "Select * From Tablella1 Where Cognome=? and Data=?"
    Dim c As String = "D'Azeglio"
    Dim d As Date = CDate("05/10/1801")
    w.Add(c)
    w.Add(d)
    Dim od As DataTable = cl.MySqlDirectQuery(s, w)
    Grid1.DataSource = od
      ArraList w = New ArrayList();
      string s = "Select * From Tablella1 Where Cognome=? and Data=?";
      string c = "D'Azeglio";
      DateTime D = System.Convert.ToDateTime("05/10/1801");
      w.Add(c);
      w.Add(d);
      DataTable od = new DataTable();
      od = cl.MySqlDirectQuery(s, w);
      Grid1.DataSource = od;

Query di selezione con parametri e recupero dei valori su una listbox (o su variabili, vettori, ecc.):

    Dim w As New ArrayList
    Dim i As Integer = 0
    tblList.Items.Clear()
    w.Add("Rossi")
    w.Add(1000)
    Dim od As DataTable = cl.MySqlDirectQuery( _
      "Select * From Tabella1 Where Cognome=? And Valore >?", w)
    Dim rows() As DataRow = od.Select()
    For i = 0 To rows.GetUpperBound(0)
      If Not rows(i) Is DBNull.Value Then
        tblList.Items.Add(rows(i)(0))
      End If
    Next
      ArrayList w = new ArrayList();
      int i = 0;
      tblList.Items.Clear();
      w.Add("Bianchi");
      w.Add(400);
      DataTable od = cl.MySqlDirectQuery(
        "select nome from tab1 where cognome=? and valore>?", w);
      DataRow[] rows = od.Select();
      for (i = 0; i <= rows.GetUpperBound(0); i++)
      {
        if (rows[i][0]!=DBNull.Value)
        {
          tblList.Items.Add(rows[i][0]);
        }
      }

Query di modifica
Sotto il nome di query di modifica si intendono tutte le operazioni di inserimento, modifica, cancellazione e rimpiazzamento dati.
Anche in questo caso si fa ricorso ad un metodo sovraccaricato, che agisce esattamente come MySqlDirectQuery.
Il metodo, che ha nome MySqlDirectMod, quindi, osserva la medesima sintassi della query di selezione, e ne ricalca il funzionamento. L'unica differenza risiede nel fatto che non restituisce ovviamente alcun valore, e quindi il suo oggetto command esegue un metodo ExecuteNonQuery().
Si omette il codice, peraltro consultabile nel sorgente, in quanto identico a MySqlDirectQuery.
Gli esempi allegati chiariranno meglio i concetti.

  Sintassi: cl.MySqlDirectMod(String[,arraylist])

Query di modifica con stringa diretta:

    cl.MySqlDirectMod("Insert into Tabella1(Cognome,Data) Values('D'Azeglio','1801-10-05'")
      cl.MySqlDirectMod("Insert into Tabella1(Cognome,Data) Values('D'Azeglio','1801-10-05'");

Query di modifica con parametri:

    Dim w As New ArrayList
    Dim s As String = "Update Tabella1 Set Cognome=?,Data=? Where Campo3=?"
    Dim c As String = "D'Azeglio"
    Dim d As Date = CDate("05/10/1801")
    Dim f As Integer = 5
    w.Add(c)
    w.Add(d)
    w.Add(f)
    cl.MySqlDirectMod(s, w)
      ArraList w = New ArrayList();
      string s = "Update Tabella1 Set Cognome=?,Data=? Where Campo3=?";
      string c = "D'Azeglio";
      DateTime d = System.Convert.ToDateTime("05/10/1801");
      int f = 5;
      w.Add(c);
      w.Add(d);
      w.Add(f);
      cl.MySqlDirectMod(s, w);

Stored procedure
Com'è noto, le stored procedure (SP) sono funzioni precompilate nel motore del database, che eseguono una o più azioni in maniera automatica. Le SP possono o meno ricevere parametri in input, e restituire o meno gruppi di righe o parametri in output.
Per tali motivi, nella libreria sono presenti, essenzialmente, due tipi di strutture: la prima, che segue il consueto schema utilizzato per le query, seguendone la sintassi; la seconda invece espressamente deputata alle SP che restituiscono parametri.

Come detto, la libreria agisce in maniera difforme dai driver ADO e ADO.NET, che usualmente vengono utilizzati per l'interfacciamento ai database.
Infatti, non vi sono strutture particolari che chiamino le SP: le SP vengono invocate come se ci si trovasse sulla riga di comando di MySql, utilizzando la normale tastiera.

In definitiva, si eseguono le SP mediante il comando "CALL" seguito dal testo, con o senza parametri, della SP. L'analisi del codice sottostante e degli esempi a corredo, chiarirà i concetti espressi.

#Region "Stored procedure"
  '//MySqlStored - Primo costruttore - Senza parametri
  '//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  'Si utilizza un trucco, consistente nel passare il comando come testo, allo scopo
  'di eseguire quella che invece è una SP
  '//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  Public Function MySqlStored(ByVal Testo As String) As DataTable
    Dim adapter As New MySqlDataAdapter
    Dim oTable As New DataTable
    If Not Flag Then
      Return Nothing
      Exit Function
    End If
    Try
      myCommand.CommandType = CommandType.Text 'Nota!!
      myCommand.CommandText = "CALL " + Testo + "();"
      adapter.SelectCommand = myCommand
      oTable.Clear()
      adapter.Fill(oTable)
      Return oTable
    Catch ex As MySqlException
      Throw
    Finally
      If Not adapter Is Nothing Then adapter.Dispose()
      If Not oTable Is Nothing Then oTable.Dispose()
    End Try
  End Function

  '//MySqlStored - Secondo costruttore - Con parametri
  '//'''''''''''''''''''''''''''''''''''''''''''''''''
  'Si utilizza un trucco, consistente nel passare il comando come testo, allo scopo
  'di eseguire quella che invece è una SP
  'Inoltre, poiché non viene usato <ExecuteNonQuery>, il presente metodo può essere
  'usato sia per le query di selezione che per le query di variazione dati.
  '//''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  Public Function MySqlStored(ByVal Testo As String, ByVal Dati As ArrayList) As DataTable
    Dim adapter As New MySqlDataAdapter
    Dim oTable As New DataTable
    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 = "CALL " + Testo + "(" + q + ");"
      myCommand.CommandType = CommandType.Text
      myCommand.CommandText = q
      adapter.SelectCommand = myCommand
      oTable.Clear()
      adapter.Fill(oTable)
      Return oTable
    Catch ex As MySqlException
      Throw
    Finally
      If Not adapter Is Nothing Then adapter.Dispose()
      If Not oTable Is Nothing Then oTable.Dispose()
    End Try
  End Function

  '// Stored procedure che ritornano parametri -
  'I parametri ritornati, raccolti in un arraylist,
  '//sono solo di tipo stringa, per cui, nell'utilizzo, è necessaria la conversione.
  Public Function MySqlParametersOutput(ByVal Testo As String, _
                                        ByVal InOutParam As ArrayList) As ArrayList
    Dim dr As MySqlDataReader
    Dim invia As String = "("
    Dim ritorna As String = ""
    Dim i As Integer = 0
    Dim j As Integer = -1
    Dim sel As String = ""
    If Not Flag Then
      Return Nothing
      Exit Function
    End If
    Try
      For i = 0 To InOutParam.Count - 1
        InOutParam(i) = MySqlPar(InOutParam(i))
        If InOutParam(i).ToString.Length <> 0 Then
          invia = invia + InOutParam(i).ToString + ","
        Else
          invia = invia + "@" + i.ToString + ","
          sel = sel + "@" + i.ToString + ","
          j += 1
        End If
      Next
      invia = "CALL " + Testo + Mid(invia, 1, invia.Length - 1) + ")"
      ritorna = "(SELECT " + Mid(sel, 1, sel.Length - 1) + ")"
      myCommand.CommandType = CommandType.Text
      myCommand.CommandText = invia
      dr = myCommand.ExecuteReader
      dr.Close()
      Dim q As New ArrayList
      myCommand.CommandText = ritorna
      dr = myCommand.ExecuteReader
      While (dr.Read())
        For i = 0 To j
          If dr(i) Is DBNull.Value Then
            q.Add(DBNull.Value)
          Else
            q.Add(MySqlParamOut(dr.GetString(i)))
          End If
        Next
      End While
      dr.Close()
      Return q
    Catch ex As MySqlException
      Throw
    End Try
  End Function
#End Region

La prima delle funzioni riportate (MySqlStored), sovraccaricata come le query, accetta il testo della SP, ma non i parametri, essendo una SP senza parametri.
La stringa testo, preceduta dal comando "CALL", viene passata direttamente a MYSql tramite il command, che restutisce un gruppo di dati raccolti in un adapter, il quale a sua volta costruisce una datatable che viene restituita dal metodo.
Come si può notare, il meccanismo è identico in tutto e per tutto a quello utilizzato per le query.

La seconda versione richiede, nella firma del metodo, i parametri in ingresso.
Ancora una volta, il meccanismo di azione è identico a quello usato nelle query, nel senso che i parametri, passati tramite un arraylist, vengono analizzati dalla funzione di servizio MySqlPar, per poi costruire la stringa da inviare al server, preceduta da un comando "CALL".
Il ritorno del metodo, se significativo (potrebbe non esserlo, se la SP non restituisce righe), viene gestito nel modo consueto.

La terza versione, scritta per le SP deputate a restituire parametri, è invece completamente diversa.
Chiunque abbia avuto a che fare con la collezione parameters dei database, conosce la complessità e la prolissità dei metodi che gestiscono la detta collezione. Sorvolo, inoltre, sulla loro utilità (ma questa è un'opinione personale).
Per comprendere pienamente MySqlParametersOutput, bisogna soffermarsi su come MySql, dalla linea di comando (come sempre), agisce in tali casi.

Il meccanismo è il seguente: tramite un'istruzione "CALL", acquisisce il testo e l'elenco dei parametri; immediatamente dopo, mediante un'istruzione "SELECT" seguita dall'indicatore "@", restituisce i parametri richiesti.
Lo stesso schema è stato seguito in MySqlParametersOutput:

  Sintassi: ArrayList = MySqlParametersOutput(String,ArrayList)

Esempio
Sp5 è una stored procedure che accetta due parametri di tipo stringa in input e restuisce due parametri, uno di tipo double e l'altro di tipo data.
La struttura è la seguente:

  CREATE PROCEDURE sp5
  /* commento a piacere, se lo si desidera */
    (IN p1 varchar(50),
     IN p2 varchar(50),
     OUT p3 double,
     OUT p4 date)
  BEGIN
    SELECT importo INTO p3 FROM tab1 WHERE Cognome=p1 AND Nome=p2;
    SELECT data INTO p4 FROM tab1 WHERE Cognome=p1 AND Nome=p2;
  END

I parametri di input/output vengono trasmessi tramite l'ArrayList [w].
I parametri di output, raccolti dall'ArrayList [t], vengono aggiunti ad una ListBox:

    Dim w As New ArrayList
    Dim t As New ArrayList
    Dim i As Integer = 0
    ReturnList.Items.Clear()
    w.Add("Rossi") '----->; In
    w.Add("Mario") '----->; In
    w.Add("") '---------->; Out
    w.Add("") '---------->; Out
    t = cl.MySqlParametersOutput("sp5", w)
    For i = 0 To 1
      ReturnList.Items.Add(t(i))
    Next
      ArrayList w = new ArrayList();
      ArrayList t = new ArrayList();
      int i = 0;
      ListBox1.Items.Clear();
      w.Add("Rossi"); //------> In
      w.Add("Mario"); //------> In
      w.Add(""); //-----------> Out
      w.Add(""); //-----------> Out
      t = cl.MySqlParametersOutput("sp5", w);
      for (i = 0; i <= 1; i++)
      {
        ListBox1.Items.Add(t[i]);
      }

Conclusione
Qui finisce la prima parte, in cui sono stati illustrati i metodi principali della libreria MyClient, per interfacciare una applicazione con MySql.

Nella seconda parte, verranno trattate le funzioni, le utility, il backup/restore e le transazioni.
Verrà inoltre fornito, oltre al codice della libreria, anche un piccolo esempio di uso, utile per iniziare a familiarizzarsi con la libreria.