Oracle et le C 4eme Partie : Acceder a Oracle via UnixODBC

Dans cet article on va voir comment accéder a une base de données Oracle via ODBC en utilisant UnixODBC. ODBC (Open DataBase Connectivity) est un standard qui permet d’accéder a une base de données via une API normé quelque soit son type du moment ou elle se conforme a la cette norme, pour plus d’infos et de théorie. UnixODBC est un projet open source qui implémente les spécifications de la norme ODBC et permet par conséquent l’accès aux sources de données.

Pour installer UnixODBC sous Ubunutu :

sudo apt-get install unixodbc unixodbc-dev

Ou a partir des sources :

wget ftp://ftp.unixodbc.org/pub/unixODBC/unixODBC-2.3.0.tar.gz
tar xvfz unixODBC-2.3.0.tar.gz
cd unixODBC-2.3.1
./configure --prefix=/usr/local/UnixODBC
make
sudo make install

Les commandes suivantes nécessitent les droits root. Créer (si ce n’est pas fait lors de l’install) les deux fichiers suivants :

toc@tocNewServer:~$ cat /etc/odbc.ini
[Oracle]
Application Attributes = T
Attributes = W
BatchAutocommitMode = IfAllSuccessful
CloseCursor = F
DisableDPM = F
DisableMTS = T
Driver = Oracle
EXECSchemaOpt =
EXECSyntax = T
Failover = T
FailoverDelay = 10
FailoverRetryCount = 10
FetchBufferSize = 64000
ForceWCHAR = F
Lobs = T
Longs = T
MetadataIdDefault = F
QueryTimeout = T
ResultSets = T
ServerName = //192.168.1.95:1521/TOCORACL
SQLGetData extensions = F
Translation DLL =
Translation Option = 0
UserID =

ET :

toc@tocNewServer:~$ cat /etc/odbcinst.ini
[ODBC]
Trace = yes
TraceFile = /tmp/odbc.log

[Oracle]
Description = Oracle ODBC Connection
Driver = /usr/lib/oracle/11.2/client/lib/libsqora.so.11.1
Setup =
FileUsage =
CPTimeout =
CPReuse =

UnixODBC vous permet de définir des sources de données (Oracle, Sybase, DB2, Teradata, PostgreSQL, MySQL, …) et de définir un driver pour chacune de ces sources (il s’agit une librairie) qui implémente lui les API permettant l’accès a la source de données. Chaque éditeur de base de données dispose de ces propres drivers.
Pour tester la connexion via ODBC, le package UnixODBC installe un utilitaire qui s’appelle isql (/usr/bin/isql) que vous pouvez utilisez pour requêter sur une base (ou Source de Données dans le jargon ODBC) :

toc@tocNewServer:~$ isql --version
unixODBC 2.2.14
toc@tocNewServer:~$ isql -v Oracle hr human
+---------------------------------------+
| Connected!                            |
|                                       |
| sql-statement                         |
| help [tablename]                      |
| quit                                  |
|                                       |
+---------------------------------------+
SQL> select OBJECT_NAME from user_objects where OBJECT_TYPE = 'TABLE';
+---------------------------------------------------------------------------------------------------------------------------------+
| OBJECT_NAME                                                                                                                     |
+---------------------------------------------------------------------------------------------------------------------------------+
| REGIONS                                                                                                                         |
| LOCATIONS                                                                                                                       |
| JOB_HISTORY                                                                                                                     |
| JOBS                                                                                                                            |
| EMPLOYEES                                                                                                                       |
| DEPARTMENTS                                                                                                                     |
| COUNTRIES                                                                                                                       |
+---------------------------------------------------------------------------------------------------------------------------------+
SQLRowCount returns -1
7 rows fetched
SQL> quit
toc@tocNewServer:~$

La syntax est :

isql SOURCE_DE_DONNEES USER PASSWORD

La source de données est le nom que vous avez utilisez dans votre fichier de conf (/etc/odbc.ini) (« Oracle » dans notre cas).

La connexion fonctionne, on peut donc écrire un programme C qui utilise l’API d’UnixODBC pour requeter en base. Voila odbc-demo.c :

/*
 * odbc-demo.c : ce programme demontre
 * un simple select dans une base utilisant UnixODBC
 */
#include <stdio.h>
#include <stdlib.h>
#include <sql.h>
#include <sqlext.h>
 
#define	BUFFER_LEN	1024
#define	OK		0
#define	KO		-1
#define	TIMEOUT		5	/* Timeout is set to 5 seconds */
#define	REC_NUMBER	1	/* Pour la fonction SQLGetDiagRec */
#define	SQLSTATE_LEN	7
#define	DB_NAME_LEN	128
#define	DB_VERSION_LEN	32
#define	STRING_REQUEST	"SELECT department_id, first_name, last_name, salary FROM HR.employees WHERE department_id is not null"
 
int odbc_connection(char *datasource, char *user, char *pwd)
{
	long status;
	SQLCHAR	 state[SQLSTATE_LEN];		/* Pour SQLSTATE code qui designe une erreur ou un warning */
	SQLINTEGER odbc_error;
	SQLSMALLINT error_text_len;
	SQLCHAR error_text[BUFFER_LEN];
	SQLCHAR db_name[DB_NAME_LEN];		/* Nom de la base de donnees */
	SQLCHAR db_version[DB_VERSION_LEN];	/* Version de la base de donnes */
	SQLHENV environment_handle;     	/* Pour mettre en place notre environnement */
	SQLHDBC connection_handle;      	/* Pour la connexion a la source de donnees */
	SQLHSTMT statment_handle;			/* Pour notre requete */
	int emp_department_id = 0;
	SQLCHAR	emp_first_name[21];
	SQLCHAR emp_last_name[26];
	float	emp_salary = 0.0;
	int number_fetched_lines = 0;
 
	/* Etape 1. allocation de la memoire pour mettre en place notre environnement ODBC */
	status = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &environment_handle);
	if ((status != SQL_SUCCESS) && (status != SQL_SUCCESS_WITH_INFO)) {
		fprintf(stderr, "SQLAllocHandle: SQL_HANDLE_ENV Failed\n");
		return KO;
	}
	/* Tous les recents driver ODBC supporte la version 3 d'ODBC */
	status = SQLSetEnvAttr(environment_handle, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, SQL_IS_INTEGER);
	if ((status != SQL_SUCCESS) && (status != SQL_SUCCESS_WITH_INFO)) {
		fprintf(stderr, "SQLSetEnvAttr Failed\n");
		SQLFreeHandle(SQL_HANDLE_ENV, environment_handle);
		return KO;
	}
	/* Etape 2. allocation de la memoire pour le handle de la connexion */
	status = SQLAllocHandle(SQL_HANDLE_DBC, environment_handle, &connection_handle);
	if ((status != SQL_SUCCESS) && (status != SQL_SUCCESS_WITH_INFO)) {
		fprintf(stderr, "SQLAllocHandle: SQL_HANDLE_DBC Failed\n");
		SQLFreeHandle(SQL_HANDLE_ENV, environment_handle);
		return KO ;
	}
	/* Mise en place d'un timeout : temps d'attente d'une connexion */
	SQLSetConnectAttr(connection_handle, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) TIMEOUT, 0);
	/* Etape 3. connexion a la source de donnees "datasource" */
	/* On peut aussi utiliser la fonction SQLDriverConnect qui prend plus de parametres */
	status = SQLConnect(connection_handle, (SQLCHAR *) datasource, SQL_NTS, (SQLCHAR *) user, SQL_NTS, (SQLCHAR *) pwd, SQL_NTS);
	if ((status != SQL_SUCCESS) && (status != SQL_SUCCESS_WITH_INFO)) {
		fprintf(stderr, "SQLConnect failed\n");
		SQLGetDiagRec(SQL_HANDLE_DBC, connection_handle, REC_NUMBER, state, &odbc_error, error_text, BUFFER_LEN, &error_text_len);
		fprintf(stderr, "%s (error number : %d)\n", error_text, (int)odbc_error);
		SQLFreeHandle(SQL_HANDLE_DBC, connection_handle);
		SQLFreeHandle(SQL_HANDLE_ENV, environment_handle);
		return KO;
	}
	fprintf(stdout, "Connection to DataSource %s succeeded\n", datasource);
	/* Le nom et la version de la base de donnees */
	SQLGetInfo(connection_handle, SQL_DBMS_NAME, (SQLPOINTER)db_name, sizeof(db_name), NULL);
	SQLGetInfo(connection_handle, SQL_DBMS_VER, (SQLPOINTER)db_version, sizeof(db_version), NULL);
	fprintf(stdout, "Database name : %s\n", db_name);
	fprintf(stdout, "Database version : %s\n", db_version);
	/* Etape 3. allocation de la memoire pour la requete */
	status = SQLAllocHandle(SQL_HANDLE_STMT, connection_handle, &statment_handle);
	if ((status != SQL_SUCCESS) && (status != SQL_SUCCESS_WITH_INFO)) {
		fprintf(stderr, "SQLAllocHandle: SQL_HANDLE_STMT Failed\n");	
		/* On se deconnecte avant de quitter */
		status = SQLDisconnect(connection_handle);
		if ((status != SQL_SUCCESS) && (status != SQL_SUCCESS_WITH_INFO)) {
			fprintf(stderr, "SQLDisconnect failed\n");
			SQLGetDiagRec(SQL_HANDLE_DBC, connection_handle, REC_NUMBER, state, &odbc_error, error_text, BUFFER_LEN, &error_text_len);
			fprintf(stderr, "%s (error number : %d)\n", error_text, (int)odbc_error);
		}
		SQLFreeHandle(SQL_HANDLE_DBC, connection_handle);
		SQLFreeHandle(SQL_HANDLE_ENV, environment_handle);
		return KO;
	}
	/* Etape 4. executer la requete */
	status = SQLExecDirect(statment_handle, (SQLCHAR *)STRING_REQUEST, SQL_NTS);
	if ((status != SQL_SUCCESS) && (status != SQL_SUCCESS_WITH_INFO)) {
		fprintf(stderr, "SQLExecDirect Failed\n");
		/* On se deconnecte avant de quitter */
		status = SQLDisconnect(connection_handle);
		if ((status != SQL_SUCCESS) && (status != SQL_SUCCESS_WITH_INFO)) {
			fprintf(stderr, "SQLDisconnect failed\n");
			SQLGetDiagRec(SQL_HANDLE_DBC, connection_handle, REC_NUMBER, state, &odbc_error, error_text, BUFFER_LEN, &error_text_len);
			fprintf(stderr, "%s (error number : %d)\n", error_text, (int)odbc_error);
		}
		SQLFreeHandle(SQL_HANDLE_STMT, statment_handle);
		SQLFreeHandle(SQL_HANDLE_DBC, connection_handle);
		SQLFreeHandle(SQL_HANDLE_ENV, environment_handle);
		return KO;
	}
	/* Etape 5. recuperer les resultats de la requete */
	/* Correspondance entre les variables et leur position dans la requete :
	 * emp_department_id correspond a department_id, il est en position 1 et est de type int
	 * emp_first_name correspond a first_name, de type string et est en position 2
	 * emp_last_name correspond a last_name, de type string et est en position 3
	 * emp_salary correspond a salary, de type float et est en position 4
	 */
	SQLBindCol(statment_handle, 1, SQL_C_LONG, &emp_department_id, sizeof(emp_department_id), &odbc_error);
	SQLBindCol(statment_handle, 2, SQL_C_CHAR, &emp_first_name, sizeof(emp_first_name), &odbc_error);
	SQLBindCol(statment_handle, 3, SQL_C_CHAR, &emp_last_name, sizeof(emp_last_name), &odbc_error);
	SQLBindCol(statment_handle, 4, SQL_C_FLOAT, &emp_salary, sizeof(emp_salary), &odbc_error);
	while(SQL_NO_DATA != (status = SQLFetch(statment_handle))) {
		fprintf(stdout, "<%d> - <%s> - <%s> - <%f>\n", emp_department_id, emp_first_name, emp_last_name, emp_salary);	
		number_fetched_lines++;
	}
	/* Afficher le nombre de lignes retourné */
	fprintf(stdout, "%d rows fetched\n", number_fetched_lines);
 
	SQLFreeHandle(SQL_HANDLE_STMT, statment_handle);
	SQLFreeHandle(SQL_HANDLE_DBC, connection_handle);
	SQLFreeHandle(SQL_HANDLE_ENV, environment_handle);
	return OK;
}
 
int main(int argc, char **argv)
{
	if (argc != 4) {
		fprintf(stdout, "Usage : %s dataSource user password\n", argv[0]);
		return KO;
	}
	odbc_connection(argv[1], argv[2], argv[3]);
	return OK;
}

Qu’on compile et exécute de la manière suivante :

toc@tocNewServer:~/toc_src/DATABASES$ gcc -Wall odbc-demo.c -o odbc-demo -lodbc
toc@tocNewServer:~/toc_src/DATABASES$ ./odbc-demo Oracle hr human
Connection to DataSource Oracle succeeded
Database name : Oracle
Database version : 11.02.0010
<50> - <Donald> - <OConnell> - <2600.000000>
<50> - <Douglas> - <Grant> - <2600.000000>
<10> - <Jennifer> - <Whalen> - <4400.000000>
<20> - <Michael> - <Hartstein> - <13000.000000>
<20> - <Pat> - <Fay> - <6000.000000>
<40> - <Susan> - <Mavris> - <6500.000000>
<70> - <Hermann> - <Baer> - <10000.000000>
<110> - <Shelley> - <Higgins> - <12008.000000>
<110> - <William> - <Gietz> - <8300.000000>
<90> - <Steven> - <King> - <24000.000000>
<90> - <Neena> - <Kochhar> - <17000.000000>
<90> - <Lex> - <De Haan> - <17000.000000>
<60> - <Alexander> - <Hunold> - <9000.000000>
<60> - <Bruce> - <Ernst> - <6000.000000>
<60> - <David> - <Austin> - <4800.000000>
<60> - <Valli> - <Pataballa> - <4800.000000>
<60> - <Diana> - <Lorentz> - <4200.000000>
<100> - <Nancy> - <Greenberg> - <12008.000000>
<100> - <Daniel> - <Faviet> - <9000.000000>
<100> - <John> - <Chen> - <8200.000000>
<100> - <Ismael> - <Sciarra> - <7700.000000>
<100> - <Jose Manuel> - <Urman> - <7800.000000>
<100> - <Luis> - <Popp> - <6900.000000>
<30> - <Den> - <Raphaely> - <11000.000000>
<30> - <Alexander> - <Khoo> - <3100.000000>
<30> - <Shelli> - <Baida> - <2900.000000>
<30> - <Sigal> - <Tobias> - <2800.000000>
<30> - <Guy> - <Himuro> - <2600.000000>
<30> - <Karen> - <Colmenares> - <2500.000000>
<50> - <Matthew> - <Weiss> - <8000.000000>
<50> - <Adam> - <Fripp> - <8200.000000>
<50> - <Payam> - <Kaufling> - <7900.000000>
<50> - <Shanta> - <Vollman> - <6500.000000>
<50> - <Kevin> - <Mourgos> - <5800.000000>
<50> - <Julia> - <Nayer> - <3200.000000>
<50> - <Irene> - <Mikkilineni> - <2700.000000>
<50> - <James> - <Landry> - <2400.000000>
<50> - <Steven> - <Markle> - <2200.000000>
<50> - <Laura> - <Bissot> - <3300.000000>
<50> - <Mozhe> - <Atkinson> - <2800.000000>
<50> - <James> - <Marlow> - <2500.000000>
<50> - <TJ> - <Olson> - <2100.000000>
<50> - <Jason> - <Mallin> - <3300.000000>
<50> - <Michael> - <Rogers> - <2900.000000>
<50> - <Ki> - <Gee> - <2400.000000>
<50> - <Hazel> - <Philtanker> - <2200.000000>
<50> - <Renske> - <Ladwig> - <3600.000000>
<50> - <Stephen> - <Stiles> - <3200.000000>
<50> - <John> - <Seo> - <2700.000000>
<50> - <Joshua> - <Patel> - <2500.000000>
<50> - <Trenna> - <Rajs> - <3500.000000>
<50> - <Curtis> - <Davies> - <3100.000000>
<50> - <Randall> - <Matos> - <2600.000000>
<50> - <Peter> - <Vargas> - <2500.000000>
<80> - <John> - <Russell> - <14000.000000>
<80> - <Karen> - <Partners> - <13500.000000>
<80> - <Alberto> - <Errazuriz> - <12000.000000>
<80> - <Gerald> - <Cambrault> - <11000.000000>
<80> - <Eleni> - <Zlotkey> - <10500.000000>
<80> - <Peter> - <Tucker> - <10000.000000>
<80> - <David> - <Bernstein> - <9500.000000>
<80> - <Peter> - <Hall> - <9000.000000>
<80> - <Christopher> - <Olsen> - <8000.000000>
<80> - <Nanette> - <Cambrault> - <7500.000000>
<80> - <Oliver> - <Tuvault> - <7000.000000>
<80> - <Janette> - <King> - <10000.000000>
<80> - <Patrick> - <Sully> - <9500.000000>
<80> - <Allan> - <McEwen> - <9000.000000>
<80> - <Lindsey> - <Smith> - <8000.000000>
<80> - <Louise> - <Doran> - <7500.000000>
<80> - <Sarath> - <Sewall> - <7000.000000>
<80> - <Clara> - <Vishney> - <10500.000000>
<80> - <Danielle> - <Greene> - <9500.000000>
<80> - <Mattea> - <Marvins> - <7200.000000>
<80> - <David> - <Lee> - <6800.000000>
<80> - <Sundar> - <Ande> - <6400.000000>
<80> - <Amit> - <Banda> - <6200.000000>
<80> - <Lisa> - <Ozer> - <11500.000000>
<80> - <Harrison> - <Bloom> - <10000.000000>
<80> - <Tayler> - <Fox> - <9600.000000>
<80> - <William> - <Smith> - <7400.000000>
<80> - <Elizabeth> - <Bates> - <7300.000000>
<80> - <Sundita> - <Kumar> - <6100.000000>
<80> - <Ellen> - <Abel> - <11000.000000>
<80> - <Alyssa> - <Hutton> - <8800.000000>
<80> - <Jonathon> - <Taylor> - <8600.000000>
<80> - <Jack> - <Livingston> - <8400.000000>
<80> - <Charles> - <Johnson> - <6200.000000>
<50> - <Winston> - <Taylor> - <3200.000000>
<50> - <Jean> - <Fleaur> - <3100.000000>
<50> - <Martha> - <Sullivan> - <2500.000000>
<50> - <Girard> - <Geoni> - <2800.000000>
<50> - <Nandita> - <Sarchand> - <4200.000000>
<50> - <Alexis> - <Bull> - <4100.000000>
<50> - <Julia> - <Dellinger> - <3400.000000>
<50> - <Anthony> - <Cabrio> - <3000.000000>
<50> - <Kelly> - <Chung> - <3800.000000>
<50> - <Jennifer> - <Dilly> - <3600.000000>
<50> - <Timothy> - <Gates> - <2900.000000>
<50> - <Randall> - <Perkins> - <2500.000000>
<50> - <Sarah> - <Bell> - <4000.000000>
<50> - <Britney> - <Everett> - <3900.000000>
<50> - <Samuel> - <McCain> - <3200.000000>
<50> - <Vance> - <Jones> - <2800.000000>
<50> - <Alana> - <Walsh> - <3100.000000>
<50> - <Kevin> - <Feeney> - <3000.000000>
106 rows fetched
toc@tocNewServer:~/toc_src/DATABASES$
Publicité

Votre 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 )

Connexion à %s