Dans cette article on va voir comment on peut accéder a Sybase en utilisant la librairie C natif de Sybase (Client-Library ou CTLIB), cette librairie est celle que toutes les nouvelles applications Sybase en C doivent utilisé, historiquement c’était une autre librairie qu’on utilisait il s’agit de la DB Library (DBLIB), cette librairie existe toujours mais juste pour une histoire de compatibilité, donc a éviter dans vos applications récentes.
La librairie en question est : /opt/sybase/OCS-15_0/lib/libsybct.so (elle dépend de la librairie CS-Library « /opt/sybase/OCS-15_0/lib/libsybcs.so » : responsable de l’allocation des « Core data structure » notamment CS_CONTEXT) et les headers sont ici : /opt/sybase/OCS-15_0/include. Pour notre démo on crée un simple fichier : sybase-hello-ctlib.c (on utilisera la table authors de la base pubs2) :
/*
* sybase-hello-ctlib.c : Un programme simple qui montre
* l'utilisation du Sybase Client Library
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Header pour la CT-Lib */
#include <ctpublic.h>
#define CTLIB_VERSION CS_CURRENT_VERSION /* Version de la CT-Lib */
#define OK 0
#define KO 1
/* Informations de connexion */
#define DB_SERVER "TCOSERVER" /* defini dans le fichier /opt/sybase/interfaces */
/* #define DB_SERVER NULL */ /* Dans ce cas il faut faire : export DSQUERY="TCOSERVER" */
#define DB_USER "tarek"
#define DB_PASSWORD "123456"
#define NUM_CLOMUNS 3
#define MAXSTRING 40
#define STATELEN 3
/* Macro utilisee en cas d'erreur */
#define EXIT_ON_FAIL(context, ret, str) \
if (ret != CS_SUCCEED) \
{ \
fprintf(stdout, "Fatal error: %s\n", str); \
if (context != NULL) \
{ \
(CS_VOID) ct_exit(context, CS_FORCE_EXIT); \
(CS_VOID) cs_ctx_drop(context); \
} \
exit(EXIT_FAILURE); \
}
/* Prototypes des fonctions de gestion des erreurs */
CS_RETCODE CS_PUBLIC cslibmsg_callback(CS_CONTEXT *context, CS_CLIENTMSG *clientmsg);
CS_RETCODE CS_PUBLIC clientmsg_callback(CS_CONTEXT *context, CS_CONNECTION *connection, CS_CLIENTMSG *clientmsg);
CS_RETCODE CS_PUBLIC servermsg_callback(CS_CONTEXT *context, CS_CONNECTION *connection, CS_SERVERMSG *servermsg);
int main(int argc, char **argv)
{
/* User et mot de passe */
CS_CHAR *username = DB_USER;
CS_CHAR *password = DB_PASSWORD;
CS_CONTEXT *context; /* Structure de Contexte */
CS_CONNECTION *connection; /* Structure de Connexion */
CS_COMMAND *cmd; /* Command structure */
/* Data format structures for column descriptions: */
CS_DATAFMT columns[NUM_CLOMUNS];
CS_INT datalength[NUM_CLOMUNS];
CS_SMALLINT indicator[NUM_CLOMUNS];
CS_INT count;
CS_RETCODE ret;
CS_RETCODE results_ret;
CS_INT result_type;
CS_CHAR name[MAXSTRING];
CS_CHAR city[MAXSTRING];
CS_CHAR state[STATELEN];
/* Etape 1: Initialisation */
/* Allocation de la structure de contexte */
context = NULL;
ret = cs_ctx_alloc(CTLIB_VERSION, &context);
EXIT_ON_FAIL(context, ret, "cs_ctx_alloc failed");
/* Initialisation de la Client-Library */
ret = ct_init(context, CTLIB_VERSION);
EXIT_ON_FAIL(context, ret, "ct_init failed");
/* Etape 2: Mise en place des fonctions de gestion des erreurs et des warnings */
/* Gestion des erreurs au niveau de la librairie */
ret = cs_config(context, CS_SET, CS_MESSAGE_CB, (CS_VOID *)cslibmsg_callback, CS_UNUSED, NULL);
EXIT_ON_FAIL(context, ret, "cs_config(CS_MESSAGE_CB) failed");
/* Gestion des erreurs au niveau du client */
ret = ct_callback(context, NULL, CS_SET, CS_CLIENTMSG_CB, (CS_VOID *)clientmsg_callback);
EXIT_ON_FAIL(context, ret, "ct_callback for client messages failed");
/* Gestion des erreurs au niveau du serveur */
ret = ct_callback(context, NULL, CS_SET, CS_SERVERMSG_CB, (CS_VOID *)servermsg_callback);
EXIT_ON_FAIL(context, ret, "ct_callback for server messages failed");
/* Etape 3: Connexion au serveur */
/* Allocation de la structure de connexion */
ret = ct_con_alloc(context, &connection);
EXIT_ON_FAIL(context, ret, "ct_con_alloc() failed");
/* Mise en place du user et du password */
ret = ct_con_props(connection, CS_SET, CS_USERNAME, username, CS_NULLTERM, NULL);
EXIT_ON_FAIL(context, ret, "Could not set user name");
ret = ct_con_props(connection, CS_SET, CS_PASSWORD, password, CS_NULLTERM, NULL);
EXIT_ON_FAIL(context, ret, "Could not set password");
/* On se connecte */
if (DB_SERVER == NULL)
{
/* Si aucun serveur n'est fourni, la variable
* d'environnement DSQUERY doit contenir le nom
* du serveur tel que defini dans le fichier
* "/opt/sybase/interfaces"
*/
ret = ct_connect(connection, NULL, 0);
}
else
{
ret = ct_connect(connection, DB_SERVER, CS_NULLTERM);
}
EXIT_ON_FAIL(context, ret, "Could not connect!");
/* Etape 4: envoi d'une requete au serveur */
/* Allocation de la structure pour envoyer la requete */
ret = ct_cmd_alloc(connection, &cmd);
EXIT_ON_FAIL(context, ret, "ct_cmd_alloc() failed");
/* Mise en place de notre requete */
ret = ct_command(cmd, CS_LANG_CMD, "SELECT au_lname, city, state from pubs2..authors", CS_NULLTERM, CS_UNUSED);
EXIT_ON_FAIL(context, ret, "ct_command() failed");
/* On envoi la requete au serveur */
ret = ct_send(cmd);
EXIT_ON_FAIL(context, ret, "ct_send() failed");
/* Etape 5: recuperation du resultat de la requete */
while ((results_ret = ct_results(cmd, &result_type)) == CS_SUCCEED)
{
switch ((int)result_type)
{
/* La requete a renvoyee un resultat */
case CS_ROW_RESULT:
/* 1ere colonne "au_lname" */
columns[0].datatype = CS_CHAR_TYPE;
columns[0].format = CS_FMT_NULLTERM;
columns[0].maxlength = MAXSTRING;
columns[0].count = 1;
columns[0].locale = NULL;
ret = ct_bind(cmd, 1, &columns[0], name, &datalength[0], &indicator[0]);
EXIT_ON_FAIL(context, ret, "ct_bind() for au_lname failed");
/* 2eme colonne "city" */
columns[1].datatype = CS_CHAR_TYPE;
columns[1].format = CS_FMT_NULLTERM;
columns[1].maxlength = MAXSTRING;
columns[1].count = 1;
columns[1].locale = NULL;
ret = ct_bind(cmd, 2, &columns[1], city, &datalength[1], &indicator[1]);
EXIT_ON_FAIL(context, ret, "ct_bind() for city failed");
/* 3eme colonne "state" */
columns[2].datatype = CS_CHAR_TYPE;
columns[2].format = CS_FMT_NULLTERM;
columns[2].maxlength = STATELEN;
columns[2].count = 1;
columns[2].locale = NULL;
ret = ct_bind(cmd, 3, &columns[2], state, &datalength[2], &indicator[2]);
EXIT_ON_FAIL(context, ret, "ct_bind() for state failed");
/* Fetch des lignes renvoyees */
while (((ret = ct_fetch(cmd, CS_UNUSED, CS_UNUSED, CS_UNUSED, &count)) == CS_SUCCEED) || (ret == CS_ROW_FAIL))
{
/* En cas d'erreur sur une ligne, on le signale */
if (ret == CS_ROW_FAIL)
{
fprintf(stdout, "Error on row %ld.\n", (long)(count + 1));
}
/* Affichage de la ligne */
fprintf(stdout, "%15s | %15s | %3s\n", name, city, state);
}
/*
* Les lignes renvoyees par la requete ont etaient affichees,
* si tout s'est bien passe alors on doit avoir : CS_END_DATA
*/
if (ret == CS_END_DATA)
{
fprintf(stdout, "\nAll done processing rows.\n");
}
/* Une erreur s'est produite */
else
{
EXIT_ON_FAIL(context, CS_FAIL, "ct_fetch failed");
}
break;
case CS_CMD_SUCCEED:
/* La requete s'est bien passee, mais n'a renvoyee aucune ligne */
fprintf(stdout, "No rows returned.\n");
break;
case CS_CMD_FAIL:
/* Une erreur s'est produite lors de l'execution de la requete */
break;
case CS_CMD_DONE:
break;
default:
/* Une erreur inatendue s'est produite */
EXIT_ON_FAIL(context, CS_FAIL, "ct_results returned unexpected result type");
break;
}
}
/* Analyse du resultat de l'etape precedente et sortie en erreur au cas ou */
switch ((int)results_ret)
{
case CS_END_RESULTS:
/* Tout est OK */
break;
case CS_FAIL:
/* Une erreur lors du traitement */
EXIT_ON_FAIL(context, CS_FAIL, "ct_results() returned CS_FAIL.");
break;
default:
/* Une erreur inatendue s'est produite */
EXIT_ON_FAIL(context, CS_FAIL, "ct_results returned unexpected return code");
break;
}
/* Etape 6: liberation de la memoire, deconnexion */
/* Liberation de la requete */
ret = ct_cmd_drop(cmd);
EXIT_ON_FAIL(context, ret, "ct_cmd_drop failed");
/* Fermeture de la connexion et liberation de la memoire associee a sa structure */
ret = ct_close(connection, CS_UNUSED);
EXIT_ON_FAIL(context, ret, "ct_close failed");
ret = ct_con_drop(connection);
EXIT_ON_FAIL(context, ret, "ct_con_drop failed");
/* Quitter la CT-Lib et liberer la memoire */
ret = ct_exit(context, CS_UNUSED);
EXIT_ON_FAIL(context, ret, "ct_exit failed");
ret = cs_ctx_drop(context);
EXIT_ON_FAIL(context, ret, "cs_ctx_drop failed");
return OK;
}
/* Fonction de gestion des erreurs au niveau du serveur */
CS_RETCODE CS_PUBLIC servermsg_callback(CS_CONTEXT *cp, CS_CONNECTION *chp, CS_SERVERMSG *msgp)
{
fprintf(stdout, "Server message:\n\t");
fprintf(stdout, "number(%ld) severity(%ld) state(%ld) line(%ld)\n",
(long)msgp->msgnumber, (long)msgp->severity,
(long)msgp->state, (long)msgp->line);
if (msgp->svrnlen > 0)
fprintf(stdout, "\tServer name: %s\n", msgp->svrname);
if (msgp->proclen > 0)
fprintf(stdout, "\tProcedure name: %s\n", msgp->proc);
fprintf(stdout, "\t%s\n", msgp->text);
return CS_SUCCEED;
}
/* Fonction de gestion des erreurs au niveau du client */
CS_RETCODE CS_PUBLIC clientmsg_callback(CS_CONTEXT *context, CS_CONNECTION *conn, CS_CLIENTMSG *emsgp)
{
fprintf(stdout, "Client Library error:\n\t");
fprintf(stdout, "severity(%ld) number(%ld) origin(%ld) layer(%ld)\n",
(long)CS_SEVERITY(emsgp->severity),
(long)CS_NUMBER(emsgp->msgnumber),
(long)CS_ORIGIN(emsgp->msgnumber),
(long)CS_LAYER(emsgp->msgnumber));
fprintf(stdout, "\t%s\n", emsgp->msgstring);
if (emsgp->osstringlen > 0)
{
fprintf(stdout, "Operating system error number(%ld):\n", (long)emsgp->osnumber);
fprintf(stdout, "\t%s\n", emsgp->osstring);
}
return CS_SUCCEED;
}
/* Fonction de gestion des erreurs au niveau de la librairie */
CS_RETCODE CS_PUBLIC cslibmsg_callback(CS_CONTEXT *context, CS_CLIENTMSG *emsgp)
{
fprintf(stdout, "CS-Library error:\n");
fprintf(stdout, "\tseverity(%ld) layer(%ld) origin(%ld) number(%ld)",
(long)CS_SEVERITY(emsgp->msgnumber),
(long)CS_LAYER(emsgp->msgnumber),
(long)CS_ORIGIN(emsgp->msgnumber),
(long)CS_NUMBER(emsgp->msgnumber));
fprintf(stdout, "\t%s\n", emsgp->msgstring);
if (emsgp->osstringlen > 0)
{
fprintf(stdout, "Operating System Error: %s\n", emsgp->osstring);
}
return CS_SUCCEED;
}
Qu’on compile comme suit :
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/sybase/OCS-15_0/lib gcc -Wall sybase-hello-ctlib.c -o sybase-hello-ctlib -I/opt/sybase/OCS-15_0/include -L/opt/sybase/OCS-15_0/lib -lsybct -lsybcs
Et qu’on exécute :
unset LANG # N'est peut etre pas necessaire
export SYBASE=/opt/sybase
export SYBASE_OCS=OCS-15_0
./sybase-hello-ctlib
Server message:
number(5701) severity(10) state(2) line(0)
Server name: TCOSERVER
Changed database context to 'master'.
White | Menlo Park | CA
Green | Oakland | CA
Carson | Berkeley | CA
O'Leary | San Jose | CA
Straight | Oakland | CA
Smith | Lawrence | KS
Bennet | Berkeley | CA
Dull | Palo Alto | CA
Gringlesby | Covelo | CA
Locksley | San Francisco | CA
Greene | Nashville | TN
Blotchet-Halls | Corvallis | OR
Yokomoto | Walnut Creek | CA
del Castillo | Ann Arbor | MI
DeFrance | Gary | IN
Stringer | Oakland | CA
MacFeather | Oakland | CA
Karsen | Oakland | CA
Panteley | Rockville | MD
Hunter | Palo Alto | CA
McBadden | Vacaville | CA
Ringer | Salt Lake City | UT
Ringer | Salt Lake City | UT
All done processing rows.
Dans un prochain article on verra comment accéder a Sybase via UnixODBC.