Sybase et le C 3eme partie : Sybase Client Library

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.

Publicités

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s