SlideShare une entreprise Scribd logo
1  sur  15
Télécharger pour lire hors ligne
Testing with MySQL embedded
                                                          by Roberto Polli



Integration Testing is not always common between C developers. MySQL API offer us a fast and
pratical way to do it using a full-featured, dummy sql embedded server.


Obviously an embedded server has its limitations, eg:
         no replication;
         one process at a time;
         missing more logic (eg. mysqldump...) but you can implement it.


See mysql embedded documentation for further info!


The MySQL C API
To access a MySQL server from a C program, we can use the API provided by MySQL. Functions are
provided by the following library files.
Client:
         /usr/lib/libmysqlclient.so.16.0.0                    - standard version
          /usr/lib/libmysqlclient_r.so.16.0.0                  - reentrant version
Server embedded:
         /usr/lib/mysql/libmysqld.a


You can use /usr/bin/mysql_config to set the build flags. That’s the standard output of


#mysql_config
Usage: /usr/bin/mysql_config [OPTIONS]
Options:
              --cflags                             [-I/usr/include/mysql                     -DBIG_JOINS=1                -fno-strict-
aliasing           -DUNIV_LINUX -DUNIV_LINUX]
              --include                     [-I/usr/include/mysql]
              --libs                          [-Wl,-Bsymbolic-functions -rdynamic -L/usr/lib/mysql -
lmysqlclient]
              --libs_r                        [-Wl,-Bsymbolic-functions -rdynamic -L/usr/lib/mysql -
lmysqlclient_r]
              --plugindir                   [/usr/lib/mysql/plugin]
              --socket                      [/var/run/mysqld/mysqld.sock]

BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680
E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it
Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v.
Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
--port                        [0]
              --version                     [5.1.41]
              --libmysqld-libs [-Wl,-Bsymbolic-functions -rdynamic -L/usr/lib/mysql -
lmysqld -ldl -lwrap -lrt]


Here is the anatomy of a MySQL client application: uses a shared library and the external server. My
test application occupy about 200KBytes.




An embedded app is bigger, because it’s statically linked with the mysqld library. It’s an almost
complete MySQL server instance! And it’s faster!




Basic working cycle with a MySQL Server
The simplest way to connect to MySQL consists to:
- initialize mysql library
- connect to server
- execute queries, eventually freeing result sets
- disconnect




BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680
E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it
Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v.
Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
Here is a sample program with some assertions, that show us the expected return values of the
functions.


#include<assert.h>
#include<stdlib.h>
#include<mysql/mysql.h>


int main(int argc, char**argv) {
          int port = 0,
                     mysql_option_flags=0;


          char *host = “localhost”,
                     *user = “mysql”,
                     *pass = “secret”,
                     *dbname = NULL,
          *unix_sock = NULL;


          // Initialize mysql library. This should be done *once*
          // moreover this function is not thread-safe, nor reentrant
          // so we should protect it with a global mutex
          assert( mysql_library_init(0, NULL, NULL) == 0 );


          MYSQL * mysql = NULL; // a pointer to a mysql database structure


          //      Inizialize             mysql         database           structure,            containing             all       database
          informations
          // this function is not thread-safe too
          assert(         (mysql = mysql_init(NULL))                          != NULL);


          // connect to the RDBMS without specifying a database name
          int error = mysql_real_connect(mysql,
            host, user, pass, dbname, port,
          unix_sock,
          mysql_option_flags);


          // eventually print errors
          if (error) goto onerror;


          // do our job
          error = mysql_query(mysql, “CREATE DATABASE pluto;”);

BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680
E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it
Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v.
Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
if (error) goto onerror;


          error = mysql_query(mysql, “USE pluto;”);
          if (error) goto onerror;


          error = mysql_query(mysql, “DROP DATABASE pluto;”);
          if (error) goto onerror;


          // disconnet and free memory and resources
          mysql_close(mysql);
          mysql_library_end();


          exit(0);
          onerror:
                     printf(“Error: %sn”, mysql_error(mysql));
                     abort();
}


Going multithreads
Using multiple threads - and multiple connections - require some logic. In fact the following
functions must be called at once in the process - and must be protected by a global mutex:
     1. mysql_library_init()
     2. mysql_init() which may call mysql_library_init()


Other MySQL methods are thread safe “per-connection”: the MYSQL* pointer is associated to a
reference the connection. So every MYSQL* pointer can be used by a single thread at once,
requiring us to do use some locking. Let’s create the following database structure, a global mutex
and some helper methods.


// a locking structure for each connection
typedef struct my_db {
          MYSQL * mysql;
          pthread_mutex_t lock;
          int lock_init;
          char* user;
          char* pass;
          char* host;
          char* dbhost;
          char* unix_sock;

BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680
E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it
Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v.
Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
int port;
          int options;
} mysql_db;
// a mutex for global calls
pthread_mutex_t db_global_mutex = PTHREAD_MUTEX_INITIALIZER;


The structure simply ties the database connection parameters, its MYSQL structure and a mutex that
makes it thread safe.
The “global mutex” avoid us to call mysql_init() in different threads.
Here are some methods, the missing ones are left as an exercise to the reader. We will add a
my_flush_result() method, *required* to clean resultset after queries.
Please see MySQL documentation for further info about it!.


Non connection-safe functions
There are two ways to protect the non thread-safe functions:
         call them in the main thread, before spawning the others;
         or use our global mutex.


The mysql_library_init() should be called once by EVERY thread using mysql - so it’s better to run it in
the main(), before spawning threads. Similarly you have to run mysq_library_end() at the end of the
process to free memory resources.


In the same way, we can allocate all our connection before running the client threads - creating a
connection pool and passing it to our threads.


/**
 * creating a connection pool
 */
int main(int argc, char** argv) {
          // some variables
          int i=0, pool_size = 5;
          mysql_db my_pool[pool_size];


          // once in the process, so no mutexes
          mysql_library_init(0, NULL, NULL);


          // again...no locks
          for (i=0; i<pool_size; i++) {


BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680
E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it
Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v.
Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
// fill the my_pool with credential, user, passwd....
                     ...initialize_db_structure(&my_pool[i]);...


                     // initialize lock
                     pthread_mutex_init(&my_pool[i].lock);
                     &my_pool[i]->lock_initialized = TRUE;


                     //initialize db
                     &my_pool[i]->mysql = mysql_init(NULL);
          }


          … pass &my_pool[i] to threads and run them...


          mysql_library_end();
}


Otherwise we just have to protect each call with our global mutex and run our sequence at every
connection...


mysql_db* my_init(mysql_db *db) {
          /// initialize mutex, WARN can’t initialize it twice,
          ///        so check if you have already initialized it!
          if (!db->lock_initialized) {
          pthread_mutex_init(&db->lock, NULL);
          db->lock_initialized=TRUE;
          }


          /// initialize mysql structure
          pthread_mutex_lock(&db->lock); // don’t use this db while initializing
          pthread_mutex_lock(&db_global_mutex);                                //     don’t       initialize           two     databases
          contemporarily
          db->mysql = mysql_init(NULL);
          pthread_mutex_unlock(&db_global_mutex);
          pthread_mutex_unlock(&db->lock);


          return db;
}




BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680
E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it
Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v.
Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
Connection-safe functions
Connection safe functions are now easy: just lock and unlock the MYSQL* pointer in use, to have
consistent values for results, counters and errors.


int my_connect(mysql_db *db) {
          pthread_mutex_lock(&db->lock);
          int error = mysql_real_connect(db->mysql,
          db->host, db->user, db->pass, db->dbname, db->port,
          db->unix_sock,
          db->options);
                     if (error) {
          printf(“Error: %sn”, mysql_error(db->mysql));
          }
          /// release lock *after* mysql_error
          pthread_mutex_unlock(&db->lock);
          return error;
}


int my_query(mysql_db* db, char*query) {
          pthread_mutex_lock(&db->lock);
          error = mysql_query(db->mysql, query);
          if (error) {
          printf(“Error: %sn”, mysql_error(db->mysql));
          } else {
          if (mysql_field_count(db->mysql) == 0) {
                               rows =         mysql_affected_rows(db->mysql);
                               printf("%lld rows affectedn",rows );
                     }
          }
          pthread_mutex_unlock(&db->lock);
          return error;
}


/**
    * clean result
    */
int my_flush_result(mysql_db *db) {
          MYSQL *mysql = db->mysql;
          MYSQL_RES* result;
          int qstatus = 0;

BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680
E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it
Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v.
Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
pthread_mutex_lock(&db->lock);
          do {
                     if ((result = mysql_store_result(mysql))) {
                               mysql_free_result(result);
                     }
                     if (mysql_field_count(mysql) == 0) {
                               printf("%lld rows affectedn",mysql_affected_rows(mysql));


                     }    else {
                               printf("Could not retrieve result setn");
                               break;
                     }
                     if ((qstatus = mysql_next_result(mysql)) > 0)
                               printf("Could not execute statementn");
          } while(qstatus == 0);
          pthread_mutex_unlock(&db->lock);
          return qstatus;
}


So we’ve just create a simple MySQL client! Once you’ve understood the common pitfalls, it’s not
so hard!


Testing with a MySQL server
Once we created our simple MySQL library, we can write our client code issuing some queries and
printing reports.


Provided we’ve a working MySQL server, we can create some test cases. First of all we’ll create the
setup() and teardown() methods to prepare our environment.


void setup(mysql_db *db) {
          mysql_library_init(0,NULL,NULL);


          db = my_init(db);
          my_connect(db);
          my_query(db, “create database pippo;”);
          my_query(db, “use pippo;”);
}


void teardown(mysql_db *db) {

BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680
E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it
Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v.
Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
my_query(db, “drop database pippo;”);
          my_close(db);
          mysql_library_end();
}


Then we create a simple test case, which issues a lot of insert. Note that this code is shortened for
better readability!


void test_sql_client(mysql_db *db) {
          int i, error;
          error = my_query(db, “create table t1(“ 
                     “id int not null auto_increment primary key”
                     ”, value varchar(25));”);
          for (i=0; i<10000; i++) {
                     error          =       my_query(db,                “insert            into          t1(id,value)               values
                     (‘%d’,’prova%d’);”, i, i);
                     my_flush_result(db);
          }
}


int main(int argc, char**argv) {
          mysql_db *db = calloc(sizeof(mysql_db));


          ...set db values....


          setup(db);
          test_sql_client(db);
          teardown(db);
          free(db);
          exit(EXIT_SUCCESS);
}


To build our program, mysql_test.c we issue


#gcc mysql_test.c -o mysql_test $(mysql_config --libs)


and with time we’ll see how long will it take to run 10k insert. Note that this will include setup and
teardown time. Writing a more complete test is left as an exercise to the reader ;)




BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680
E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it
Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v.
Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
# time ./mysql_test


What’s MySQL embedded
To run the previous file, named mysql_test.c, we need a working MySQL instance. Instead if we
don’t have it - or we like more isolation and speed, we could use the MySQL embedded. Using
MySQL embedded means you don’t need to connect to a server, but you’ll write directly into the
MySQL database files. And it’s fast!


To use MySQL embedded, you just need to link your file with the mysqld library:


#gcc mysql_test.c -o mysql_test $(mysql_config --libmysqld-libs)


The server instructions will be added to your program, which will increase its size.


While the core client code remains the same, you need to modify a bit your initialization structure,
passing some parameters to the mysql_library_init() function. Those parameters are the same that
you would pass to mysqld server process, eg. --datadir=path.


If you have never run MySQL server by command line, or you’re not familiar with my.conf please,
take some time to play with it before continuing.


In our case we should only modify the setup() function to reflect our changes. Before that we need
to sketch the MySQL database directory structure.


MySQL directory structure
MySQL stores databases and authentication information in the /var/lib/mysql directory. The
forementioned database would have the following structure:


/var/lib/mysql/
├── mysql <- the core database containing user/password credentials
├── pippo <- a newly created database


By default, MySQL embedded will use the /var/lib/mysql directory, so interfering with our server and
requiring RW access to /var/lib/mysql. This is usually not what we want. Instead we want to put
everything in another place, eg. a temporary directory under /tmp/. So when we run the
embedded server, we should create a new directory and tell it to bootstrap there a new MySQL
installation. Let’s do it.


BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680
E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it
Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v.
Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
Bootstrapping MySQL embedded
To run the embedded server we need to provide:
         a directory where to store the new mysql databases;
         the parametes to run the embedded server;
         the server groups.


Server groups are the stanzas of my.conf file from which mysqld - and our embedded application -
read configuration options.


A typical my.conf files has the following structure:


[mysqld]
...options for server...
[embedded]
...options for the embedded server...


[server]
...options for server...


To specify custom parameters relative to our application, we could add the following sections to
our my.conf, or - with more isolation - use another my.conf containing the specified sections.


[embedded]
...options for *all* embedded applications...
key_buffer_size=32M


[xxx_SERVER]
...replace xxx with program name (eg. argv[0]) and
add options for this specific program …
datadir=/tmp/


[xxx_CLIENT]
...replace xxx with program name (eg. argv[0]) and
add options for this specific program...
datadir=/tmp


We can even specify those parameters as arguments to the mysql_server_init()


BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680
E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it
Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v.
Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
void embedded_mysql_library_init(char* progname) {
          /// create a temporary directory where to store the db
          char *datadir = strdup("/tmp/mysqlembedXXXXXX");
          assert( mkdtemp(datadir)!=NULL );


          /// prepare server parameters
          const char *server_options[] = {
                               progname,           /* this string is not used */
                               "-h", datadir,
                               "--key_buffer_size=32M",
                               "--bootstrap", // create the required mysql files
                               NULL
          };
          int num_elements = sizeof(server_options) / sizeof(char *) -1;




          /// create server groups for my.cnf
          sprintf(progname_client, "%s_CLIENT", progname);
          sprintf(progname_server, "%s_SERVER", progname);
          char *server_groups[] = {
                               progname_server,
                               progname_client,
                               "embedded",
                               NULL // end of arguments
          };
/// initialize the embedded server
          assert(mysql_library_init(num_elements,                                          (char**)                 server_options,
server_groups) == 0);
}




void embedded_my_init(mysql_db *db) {
          pthread_mutex_lock(&db->lock, NULL);
          /// initialize and set options
          assert( (mysql = mysql_init(db->mysql)) != NULL );
          assert(                      mysql_options(db->mysql,                                   MYSQL_READ_DEFAULT_GROUP,
          progname_client) == 0);
                     assert(        mysql_options(db->mysql,                      MYSQL_OPT_USE_EMBEDDED_CONNECTION,
          NULL) == 0);

BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680
E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it
Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v.
Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
pthread_mutex_unlock(&db->lock, NULL);
          return db;
}


With our new methods, the setup function become:


void setup(mysql_db *db, char* progname) {
          embedded_mysql_library_init(progname);
          db = embeded_my_init(db);


          // from here everything is the same
          my_connect(db);
          my_query(db, “create database pippo;”);
          my_query(db, “use pippo;”);
}


And our new main():


int main(int argc, char**argv) {
          mysql_db *db = calloc(sizeof(mysql_db));


          ...set db values....


          setup(db, argv[0]);
          test_sql_client(db);
          teardown(db);


          exit(EXIT_SUCCESS);
}


Replacing the old one with this one, everything will work nicely in both cases. When you link against
libmysqlclient, all the different initialization variables - only applicable to embedded server - are
ignored.


Duplicate test files with embedded sql
To run both tests you have to compile twice your software:


#gcc mysql_test.c -o mysql_server_test $(mysql_config --libs)

BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680
E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it
Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v.
Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
#gcc mysql_test.c -o mysql_embedded_test $(mysql_config --libmysqld-libs)


Now you have two executables, one using the client connection, one the embedded server.


Here are some speed results for 50k INSERTs run locally on my Laptop. You’re welcome to provide
further stats for SELECT, UPDATE, INSERT or UPDATE and TRANSACTIONS!
CPU: 2 x Intel(R) Core(TM)2 Duo CPU @2.1Ghz
RAM: 8Gb



                                         MySQL Server InnoDB                              21 sec

                                         MySQL Server MemoryDB                            7sec

                                         MySQL Embedded InnoDB                            5.7 sec

                                         MySQL Embedded MemoryDB                          5.6 sec




BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680
E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it
Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v.
Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
Licenza d'uso “Attribuzione - Non commerciale - Non opere derivate”, secondo i criteri
internazionali Creative Commons (http://creativecommons.org/licenses/by-nc-nd/2.5/it/)




BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680
E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it
Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v.
Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015

Contenu connexe

En vedette

Developing Applications with MySQL and Java for beginners
Developing Applications with MySQL and Java for beginnersDeveloping Applications with MySQL and Java for beginners
Developing Applications with MySQL and Java for beginnersSaeid Zebardast
 
Modul sql tingkat dasar
Modul sql tingkat dasarModul sql tingkat dasar
Modul sql tingkat dasareppoy jeprudht
 
Modul MySQL TIngkat Lanjut
Modul MySQL TIngkat LanjutModul MySQL TIngkat Lanjut
Modul MySQL TIngkat LanjutM Fahmi Ansori
 
Library Management System
Library Management SystemLibrary Management System
Library Management SystemAditya Shah
 
Library mangement system project srs documentation.doc
Library mangement system project srs documentation.docLibrary mangement system project srs documentation.doc
Library mangement system project srs documentation.docjimmykhan
 

En vedette (9)

Developing Applications with MySQL and Java for beginners
Developing Applications with MySQL and Java for beginnersDeveloping Applications with MySQL and Java for beginners
Developing Applications with MySQL and Java for beginners
 
Modul sql tingkat dasar
Modul sql tingkat dasarModul sql tingkat dasar
Modul sql tingkat dasar
 
MySQL for beginners
MySQL for beginnersMySQL for beginners
MySQL for beginners
 
Library Management System
Library Management SystemLibrary Management System
Library Management System
 
Java for beginners
Java for beginnersJava for beginners
Java for beginners
 
Modul MySQL TIngkat Lanjut
Modul MySQL TIngkat LanjutModul MySQL TIngkat Lanjut
Modul MySQL TIngkat Lanjut
 
Library Management System
Library Management SystemLibrary Management System
Library Management System
 
Html
HtmlHtml
Html
 
Library mangement system project srs documentation.doc
Library mangement system project srs documentation.docLibrary mangement system project srs documentation.doc
Library mangement system project srs documentation.doc
 

Similaire à Testing with MySQL embedded

Testing with my sql embedded
Testing with my sql embeddedTesting with my sql embedded
Testing with my sql embeddedRoberto Polli
 
Perl Stored Procedures for MySQL (2009)
Perl Stored Procedures for MySQL (2009)Perl Stored Procedures for MySQL (2009)
Perl Stored Procedures for MySQL (2009)Antony T Curtis
 
Multiple instances on linux
Multiple instances on linuxMultiple instances on linux
Multiple instances on linuxVasudeva Rao
 
Applying profilers to my sql (fosdem 2017)
Applying profilers to my sql (fosdem 2017)Applying profilers to my sql (fosdem 2017)
Applying profilers to my sql (fosdem 2017)Valeriy Kravchuk
 
Writing & Sharing Great Modules - Puppet Camp Boston
Writing & Sharing Great Modules - Puppet Camp BostonWriting & Sharing Great Modules - Puppet Camp Boston
Writing & Sharing Great Modules - Puppet Camp BostonPuppet
 
Multiple instances second method
Multiple instances second methodMultiple instances second method
Multiple instances second methodVasudeva Rao
 
My First 100 days with a MySQL DBMS (WP)
My First 100 days with a MySQL DBMS (WP)My First 100 days with a MySQL DBMS (WP)
My First 100 days with a MySQL DBMS (WP)Gustavo Rene Antunez
 
Architecting cloud
Architecting cloudArchitecting cloud
Architecting cloudTahsin Hasan
 
How (not) to kill your MySQL infrastructure
How (not) to kill your MySQL infrastructureHow (not) to kill your MySQL infrastructure
How (not) to kill your MySQL infrastructureMiklos Szel
 
MySQL replication & cluster
MySQL replication & clusterMySQL replication & cluster
MySQL replication & clusterelliando dias
 
20141011 mastering mysqlnd
20141011 mastering mysqlnd20141011 mastering mysqlnd
20141011 mastering mysqlnddo_aki
 
How to Contribute Code to MySQL?
How to Contribute Code to MySQL?How to Contribute Code to MySQL?
How to Contribute Code to MySQL?Thava Alagu
 
MySQL database replication
MySQL database replicationMySQL database replication
MySQL database replicationPoguttuezhiniVP
 
MySQL? Load? Clustering! Balancing! PECL/mysqlnd_ms 1.4
MySQL? Load? Clustering! Balancing! PECL/mysqlnd_ms 1.4MySQL? Load? Clustering! Balancing! PECL/mysqlnd_ms 1.4
MySQL? Load? Clustering! Balancing! PECL/mysqlnd_ms 1.4Ulf Wendel
 
MySQL 101 PHPTek 2017
MySQL 101 PHPTek 2017MySQL 101 PHPTek 2017
MySQL 101 PHPTek 2017Dave Stokes
 

Similaire à Testing with MySQL embedded (20)

Testing with my sql embedded
Testing with my sql embeddedTesting with my sql embedded
Testing with my sql embedded
 
Mysql ppt
Mysql pptMysql ppt
Mysql ppt
 
Perl Stored Procedures for MySQL (2009)
Perl Stored Procedures for MySQL (2009)Perl Stored Procedures for MySQL (2009)
Perl Stored Procedures for MySQL (2009)
 
Multiple instances on linux
Multiple instances on linuxMultiple instances on linux
Multiple instances on linux
 
Applying profilers to my sql (fosdem 2017)
Applying profilers to my sql (fosdem 2017)Applying profilers to my sql (fosdem 2017)
Applying profilers to my sql (fosdem 2017)
 
Writing & Sharing Great Modules - Puppet Camp Boston
Writing & Sharing Great Modules - Puppet Camp BostonWriting & Sharing Great Modules - Puppet Camp Boston
Writing & Sharing Great Modules - Puppet Camp Boston
 
Multiple instances second method
Multiple instances second methodMultiple instances second method
Multiple instances second method
 
Sql installation
Sql installationSql installation
Sql installation
 
My First 100 days with a MySQL DBMS (WP)
My First 100 days with a MySQL DBMS (WP)My First 100 days with a MySQL DBMS (WP)
My First 100 days with a MySQL DBMS (WP)
 
Architecting cloud
Architecting cloudArchitecting cloud
Architecting cloud
 
How (not) to kill your MySQL infrastructure
How (not) to kill your MySQL infrastructureHow (not) to kill your MySQL infrastructure
How (not) to kill your MySQL infrastructure
 
MySQL replication & cluster
MySQL replication & clusterMySQL replication & cluster
MySQL replication & cluster
 
Slides
SlidesSlides
Slides
 
20141011 mastering mysqlnd
20141011 mastering mysqlnd20141011 mastering mysqlnd
20141011 mastering mysqlnd
 
How to Contribute Code to MySQL?
How to Contribute Code to MySQL?How to Contribute Code to MySQL?
How to Contribute Code to MySQL?
 
MySQL database replication
MySQL database replicationMySQL database replication
MySQL database replication
 
Mysql tutorial 5257
Mysql tutorial 5257Mysql tutorial 5257
Mysql tutorial 5257
 
Mysql tutorial
Mysql tutorialMysql tutorial
Mysql tutorial
 
MySQL? Load? Clustering! Balancing! PECL/mysqlnd_ms 1.4
MySQL? Load? Clustering! Balancing! PECL/mysqlnd_ms 1.4MySQL? Load? Clustering! Balancing! PECL/mysqlnd_ms 1.4
MySQL? Load? Clustering! Balancing! PECL/mysqlnd_ms 1.4
 
MySQL 101 PHPTek 2017
MySQL 101 PHPTek 2017MySQL 101 PHPTek 2017
MySQL 101 PHPTek 2017
 

Plus de Babel

Sophos Complete Security: arte e scienza della sicurezza
Sophos Complete Security: arte e scienza della sicurezzaSophos Complete Security: arte e scienza della sicurezza
Sophos Complete Security: arte e scienza della sicurezzaBabel
 
L’innovazione a difesa della tradizione: il caso dell’Archivio Storico della ...
L’innovazione a difesa della tradizione: il caso dell’Archivio Storico della ...L’innovazione a difesa della tradizione: il caso dell’Archivio Storico della ...
L’innovazione a difesa della tradizione: il caso dell’Archivio Storico della ...Babel
 
L’innovazione a difesa della tradizione: il caso dell’Archivio Storico della ...
L’innovazione a difesa della tradizione: il caso dell’Archivio Storico della ...L’innovazione a difesa della tradizione: il caso dell’Archivio Storico della ...
L’innovazione a difesa della tradizione: il caso dell’Archivio Storico della ...Babel
 
Intercettazioni: guida alle nuove norme per i provider
Intercettazioni: guida alle nuove norme per i providerIntercettazioni: guida alle nuove norme per i provider
Intercettazioni: guida alle nuove norme per i providerBabel
 
Dalla virtualizzazione al private cloud: Il Patronato INCA rinnova la fiducia...
Dalla virtualizzazione al private cloud: Il Patronato INCA rinnova la fiducia...Dalla virtualizzazione al private cloud: Il Patronato INCA rinnova la fiducia...
Dalla virtualizzazione al private cloud: Il Patronato INCA rinnova la fiducia...Babel
 
Will iPython replace Bash?
Will iPython replace Bash?Will iPython replace Bash?
Will iPython replace Bash?Babel
 
La gestione integrata della sicurezza in ANSA: dal firewalling all'UTM
La gestione integrata della sicurezza in ANSA: dal firewalling all'UTMLa gestione integrata della sicurezza in ANSA: dal firewalling all'UTM
La gestione integrata della sicurezza in ANSA: dal firewalling all'UTMBabel
 
Installare i server via rete con Cobbler
Installare i server via rete con CobblerInstallare i server via rete con Cobbler
Installare i server via rete con CobblerBabel
 
Shell Control Box - Il Gusto della Sicurezza
Shell Control Box - Il Gusto della SicurezzaShell Control Box - Il Gusto della Sicurezza
Shell Control Box - Il Gusto della SicurezzaBabel
 
Tracciamento delle attività di amministrazione mediante i BalaBit Shell Contr...
Tracciamento delle attività di amministrazione mediante i BalaBit Shell Contr...Tracciamento delle attività di amministrazione mediante i BalaBit Shell Contr...
Tracciamento delle attività di amministrazione mediante i BalaBit Shell Contr...Babel
 
La Desktop Virtualization
La Desktop VirtualizationLa Desktop Virtualization
La Desktop VirtualizationBabel
 
Crittografia e integrazione dei sistemi con Python
Crittografia e integrazione dei sistemi con PythonCrittografia e integrazione dei sistemi con Python
Crittografia e integrazione dei sistemi con PythonBabel
 
Intercettazioni e posta elettronica: le misure di sicurezza per i gestori
Intercettazioni e posta elettronica: le misure di sicurezza per i gestoriIntercettazioni e posta elettronica: le misure di sicurezza per i gestori
Intercettazioni e posta elettronica: le misure di sicurezza per i gestoriBabel
 
Babel presenta: Opsview
Babel presenta: OpsviewBabel presenta: Opsview
Babel presenta: OpsviewBabel
 
Monitoraggio di infrastrutture IT mediante Opsview Enteprise V4
Monitoraggio di infrastrutture IT mediante Opsview Enteprise V4Monitoraggio di infrastrutture IT mediante Opsview Enteprise V4
Monitoraggio di infrastrutture IT mediante Opsview Enteprise V4Babel
 
OpenVAS, lo strumento open source per il vulnerability assessment
OpenVAS, lo strumento open source per il vulnerability assessmentOpenVAS, lo strumento open source per il vulnerability assessment
OpenVAS, lo strumento open source per il vulnerability assessmentBabel
 
Cross compiler per uso domestico
Cross compiler per uso domesticoCross compiler per uso domestico
Cross compiler per uso domesticoBabel
 
Sicurezza flessibile con SELinux: architettura e configurazione
Sicurezza flessibile con SELinux: architettura e configurazioneSicurezza flessibile con SELinux: architettura e configurazione
Sicurezza flessibile con SELinux: architettura e configurazioneBabel
 
Ridirezionamento di I/O con Bash: un breve approfondimento
Ridirezionamento di I/O con Bash: un breve approfondimentoRidirezionamento di I/O con Bash: un breve approfondimento
Ridirezionamento di I/O con Bash: un breve approfondimentoBabel
 
Nagios in alta affidabilità con strumenti open source
Nagios in alta affidabilità con strumenti open sourceNagios in alta affidabilità con strumenti open source
Nagios in alta affidabilità con strumenti open sourceBabel
 

Plus de Babel (20)

Sophos Complete Security: arte e scienza della sicurezza
Sophos Complete Security: arte e scienza della sicurezzaSophos Complete Security: arte e scienza della sicurezza
Sophos Complete Security: arte e scienza della sicurezza
 
L’innovazione a difesa della tradizione: il caso dell’Archivio Storico della ...
L’innovazione a difesa della tradizione: il caso dell’Archivio Storico della ...L’innovazione a difesa della tradizione: il caso dell’Archivio Storico della ...
L’innovazione a difesa della tradizione: il caso dell’Archivio Storico della ...
 
L’innovazione a difesa della tradizione: il caso dell’Archivio Storico della ...
L’innovazione a difesa della tradizione: il caso dell’Archivio Storico della ...L’innovazione a difesa della tradizione: il caso dell’Archivio Storico della ...
L’innovazione a difesa della tradizione: il caso dell’Archivio Storico della ...
 
Intercettazioni: guida alle nuove norme per i provider
Intercettazioni: guida alle nuove norme per i providerIntercettazioni: guida alle nuove norme per i provider
Intercettazioni: guida alle nuove norme per i provider
 
Dalla virtualizzazione al private cloud: Il Patronato INCA rinnova la fiducia...
Dalla virtualizzazione al private cloud: Il Patronato INCA rinnova la fiducia...Dalla virtualizzazione al private cloud: Il Patronato INCA rinnova la fiducia...
Dalla virtualizzazione al private cloud: Il Patronato INCA rinnova la fiducia...
 
Will iPython replace Bash?
Will iPython replace Bash?Will iPython replace Bash?
Will iPython replace Bash?
 
La gestione integrata della sicurezza in ANSA: dal firewalling all'UTM
La gestione integrata della sicurezza in ANSA: dal firewalling all'UTMLa gestione integrata della sicurezza in ANSA: dal firewalling all'UTM
La gestione integrata della sicurezza in ANSA: dal firewalling all'UTM
 
Installare i server via rete con Cobbler
Installare i server via rete con CobblerInstallare i server via rete con Cobbler
Installare i server via rete con Cobbler
 
Shell Control Box - Il Gusto della Sicurezza
Shell Control Box - Il Gusto della SicurezzaShell Control Box - Il Gusto della Sicurezza
Shell Control Box - Il Gusto della Sicurezza
 
Tracciamento delle attività di amministrazione mediante i BalaBit Shell Contr...
Tracciamento delle attività di amministrazione mediante i BalaBit Shell Contr...Tracciamento delle attività di amministrazione mediante i BalaBit Shell Contr...
Tracciamento delle attività di amministrazione mediante i BalaBit Shell Contr...
 
La Desktop Virtualization
La Desktop VirtualizationLa Desktop Virtualization
La Desktop Virtualization
 
Crittografia e integrazione dei sistemi con Python
Crittografia e integrazione dei sistemi con PythonCrittografia e integrazione dei sistemi con Python
Crittografia e integrazione dei sistemi con Python
 
Intercettazioni e posta elettronica: le misure di sicurezza per i gestori
Intercettazioni e posta elettronica: le misure di sicurezza per i gestoriIntercettazioni e posta elettronica: le misure di sicurezza per i gestori
Intercettazioni e posta elettronica: le misure di sicurezza per i gestori
 
Babel presenta: Opsview
Babel presenta: OpsviewBabel presenta: Opsview
Babel presenta: Opsview
 
Monitoraggio di infrastrutture IT mediante Opsview Enteprise V4
Monitoraggio di infrastrutture IT mediante Opsview Enteprise V4Monitoraggio di infrastrutture IT mediante Opsview Enteprise V4
Monitoraggio di infrastrutture IT mediante Opsview Enteprise V4
 
OpenVAS, lo strumento open source per il vulnerability assessment
OpenVAS, lo strumento open source per il vulnerability assessmentOpenVAS, lo strumento open source per il vulnerability assessment
OpenVAS, lo strumento open source per il vulnerability assessment
 
Cross compiler per uso domestico
Cross compiler per uso domesticoCross compiler per uso domestico
Cross compiler per uso domestico
 
Sicurezza flessibile con SELinux: architettura e configurazione
Sicurezza flessibile con SELinux: architettura e configurazioneSicurezza flessibile con SELinux: architettura e configurazione
Sicurezza flessibile con SELinux: architettura e configurazione
 
Ridirezionamento di I/O con Bash: un breve approfondimento
Ridirezionamento di I/O con Bash: un breve approfondimentoRidirezionamento di I/O con Bash: un breve approfondimento
Ridirezionamento di I/O con Bash: un breve approfondimento
 
Nagios in alta affidabilità con strumenti open source
Nagios in alta affidabilità con strumenti open sourceNagios in alta affidabilità con strumenti open source
Nagios in alta affidabilità con strumenti open source
 

Dernier

2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CVKhem
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUK Journal
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxKatpro Technologies
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...apidays
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)wesley chun
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?Antenna Manufacturer Coco
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...Neo4j
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsJoaquim Jorge
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 

Dernier (20)

2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 

Testing with MySQL embedded

  • 1. Testing with MySQL embedded by Roberto Polli Integration Testing is not always common between C developers. MySQL API offer us a fast and pratical way to do it using a full-featured, dummy sql embedded server. Obviously an embedded server has its limitations, eg:  no replication;  one process at a time;  missing more logic (eg. mysqldump...) but you can implement it. See mysql embedded documentation for further info! The MySQL C API To access a MySQL server from a C program, we can use the API provided by MySQL. Functions are provided by the following library files. Client:  /usr/lib/libmysqlclient.so.16.0.0 - standard version /usr/lib/libmysqlclient_r.so.16.0.0 - reentrant version Server embedded:  /usr/lib/mysql/libmysqld.a You can use /usr/bin/mysql_config to set the build flags. That’s the standard output of #mysql_config Usage: /usr/bin/mysql_config [OPTIONS] Options: --cflags [-I/usr/include/mysql -DBIG_JOINS=1 -fno-strict- aliasing -DUNIV_LINUX -DUNIV_LINUX] --include [-I/usr/include/mysql] --libs [-Wl,-Bsymbolic-functions -rdynamic -L/usr/lib/mysql - lmysqlclient] --libs_r [-Wl,-Bsymbolic-functions -rdynamic -L/usr/lib/mysql - lmysqlclient_r] --plugindir [/usr/lib/mysql/plugin] --socket [/var/run/mysqld/mysqld.sock] BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680 E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v. Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
  • 2. --port [0] --version [5.1.41] --libmysqld-libs [-Wl,-Bsymbolic-functions -rdynamic -L/usr/lib/mysql - lmysqld -ldl -lwrap -lrt] Here is the anatomy of a MySQL client application: uses a shared library and the external server. My test application occupy about 200KBytes. An embedded app is bigger, because it’s statically linked with the mysqld library. It’s an almost complete MySQL server instance! And it’s faster! Basic working cycle with a MySQL Server The simplest way to connect to MySQL consists to: - initialize mysql library - connect to server - execute queries, eventually freeing result sets - disconnect BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680 E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v. Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
  • 3. Here is a sample program with some assertions, that show us the expected return values of the functions. #include<assert.h> #include<stdlib.h> #include<mysql/mysql.h> int main(int argc, char**argv) { int port = 0, mysql_option_flags=0; char *host = “localhost”, *user = “mysql”, *pass = “secret”, *dbname = NULL, *unix_sock = NULL; // Initialize mysql library. This should be done *once* // moreover this function is not thread-safe, nor reentrant // so we should protect it with a global mutex assert( mysql_library_init(0, NULL, NULL) == 0 ); MYSQL * mysql = NULL; // a pointer to a mysql database structure // Inizialize mysql database structure, containing all database informations // this function is not thread-safe too assert( (mysql = mysql_init(NULL)) != NULL); // connect to the RDBMS without specifying a database name int error = mysql_real_connect(mysql, host, user, pass, dbname, port, unix_sock, mysql_option_flags); // eventually print errors if (error) goto onerror; // do our job error = mysql_query(mysql, “CREATE DATABASE pluto;”); BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680 E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v. Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
  • 4. if (error) goto onerror; error = mysql_query(mysql, “USE pluto;”); if (error) goto onerror; error = mysql_query(mysql, “DROP DATABASE pluto;”); if (error) goto onerror; // disconnet and free memory and resources mysql_close(mysql); mysql_library_end(); exit(0); onerror: printf(“Error: %sn”, mysql_error(mysql)); abort(); } Going multithreads Using multiple threads - and multiple connections - require some logic. In fact the following functions must be called at once in the process - and must be protected by a global mutex: 1. mysql_library_init() 2. mysql_init() which may call mysql_library_init() Other MySQL methods are thread safe “per-connection”: the MYSQL* pointer is associated to a reference the connection. So every MYSQL* pointer can be used by a single thread at once, requiring us to do use some locking. Let’s create the following database structure, a global mutex and some helper methods. // a locking structure for each connection typedef struct my_db { MYSQL * mysql; pthread_mutex_t lock; int lock_init; char* user; char* pass; char* host; char* dbhost; char* unix_sock; BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680 E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v. Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
  • 5. int port; int options; } mysql_db; // a mutex for global calls pthread_mutex_t db_global_mutex = PTHREAD_MUTEX_INITIALIZER; The structure simply ties the database connection parameters, its MYSQL structure and a mutex that makes it thread safe. The “global mutex” avoid us to call mysql_init() in different threads. Here are some methods, the missing ones are left as an exercise to the reader. We will add a my_flush_result() method, *required* to clean resultset after queries. Please see MySQL documentation for further info about it!. Non connection-safe functions There are two ways to protect the non thread-safe functions:  call them in the main thread, before spawning the others;  or use our global mutex. The mysql_library_init() should be called once by EVERY thread using mysql - so it’s better to run it in the main(), before spawning threads. Similarly you have to run mysq_library_end() at the end of the process to free memory resources. In the same way, we can allocate all our connection before running the client threads - creating a connection pool and passing it to our threads. /** * creating a connection pool */ int main(int argc, char** argv) { // some variables int i=0, pool_size = 5; mysql_db my_pool[pool_size]; // once in the process, so no mutexes mysql_library_init(0, NULL, NULL); // again...no locks for (i=0; i<pool_size; i++) { BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680 E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v. Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
  • 6. // fill the my_pool with credential, user, passwd.... ...initialize_db_structure(&my_pool[i]);... // initialize lock pthread_mutex_init(&my_pool[i].lock); &my_pool[i]->lock_initialized = TRUE; //initialize db &my_pool[i]->mysql = mysql_init(NULL); } … pass &my_pool[i] to threads and run them... mysql_library_end(); } Otherwise we just have to protect each call with our global mutex and run our sequence at every connection... mysql_db* my_init(mysql_db *db) { /// initialize mutex, WARN can’t initialize it twice, /// so check if you have already initialized it! if (!db->lock_initialized) { pthread_mutex_init(&db->lock, NULL); db->lock_initialized=TRUE; } /// initialize mysql structure pthread_mutex_lock(&db->lock); // don’t use this db while initializing pthread_mutex_lock(&db_global_mutex); // don’t initialize two databases contemporarily db->mysql = mysql_init(NULL); pthread_mutex_unlock(&db_global_mutex); pthread_mutex_unlock(&db->lock); return db; } BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680 E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v. Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
  • 7. Connection-safe functions Connection safe functions are now easy: just lock and unlock the MYSQL* pointer in use, to have consistent values for results, counters and errors. int my_connect(mysql_db *db) { pthread_mutex_lock(&db->lock); int error = mysql_real_connect(db->mysql, db->host, db->user, db->pass, db->dbname, db->port, db->unix_sock, db->options); if (error) { printf(“Error: %sn”, mysql_error(db->mysql)); } /// release lock *after* mysql_error pthread_mutex_unlock(&db->lock); return error; } int my_query(mysql_db* db, char*query) { pthread_mutex_lock(&db->lock); error = mysql_query(db->mysql, query); if (error) { printf(“Error: %sn”, mysql_error(db->mysql)); } else { if (mysql_field_count(db->mysql) == 0) { rows = mysql_affected_rows(db->mysql); printf("%lld rows affectedn",rows ); } } pthread_mutex_unlock(&db->lock); return error; } /** * clean result */ int my_flush_result(mysql_db *db) { MYSQL *mysql = db->mysql; MYSQL_RES* result; int qstatus = 0; BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680 E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v. Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
  • 8. pthread_mutex_lock(&db->lock); do { if ((result = mysql_store_result(mysql))) { mysql_free_result(result); } if (mysql_field_count(mysql) == 0) { printf("%lld rows affectedn",mysql_affected_rows(mysql)); } else { printf("Could not retrieve result setn"); break; } if ((qstatus = mysql_next_result(mysql)) > 0) printf("Could not execute statementn"); } while(qstatus == 0); pthread_mutex_unlock(&db->lock); return qstatus; } So we’ve just create a simple MySQL client! Once you’ve understood the common pitfalls, it’s not so hard! Testing with a MySQL server Once we created our simple MySQL library, we can write our client code issuing some queries and printing reports. Provided we’ve a working MySQL server, we can create some test cases. First of all we’ll create the setup() and teardown() methods to prepare our environment. void setup(mysql_db *db) { mysql_library_init(0,NULL,NULL); db = my_init(db); my_connect(db); my_query(db, “create database pippo;”); my_query(db, “use pippo;”); } void teardown(mysql_db *db) { BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680 E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v. Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
  • 9. my_query(db, “drop database pippo;”); my_close(db); mysql_library_end(); } Then we create a simple test case, which issues a lot of insert. Note that this code is shortened for better readability! void test_sql_client(mysql_db *db) { int i, error; error = my_query(db, “create table t1(“ “id int not null auto_increment primary key” ”, value varchar(25));”); for (i=0; i<10000; i++) { error = my_query(db, “insert into t1(id,value) values (‘%d’,’prova%d’);”, i, i); my_flush_result(db); } } int main(int argc, char**argv) { mysql_db *db = calloc(sizeof(mysql_db)); ...set db values.... setup(db); test_sql_client(db); teardown(db); free(db); exit(EXIT_SUCCESS); } To build our program, mysql_test.c we issue #gcc mysql_test.c -o mysql_test $(mysql_config --libs) and with time we’ll see how long will it take to run 10k insert. Note that this will include setup and teardown time. Writing a more complete test is left as an exercise to the reader ;) BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680 E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v. Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
  • 10. # time ./mysql_test What’s MySQL embedded To run the previous file, named mysql_test.c, we need a working MySQL instance. Instead if we don’t have it - or we like more isolation and speed, we could use the MySQL embedded. Using MySQL embedded means you don’t need to connect to a server, but you’ll write directly into the MySQL database files. And it’s fast! To use MySQL embedded, you just need to link your file with the mysqld library: #gcc mysql_test.c -o mysql_test $(mysql_config --libmysqld-libs) The server instructions will be added to your program, which will increase its size. While the core client code remains the same, you need to modify a bit your initialization structure, passing some parameters to the mysql_library_init() function. Those parameters are the same that you would pass to mysqld server process, eg. --datadir=path. If you have never run MySQL server by command line, or you’re not familiar with my.conf please, take some time to play with it before continuing. In our case we should only modify the setup() function to reflect our changes. Before that we need to sketch the MySQL database directory structure. MySQL directory structure MySQL stores databases and authentication information in the /var/lib/mysql directory. The forementioned database would have the following structure: /var/lib/mysql/ ├── mysql <- the core database containing user/password credentials ├── pippo <- a newly created database By default, MySQL embedded will use the /var/lib/mysql directory, so interfering with our server and requiring RW access to /var/lib/mysql. This is usually not what we want. Instead we want to put everything in another place, eg. a temporary directory under /tmp/. So when we run the embedded server, we should create a new directory and tell it to bootstrap there a new MySQL installation. Let’s do it. BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680 E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v. Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
  • 11. Bootstrapping MySQL embedded To run the embedded server we need to provide:  a directory where to store the new mysql databases;  the parametes to run the embedded server;  the server groups. Server groups are the stanzas of my.conf file from which mysqld - and our embedded application - read configuration options. A typical my.conf files has the following structure: [mysqld] ...options for server... [embedded] ...options for the embedded server... [server] ...options for server... To specify custom parameters relative to our application, we could add the following sections to our my.conf, or - with more isolation - use another my.conf containing the specified sections. [embedded] ...options for *all* embedded applications... key_buffer_size=32M [xxx_SERVER] ...replace xxx with program name (eg. argv[0]) and add options for this specific program … datadir=/tmp/ [xxx_CLIENT] ...replace xxx with program name (eg. argv[0]) and add options for this specific program... datadir=/tmp We can even specify those parameters as arguments to the mysql_server_init() BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680 E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v. Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
  • 12. void embedded_mysql_library_init(char* progname) { /// create a temporary directory where to store the db char *datadir = strdup("/tmp/mysqlembedXXXXXX"); assert( mkdtemp(datadir)!=NULL ); /// prepare server parameters const char *server_options[] = { progname, /* this string is not used */ "-h", datadir, "--key_buffer_size=32M", "--bootstrap", // create the required mysql files NULL }; int num_elements = sizeof(server_options) / sizeof(char *) -1; /// create server groups for my.cnf sprintf(progname_client, "%s_CLIENT", progname); sprintf(progname_server, "%s_SERVER", progname); char *server_groups[] = { progname_server, progname_client, "embedded", NULL // end of arguments }; /// initialize the embedded server assert(mysql_library_init(num_elements, (char**) server_options, server_groups) == 0); } void embedded_my_init(mysql_db *db) { pthread_mutex_lock(&db->lock, NULL); /// initialize and set options assert( (mysql = mysql_init(db->mysql)) != NULL ); assert( mysql_options(db->mysql, MYSQL_READ_DEFAULT_GROUP, progname_client) == 0); assert( mysql_options(db->mysql, MYSQL_OPT_USE_EMBEDDED_CONNECTION, NULL) == 0); BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680 E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v. Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
  • 13. pthread_mutex_unlock(&db->lock, NULL); return db; } With our new methods, the setup function become: void setup(mysql_db *db, char* progname) { embedded_mysql_library_init(progname); db = embeded_my_init(db); // from here everything is the same my_connect(db); my_query(db, “create database pippo;”); my_query(db, “use pippo;”); } And our new main(): int main(int argc, char**argv) { mysql_db *db = calloc(sizeof(mysql_db)); ...set db values.... setup(db, argv[0]); test_sql_client(db); teardown(db); exit(EXIT_SUCCESS); } Replacing the old one with this one, everything will work nicely in both cases. When you link against libmysqlclient, all the different initialization variables - only applicable to embedded server - are ignored. Duplicate test files with embedded sql To run both tests you have to compile twice your software: #gcc mysql_test.c -o mysql_server_test $(mysql_config --libs) BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680 E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v. Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
  • 14. #gcc mysql_test.c -o mysql_embedded_test $(mysql_config --libmysqld-libs) Now you have two executables, one using the client connection, one the embedded server. Here are some speed results for 50k INSERTs run locally on my Laptop. You’re welcome to provide further stats for SELECT, UPDATE, INSERT or UPDATE and TRANSACTIONS! CPU: 2 x Intel(R) Core(TM)2 Duo CPU @2.1Ghz RAM: 8Gb MySQL Server InnoDB 21 sec MySQL Server MemoryDB 7sec MySQL Embedded InnoDB 5.7 sec MySQL Embedded MemoryDB 5.6 sec BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680 E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v. Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015
  • 15. Licenza d'uso “Attribuzione - Non commerciale - Non opere derivate”, secondo i criteri internazionali Creative Commons (http://creativecommons.org/licenses/by-nc-nd/2.5/it/) BABEL S.r.l. - P.zza S.Benedetto da Norcia 33 - 00040, Pomezia (RM) - Tel:. +39 06.9826.9600 - Fax. +39 06.9826.9680 E-Mail: info@babel.it – PEC: babel@pec.babel.it – WEB: http://www.babel.it Res. Imprese di Roma N° 06062681009 - N.R.E.A. 953123 - P.I. e C.F. 06062681009 Cap. Soc. € 102.774,00 i.v. Società soggetta alla direzione e coordinamento della Par-tec S.p.A - Società iscritta al registro delle Imprese di Milano al numero 1293820015