From 5db391b60a5ad506ce4f50dbd62074c81da8c6e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADtor=20Santos=20Costa?= Date: Thu, 5 Nov 2015 17:06:15 +0000 Subject: [PATCH] separate DB dependent from DB index. --- packages/myddas/CMakeLists.txt | 2 +- packages/myddas/myddas_types.h | 113 +++ packages/myddas/myddas_util.h | 41 + packages/myddas/myddas_util_connection.c | 6 + packages/myddas/mysql/CMakeLists.txt | 42 + packages/myddas/mysql/myddas_mysql.c | 737 +++++++++++++++++ packages/myddas/mysql/myddas_util.c | 101 +++ packages/myddas/odbc/CMakeLists.txt | 38 + packages/myddas/odbc/myddas_odbc.c | 874 ++++++++++++++++++++ packages/myddas/postgres/CMakeLists.txt | 38 + packages/myddas/postgres/myddas_postgres.c | 902 +++++++++++++++++++++ packages/myddas/sqlite3/CMakeLists.txt | 32 + packages/myddas/sqlite3/myddas_sqlite3.c | 770 ++++++++++++++++++ 13 files changed, 3695 insertions(+), 1 deletion(-) create mode 100644 packages/myddas/myddas_types.h create mode 100644 packages/myddas/myddas_util.h create mode 100644 packages/myddas/myddas_util_connection.c create mode 100644 packages/myddas/mysql/CMakeLists.txt create mode 100644 packages/myddas/mysql/myddas_mysql.c create mode 100644 packages/myddas/mysql/myddas_util.c create mode 100644 packages/myddas/odbc/CMakeLists.txt create mode 100644 packages/myddas/odbc/myddas_odbc.c create mode 100644 packages/myddas/postgres/CMakeLists.txt create mode 100644 packages/myddas/postgres/myddas_postgres.c create mode 100644 packages/myddas/sqlite3/CMakeLists.txt create mode 100644 packages/myddas/sqlite3/myddas_sqlite3.c diff --git a/packages/myddas/CMakeLists.txt b/packages/myddas/CMakeLists.txt index fbef1c1f9..ef14e8701 100644 --- a/packages/myddas/CMakeLists.txt +++ b/packages/myddas/CMakeLists.txt @@ -77,7 +77,7 @@ source_group(generated FILES ${MYDDAS_YAP}) add_library (myddas OBJECT ${MYDDAS_SOURCES} ${MYDDAS_UTIL_SOURCES} - ${MYDDAS_HEADES} + ${MYDDAS_HEADERS} ) set_target_properties (myddas PROPERTIES diff --git a/packages/myddas/myddas_types.h b/packages/myddas/myddas_types.h new file mode 100644 index 000000000..6c48e8023 --- /dev/null +++ b/packages/myddas/myddas_types.h @@ -0,0 +1,113 @@ +/* MYDDAS TYPES */ +/* sizeof(MyddasPointer) Equal to the size of a integer on the given architecture */ +/* sizeof(MyddasInt32) = 4 (Always) */ +/* sizeof(MyddasUInt32) = 4 (Always) */ + +#if SIZEOF_INT_P==4 + +# if SIZEOF_INT==4 +/* */ typedef int MyddasInt; +/* */ typedef unsigned int MyddasUInt; +/* */ typedef unsigned int MyddasPointer; +/* */ typedef int MyddasInt32; +/* */ typedef unsigned int MyddasUInt32; +# elif SIZEOF_LONG_INT==4 +/* */ typedef long int MyddasInt; +/* */ typedef unsigned long int MyddasUInt; +/* */ typedef unsigned long int MyddasPointer; +/* */ typedef long int MyddasInt32; +/* */ typedef unsigned long int MyddasUInt32; +# else +# error MYDDAS require integer types of the same size as a pointer +# endif + +# if SIZEOF_SHORT_INT==2 +/* */ typedef short int MyddasSInt; +/* */ typedef unsigned short int MyddasUSInt; +# else +# error MYDDAS requires integer types half the size of a pointer +# endif + +# if SIZEOF_LONG_INT==8 +/* */ typedef long int MyddasLInt; +/* */ typedef unsigned long int MyddasULInt; +# elif SIZEOF_LONG_LONG_INT==8 +/* */ typedef long long int MyddasLInt; +/* */ typedef unsigned long long int MyddasULInt; +# else +# error MYDDAS requires integer types double the size of a pointer +# endif + +#elif SIZEOF_INT_P==8 + +# if SIZEOF_INT==8 +/* */ typedef int MyddasInt; +/* */ typedef unsigned int MyddasUInt; +/* */ typedef int MyddasLInt; +/* */ typedef unsigned int MyddasULInt; +/* */ typedef unsigned int MyddasPointer; +# elif SIZEOF_LONG_INT==8 +/* */ typedef long int MyddasInt; +/* */ typedef unsigned long int MyddasUInt; +/* */ typedef int MyddasLInt; +/* */ typedef unsigned int MyddasULInt; +/* */ typedef unsigned long int MyddasPointer; +# elif SIZEOF_LONG_LONG_INT==8 +/* */ typedef long long int MyddasInt; +/* */ typedef unsigned long long int MyddasUInt; +/* */ typedef int MyddasLInt; +/* */ typedef unsigned int MyddasULInt; +/* */ typedef unsigned long long int MyddasPointer; +# else +# error MYDDAS requires integer types of the same size as a pointer +# endif + +# if SIZEOF_SHORT_INT==4 +/* */ typedef short int MyddasSInt; +/* */ typedef unsigned short int MyddasUSInt; +/* */ typedef short int MyddasInt32; +/* */ typedef unsigned short int MyddasUInt32; +# elif SIZEOF_INT==4 +/* */ typedef int MyddasSInt; +/* */ typedef unsigned int MyddasUSInt; +/* */ typedef int MyddasInt32; +/* */ typedef unsigned int MyddasUInt32; +# else +# error MYDDAS requires integer types half the size of a pointer +# endif + +#else +# error MYDDAS requires pointers of size 4 or 8 +#endif + + +#ifdef DEBUG +#define MYDDAS_MALLOC(POINTER,TYPE) \ + { \ + POINTER = (TYPE *) malloc(sizeof(TYPE)); \ + Yap_REGS.MYDDAS_GLOBAL_POINTER->memory_allocated+=sizeof(TYPE); \ + /*printf ("MALLOC %p %s %d\n",POINTER,__FILE__,__LINE__);*/ \ + Yap_REGS.MYDDAS_GLOBAL_POINTER->malloc_called++; \ + } +#else +#define MYDDAS_MALLOC(POINTER,TYPE) \ + { \ + POINTER = (TYPE *) malloc(sizeof(TYPE)); \ + } +#endif + +#ifdef DEBUG +#define MYDDAS_FREE(POINTER,TYPE) \ + { \ + Yap_REGS.MYDDAS_GLOBAL_POINTER->memory_freed+=sizeof(TYPE); \ + Yap_REGS.MYDDAS_GLOBAL_POINTER->free_called++; \ + /*printf ("FREE %p %s %d\n",POINTER,__FILE__,__LINE__);*/ \ + free(POINTER); \ + } +#else +#define MYDDAS_FREE(POINTER,TYPE) \ + { \ + free(POINTER); \ + } +#endif + diff --git a/packages/myddas/myddas_util.h b/packages/myddas/myddas_util.h new file mode 100644 index 000000000..d75adea43 --- /dev/null +++ b/packages/myddas/myddas_util.h @@ -0,0 +1,41 @@ + +#include "myddas_structs.h" + +void myddas_util_error_message(char *message ,Int line,char *file); + +/* Search for the predicate in the given predicate list*/ +MYDDAS_UTIL_CONNECTION myddas_util_search_connection(void *conn); + +MYDDAS_UTIL_CONNECTION +myddas_init_initialize_connection(void *conn,void *enviromment, + MYDDAS_API api, + MYDDAS_UTIL_CONNECTION next); + +MYDDAS_UTIL_CONNECTION +myddas_util_add_connection(void *conn, void *enviromment, MYDDAS_API api); + +MYDDAS_UTIL_PREDICATE +myddas_init_initialize_predicate(const char *pred_name, int pred_arity, + const char *pred_module, MYDDAS_UTIL_PREDICATE next); + +MYDDAS_UTIL_PREDICATE +myddas_util_find_predicate(const char *pred_name, Int pred_arity, + const char *pred_module, MYDDAS_UTIL_PREDICATE list); + +UInt +myddas_util_get_total_multi_queries_number(MYDDAS_UTIL_CONNECTION con); + +void +myddas_util_set_total_multi_queries_number(MYDDAS_UTIL_CONNECTION con, UInt number); + +void *myddas_util_get_pred_next(void *pointer); + +MyddasInt myddas_util_get_pred_arity(void *pointer); + +const char *myddas_util_get_pred_name(void *pointer); + +const char *myddas_util_get_pred_module(void *pointer); + +void *myddas_util_get_list_pred(MYDDAS_UTIL_CONNECTION node); + +void myddas_util_delete_predicate_list(MYDDAS_UTIL_PREDICATE preds_list); diff --git a/packages/myddas/myddas_util_connection.c b/packages/myddas/myddas_util_connection.c new file mode 100644 index 000000000..90e4564f2 --- /dev/null +++ b/packages/myddas/myddas_util_connection.c @@ -0,0 +1,6 @@ +#include +#include + +#include "Yap.h" +#include "cut_c.h" + diff --git a/packages/myddas/mysql/CMakeLists.txt b/packages/myddas/mysql/CMakeLists.txt new file mode 100644 index 000000000..465bd3fda --- /dev/null +++ b/packages/myddas/mysql/CMakeLists.txt @@ -0,0 +1,42 @@ + +set( YAPMYSQL_SOURCES + myddas_mysql.c +) + +set(SO_MAJOR 1) +set(SO_MINOR 0) +set(SO_PATCH 0) + +macro_optional_find_package(MYSQL ON) + +macro_log_feature (MYSQL_FOUND "MySQL" + "MYSQL Driver for MYDDAS Data-Base Interface " + "http://www.mysql.org" FALSE) + + if (MYSQL_FOUND) + # MYSQL_INCLUDE_DIR - where to find mysql.h, etc. + # MYSQL_LIBRARIES - List of libraries when using MySQL. + # MYSQL_FOUND - True if MySQL found. +add_definitions (-DMYDDAS_MYSQL=1) +add_library (Yapmysql SHARED ${YAPMYSQL_SOURCES}) +target_link_libraries(Yapmysql myddas libYap) +include_directories (${MYSQL_INCLUDE_DIR} ..) +else() + add_definitions (-DMYDDAS_MYSQL=0) + endif (MYSQL_FOUND) + +set_target_properties (Yapmysql PROPERTIES + POSITION_INDEPENDENT_CODE ON + VERSION "${SO_MAJOR}.${SO_MINOR}.${SO_PATCH}" + SOVERSION ${SO_MAJOR} + ) + + + install(TARGETS Yapmysql + LIBRARY DESTINATION ${libdir} + ) + +cmake_dependent_option (USE_MYDDAS_top_level + "enable the MYDDAS top-level (REPL) support for MySQL" OFF + 'USE_MYDDAS AND MYSQL_FOUND' OFF) +#TODO: diff --git a/packages/myddas/mysql/myddas_mysql.c b/packages/myddas/mysql/myddas_mysql.c new file mode 100644 index 000000000..72884db5d --- /dev/null +++ b/packages/myddas/mysql/myddas_mysql.c @@ -0,0 +1,737 @@ +/************************************************************************* +* * +* YAP Prolog * +* * +* Yap Prolog was developed at NCCUP - Universidade do Porto * +* * +* Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997 * +* * +************************************************************************** +* * +* File: myddas_mysql.c * +* Last rev: 22/03/05 * +* mods: * +* comments: Predicates for comunicating with a mysql database system * +* * +*************************************************************************/ + +#if MYDDAS_MYSQL + +#include +#include +#include +#include +#include "Yap.h" +#include "Yatom.h" +#include "cut_c.h" +#include "myddas_structs.h" +#ifdef MYDDAS_STATS +#include "myddas_statistics.h" +#endif +#include "myddas_wkb2prolog.h" + +#define IS_SQL_INT(FIELD) FIELD == FIELD_TYPE_INT24 || \ + FIELD == FIELD_TYPE_LONG || \ + FIELD == FIELD_TYPE_LONGLONG || \ + FIELD == FIELD_TYPE_SHORT || \ + FIELD == FIELD_TYPE_TINY + +#define IS_SQL_FLOAT(FIELD) FIELD == FIELD_TYPE_DECIMAL || \ + FIELD == FIELD_TYPE_DOUBLE || \ + FIELD == FIELD_TYPE_FLOAT + +#define IS_SQL_GEOMETRY(FIELD) FIELD == FIELD_TYPE_GEOMETRY + +static Int null_id = 0; + +static Int c_db_my_connect( USES_REGS1 ); +static Int c_db_my_disconnect( USES_REGS1 ); +static Int c_db_my_number_of_fields( USES_REGS1 ); +static Int c_db_my_get_attributes_types( USES_REGS1 ); +static Int c_db_my_query( USES_REGS1 ); +static Int c_db_my_table_write( USES_REGS1 ); +static Int c_db_my_row( USES_REGS1 ); +static Int c_db_my_row_cut( USES_REGS1 ); +static Int c_db_my_get_fields_properties( USES_REGS1 ); +static Int c_db_my_get_next_result_set( USES_REGS1 ); +static Int c_db_my_get_database( USES_REGS1 ); +static Int c_db_my_change_database( USES_REGS1 ); + +void Yap_InitMYDDAS_MySQLPreds(void) +{ + /* db_connect: Host x User x Passwd x Database x Connection x ERROR_CODE */ + Yap_InitCPred("c_db_my_connect", 7, c_db_my_connect, 0); + + /* db_number_of_fields: Relation x Connection x NumberOfFields */ + Yap_InitCPred("c_db_my_number_of_fields",3, c_db_my_number_of_fields, 0); + + /* db_get_attributes_types: Relation x TypesList */ + Yap_InitCPred("c_db_my_get_attributes_types", 3, c_db_my_get_attributes_types, 0); + + /* db_query: SQLQuery x ResultSet x Connection */ + Yap_InitCPred("c_db_my_query", 5, c_db_my_query, 0); + + /* db_disconnect: Connection */ + Yap_InitCPred("c_db_my_disconnect", 1,c_db_my_disconnect, 0); + + /* db_table_write: Result Set */ + Yap_InitCPred("c_db_my_table_write", 1, c_db_my_table_write, 0); + + /* db_get_fields_properties: PredName x Connnection x PropertiesList*/ + Yap_InitCPred("c_db_my_get_fields_properties",3,c_db_my_get_fields_properties,0); + + + Yap_InitCPred("c_db_my_get_next_result_set",2,c_db_my_get_next_result_set,0); + + /* c_db_my_get_database: Connnection x DataBaseName */ + Yap_InitCPred("c_db_my_get_database",2,c_db_my_get_database,0); + + /* c_db_my_change_database: Connnection x DataBaseName */ + Yap_InitCPred("c_db_my_change_database",2,c_db_my_change_database,0); + + +} + +void Yap_InitBackMYDDAS_MySQLPreds(void) +{ + /* db_row: ResultSet x Arity x ListOfArgs */ + Yap_InitCPredBackCut("c_db_my_row", 3, sizeof(Int), + c_db_my_row, + c_db_my_row, + c_db_my_row_cut, 0); + +} + +static Int +c_db_my_connect( USES_REGS1 ) { + Term arg_host = Deref(ARG1); + Term arg_user = Deref(ARG2); + Term arg_passwd = Deref(ARG3); + Term arg_database = Deref(ARG4); + Term arg_port = Deref(ARG5); + Term arg_socket = Deref(ARG6); + Term arg_conn = Deref(ARG7); + + MYSQL *conn; + + MYDDAS_UTIL_CONNECTION new = NULL; + + const char *host = AtomName(AtomOfTerm(arg_host)); + const char *user = AtomName(AtomOfTerm(arg_user)); + const char *passwd = AtomName(AtomOfTerm(arg_passwd)); + const char *database = AtomName(AtomOfTerm(arg_database)); + Int port = IntegerOfTerm(arg_port); + + const char *socket; + if (IsNonVarTerm(arg_socket)) + socket = AtomName(AtomOfTerm(arg_socket)); + else + socket = NULL; + + conn = mysql_init(NULL); + if (conn == NULL) { +#ifdef DEBUG + printf("ERROR: ** c_db_my_connect ** error on init\n"); +#endif + return FALSE; + } + + if (mysql_real_connect(conn, host, user, passwd, database, port, socket, CLIENT_MULTI_STATEMENTS) == NULL) { +#ifdef DEBUG + printf("ERROR: ** c_db_my_connect ** error on connect\n"); +#endif + return FALSE; + } + + if (!Yap_unify(arg_conn, MkIntegerTerm((Int)conn))) + return FALSE; + else + { + /* Criar um novo no na lista de ligacoes*/ + new = myddas_util_add_connection(conn,NULL,MYDDAS_MYSQL); + + if (new == NULL){ +#ifdef DEBUG + printf("ERROR: ** c_db_my_connect ** Error allocating memory\n"); +#endif + return FALSE; + } + return TRUE; + } +} + +/* db_query: SQLQuery x ResultSet x Connection */ +static Int +c_db_my_query( USES_REGS1 ) { + Term arg_sql_query = Deref(ARG1); + Term arg_result_set = Deref(ARG2); + Term arg_conn = Deref(ARG3); + Term arg_mode = Deref(ARG4); + Term arg_arity = Deref(ARG5); + + const char *sql = AtomName(AtomOfTerm(arg_sql_query)); + const char *mode = AtomName(AtomOfTerm(arg_mode)); + MYSQL *conn = (MYSQL *) (IntegerOfTerm(arg_conn)); + + MYSQL_RES *res_set; + + MyddasInt length=strlen(sql); + +#ifdef MYDDAS_STATS + MYDDAS_UTIL_CONNECTION node = myddas_util_search_connection(conn); + MyddasULInt count = 0; + + /* Count the number of querys made to the server */ + MyddasULInt number_querys; + MYDDAS_STATS_CON_GET_NUMBER_QUERIES_MADE(node,number_querys); + MYDDAS_STATS_CON_SET_NUMBER_QUERIES_MADE(node,++number_querys); + MYDDAS_STATS_CON_GET_NUMBER_QUERIES_MADE_COUNT(node,count); + MYDDAS_STATS_CON_SET_NUMBER_QUERIES_MADE_COUNT(node,++count); + + /* Measure time spent by the MySQL Server + processing the SQL Query */ + MYDDAS_STATS_TIME start,end,total_time,diff; + start = myddas_stats_walltime(); +#endif + + /* Send query to server and process it */ + if (mysql_real_query(conn, sql, length) != 0) + { +#ifdef DEBUG + printf("ERROR: **c_db_my_query** Error on query! %s\n",sql); +#endif + return FALSE; + } + +#ifdef MYDDAS_STATS + /* Measure time spent by the MySQL Server + processing the SQL Query */ + end = myddas_stats_walltime(); + + MYDDAS_STATS_INITIALIZE_TIME_STRUCT(diff,time_copy); + myddas_stats_subtract_time(diff,end,start); + diff = myddas_stats_time_copy_to_final(diff); + + MYDDAS_FREE(end,struct myddas_stats_time_struct); + MYDDAS_FREE(start,struct myddas_stats_time_struct); + + MYDDAS_STATS_CON_GET_TOTAL_TIME_DBSERVER(node,total_time); + /* Automacally updates the MYDDAS_STRUCTURE */ + myddas_stats_add_time(total_time,diff,total_time); + MYDDAS_STATS_CON_GET_TOTAL_TIME_DBSERVER_COUNT(node,count); + MYDDAS_STATS_CON_SET_TOTAL_TIME_DBSERVER_COUNT(node,++count); + + MYDDAS_STATS_TIME time = NULL; + MYDDAS_STATS_CON_GET_LAST_TIME_DBSERVER(node,time); + myddas_stats_move_time(diff,time); + MYDDAS_STATS_CON_GET_LAST_TIME_DBSERVER_COUNT(node,count); + MYDDAS_STATS_CON_SET_LAST_TIME_DBSERVER_COUNT(node,++count); +#endif + + /* guardar os tuplos do lado do cliente */ + if (strcmp(mode,"store_result")!=0) //True + res_set = mysql_use_result(conn); + else{ + +#ifdef MYDDAS_STATS + /* Measure time spent by the MySQL Server + transferring the result of the last query + back to the client */ + start = myddas_stats_walltime(); +#endif + res_set = mysql_store_result(conn); +#ifdef MYDDAS_STATS + /* Measure time spent by the MySQL Server + transferring the result of the last query + back to the client */ + end = myddas_stats_walltime(); + + MYDDAS_STATS_INITIALIZE_TIME_STRUCT(diff,time_copy); + myddas_stats_subtract_time(diff,end,start); + diff = myddas_stats_time_copy_to_final(diff); + + MYDDAS_FREE(end,struct myddas_stats_time_struct); + MYDDAS_FREE(start,struct myddas_stats_time_struct); + + MYDDAS_STATS_CON_GET_TOTAL_TIME_TRANSFERING(node,total_time); + /* Automacally updates the MYDDAS_STRUCTURE */ + myddas_stats_add_time(total_time,diff,total_time); + MYDDAS_STATS_CON_GET_TOTAL_TIME_TRANSFERING_COUNT(node,count); + MYDDAS_STATS_CON_SET_TOTAL_TIME_TRANSFERING_COUNT(node,++count); + + time = NULL; + MYDDAS_STATS_CON_GET_LAST_TIME_TRANSFERING(node,time); + MYDDAS_STATS_CON_GET_LAST_TIME_TRANSFERING_COUNT(node,count); + MYDDAS_STATS_CON_SET_LAST_TIME_TRANSFERING_COUNT(node,++count); + myddas_stats_move_time(diff,time); + + /* Measure the number of Rows returned from the server */ + if (res_set != NULL) + { + /* With an INSERT statement, mysql_(use or store)_result() + returns a NULL pointer*/ + + /* This is only works if we use mysql_store_result */ + MyddasUInt numberRows = mysql_num_rows(res_set); + MyddasUInt rows; + + MYDDAS_STATS_CON_GET_TOTAL_ROWS(node,rows); + numberRows = numberRows + rows; + MYDDAS_STATS_CON_SET_TOTAL_ROWS(node,numberRows); + MYDDAS_STATS_CON_GET_TOTAL_ROWS_COUNT(node,count); + MYDDAS_STATS_CON_SET_TOTAL_ROWS_COUNT(node,++count); + + /* Calculate the ammount of data sent by the server */ + MyddasUInt total,number_fields = mysql_num_fields(res_set); + MYSQL_ROW row; + MyddasULInt i; + total=0; + while ((row = mysql_fetch_row(res_set)) != NULL){ + mysql_field_seek(res_set,0); + + for(i=0;i TypesList */ +static Int +c_db_my_get_attributes_types( USES_REGS1 ) { + Term arg_relation = Deref(ARG1); + Term arg_conn = Deref(ARG2); + Term arg_types_list = Deref(ARG3); + + const char *relation = AtomName(AtomOfTerm(arg_relation)); + MYSQL *conn = (MYSQL *) IntegerOfTerm(arg_conn); + char sql[256]; + + MYSQL_RES *res_set; + MYSQL_ROW row; + Term head, list; + + sprintf(sql,"DESCRIBE `%s`",relation); + + Int length = strlen(sql); + + /* executar a query SQL */ + if (mysql_real_query(conn, sql, length) != 0) + { +#ifdef DEBUG + printf("Erro na query! %s\n",sql); +#endif + return FALSE; + } + /* guardar os tuplos do lado do cliente */ + if ((res_set = mysql_store_result(conn)) == NULL) + { +#ifdef DEBUG + printf("Query vazia!\n"); +#endif + return FALSE; + } + + list = arg_types_list; + + while ((row = mysql_fetch_row(res_set)) != NULL) + { + head = HeadOfTerm(list); + Yap_unify(head, MkAtomTerm(Yap_LookupAtom(row[0]))); + list = TailOfTerm(list); + head = HeadOfTerm(list); + list = TailOfTerm(list); + + if (strncmp(row[1], "smallint",8) == 0 || strncmp(row[1],"int",3) == 0 || + strncmp(row[1], "mediumint",9) == 0 || strncmp(row[1], "tinyint",7) == 0 || + strncmp(row[1], "bigint",6) == 0 || strcmp(row[1], "year") == 0) + Yap_unify(head, MkAtomTerm(Yap_LookupAtom("integer"))); + else if (strcmp(row[1], "float") == 0 || strncmp(row[1], "double",6) == 0 + || strcmp(row[1], "real") == 0) + Yap_unify(head, MkAtomTerm(Yap_LookupAtom("real"))); + else Yap_unify(head, MkAtomTerm(Yap_LookupAtom("string"))); + } + + mysql_free_result(res_set); + return TRUE; + +} + +/* db_disconnect */ +static Int +c_db_my_disconnect( USES_REGS1 ) { + Term arg_conn = Deref(ARG1); + + MYSQL *conn = (MYSQL *) IntegerOfTerm(arg_conn); + + if ((myddas_util_search_connection(conn)) != NULL) + { + myddas_util_delete_connection(conn); + mysql_close(conn); + return TRUE; + } + else + { + return FALSE; + } +} + +/* db_table_write: Result Set */ +static Int +c_db_my_table_write( USES_REGS1 ) { + Term arg_res_set = Deref(ARG1); + + MYSQL_RES *res_set = (MYSQL_RES *) IntegerOfTerm(arg_res_set); + + myddas_util_table_write(res_set); + mysql_free_result(res_set); + + return TRUE; +} + +static Int +c_db_my_row_cut( USES_REGS1 ) { + MYSQL_RES *mysql_res=NULL; + + mysql_res = (MYSQL_RES *) IntegerOfTerm(EXTRA_CBACK_CUT_ARG(Term,1)); + mysql_free_result(mysql_res); + return TRUE; +} + +/* db_row: ResultSet x Arity_ListOfArgs x ListOfArgs -> */ +static Int +c_db_my_row( USES_REGS1 ) { +#ifdef MYDDAS_STATS +/* Measure time used by the */ +/* c_db_my_row function */ + MYDDAS_STATS_TIME start,end,total_time,diff; + MyddasULInt count = 0; + start = myddas_stats_walltime(); +#endif + Term arg_result_set = Deref(ARG1); + Term arg_arity = Deref(ARG2); + Term arg_list_args = Deref(ARG3); + + MYSQL_RES *res_set = (MYSQL_RES *) IntegerOfTerm(arg_result_set); + EXTRA_CBACK_ARG(3,1)=(CELL) MkIntegerTerm((Int)res_set); + MYSQL_ROW row; + MYSQL_FIELD *field; + + + Term head, list, null_atom[1]; + Int i, arity; + + arity = IntegerOfTerm(arg_arity); + + while(TRUE) + { + if ((row = mysql_fetch_row(res_set)) != NULL) + { + mysql_field_seek(res_set,0); + list = arg_list_args; + + for (i = 0; i < arity; i++) + { + /* Here we perform data type conversion. */ + field = mysql_fetch_field(res_set); + head = HeadOfTerm(list); + list = TailOfTerm(list); + + if (row[i] == NULL) + { + null_atom[0] = MkIntegerTerm(null_id++); + + if (!Yap_unify(head, Yap_MkApplTerm(Yap_MkFunctor(Yap_LookupAtom("null"),1),1,null_atom))) + continue; + } + else + { + if (IS_SQL_INT(field->type)) + { + if (!Yap_unify(head, MkIntegerTerm(atoi(row[i])))) + continue; + } + else if (IS_SQL_FLOAT(field->type)) + { + if (!Yap_unify(head, MkFloatTerm(atof(row[i])))) + continue; + } + else if (IS_SQL_GEOMETRY(field->type)) + { + if (!Yap_unify(head, wkb2prolog(row[i]))) + continue; + } + else + { + if (!Yap_unify(head, MkAtomTerm(Yap_LookupAtom(row[i])))) + continue; + } + } + } +#ifdef MYDDAS_STATS + end = myddas_stats_walltime(); + + MYDDAS_STATS_INITIALIZE_TIME_STRUCT(diff,time_copy); + myddas_stats_subtract_time(diff,end,start); + diff = myddas_stats_time_copy_to_final(diff); + + MYDDAS_FREE(end,struct myddas_stats_time_struct); + MYDDAS_FREE(start,struct myddas_stats_time_struct); + + MYDDAS_STATS_GET_DB_ROW_FUNCTION(total_time); + myddas_stats_add_time(total_time,diff,total_time); + MYDDAS_STATS_GET_DB_ROW_FUNCTION_COUNT(count); + MYDDAS_STATS_SET_DB_ROW_FUNCTION_COUNT(++count); + + MYDDAS_FREE(diff,struct myddas_stats_time_struct); +#endif /* MYDDAS_STATS */ + return TRUE; + } + else + { + mysql_free_result(res_set); +#ifdef MYDDAS_STATS + end = myddas_stats_walltime(); + + MYDDAS_STATS_INITIALIZE_TIME_STRUCT(diff,time_copy); + myddas_stats_subtract_time(diff,end,start); + diff = myddas_stats_time_copy_to_final(diff); + + MYDDAS_FREE(end,struct myddas_stats_time_struct); + MYDDAS_FREE(start,struct myddas_stats_time_struct); + + MYDDAS_STATS_GET_DB_ROW_FUNCTION(total_time); + myddas_stats_add_time(total_time,diff,total_time); + MYDDAS_STATS_GET_DB_ROW_FUNCTION_COUNT(count); + MYDDAS_STATS_SET_DB_ROW_FUNCTION_COUNT(++count); + + MYDDAS_FREE(diff,struct myddas_stats_time_struct); +#endif /* MYDDAS_STATS */ + cut_fail(); /* This macro already does a return FALSE */ + } + + } + +} + +static Int +c_db_my_get_fields_properties( USES_REGS1 ) { + Term nome_relacao = Deref(ARG1); + Term arg_conn = Deref(ARG2); + Term fields_properties_list = Deref(ARG3); + Term head, list; + + const char *relacao = AtomName(AtomOfTerm(nome_relacao)); + char sql[256]; + Int num_fields,i; + MYSQL_FIELD *fields; + MYSQL_RES *res_set; + MYSQL *conn = (MYSQL *) (IntegerOfTerm(arg_conn)); + + + /* LIMIT 0 -> We only need the meta information about the fields + to know their properties, we don't need the results of the + query*/ + sprintf (sql,"SELECT * FROM `%s` LIMIT 0",relacao); + + Int length=strlen(sql); + + /* executar a query SQL */ + if (mysql_real_query(conn, sql, length) != 0) + { +#ifdef DEBUG + printf("Erro na query! %s\n",sql); +#endif + return FALSE; + } + + Functor functor = Yap_MkFunctor(Yap_LookupAtom("property"),4); + + Term properties[4]; + + + /* guardar os tuplos do lado do cliente */ + /* nao precisamos do resultado, mas apenas no res_set */ + /* para obter a informa��o atrav�s do mysql_fetch_fields*/ + res_set = mysql_store_result(conn); + + num_fields = mysql_num_fields(res_set); + fields = mysql_fetch_fields(res_set); + + list = fields_properties_list; + + + + for (i=0;idb)))) + return FALSE; + + return TRUE; + +} + +static Int +c_db_my_change_database( USES_REGS1 ) { + Term arg_con = Deref(ARG1); + Term arg_database = Deref(ARG2); + + MYSQL *con = (MYSQL *) (IntegerOfTerm(arg_con)); + const char *database = AtomName(AtomOfTerm(arg_database)); + + if (mysql_select_db(con,database)!=0) + return FALSE; + + return TRUE; +} + +#else + +void Yap_InitMYDDAS_MySQLPreds(void); +void Yap_InitBackMYDDAS_MySQLPreds(void); + +void Yap_InitMYDDAS_MySQLPreds(void) +{ +} + +void Yap_InitBackMYDDAS_MySQLPreds(void) +{ +} + +#endif diff --git a/packages/myddas/mysql/myddas_util.c b/packages/myddas/mysql/myddas_util.c new file mode 100644 index 000000000..4145e2a01 --- /dev/null +++ b/packages/myddas/mysql/myddas_util.c @@ -0,0 +1,101 @@ +/************************************************************************* +* * +* YAP Prolog * +* * +* Yap Prolog was developed at NCCUP - Universidade do Porto * +* * +* Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997 * +* * +************************************************************************** +* * +* File: myddas_mysql.c * +* Last rev: 22/03/05 * +* mods: * +* comments: Predicates for comunicating with a mysql database system * +* * +*************************************************************************/ + +#include +#include +#include + +/* Auxilary function to table_write*/ +static void +n_print(Int , char ); + +/* Auxilary function to table_write*/ +static void +n_print(Int n, char c) +{ + for(;n>0;n--) printf("%c",c); +} + +void +myddas_util_table_write(MYSQL_RES *res_set){ + + MYSQL_ROW row; + MYSQL_FIELD *fields; + Int i,f; + + if (mysql_num_rows(res_set) == 0) + { + printf ("Empty Set\n"); + return; + } + + f = mysql_num_fields(res_set); + + fields = mysql_fetch_field(res_set); + for(i=0;ifields[i].max_length) fields[i].max_length=strlen(fields[i].name); + n_print(fields[i].max_length+2,'-'); + } + printf("+\n"); + + for(i=0;i +#include +#include +#include "Yap.h" +#include "Yatom.h" +#include "myddas.h" +#include "cut_c.h" +#include +#include + +static Int null_id = 0; + +static Int c_db_odbc_connect( USES_REGS1 ); +static Int c_db_odbc_disconnect( USES_REGS1 ); +static Int c_db_odbc_number_of_fields( USES_REGS1 ); +static Int c_db_odbc_get_attributes_types( USES_REGS1 ); +static Int c_db_odbc_query( USES_REGS1 ); +static Int c_db_odbc_row( USES_REGS1 ); +static Int c_db_odbc_row_cut( USES_REGS1 ); +static Int c_db_odbc_get_fields_properties( USES_REGS1 ); +static Int c_db_odbc_number_of_fields_in_query( USES_REGS1 ); + +static int +odbc_error(SQLSMALLINT type, SQLHANDLE hdbc, char *msg, char *print) +{ + SQLCHAR SqlState[6], Msg[SQL_MAX_MESSAGE_LENGTH]; + SQLINTEGER NativeError; + SQLSMALLINT i=1, MsgLen; + + SQLGetDiagRec(type, hdbc,i,SqlState,&NativeError,Msg, sizeof(Msg), &MsgLen); + fprintf(stderr,"%% error in SQLConnect: %s got error code %s\n%% SQL Message: %s\n", print, SqlState, Msg); + return FALSE; +} + +static int +SQLALLOCHANDLE(SQLSMALLINT HandleType, SQLHANDLE hdbc, SQLHANDLE *outHandle, char *print) +{ + SQLRETURN retcode; + + retcode = SQLAllocHandle(HandleType,hdbc,outHandle); + if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) + { + return odbc_error(HandleType, hdbc, "SQLAllocHandle(ENV)", print); + } + return TRUE; +} + +static int +SQLSETENVATTR(SQLHENV henv, SQLINTEGER att, SQLPOINTER p, SQLINTEGER len, char *print) +{ + SQLRETURN retcode; + + retcode = SQLSetEnvAttr(henv,att,p,len); + if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) + { + return odbc_error(SQL_HANDLE_ENV, henv, "SQLSetEnvAttr", print); + } + return TRUE; +} + +static int SQLCONNECT(SQLHDBC hdbc, + SQLCHAR *driver, + SQLCHAR *user, + SQLCHAR *password, + char *print) +{ + SQLRETURN retcode; + + retcode = SQLConnect(hdbc,driver,SQL_NTS,user,SQL_NTS,password,SQL_NTS); + if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) + return odbc_error(SQL_HANDLE_DBC, hdbc, "SQLConnect", print); + return TRUE; +} + +static int SQLEXECDIRECT(SQLHSTMT StatementHandle, + SQLCHAR * StatementText, + char *print) +{ + SQLRETURN retcode; + retcode = SQLExecDirect(StatementHandle,StatementText,SQL_NTS); + + if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) + return odbc_error(SQL_HANDLE_STMT, StatementHandle, "SQLExecDirect", print); + return TRUE; +} + +static int SQLDESCRIBECOL(SQLHSTMT sth, + SQLSMALLINT colno, + SQLCHAR * colname, + SQLSMALLINT bflength, + SQLSMALLINT * nmlengthp, + SQLSMALLINT * dtptr, + SQLULEN * colszptr, + SQLSMALLINT * ddptr, + SQLSMALLINT * nullableptr, + char * print) +{ + SQLRETURN retcode; + retcode = SQLDescribeCol(sth, colno, colname, bflength, + nmlengthp, dtptr, colszptr, ddptr, + nullableptr); + + if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) + return odbc_error(SQL_HANDLE_STMT, sth, "SQLDescribeCol", print); + return TRUE; +} + +static int SQLSETCONNECTATTR(SQLHDBC hdbc, + SQLINTEGER attr, + SQLPOINTER vptr, + SQLINTEGER slen, + char * print) +{ + SQLRETURN retcode; + retcode = SQLSetConnectAttr(hdbc, attr, vptr, slen); + + if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) + return odbc_error(SQL_HANDLE_STMT, hdbc, "SQLSetConnectAttr", print); + return TRUE; +} + +static int SQLBINDCOL(SQLHSTMT sthandle, + SQLUSMALLINT colno, + SQLSMALLINT tt, + SQLPOINTER tvptr, + SQLLEN blen, + SQLLEN * strl, + char * print) +{ + SQLRETURN retcode; + retcode = SQLBindCol(sthandle,colno,tt,tvptr,blen,strl); + + if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) + return odbc_error(SQL_HANDLE_STMT, sthandle, "SQLBindCol", print); + return TRUE; +} + +static int SQLNUMRESULTCOLS(SQLHSTMT sthandle, + SQLSMALLINT * ncols, + char * print) +{ + SQLRETURN retcode; + retcode = SQLNumResultCols(sthandle,ncols); + + if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) + return odbc_error(SQL_HANDLE_STMT, sthandle, "SQLNumResultCols", print); + return TRUE; +} + +static int SQLCLOSECURSOR(SQLHSTMT sthandle, + char * print) +{ + SQLRETURN retcode; + retcode = SQLCloseCursor(sthandle); + + if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) + return odbc_error(SQL_HANDLE_STMT, sthandle, "SQLCloseCursor", print); + return TRUE; +} + + +#define SQLFETCH(A,print) \ +{ \ + SQLRETURN retcode; \ + retcode = SQLFetch(A); \ + if (retcode == SQL_NO_DATA) \ + break; \ + if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) \ + { \ + printf("Error in SQLFETCH: %s\n",print); \ + return FALSE; \ + } \ +} + +static int SQLGETDATA(SQLHSTMT sthandle, + SQLUSMALLINT Col_or_Param_Num, + SQLSMALLINT TargetType, + SQLPOINTER TargetValuePtr, + SQLLEN BufferLength, + SQLLEN * StrLen_or_IndPtr, + char * print) +{ + SQLRETURN retcode; + retcode = SQLGetData(sthandle, Col_or_Param_Num, + TargetType, TargetValuePtr, + BufferLength, StrLen_or_IndPtr); + + if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) + return odbc_error(SQL_HANDLE_STMT, sthandle, "SQLGetData", print); + return TRUE; +} + +static int SQLDISCONNECT(SQLHSTMT sthandle, + char * print) +{ + SQLRETURN retcode; + retcode = SQLDisconnect(sthandle); + + if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) + return odbc_error(SQL_HANDLE_DBC, sthandle, "SQLDisconnect", print); + return TRUE; +} + +static int SQLFREEHANDLE(SQLSMALLINT HandleType, + SQLHANDLE Handle, + char * print) +{ + SQLRETURN retcode; + retcode = SQLFreeHandle(HandleType, Handle); + + if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) + return odbc_error(HandleType, Handle, "SQLDisconnect", print); + return TRUE; +} + +static int SQLPRIMARYKEYS(SQLHSTMT StatementHandle, + SQLCHAR * CatalogName, + SQLSMALLINT NameLength1, + SQLCHAR * SchemaName, + SQLSMALLINT NameLength2, + SQLCHAR * TableName, + SQLSMALLINT NameLength3, + char * print) +{ + SQLRETURN retcode; + retcode = SQLPrimaryKeys(StatementHandle, + CatalogName, NameLength1, + SchemaName, NameLength2, + TableName, NameLength3 + ); + + if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) + return odbc_error(SQL_HANDLE_STMT, StatementHandle, "SQLPrimaryKeys", print); + return TRUE; +} + +/******************************************** + NOT IN USE +static int SQLGETTYPEINFO(SQLHSTMT StatementHandle, + SQLSMALLINT DataType, + char * print) +{ + SQLRETURN retcode; + retcode = SQLGetTypeInfo(StatementHandle, DataType); + + if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) + return odbc_error(SQL_HANDLE_STMT, StatementHandle, "SQLGetTypeInfo", print); + return TRUE; +} +********************************************/ + +static int SQLCOLATTRIBUTE( SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, + SQLUSMALLINT FieldIdentifier, + SQLPOINTER CharacterAttributePtr, + SQLSMALLINT BufferLength, + SQLSMALLINT * StringLengthPtr, + SQLLEN * NumericAttributePtr, + char * print) +{ + SQLRETURN retcode; + retcode = SQLColAttribute(StatementHandle, + ColumnNumber, + FieldIdentifier, + CharacterAttributePtr, + BufferLength, + StringLengthPtr, + NumericAttributePtr +); + + if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) + return odbc_error(SQL_HANDLE_STMT, StatementHandle, "SQLColAttribute", print); + return TRUE; +} + + +/* Verificar tipo de dados*/ +#define IS_SQL_INT(FIELD) FIELD == SQL_DECIMAL || \ + FIELD == SQL_NUMERIC || \ + FIELD == SQL_SMALLINT || \ + FIELD == SQL_INTEGER || \ + FIELD == SQL_TINYINT || \ + FIELD == SQL_BIGINT + +#define IS_SQL_FLOAT(FIELD) FIELD == SQL_FLOAT || \ + FIELD == SQL_DOUBLE || \ + FIELD == SQL_REAL + + + + +static Int +c_db_odbc_connect( USES_REGS1 ) { + Term arg_driver = Deref(ARG1); + Term arg_user = Deref(ARG2); + Term arg_passwd = Deref(ARG3); + Term arg_conn = Deref(ARG4); + + MYDDAS_UTIL_CONNECTION new = NULL; + + const char *driver = AtomName(AtomOfTerm(arg_driver)); + const char *user = AtomName(AtomOfTerm(arg_user)); + const char *passwd = AtomName(AtomOfTerm(arg_passwd)); + + SQLHENV henv; + SQLHDBC hdbc; + + /*Allocate environment handle */ + if (!SQLALLOCHANDLE(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv, "connect")) + return FALSE; + /* Set the ODBC version environment attribute */ + if (!SQLSETENVATTR(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0, "connect")) + return FALSE; + /* Allocate connection handle */ + if (!SQLALLOCHANDLE(SQL_HANDLE_DBC, henv, &hdbc, "connect")) + return FALSE; + /* Set login timeout to 6 seconds. */ + if (!SQLSETCONNECTATTR(hdbc, SQL_LOGIN_TIMEOUT,(SQLPOINTER) 6, 0, "connect")) + return FALSE; + /* Connect to data source */ + if (!SQLCONNECT(hdbc, + (SQLCHAR*) driver, + (SQLCHAR*) user, + (SQLCHAR*) passwd, "connect")) + return FALSE; + if (!Yap_unify(arg_conn, MkIntegerTerm((Int)(hdbc)))) + return FALSE; + else + { + /* Criar um novo no na lista de ligacoes*/ + //new = add_connection(&TOP,hdbc,henv); + new = myddas_util_add_connection(hdbc,henv,MYDDAS_ODBC); + if (new == NULL){ + fprintf(stderr,"Error: could not allocate list memory\n"); + return FALSE; + } + return TRUE; + } +} + +/* db_query: SQLQuery x ResultSet x Arity x BindList x Connection */ +static Int +c_db_odbc_query( USES_REGS1 ) { + Term arg_sql_query = Deref(ARG1); + Term arg_result_set = Deref(ARG2); + Term arg_arity = Deref(ARG3); + Term arg_bind_list = Deref(ARG4); + Term arg_conn = Deref(ARG5); + + SQLCHAR *sql = (SQLCHAR *)AtomName(AtomOfTerm(arg_sql_query)); + + + SQLHDBC hdbc =(SQLHDBC) (IntegerOfTerm(arg_conn)); + SQLHSTMT hstmt; + SQLSMALLINT type; + Int arity; + Int i; + + /*Allocate an handle for the query*/ + if (!SQLALLOCHANDLE(SQL_HANDLE_STMT, hdbc, &hstmt, "db_query")) + return FALSE; + /* Executes the query*/ + if (!SQLEXECDIRECT(hstmt, sql, "db_query")) + return FALSE; + + if (IsNonVarTerm(arg_arity)){ + arity = IntegerOfTerm(arg_arity); + + + char *bind_space=NULL; + + //const Int functor_arity=3; + const Short functor_arity=3; + Functor functor = Yap_MkFunctor(Yap_LookupAtom("bind"),functor_arity); + Term properties[functor_arity]; + + Term head,list=arg_bind_list; + + SQLULEN ColumnSizePtr; + SQLLEN *data_info=NULL; + + for (i=1;i<=arity;i++) + { + head = HeadOfTerm(list); + list = TailOfTerm(list); + + if (!SQLDESCRIBECOL(hstmt,i,NULL,0,NULL,&type,&ColumnSizePtr,NULL,NULL,"db_query")) + return FALSE; + + /* +1 because of '\0' */ + bind_space = malloc(sizeof(char)*(ColumnSizePtr+1)); + data_info = malloc(sizeof(SQLINTEGER)); + if (!SQLBINDCOL(hstmt,i,SQL_C_CHAR,bind_space,(ColumnSizePtr+1),data_info,"db_query")) { + return FALSE; + } + + properties[0] = MkIntegerTerm((Int)bind_space); + properties[2] = MkIntegerTerm((Int)data_info); + + if (IS_SQL_INT(type)) + properties[1]=MkAtomTerm(Yap_LookupAtom("integer")); + else if (IS_SQL_FLOAT(type)) + properties[1]=MkAtomTerm(Yap_LookupAtom("real")); + else + properties[1]=MkAtomTerm(Yap_LookupAtom("string")); + + Yap_unify(head,Yap_MkApplTerm(functor,functor_arity,properties)); + continue; + + } + } + + if (!Yap_unify(arg_result_set, MkIntegerTerm((Int) hstmt))) + { + if (!SQLCLOSECURSOR(hstmt,"db_query")) + return FALSE; + if (!SQLFREEHANDLE(SQL_HANDLE_STMT, hstmt, "db_query")) + return FALSE; + return FALSE; + } + return TRUE; +} + +static Int +c_db_odbc_number_of_fields( USES_REGS1 ) { + Term arg_relation = Deref(ARG1); + Term arg_conn = Deref(ARG2); + Term arg_fields = Deref(ARG3); + + + const char *relation = AtomName(AtomOfTerm(arg_relation)); + + SQLHDBC hdbc =(SQLHDBC) (IntegerOfTerm(arg_conn)); + SQLHSTMT hstmt; + + char sql[256]; + SQLSMALLINT number_fields; + + sprintf(sql,"SELECT column_name from INFORMATION_SCHEMA.COLUMNS where table_name = \'%s\' GROUP BY column_name, dtd_identifier ORDER BY CAST(dtd_identifier AS INTEGER)",relation); + + if (!SQLALLOCHANDLE(SQL_HANDLE_STMT, hdbc, &hstmt, "db_number_of_fields")) + return FALSE; + if (!SQLEXECDIRECT(hstmt, (SQLCHAR *)sql, "db_number_of_fields")) + return FALSE; + + /* Calcula o numero de campos*/ + number_fields=0; + while(TRUE) { + SQLFETCH(hstmt,"db_number_of_fields"); + number_fields++; + } + + if (!SQLCLOSECURSOR(hstmt,"db_number_of_fields")) + return FALSE; + if (!SQLFREEHANDLE(SQL_HANDLE_STMT, hstmt, "db_number_of_fields")) + return FALSE; + + if (!Yap_unify(arg_fields, MkIntegerTerm(number_fields))) + return FALSE; + return TRUE; +} + + +/* db_get_attributes_types: RelName x Connection -> TypesList */ +static Int +c_db_odbc_get_attributes_types( USES_REGS1 ) { + Term arg_relation = Deref(ARG1); + Term arg_conn = Deref(ARG2); + Term arg_types_list = Deref(ARG3); + + const char *relation = AtomName(AtomOfTerm(arg_relation)); + SQLHDBC hdbc =(SQLHDBC) (IntegerOfTerm(arg_conn)); + SQLHSTMT hstmt; + + char sql[256]; + Term head, list; + list = arg_types_list; + + sprintf(sql,"SELECT column_name,data_type from INFORMATION_SCHEMA.COLUMNS WHERE table_name = \'%s\' GROUP BY column_name, dtd_identifier ORDER BY CAST(dtd_identifier AS INTEGER)",relation); + + if (!SQLALLOCHANDLE(SQL_HANDLE_STMT, hdbc, &hstmt, "db_get_attributes_types")) + return FALSE; + if (!SQLEXECDIRECT(hstmt, (SQLCHAR *)sql, "db_get_attributes_types")) + return FALSE; + + while (TRUE) + { + SQLFETCH(hstmt, "db_get_attributes_types"); + + /* Tentar fazer de uma maneira que a gente consiga calcular o tamanho que o + nome do campo vai ocupar, assim podemos alocar memoria dinamicamente*/ + sql[0]='\0'; + if (!SQLGETDATA(hstmt, 1, SQL_C_CHAR, sql, 256, NULL, "db_get_attributes_types")) + return FALSE; + + head = HeadOfTerm(list); + Yap_unify(head, MkAtomTerm(Yap_LookupAtom(sql))); + list = TailOfTerm(list); + head = HeadOfTerm(list); + list = TailOfTerm(list); + + sql[0]='\0'; + if (!SQLGETDATA(hstmt, 2, SQL_C_CHAR, sql, 256, NULL, "db_get_attributes_types")) + return FALSE; + + if (strncmp(sql, "smallint",8) == 0 || strncmp(sql,"int",3) == 0 || + strncmp(sql, "mediumint",9) == 0 || strncmp(sql, "tinyint",7) == 0 || + strncmp(sql, "bigint",6) == 0 || strcmp(sql, "year") == 0) + Yap_unify(head, MkAtomTerm(Yap_LookupAtom("integer"))); + else + if (strcmp(sql, "float") == 0 || strncmp(sql, "double",6) == 0 + || strcmp(sql, "real") == 0) + Yap_unify(head, MkAtomTerm(Yap_LookupAtom("real"))); + else + Yap_unify(head, MkAtomTerm(Yap_LookupAtom("string"))); + } + + if (!SQLCLOSECURSOR(hstmt,"db_get_attributes_types")) + return FALSE; + if (!SQLFREEHANDLE(SQL_HANDLE_STMT, hstmt, "db_get_attributes_types")) + return FALSE; + return TRUE; +} + +/* db_disconnect */ +static Int +c_db_odbc_disconnect( USES_REGS1 ) { + Term arg_conn = Deref(ARG1); + + SQLHDBC conn = (SQLHDBC) (IntegerOfTerm(arg_conn)); + SQLHENV henv = myddas_util_get_odbc_enviromment(conn); + + if ((myddas_util_search_connection(conn)) != NULL) + { + myddas_util_delete_connection(conn); + /* More information about this process on + msdn.microsoft.com*/ + if (!SQLDISCONNECT(conn,"db_disconnect")) + return FALSE; + if (!SQLFREEHANDLE(SQL_HANDLE_DBC,conn,"db_disconnect")) + return FALSE; + if (!SQLFREEHANDLE(SQL_HANDLE_ENV,henv,"db_disconnect")) + return FALSE; + + return TRUE; + } + else + return FALSE; +} + +static Int +c_db_odbc_row_cut( USES_REGS1 ) { + + SQLHSTMT hstmt = (SQLHSTMT) IntegerOfTerm(EXTRA_CBACK_CUT_ARG(Term,1)); + + if (!SQLCLOSECURSOR(hstmt,"db_row_cut")) + return FALSE; + if (!SQLFREEHANDLE(SQL_HANDLE_STMT, hstmt, "db_row_cut")) + return FALSE; + + return TRUE; +} + +static int +release_list_args(Term arg_list_args, Term arg_bind_list, const char *error_msg) +{ + Term list = arg_list_args; + Term list_bind = arg_bind_list; + + while (IsPairTerm(list_bind)) + { + Term head_bind = HeadOfTerm(list_bind); + + list = TailOfTerm(list); + list_bind = TailOfTerm(list_bind); + + free((char *)IntegerOfTerm(ArgOfTerm(1,head_bind))); + free((SQLINTEGER *)IntegerOfTerm(ArgOfTerm(3,head_bind))); + } + return TRUE; +} + +/* db_row: ResultSet x BindList x ListOfArgs -> */ +static Int +c_db_odbc_row( USES_REGS1 ) { + Term arg_result_set = Deref(ARG1); + Term arg_bind_list = Deref(ARG2); + Term arg_list_args = Deref(ARG3); + + SQLHSTMT hstmt = (SQLHSTMT) IntegerOfTerm(arg_result_set); + + /* EXTRA_CBACK_ARG(ARIDADE,LOCAL_ONDE_COLOCAR_VALOR)*/ + EXTRA_CBACK_ARG(3,1)=(CELL) MkIntegerTerm((Int)hstmt); + + Term head, list, null_atom[1]; + Term head_bind, list_bind; + + SQLRETURN retcode = SQLFetch(hstmt); + if (retcode == SQL_NO_DATA) + { + if (!SQLCLOSECURSOR(hstmt,"db_row")) + return FALSE; + if (!SQLFREEHANDLE(SQL_HANDLE_STMT, hstmt, "db_row")) + return FALSE; + if (!release_list_args(arg_list_args, arg_bind_list, "db_row")) { + return FALSE; + } + + cut_fail(); + return FALSE; + } + if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) + { + printf("erro no SQLFETCH number of fields\n"); + return FALSE; + } + + char *bind_value=NULL; + Term type; + + list = arg_list_args; + list_bind = arg_bind_list; + SQLINTEGER *data_info=NULL; + + while (IsPairTerm(list_bind)) + { + head = HeadOfTerm(list); + list = TailOfTerm(list); + + head_bind = HeadOfTerm(list_bind); + list_bind = TailOfTerm(list_bind); + + bind_value = (char *)IntegerOfTerm(ArgOfTerm(1,head_bind)); + type = ArgOfTerm(2,head_bind); + data_info = (SQLINTEGER *)IntegerOfTerm(ArgOfTerm(3,head_bind)); + + if ((*data_info) == SQL_NULL_DATA){ + null_atom[0] = MkIntegerTerm(null_id++); + if (!Yap_unify(head, Yap_MkApplTerm(Yap_MkFunctor(Yap_LookupAtom("null"),1),1,null_atom))) + continue; + } + else + { + + if (!strcmp(AtomName(AtomOfTerm(type)),"integer")) + { + if (!Yap_unify(head, MkIntegerTerm(atol(bind_value)))) + continue; + } + else if (!strcmp(AtomName(AtomOfTerm(type)),"real")) + { + if (!Yap_unify(head, MkFloatTerm(atof(bind_value)))) + continue; + } + else if (!strcmp(AtomName(AtomOfTerm(type)),"string")) + { + if (!Yap_unify(head, MkAtomTerm(Yap_LookupAtom(bind_value)))) + continue; + } + } + } + return TRUE; +} + + +/* Mudar esta funcao de forma a nao fazer a consulta, pois + no predicate db_sql_selet vai fazer duas vezes a mesma consutla*/ +static Int +c_db_odbc_number_of_fields_in_query( USES_REGS1 ) { + Term arg_query = Deref(ARG1); + Term arg_conn = Deref(ARG2); + Term arg_fields = Deref(ARG3); + + const char *sql = AtomName(AtomOfTerm(arg_query)); + + SQLHDBC hdbc =(SQLHDBC) (IntegerOfTerm(arg_conn)); + SQLHSTMT hstmt; + SQLSMALLINT number_cols=0; + + if (!SQLALLOCHANDLE(SQL_HANDLE_STMT, hdbc, &hstmt, + "db_number_of_fields_in_query")) + return FALSE; + if (!SQLEXECDIRECT(hstmt ,(SQLCHAR *)sql, + "db_number_of_fields_in_query")) + return FALSE; + + if (!SQLNUMRESULTCOLS(hstmt,&number_cols, + "db_number_of_fields_in_query")) + return FALSE; + + if (!Yap_unify(arg_fields, MkIntegerTerm(number_cols))){ + if (!SQLCLOSECURSOR(hstmt,"db_number_of_fields_in_query")) + return FALSE; + if (!SQLFREEHANDLE(SQL_HANDLE_STMT, hstmt, "db_number_of_fields_in_query")) + return FALSE; + + return FALSE; + } + + if (!SQLCLOSECURSOR(hstmt,"db_number_of_fields_in_query")) + return FALSE; + if (!SQLFREEHANDLE(SQL_HANDLE_STMT, hstmt, "db_number_of_fields_in_query")) + return FALSE; + + return TRUE; +} + +static Int +c_db_odbc_get_fields_properties( USES_REGS1 ) { + Term nome_relacao = Deref(ARG1); + Term arg_conn = Deref(ARG2); + Term fields_properties_list = Deref(ARG3); + Term head, list; + + SQLCHAR *relacao = (SQLCHAR *)AtomName(AtomOfTerm(nome_relacao)); + char sql[256]; + char name[200]; + Int i; + + + SQLSMALLINT num_fields=0; + SQLSMALLINT NullablePtr=0; + SQLLEN AutoIncrementPointer=0; + SQLHSTMT hstmt,hstmt2; + SQLHDBC hdbc =(SQLHDBC) (IntegerOfTerm(arg_conn)); + + + /* LIMIT 0 -> We don't need the results of the query, + only the information about the fields of the relation*/ + sprintf (sql,"SELECT * FROM `%s` LIMIT 0",relacao); + + /*Allocate an handle for the query*/ + if (!SQLALLOCHANDLE(SQL_HANDLE_STMT, hdbc, &hstmt, "db_get_fields_properties")) + return FALSE; + /* Executes the query*/ + if (!SQLEXECDIRECT(hstmt ,(SQLCHAR *)sql, "db_get_fields_properties")) + return FALSE; + + Functor functor = Yap_MkFunctor(Yap_LookupAtom("property"),4); + Term properties[4]; + + if (!SQLNUMRESULTCOLS(hstmt,&num_fields, + "db_get_fields_properties")) + return FALSE; + + list = fields_properties_list; + + SQLSMALLINT bind_prim_key; + // rows in odbc start at 1 :) + Short *null=(Short *)malloc(sizeof(Short)*(1+num_fields)); + + if (!SQLALLOCHANDLE(SQL_HANDLE_STMT, hdbc, &hstmt2, "db_get_fields_properties")) + return FALSE; + /* Executes the query*/ + if (!SQLPRIMARYKEYS(hstmt2,NULL,0,NULL,0,relacao,SQL_NTS, "db_get_fields_properties")) + return FALSE; + /* Associates bind value for the 5 column*/ + if (!SQLBINDCOL(hstmt2,5,SQL_C_SSHORT,&bind_prim_key,sizeof(SQLSMALLINT),NULL, + "db_get_fields_properties")) + return FALSE; + + while(1) + { + SQLFETCH(hstmt2,"db_get_fields_properties"); + null[bind_prim_key]=1; + } + + if (!SQLCLOSECURSOR(hstmt2,"db_get_fields_properties")) + return FALSE; + if (!SQLFREEHANDLE(SQL_HANDLE_STMT, hstmt2, "db_get_fields_properties")) + return FALSE; + + for (i=1;i<=num_fields;i++) + { + head = HeadOfTerm(list); + name[0]='\0'; + SQLDESCRIBECOL(hstmt,i,(SQLCHAR *)name,200,NULL,NULL,NULL,NULL,&NullablePtr, + "db_get_fields_properties"); + + if (!SQLCOLATTRIBUTE(hstmt,i,SQL_DESC_AUTO_UNIQUE_VALUE,NULL,0,NULL,&AutoIncrementPointer, + "db_get_fields_properties")) + return FALSE; + + properties[0] = MkAtomTerm(Yap_LookupAtom(name)); + + + if (NullablePtr & SQL_NULLABLE) + properties[1] = MkIntegerTerm(1); //Can't be NULL + else + properties[1] = MkIntegerTerm(0); + + if (null[i] == 1) + properties[2] = MkIntegerTerm(1); //It''s a primary key + else + properties[2] = MkIntegerTerm(0); + + if (AutoIncrementPointer & SQL_TRUE) + properties[3] = MkIntegerTerm(1); //It's auto_incremented field + else + properties[3] = MkIntegerTerm(0); + + + list = TailOfTerm(list); + if (!Yap_unify(head, Yap_MkApplTerm(functor,4,properties))){ + return FALSE; + } + } + + if (!SQLCLOSECURSOR(hstmt,"db_get_fields_properties")) + return FALSE; + if (!SQLFREEHANDLE(SQL_HANDLE_STMT, hstmt2, "db_get_fields_properties")) + return FALSE; + return TRUE; +} + + + +void Yap_InitMYDDAS_ODBCPreds(void) +{ + /* db_connect: Host x User x Passwd x Database x Connection */ + Yap_InitCPred("c_db_odbc_connect", 4, c_db_odbc_connect, 0); + + /* db_number_of_fields: Relation x Connection x NumberOfFields */ + Yap_InitCPred("c_db_odbc_number_of_fields",3, c_db_odbc_number_of_fields, 0); + + /* db_number_of_fields_in_query: SQLQuery x Connection x NumberOfFields */ + Yap_InitCPred("c_db_odbc_number_of_fields_in_query",3, c_db_odbc_number_of_fields_in_query, 0); + + /* db_get_attributes_types: Relation x TypesList */ + Yap_InitCPred("c_db_odbc_get_attributes_types", 3, c_db_odbc_get_attributes_types, 0); + + /* db_query: SQLQuery x ResultSet x Connection */ + Yap_InitCPred("c_db_odbc_query", 5, c_db_odbc_query, 0); + + /* db_disconnect: Connection */ + Yap_InitCPred("c_db_odbc_disconnect", 1,c_db_odbc_disconnect, 0); + + /* db_get_fields_properties: PredName x Connnection x PropertiesList */ + Yap_InitCPred("c_db_odbc_get_fields_properties",3,c_db_odbc_get_fields_properties,0); + +} + + +void Yap_InitBackMYDDAS_ODBCPreds(void) +{ + + /* db_row: ResultSet x ListOfArgs */ + Yap_InitCPredBackCut("c_db_odbc_row", 3, sizeof(Int), + c_db_odbc_row, + c_db_odbc_row, + c_db_odbc_row_cut, 0); + +} + +#endif /*MYDDAS_ODBC*/ diff --git a/packages/myddas/postgres/CMakeLists.txt b/packages/myddas/postgres/CMakeLists.txt new file mode 100644 index 000000000..8c10601ca --- /dev/null +++ b/packages/myddas/postgres/CMakeLists.txt @@ -0,0 +1,38 @@ + +set( YAPPOSTGRES_SOURCES + myddas_postgres.c +) + +set(SO_MAJOR 1) +set(SO_MINOR 0) +set(SO_PATCH 0) + +macro_optional_find_package(POSTGRESQL ON) + +macro_log_feature (POSTGRES_FOUND "postgres" + "POSTGRES Driver for MYDDAS Data-Base Interface " + "http://www.postgres.org" FALSE) + +add_library (Yappostgres SHARED ${YAPPOSTGRES_SOURCES}) + +if (POSTGRES_FOUND) + # POSTGRES_INCLUDE_DIRECTORIES, where to find sql.h + # POSTGRES_LIBRARIES, the libraries to link against to use POSTGRES + # POSTGRES_FOUND. If false, you cannot build anything that requires Postgres. + add_definitions (-DMYDDAS_POSTGRES=1) + target_link_libraries(Yappostgres libYap ${POSTGRES_LIBRARIES}) + include_directories (${POSTGRES_INCLUDE_DIRECTORIES} ..) +else() + add_definitions (-DMYDDAS_POSTGRES=0) +endif (POSTGRES_FOUND) + +set_target_properties (Yappostgres PROPERTIES + POSITION_INDEPENDENT_CODE ON + VERSION "${SO_MAJOR}.${SO_MINOR}.${SO_PATCH}" + SOVERSION ${SO_MAJOR} + ) + + install(TARGETS Yappostgres + LIBRARY DESTINATION ${libdir} + ) + diff --git a/packages/myddas/postgres/myddas_postgres.c b/packages/myddas/postgres/myddas_postgres.c new file mode 100644 index 000000000..d4160bf64 --- /dev/null +++ b/packages/myddas/postgres/myddas_postgres.c @@ -0,0 +1,902 @@ +/************************************************************************* +* * +* YAP Prolog * +* * +* Yap Prolog was developed at NCCUP - Universidade do Porto * +* * +* Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997 * +* * +************************************************************************** +* * +* File: myddas_postgres.c * +* Last rev: 22/03/05 * +* mods: * +* comments: Predicates for comunicating with a pq database system * +* * +*************************************************************************/ + +#if MYDDAS_POSTGRES + +#include + +#define BOOLOID 16 +#define BYTEOID 17 +#define CHAROID 18 +#define NAMEOID 19 +#define INT8OID 20 +#define INT2OID 21 +#define INT2VECTOROID 22 +#define INT4OID 23 +#define REGPROCOID 24 +#define TEXTOID 25 +#define OIDOID 26 +#define TIDOID 27 +#define XIDOID 28 +#define CIDOID 29 +#define OIDVECTOROID 30 +#define JSONOID 114 +#define XMLOID 142 +#define PGNODETREEOID 194 +#define POINTOID 600 +#define LSEGOID 601 +#define PATHOID 602 +#define BOXOID 603 +#define POLYGONOID 604 +#define LINEOID 628 +#define FLOAT4OID 700 +#define FLOAT8OID 701 +#define ABSTIMEOID 702 +#define RELTIMEOID 703 +#define TINTERVALOID 704 +#define UNKNOWNOID 705 +#define CIRCLEOID 718 +#define CASHOID 790 +#define MACADDROID 829 +#define INETOID 869 +#define CIDROID 650 +#define INT2ARRAYOID 1005 +#define INT4ARRAYOID 1007 +#define TEXTARRAYOID 1009 +#define OIDARRAYOID 1028 +#define FLOAT4ARRAYOID 1021 +#define ACLITEMOID 1033 +#define CSTRINGARRAYOID 1263 +#define BPCHAROID 1042 +#define VARCHAROID 1043 +#define DATEOID 1082 +#define TIMEOID 1083 +#define TIMESTAMPOID 1114 +#define TIMESTAMPTZOID 1184 +#define INTERVALOID 1186 +#define TIMETZOID 1266 +#define BITOID 1560 +#define VARBITOID 1562 +#define NUMERICOID 1700 +#define REFCURSOROID 1790 +#define REGPROCEDUREOID 2202 +#define REGOPEROID 2203 +#define REGOPERATOROID 2204 +#define REGCLASSOID 2205 +#define REGTYPEOID 2206 +#define REGTYPEARRAYOID 2211 +#define UUIDOID 2950 +#define LSNOID 3220 +#define TSVECTOROID 3614 +#define GTSVECTOROID 3642 +#define TSQUERYOID 3615 +#define REGCONFIGOID 3734 +#define REGDICTIONARYOID 3769 +#define JSONBOID 3802 +#define INT4RANGEOID 3904 +#define RECORDOID 2249 +#define RECORDARRAYOID 2287 +#define CSTRINGOID 2275 +#define ANYOID 2276 +#define ANYARRAYOID 2277 +#define VOIDOID 2278 +#define TRIGGEROID 2279 +#define EVTTRIGGEROID 3838 +#define LANGUAGE_HANDLEROID 2280 +#define INTERNALOID 2281 +#define OPAQUEOID 2282 +#define ANYELEMENTOID 2283 +#define ANYNONARRAYOID 2776 +#define ANYENUMOID 3500 +#define FDW_HANDLEROID 3115 +#define ANYRANGEOID 3831 + +#include "Yap.h" +#include "Yatom.h" +#include "YapText.h" +#include "cut_c.h" +#include "eval.h" +#include "myddas.h" +#ifdef MYDDAS_STATS +#include "myddas_structs.h" +#include "myddas_statistics.h" +#endif +#include "myddas_wkb2prolog.h" + +#define CALL_POSTGRES(f) \ +{ \ + res = PQ ## f; \ + if (PQstatus(db) != CONNECTION_OK) { \ + fprintf(stderr, "Error %s when calling %s, line %d\n", \ + PQerrorMessage(db), #f,__LINE__); \ + exit (1); \ + } \ + PQclear(res); \ +} \ + +#define GET_POSTGRES(f, res) \ +{ \ + res = PQ ## f; \ + if (PQstatus(db) != CONNECTION_OK) { \ + fprintf (stderr, "%s failed with status %s, line %d\n", \ + #f, PQerrorMessage (db), __LINE__); \ + exit (1); \ + } \ +} \ + + + +static Int NULL_id = 0; + +typedef struct result_set { + PGconn *db; + PGresult *res; + const char *stmt; + int i ; + int nrows; + int ncols; +} resultSet; + +void Yap_InitMYDDAS_PGPreds(void); +void Yap_InitBackMYDDAS_PGPreds(void); + + +static Int c_postgres_connect( USES_REGS1 ); +static Int c_postgres_disconnect( USES_REGS1 ); +static Int c_postgres_number_of_fields( USES_REGS1 ); +static Int c_postgres_get_attributes_types( USES_REGS1 ); +static Int c_postgres_query( USES_REGS1 ); +static Int c_postgres_table_write( USES_REGS1 ); +static Int c_postgres_row( USES_REGS1 ); +static Int c_postgres_row_cut( USES_REGS1 ); +static Int c_postgres_get_fields_properties( USES_REGS1 ); +static Int c_postgres_get_next_result_set( USES_REGS1 ); +static Int c_postgres_get_database( USES_REGS1 ); +static Int c_postgres_change_database( USES_REGS1 ); + +void Yap_InitMYDDAS_PGPreds(void) +{ + /* db_dbect: Host x User x Passwd x Database x dbection x ERROR_CODE */ + Yap_InitCPred("c_postgres_connect", 7, c_postgres_connect, 0); + + /* db_number_of_fields: Relation x connection x NumberOfFields */ + Yap_InitCPred("c_postgres_number_of_fields",3, c_postgres_number_of_fields, 0); + + /* db_get_attributes_types: Relation x TypesList */ + Yap_InitCPred("c_postgres_get_attributes_types", 3, c_postgres_get_attributes_types, 0); + + /* db_query: SQLQuery x ResultSet x conection */ + Yap_InitCPred("c_postgres_query", 5, c_postgres_query, 0); + + /* db_disconnect: connection */ + Yap_InitCPred("c_postgres_disconnect", 1,c_postgres_disconnect, 0); + + /* db_table_write: Result Set */ + Yap_InitCPred("c_postgres_table_write", 1, c_postgres_table_write, 0); + + /* db_get_fields_properties: PredName x connection x PropertiesList*/ + Yap_InitCPred("c_postgres_get_fields_properties",3,c_postgres_get_fields_properties,0); + + Yap_InitCPred("c_postgres_get_next_result_set",2,c_postgres_get_next_result_set,0); + + /* c_postgres_get_database: connection x DataBaseName */ + Yap_InitCPred("c_postgres_get_database",2,c_postgres_get_database,0); + + /* c_postgres_change_database: connection x DataBaseName */ + Yap_InitCPred("c_postgres_change_database",2,c_postgres_change_database,0); + + +} + +void Yap_InitBackMYDDAS_PGPreds(void) +{ + /* db_row: ResultSet x Arity x ListOfArgs */ + Yap_InitCPredBackCut("c_postgres_row", 3, sizeof(Int), + c_postgres_row, + c_postgres_row, + c_postgres_row_cut, 0); + +} + +static Int +c_postgres_connect( USES_REGS1 ) { + int i=0; + Term arg_host = Deref(ARG1); + Term arg_user = Deref(ARG2); + Term arg_passwd = Deref(ARG3); + Term arg_database = Deref(ARG4); + Term arg_port = Deref(ARG5); + Term arg_socket = Deref(ARG6); + Term arg_conn = ARG7; + Term tempty = MkAtomTerm(Yap_LookupAtom("")); + Term tzero = MkIntTerm(0); + + const char *keywords[8], *values[8]; + char ports[16]; + + if (IsApplTerm(arg_host)) { + keywords[i] = "hostaddr"; + values[i++] = RepAtom(AtomOfTerm(ArgOfTerm(1, arg_host)))->StrOfAE; + } else { + keywords[i] = "host"; + values[i++] = RepAtom(AtomOfTerm(arg_host))->StrOfAE; + } + if (IsNonVarTerm(arg_user) && arg_user != tempty && IsAtomTerm(arg_user)) { + keywords[i] = "user"; + values[i++] = RepAtom(AtomOfTerm(arg_user))->StrOfAE; + } + if (IsNonVarTerm(arg_user) && arg_passwd != tempty && IsAtomTerm(arg_passwd)) { + keywords[i] = "password"; + values[i++] = RepAtom(AtomOfTerm(arg_passwd))->StrOfAE; + } + if (IsNonVarTerm(arg_user) && arg_database != tempty && IsAtomTerm(arg_database)) { + keywords[i] = "dbase"; + values[i++] = RepAtom(AtomOfTerm(arg_database))->StrOfAE; + } + if (IsNonVarTerm(arg_user) && arg_port != tzero && IsIntTerm(arg_port)) { + keywords[i] = "port"; + snprintf(ports, sizeof(ports)-1, "%d", (int)IntOfTerm(arg_port)); + values[i++] = ports; + } else if (IsNonVarTerm(arg_socket) && arg_socket != tempty && IsIntTerm(arg_socket)) { + keywords[i] = "port"; + values[i++] = RepAtom(AtomOfTerm(arg_database))->StrOfAE; + } + keywords[i] = NULL; + values[i++] = NULL; + + /* Make a connection to the database */ + PGconn *db = PQconnectdbParams(keywords, values, 0); + + /* Check to see that the backend c onnection was successfully made */ + if (PQstatus(db) != CONNECTION_OK) + { + fprintf(stderr, "Connection to database failed: %s", + PQerrorMessage(db)); + return FALSE; + } + + if (!Yap_unify(arg_conn, MkAddressTerm(db))) + return FALSE; + else + { + /* Criar um novo no na lista de ligacoes*/ + MYDDAS_UTIL_CONNECTION cnew = myddas_util_add_connection(db,NULL,MYDDAS_POSTGRES); + + if (cnew == NULL){ +#ifdef DEBUG + printf("ERROR: ** c_db_my_connect ** Error allocating memory\n"); +#endif + return FALSE; + } + return TRUE; + } + +} + + + +static MYDDAS_STATS_TIME +myddas_stat_init_query( PGconn *db ) +{ +#ifdef MYDDAS_STATS + MYDDAS_UTIL_connecTION node = myddas_util_search_connection(db); + MyddasULInt count = 0; + + /* Count the number of querys made to the server */ + MyddasULInt number_querys; + MYDDAS_STATS_CON_GET_NUMBER_QUERIES_MADE(node,number_querys); + MYDDAS_STATS_CON_SET_NUMBER_QUERIES_MADE(node,++number_querys); + MYDDAS_STATS_CON_GET_NUMBER_QUERIES_MADE_COUNT(node,count); + MYDDAS_STATS_CON_SET_NUMBER_QUERIES_MADE_COUNT(node,++count); + /* Measure time spent by the PG Server + processing the SQL Query */ + return myddas_stats_walltime(); +#else + return NULL; +#endif +} + + +static MYDDAS_STATS_TIME +myddas_stat_end_query( MYDDAS_STATS_TIME start ) +{ + MYDDAS_STATS_TIME diff = NULL; +#ifdef MYDDAS_STATS + /* Measure time spent by the PG Server + processing the SQL Query */ + MYDDAS_STATS_TIME end = myddas_stats_walltime(); + + MYDDAS_STATS_INITIALIZE_TIME_STRUCT(diff,time_copy); + myddas_stats_subtract_time(diff,end,start); + diff = myddas_stats_time_copy_to_final(diff); + + MYDDAS_FREE(end,struct myddas_stats_time_struct); + MYDDAS_FREE(start,struct myddas_stats_time_struct); + + MYDDAS_STATS_CON_GET_TOTAL_TIME_DBSERVER(node,total_time); + /* Automacally updates the MYDDAS_STRUCTURE */ + myddas_stats_add_time(total_time,diff,total_time); + MYDDAS_STATS_CON_GET_TOTAL_TIME_DBSERVER_COUNT(node,count); + MYDDAS_STATS_CON_SET_TOTAL_TIME_DBSERVER_COUNT(node,++count); + + MYDDAS_STATS_TIME time = NULL; + MYDDAS_STATS_CON_GET_LAST_TIME_DBSERVER(node,time); + myddas_stats_move_time(diff,time); + MYDDAS_STATS_CON_GET_LAST_TIME_DBSERVER_COUNT(node,count); + MYDDAS_STATS_CON_SET_LAST_TIME_DBSERVER_COUNT(node,++count); +#endif + return diff; +} + +#ifdef MYDDAS_STATS +/* measure transfer time */ +static void +myddas_stat_transfer_query( MYDDAS_STATS_TIME diff ) +{ + /* Measure time spent by the PG Server + transferring the result of the last query + back to the client */ + start = myddas_stats_walltime(); + /* Measure time spent by the PG Server + transferring the result of the last query + back to the client */ + end = myddas_stats_walltime(); + + MYDDAS_STATS_INITIALIZE_TIME_STRUCT(diff,time_copy); + myddas_stats_subtract_time(diff,end,start); + diff = MYDDAS_STATS_TIME_copy_to_final(diff); + + MYDDAS_FREE(end,struct myddas_stats_time_struct); + MYDDAS_FREE(start,struct myddas_stats_time_struct); + + MYDDAS_STATS_CON_GET_TOTAL_TIME_TRANSFERING(node,total_time); + /* Automacally updates the MYDDAS_STRUCTURE */ + myddas_stats_add_time(total_time,diff,total_time); + MYDDAS_STATS_CON_GET_TOTAL_TIME_TRANSFERING_COUNT(node,count); + MYDDAS_STATS_CON_SET_TOTAL_TIME_TRANSFERING_COUNT(node,++count); + + time = NULL; + MYDDAS_STATS_CON_GET_LAST_TIME_TRANSFERING(node,time); + MYDDAS_STATS_CON_GET_LAST_TIME_TRANSFERING_COUNT(node,count); + MYDDAS_STATS_CON_SET_LAST_TIME_TRANSFERING_COUNT(node,++count); + myddas_stats_move_time(diff,time); + + /* Measure the number of Rows returned from the server */ + if (res_set != NULL) + { + /* With an INSERT statement, PG_(use or store)_result() + returns a NULL pointer*/ + + /* This is only works if we use PG_store_result */ + MyddasUInt numberRows = PG_num_rows(res_set); + MyddasUInt rows; + myddas_stat_transfer_query( diff ); + + MYDDAS_STATS_CON_GET_TOTAL_ROWS(node,rows); + numberRows = numberRows + rows; + MYDDAS_STATS_CON_SET_TOTAL_ROWS(node,numberRows); + MYDDAS_STATS_CON_GET_TOTAL_ROWS_COUNT(node,count); + MYDDAS_STATS_CON_SET_TOTAL_ROWS_COUNT(node,++count); + + /* Calculate the ammount of data sent by the server */ + MyddasUInt total,number_fields = PG_num_fields(res_set); + PG_ROW row; + MyddasULInt i; + total=0; + while ((row = PG_fetch_row(res_set)) != NULL){ + PG_field_seek(res_set,0); + + for(i=0;idb = db; + rs->i = 0; + + + MYDDAS_STATS_TIME start, end; + start = myddas_stat_init_query( db ); + + const char *stmt = AtomName(Yap_LookupAtom(sql)); + + /* Send query to server and process it */ + if (strcmp(mode,"store_result")!=0) { + // Leave data for extraction + CALL_POSTGRES (prepare(db, stmt, sql, 0, NULL) ); + GET_POSTGRES (describePrepared(db, stmt) , res ); + rs->stmt = stmt; + rs->res = NULL; + rs->nrows = -1; + rs->ncols = PQnfields(res); + if (!Yap_unify(arg_arity, MkIntegerTerm(rs->ncols))) + { + free(rs); + return FALSE; + } + if (!Yap_unify(arg_result_set, MkAddressTerm( rs))) + { + free(rs); + return FALSE; + } + return TRUE; + } else { + // construct an intermediate table, res_set + int nrows, ncols; + + GET_POSTGRES (exec(db, sql), res ); + end = myddas_stat_end_query( start ); + ncols = PQnfields(res); + nrows = PQntuples(res); + //INSERT statements don't return any res_set + if (nrows == 0) { + return TRUE; + } + if (!Yap_unify(arg_arity, MkIntegerTerm(nrows))){ + /* + * Should PGclear PGresult whenever it is no longer needed to avoid memory + * leaks + */ + free(rs); + PQclear(res); + return FALSE; + } + rs->stmt = NULL; + rs->res = res; + rs->nrows = nrows; + rs->ncols = ncols; + if (!Yap_unify(arg_result_set, MkAddressTerm( rs))) + { + free(rs); + PQclear(res); + return FALSE; + } + } + return TRUE; +} + + +static Int +c_postgres_number_of_fields( USES_REGS1 ) { + Term arg_relation = Deref(ARG1); + Term arg_db = Deref(ARG2); + Term arg_fields = ARG3; + + const char *relation = AtomName(AtomOfTerm(arg_relation)); + PGconn *db = AddressOfTerm(arg_db); + const char *stmt; + PGresult *res; + + char sql[256]; + + snprintf(sql, 255, "SELECT * FROM TABLE `%s`",relation); + // Leave data for extraction + stmt = AtomName(Yap_LookupAtom(sql)); + CALL_POSTGRES (prepare(db, stmt, sql, 0, NULL) ); + + /* executar a query SQL */ + + int nrows = PQntuples(res); + + PQclear( res ); + + return Yap_unify(arg_fields, MkIntegerTerm( nrows )); +} + + +/* db_get_attributes_types: RelName x connection -> TypesList */ +static Int +c_postgres_get_attributes_types( USES_REGS1 ) { + Term arg_relation = Deref(ARG1); + Term arg_db = Deref(ARG2); + Term arg_types_list = Deref(ARG3); + Term list, head; + + const char *relation = AtomName(AtomOfTerm(arg_relation)); + PGconn *db = AddressOfTerm(arg_db); + char sql[256]; + //int row; + PGresult *res; + + const char *stmt; + Int rc = TRUE; + + sprintf(sql,"SELECT * FROM TABLE `%s`",relation); + // Leave data for extraction + stmt = AtomName(Yap_LookupAtom(sql)); + // Leave data for extraction + GET_POSTGRES (prepare(db, stmt, sql, 0, NULL), res ); + + /* executar a query SQL */ + + + int cols = PQnfields( res ), col; + + list = arg_types_list; + + for (col = 0; col < cols; col++) + { + const char *tm; + + + head = HeadOfTerm(list); + rc = ( + rc && Yap_unify(head, MkAtomTerm(Yap_LookupAtom(PQfname(res, col))) ) ); + list = TailOfTerm(list); + head = HeadOfTerm(list); + list = TailOfTerm(list); + + Oid type = PQftype(res, col); + switch(type) { + case BYTEOID: + case CHAROID: + case INT2OID: + case INT4OID: + case INT8OID: + tm = "integer"; + break; + case FLOAT4OID: + case FLOAT8OID: + tm = "real"; + break; + case NAMEOID: + tm = "atom"; + break; + case TEXTOID: + tm = "string"; + break; + default: + tm = "unknown type"; + break; + } + if (!Yap_unify(head, MkAtomTerm(Yap_LookupAtom(tm))) ) + rc = FALSE; + } + + PQclear( res ); + + return rc; + +} + +/* db_disconnect */ +static Int +c_postgres_disconnect( USES_REGS1 ) { + Term arg_db = Deref(ARG1); + + PGconn *db = AddressOfTerm(arg_db); + + if ((myddas_util_search_connection(db)) != NULL) + { + myddas_util_delete_connection(db); + PQfinish(db); + return TRUE; + } + else + { + return FALSE; + } +} + +/* db_table_write: Result Set */ +static Int +c_postgres_table_write( USES_REGS1 ) { + /* + Term arg_res_set = Deref(ARG1); + PG_RES *res_set = (PG_RES *) IntegerOfTerm(arg_res_set); + + mydas_util_table_write(res_set); + PG_free_result(res_set); + */ + return TRUE; +} + +static Int +c_postgres_get_fields_properties( USES_REGS1 ) { + Term nome_relacao = Deref(ARG1); + Term arg_db = Deref(ARG2); + Term fields_properties_list = Deref(ARG3); + Term head, list; + PGresult *res; + + const char *relation = AtomName(AtomOfTerm(nome_relacao)); + char sql[256]; + Int num_fields,i; + PGconn *db = AddressOfTerm(arg_db); + + sprintf(sql,"\\d+ `%s`",relation); + + GET_POSTGRES (exec(db, sql), res ); + +#if MYDDAS_STATS + end = myddas_stat_end_query( start ); +#endif + + Functor functor = Yap_MkFunctor(Yap_LookupAtom("property"),4); + + list = fields_properties_list; + + num_fields = PQntuples(res); + +int coln; +const char *propname; + + if (( coln = PQfnumber(res,"Column")) < 0) + return FALSE; + if (( propname = PQfname(res,2)) == NULL) + return FALSE; + if (!strstr(propname, "Table") || !strstr(propname, "Modifiers") ) + return FALSE; + + for (i=0;idb; + + res_set = AddressOfTerm(EXTRA_CBACK_CUT_ARG(Term,1)); + PQclear( res_set->res ); + PQfinish( db ); + free(res_set); + return TRUE; +} + +#define cvt( s ) cvt__( s PASS_REGS ) + +static Term +cvt__(const char *s USES_REGS) { + return Yap_CharsToTDQ( s, CurrentModule, ENC_ISO_LATIN1 PASS_REGS); +} + +/* db_row: ResultSet x Arity_ListOfArgs x ListOfArgs -> */ +static Int +c_postgres_row( USES_REGS1 ) { +#ifdef MYDDAS_STATS + /* Measure time used by the */ + /* c_postgres_row function */ + //MYDDAS_STATS_TIME start,end,total_time,diff; + MyddasULInt count = 0; + MYDDAS_STATS_TIME start, end; + start = myddas_stats_walltime(); +#endif + Term arg_result_set = Deref(ARG1); + Term arg_arity = Deref(ARG2); + Term arg_list_args = Deref(ARG3); + Int rc = TRUE; + + if (IsVarTerm( arg_result_set ) ) { + if (!c_postgres_query( PASS_REGS1 ) ) { + cut_fail(); + } + arg_result_set = Deref(ARG1); + EXTRA_CBACK_ARG(3,1)= arg_result_set ; + EXTRA_CBACK_ARG(3,2)= MkIntegerTerm(0) ; + } + struct result_set *res_set = AddressOfTerm(arg_result_set); + + Term head, list, NULL_atom[1]; + Int arity; + list = arg_list_args; + arity = IntegerOfTerm(arg_arity); + PGconn *db = res_set->db; + if (res_set->stmt == NULL ) { + CACHE_REGS + Int i= IntegerOfTerm(EXTRA_CBACK_CUT_ARG(Term,2)), j; + Int rc = true; + // data needs to be copied to Prolog + // row by row +#ifdef MYDDAS_STATS + MYDDAS_STATS_TIME diff; + + MYDDAS_STATS_INITIALIZE_TIME_STRUCT(diff,time_copy); +#endif + for (j = 0; j < arity; j++) + { + /* Ts -> List */ + const char *field = PQgetvalue(res_set->res, i, j); + head = HeadOfTerm(list); + list = TailOfTerm(list); + rc = (rc && Yap_unify(head, cvt( field )) ); + } + if (rc) { + EXTRA_CBACK_ARG(3,2)= MkIntegerTerm(i+1); + return rc; + } +#ifdef MYDDAS_STATS + myddas_stat_transfer_query( diff ); +#endif + cut_fail(); + } + // busy-waiting + if (!PQsetnonblocking(res_set->db, 1)) { + fprintf(stderr, "Connection to database failed: %s", + PQerrorMessage(db)); + return FALSE; + + } + if (!PQsendQueryPrepared(res_set->db, res_set->stmt, 0, NULL, NULL, NULL, 0)) { + fprintf(stderr, "Connection to database failed: %s", + PQerrorMessage(db)); + return FALSE; + + } + PGresult *res; + while((res = PQgetResult(res_set->db)) != NULL); + int i; + if ((i = res_set->nrows) == 0) { + // no more data + PQfinish( res_set->db ); + free(res_set); +#ifdef MYDDAS_STATS + end = myddas_stats_walltime(); + + MYDDAS_STATS_INITIALIZE_TIME_STRUCT(diff,time_copy); + myddas_stats_subtract_time(diff,end,start); + diff = myddas_stats_time_copy_to_final(diff); + + MYDDAS_FREE(end,struct myddas_stats_time_struct); + MYDDAS_FREE(start,struct myddas_stats_time_struct); + + MYDDAS_STATS_GET_DB_ROW_FUNCTION(total_time); + myddas_stats_add_time(total_time,diff,total_time); + MYDDAS_STATS_GET_DB_ROW_FUNCTION_COUNT(count); + MYDDAS_STATS_SET_DB_ROW_FUNCTION_COUNT(++count); + + MYDDAS_FREE(diff,struct myddas_stats_time_struct); +#endif /* MYDDAS_STATS */ + cut_fail(); /* This macro already does a return FALSE */ + + } else if (i < res_set->nrows) { + list = arg_list_args; + Term tf; + int j; + + for (j = 0; j < arity; j++) + { + /* convert data types here */ + head = HeadOfTerm(list); + list = TailOfTerm(list); + + if (!PQgetisnull(res, i, j)) { + const char *col = PQgetvalue(res, i, j); + tf = MkAtomTerm(Yap_LookupAtom(col)); + } else { + NULL_atom[0] = MkIntegerTerm(NULL_id++); + tf = Yap_MkApplTerm(Yap_MkFunctor(Yap_LookupAtom("NULL"),1),1,NULL_atom); + } + } + if (!Yap_unify(head, tf)) + rc = FALSE; +#ifdef MYDDAS_STATS + end = myddas_stats_walltime(); + + myddas_stats_subtract_time(diff,end,start); + diff = myddas_stats_time_copy_to_final(diff); + + MYDDAS_FREE(end,struct myddas_stats_time_struct); + MYDDAS_FREE(start,struct myddas_stats_time_struct); + + MYDDAS_STATS_GET_DB_ROW_FUNCTION(total_time); + myddas_stats_add_time(total_time,diff,total_time); + MYDDAS_STATS_GET_DB_ROW_FUNCTION_COUNT(count); + MYDDAS_STATS_SET_DB_ROW_FUNCTION_COUNT(++count); + + MYDDAS_FREE(diff,struct myddas_stats_time_struct); +#endif /* MYDDAS_STATS */ + } return rc; +} + +#else + +void Yap_InitMYDDAS_PGPreds(void); +void Yap_InitBackMYDDAS_PGPreds(void); + +void Yap_InitMYDDAS_PGPreds(void) +{ +} + +void Yap_InitBackMYDDAS_PGPreds(void) +{ +} + +#endif diff --git a/packages/myddas/sqlite3/CMakeLists.txt b/packages/myddas/sqlite3/CMakeLists.txt new file mode 100644 index 000000000..ef6a95170 --- /dev/null +++ b/packages/myddas/sqlite3/CMakeLists.txt @@ -0,0 +1,32 @@ + +set( YAPSQLITE3_SOURCES + myddas_sqlite3.c + ) + +add_library (Yapsqlite3 SHARED ${YAPSQLITE3_SOURCES}) +macro_optional_find_package(SQLITE3 ON) + + macro_log_feature (SQLITE3_FOUND "Sqlite3" + "Sqlite3 Data-Base " + "http://www.sqlite3ql.org" FALSE) + +if (SQLITE3_FOUND) + # SQLITE3_INCLUDE_DIRECTORIES, where to find sql.h + # SQLITE3_LIBRARIES, the libraries to link against to use SQLITE3 + # SQLITE3_FOUND. If false, you cannot build anything that requires Sqlite3. + add_definitions (target PUBLIC YapMyddasUtils Yapsqlite3 MYDDAS_SQLITE3=1) + target_link_libraries(Yapsqlite3 ${SQLITE3_LIBRARIES} libYap) + include_directories (${SQLITE3_INCLUDE_DIRECTORIES} ..) + +endif (SQLITE3_FOUND) + +set_target_properties (Yapsqlite3 PROPERTIES + POSITION_INDEPENDENT_CODE ON + VERSION "${SO_MAJOR}.${SO_MINOR}.${SO_PATCH}" + SOVERSION ${SO_MAJOR} +) + + install(TARGETS Yapsqlite3 + LIBRARY DESTINATION ${libdir} + ) + diff --git a/packages/myddas/sqlite3/myddas_sqlite3.c b/packages/myddas/sqlite3/myddas_sqlite3.c new file mode 100644 index 000000000..b5b19a6fe --- /dev/null +++ b/packages/myddas/sqlite3/myddas_sqlite3.c @@ -0,0 +1,770 @@ +/************************************************************************* +* * +* YAP Prolog * +* * +* Yap Prolog was developed at NCCUP - Universidade do Porto * +* * +* Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997 * +* * +************************************************************************** +* * +* File: myddas_sqlite3.c * +* Last rev: 22/03/05 * +* mods: * +* comments: Predicates for comunicating with a sqlite3 database system * +* * +*************************************************************************/ + +#if MYDDAS_SQLITE3 + +#include +#include +#include +#include +#include "Yap.h" +#include "Yatom.h" +#include "YapText.h" +#include "cut_c.h" +#include "eval.h" +#include "myddas.h" +#ifdef MYDDAS_STATS +#include "myddas_structs.h" +#include "myddas_statistics.h" +#endif +#include "myddas_wkb2prolog.h" + +#define CALL_SQLITE(f) \ +{ \ + int i; \ + i = sqlite3_ ## f; \ + if (i != SQLITE_OK) { \ + fprintf (stderr, "%s failed with status %d: %s\n", \ + #f, i, sqlite3_errmsg (db)); \ + exit (1); \ + } \ +} \ + +#define CALL_SQLITE_EXPECT(f,x) \ +{ \ + int i; \ + i = sqlite3_ ## f; \ + if (i != SQLITE_ ## x) { \ + fprintf (stderr, "%s failed with status %d: %s\n", \ + #f, i, sqlite3_errmsg (db)); \ + exit (1); \ + } \ +} \ + +static Int null_id = 0; + +typedef struct result_set { + sqlite3_stmt *stmt; + sqlite3 *db; + char **res_set; + int nrows; + int length; +} resultSet; + +void Yap_InitMYDDAS_SQLITE3Preds(void); +void Yap_InitBackMYDDAS_SQLITE3Preds(void); + + +static Int c_sqlite3_connect( USES_REGS1 ); +static Int c_sqlite3_disconnect( USES_REGS1 ); +static Int c_sqlite3_number_of_fields( USES_REGS1 ); +static Int c_sqlite3_get_attributes_types( USES_REGS1 ); +static Int c_sqlite3_query( USES_REGS1 ); +static Int c_sqlite3_table_write( USES_REGS1 ); +static Int c_sqlite3_row( USES_REGS1 ); +static Int c_sqlite3_row_cut( USES_REGS1 ); +static Int c_sqlite3_get_fields_properties( USES_REGS1 ); +static Int c_sqlite3_get_next_result_set( USES_REGS1 ); +static Int c_sqlite3_get_database( USES_REGS1 ); +static Int c_sqlite3_change_database( USES_REGS1 ); + +void Yap_InitMYDDAS_SQLITE3Preds(void) +{ + /* db_dbect: Host x User x Passwd x Database x dbection x ERROR_CODE */ + Yap_InitCPred("c_sqlite3_connect", 4, c_sqlite3_connect, 0); + + /* db_number_of_fields: Relation x connection x NumberOfFields */ + Yap_InitCPred("c_sqlite3_number_of_fields",3, c_sqlite3_number_of_fields, 0); + + /* db_get_attributes_types: Relation x TypesList */ + Yap_InitCPred("c_sqlite3_get_attributes_types", 3, c_sqlite3_get_attributes_types, 0); + + /* db_query: SQLQuery x ResultSet x conection */ + Yap_InitCPred("c_sqlite3_query", 5, c_sqlite3_query, 0); + + /* db_disconnect: connection */ + Yap_InitCPred("c_sqlite3_disconnect", 1,c_sqlite3_disconnect, 0); + + /* db_table_write: Result Set */ + Yap_InitCPred("c_sqlite3_table_write", 1, c_sqlite3_table_write, 0); + + /* db_get_fields_properties: PredName x connection x PropertiesList*/ + Yap_InitCPred("c_sqlite3_get_fields_properties",3,c_sqlite3_get_fields_properties,0); + + Yap_InitCPred("c_sqlite3_get_next_result_set",2,c_sqlite3_get_next_result_set,0); + + /* c_sqlite3_get_database: connection x DataBaseName */ + Yap_InitCPred("c_sqlite3_get_database",2,c_sqlite3_get_database,0); + + /* c_sqlite3_change_database: connection x DataBaseName */ + Yap_InitCPred("c_sqlite3_change_database",2,c_sqlite3_change_database,0); + + +} + +void Yap_InitBackMYDDAS_SQLITE3Preds(void) +{ + /* db_row: ResultSet x Arity x ListOfArgs */ + Yap_InitCPredBackCut("c_sqlite3_row", 3, sizeof(Int), + c_sqlite3_row, + c_sqlite3_row, + c_sqlite3_row_cut, 0); + +} + +static Int +c_sqlite3_connect( USES_REGS1 ) { + + Term arg_file = Deref(ARG1); + Term arg_db = ARG4; + + MYDDAS_UTIL_CONNECTION new = NULL; + sqlite3 *db; + + char *file = AtomName(AtomOfTerm(arg_file)); + + CALL_SQLITE( open(file, &db) ); + + if (!Yap_unify(arg_db, MkAddressTerm(db))) + return FALSE; + else + { + /* Criar um novo no na lista de ligacoes*/ + new = myddas_util_add_connection(db,NULL,API_SQLITE3); + + if (new == NULL){ +#ifdef DEBUG + fprintf(stderr, "ERROR: ** c_db_my_connect ** Error allocating memory\n"); +#endif + return FALSE; + } + return TRUE; + } + +} + +static MYDDAS_STATS_TIME +myddas_stat_init_query( sqlite3 *db ) +{ +#ifdef MYDDAS_STATS + MYDDAS_UTIL_connecTION node = myddas_util_search_connection(db); + MyddasULInt count = 0; + + /* Count the number of querys made to the server */ + MyddasULInt number_querys; + MYDDAS_STATS_CON_GET_NUMBER_QUERIES_MADE(node,number_querys); + MYDDAS_STATS_CON_SET_NUMBER_QUERIES_MADE(node,++number_querys); + MYDDAS_STATS_CON_GET_NUMBER_QUERIES_MADE_COUNT(node,count); + MYDDAS_STATS_CON_SET_NUMBER_QUERIES_MADE_COUNT(node,++count); + /* Measure time spent by the sqlite3 Server + processing the SQL Query */ + return myddas_stats_walltime(); +#else + return NULL; +#endif +} + + +static MYDDAS_STATS_TIME +myddas_stat_end_query( MYDDAS_STATS_TIME start ) +{ + MYDDAS_STATS_TIME diff = NULL; +#ifdef MYDDAS_STATS + /* Measure time spent by the sqlite3 Server + processing the SQL Query */ + end = myddas_stats_walltime(); + + MYDDAS_STATS_INITIALIZE_TIME_STRUCT(diff,time_copy); + myddas_stats_subtract_time(diff,end,start); + diff = myddas_stats_time_copy_to_final(diff); + + MYDDAS_FREE(end,struct myddas_stats_time_struct); + MYDDAS_FREE(start,struct myddas_stats_time_struct); + + MYDDAS_STATS_CON_GET_TOTAL_TIME_DBSERVER(node,total_time); + /* Automacally updates the MYDDAS_STRUCTURE */ + myddas_stats_add_time(total_time,diff,total_time); + MYDDAS_STATS_CON_GET_TOTAL_TIME_DBSERVER_COUNT(node,count); + MYDDAS_STATS_CON_SET_TOTAL_TIME_DBSERVER_COUNT(node,++count); + + MYDDAS_STATS_TIME time = NULL; + MYDDAS_STATS_CON_GET_LAST_TIME_DBSERVER(node,time); + myddas_stats_move_time(diff,time); + MYDDAS_STATS_CON_GET_LAST_TIME_DBSERVER_COUNT(node,count); + MYDDAS_STATS_CON_SET_LAST_TIME_DBSERVER_COUNT(node,++count); +#endif + return diff; +} + +#ifdef MYDDAS_STATS +/* measure transfer time */ +static void +myddas_stat_transfer_query( MYDDAS_STATS_TIME diff ) +{ + /* Measure time spent by the sqlite3 Server + transferring the result of the last query + back to the client */ + start = myddas_stats_walltime(); + /* Measure time spent by the sqlite3 Server + transferring the result of the last query + back to the client */ + end = myddas_stats_walltime(); + + MYDDAS_STATS_INITIALIZE_TIME_STRUCT(diff,time_copy); + myddas_stats_subtract_time(diff,end,start); + diff = MYDDAS_STATS_TIME_copy_to_final(diff); + + MYDDAS_FREE(end,struct myddas_stats_time_struct); + MYDDAS_FREE(start,struct myddas_stats_time_struct); + + MYDDAS_STATS_CON_GET_TOTAL_TIME_TRANSFERING(node,total_time); + /* Automacally updates the MYDDAS_STRUCTURE */ + myddas_stats_add_time(total_time,diff,total_time); + MYDDAS_STATS_CON_GET_TOTAL_TIME_TRANSFERING_COUNT(node,count); + MYDDAS_STATS_CON_SET_TOTAL_TIME_TRANSFERING_COUNT(node,++count); + + time = NULL; + MYDDAS_STATS_CON_GET_LAST_TIME_TRANSFERING(node,time); + MYDDAS_STATS_CON_GET_LAST_TIME_TRANSFERING_COUNT(node,count); + MYDDAS_STATS_CON_SET_LAST_TIME_TRANSFERING_COUNT(node,++count); + myddas_stats_move_time(diff,time); + + /* Measure the number of Rows returned from the server */ + if (res_set != NULL) + { + /* With an INSERT statement, sqlite3_(use or store)_result() + returns a NULL pointer*/ + + /* This is only works if we use sqlite3_store_result */ + MyddasUInt numberRows = sqlite3_num_rows(res_set); + MyddasUInt rows; + myddas_stat_transfer_query( diff ); + + MYDDAS_STATS_CON_GET_TOTAL_ROWS(node,rows); + numberRows = numberRows + rows; + MYDDAS_STATS_CON_SET_TOTAL_ROWS(node,numberRows); + MYDDAS_STATS_CON_GET_TOTAL_ROWS_COUNT(node,count); + MYDDAS_STATS_CON_SET_TOTAL_ROWS_COUNT(node,++count); + + /* Calculate the ammount of data sent by the server */ + MyddasUInt total,number_fields = sqlite3_num_fields(res_set); + sqlite3_ROW row; + MyddasULInt i; + total=0; + while ((row = sqlite3_fetch_row(res_set)) != NULL){ + sqlite3_field_seek(res_set,0); + + for(i=0;idb = db; + + start = myddas_stat_init_query( db ); + + /* Send query to server and process it */ + if (strcmp(mode,"store_result")!=0) { + // Leave data for extraction + printf(" SQL 0: %s\n", sql); + CALL_SQLITE (prepare_v2(db, sql, -1, &stmt, NULL) ); + rs->stmt = stmt; + rs->res_set = NULL; + rs->nrows = -1; + rs->length = sqlite3_column_count( stmt ); + if (!Yap_unify(arg_arity, MkIntegerTerm(rs->length))) + { + free(rs); + return FALSE; + } + if (!Yap_unify(arg_result_set, MkAddressTerm( rs))) + { + free(rs); + return FALSE; + } + return TRUE; + } else{ + // construct an intermediate table, res_set + char **res_set; + char *msg; + int nrows; + + CALL_SQLITE (get_table(db, sql, &res_set, &nrows, &length, &msg) ); + + //end = myddas_stat_end_query( start ); + if (res_set == NULL) + { +#ifdef DEBUG + printf("Empty Query!\n"); +#endif + return TRUE; + } + //INSERT statements don't return any res_set + if (nrows == 0) { + return TRUE; + } + if (!Yap_unify(arg_arity, MkIntegerTerm(nrows))){ + free(rs); + sqlite3_free_table(res_set); + return FALSE; + } + rs->stmt = NULL; + rs->res_set = res_set; + rs->nrows = nrows; + rs->length = length; + if (!Yap_unify(arg_result_set, MkAddressTerm( rs))) + { + free(rs); + sqlite3_free_table(res_set); + return FALSE; + } + } + return TRUE; +} + + +static Int +c_sqlite3_number_of_fields( USES_REGS1 ) { + Term arg_relation = Deref(ARG1); + Term arg_db = Deref(ARG2); + Term arg_fields = ARG3; + + char *relation = AtomName(AtomOfTerm(arg_relation)); + sqlite3 *db = AddressOfTerm(arg_db); + sqlite3_stmt *stmt; + + char sql[256]; + + sprintf(sql,"SELECT * FROM `%s`",relation); + + /* executar a query SQL */ + printf(" SQL 1: %s\n", sql); +CALL_SQLITE (prepare_v2(db, sql, -1, &stmt, NULL) ); + + int fields = sqlite3_column_count( stmt ); + + CALL_SQLITE (finalize( stmt ) ); + + return Yap_unify(arg_fields, MkIntegerTerm( fields )); +} + + +/* db_get_attributes_types: RelName x connection -> TypesList */ +static Int +c_sqlite3_get_attributes_types( USES_REGS1 ) { + Term arg_relation = Deref(ARG1); + Term arg_db = Deref(ARG2); + Term arg_types_list = Deref(ARG3); + Term list, head; + + char *relation = AtomName(AtomOfTerm(arg_relation)); + sqlite3 *db = (sqlite3 *) IntegerOfTerm(arg_db); + char sql[256]; + int row; + + sqlite3_stmt *stmt; + Int rc = TRUE; + + sprintf(sql,"SELECT * FROM `%s`",relation); + + /* executar a query SQL */ + printf(" SQL 3: %s\n", sql); + CALL_SQLITE (prepare_v2(db, sql, -1, &stmt, NULL) ); + + int fields = sqlite3_column_count( stmt ); + + list = arg_types_list; + + for (row = 0; row < fields; row++) + { + const char *tm; + + + head = HeadOfTerm(list); + rc = ( + rc && Yap_unify(head, MkAtomTerm(Yap_LookupAtom(sqlite3_column_name(stmt, row))) ) ); + list = TailOfTerm(list); + head = HeadOfTerm(list); + list = TailOfTerm(list); + + int type = sqlite3_column_type(stmt,row ); + switch(type) { + case SQLITE_INTEGER: + tm = "integer"; + break; + case SQLITE_FLOAT: + tm = "real"; + break; + case SQLITE_TEXT: + tm = "string"; + break; + case SQLITE_BLOB: + tm = "blob"; + break; + case SQLITE_NULL: + default: + tm = ""; + break; + } + if (!Yap_unify(head, MkAtomTerm(Yap_LookupAtom(tm))) ) + rc = FALSE; + } + + CALL_SQLITE (finalize( stmt ) ); + + return rc; + +} + +/* db_disconnect */ +static Int +c_sqlite3_disconnect( USES_REGS1 ) { + Term arg_db = Deref(ARG1); + + sqlite3 *db = (sqlite3 *) IntegerOfTerm(arg_db); + + if ((myddas_util_search_connection(db)) != NULL) + { + myddas_util_delete_connection(db); + sqlite3_close(db); + return TRUE; + } + else + { + return FALSE; + } +} + +/* db_table_write: Result Set */ +static Int +c_sqlite3_table_write( USES_REGS1 ) { + /* + Term arg_res_set = Deref(ARG1); + sqlite3_RES *res_set = (sqlite3_RES *) IntegerOfTerm(arg_res_set); + + mydas_util_table_write(res_set); + sqlite3_free_result(res_set); + */ + return TRUE; +} + +static Int +c_sqlite3_get_fields_properties( USES_REGS1 ) { + Term nome_relacao = Deref(ARG1); + Term arg_db = Deref(ARG2); + Term fields_properties_list = Deref(ARG3); + Term head, list; + + char *relation = AtomName(AtomOfTerm(nome_relacao)); + char sql[256]; + Int num_fields,i; + sqlite3 *db = (sqlite3 *) (IntegerOfTerm(arg_db)); + + sqlite3_stmt *stmt; + + sprintf(sql,"SELECT * FROM `%s`",relation); + + /* executar a query SQL */ + printf(" SQL 4: %s\n", sql); + CALL_SQLITE (prepare_v2(db, sql, -1, &stmt, NULL) ); + + Functor functor = Yap_MkFunctor(Yap_LookupAtom("property"),4); + + Term properties[4]; + + list = fields_properties_list; + + num_fields = sqlite3_column_count( stmt ); + + for (i=0;istmt = stmt; + rs->res_set = NULL; + rs->nrows = -1; + rs->length = sqlite3_column_count( stmt ); + rs->db = db; + Yap_unify(arg_next_res_set, MkAddressTerm(rs)); + } + return TRUE; +} + +static Int +c_sqlite3_get_database( USES_REGS1 ) { + Term arg_con = Deref(ARG1); + Term arg_database = Deref(ARG2); + + if (!Yap_unify(arg_database,arg_con)) + return FALSE; + + return TRUE; + +} + +static Int +c_sqlite3_change_database( USES_REGS1 ) { + /* no-op for now */ + return TRUE; +} + +static Int +c_sqlite3_row_cut( USES_REGS1 ) { + struct result_set *res_set=NULL; + + res_set = AddressOfTerm(CBACK_CUT_ARG(1)); + sqlite3 *db = res_set->db; + CALL_SQLITE( finalize( res_set->stmt ) ); + free(res_set); + return TRUE; +} + +#define cvt( s ) cvt__( s PASS_REGS ) + +static Term +cvt__(const char *s USES_REGS) { + return Yap_CharsToTDQ( s, CurrentModule PASS_REGS); +} + +/* db_row: ResultSet x Arity_ListOfArgs x ListOfArgs -> */ +static Int +c_sqlite3_row( USES_REGS1 ) { +#ifdef MYDDAS_STATS + /* Measure time used by the */ + /* c_sqlite3_row function */ + //MYDDAS_STATS_TIME start,end,total_time,diff; + MyddasULInt count = 0; + start = myddas_stats_walltime(); +#endif + Term arg_result_set = Deref(ARG1); + Term arg_arity = Deref(ARG2); + Term arg_list_args = Deref(ARG3); + Int rc = TRUE; + + if (IsVarTerm( arg_result_set ) ) { + if (!c_sqlite3_query( PASS_REGS1 ) ) { + cut_fail(); + } + arg_result_set = Deref(ARG1); + EXTRA_CBACK_ARG(3,1)= arg_result_set ; + EXTRA_CBACK_ARG(3,2)= MkIntegerTerm(0) ; + } + struct result_set *res_set = AddressOfTerm(arg_result_set); + + Term head, list, null_atom[1]; + Int i, arity; + list = arg_list_args; + arity = IntegerOfTerm(arg_arity); + sqlite3 *db = res_set->db; + if (res_set->stmt == NULL ) { + CACHE_REGS + Int indx = IntegerOfTerm(EXTRA_CBACK_ARG(3,2)); + Int rc = true; + // data needs to be copied to Prolog + // row by row +#ifdef MYDDAS_STATS + MYDDAS_STATS_TIME diff; + + MYDDAS_STATS_INITIALIZE_TIME_STRUCT(diff,time_copy); +#endif + while (indx/arity < res_set->nrows) + { + for (i = 0; i < arity; i++) + { + /* Ts -> List */ + const char *field = res_set->res_set[indx++]; + head = HeadOfTerm(list); + list = TailOfTerm(list); + rc = (rc && Yap_unify(head, cvt( field )) ); + } + if (rc) + return rc; + } +#ifdef MYDDAS_STATS + myddas_stat_transfer_query( diff ); +#endif + cut_fail(); + } + // busy-waiting + int res; + while((res = sqlite3_step(res_set->stmt)) == SQLITE_BUSY); + if (res == SQLITE_DONE) { + // no more data + CALL_SQLITE (finalize( res_set->stmt ) ); + free(res_set); +#ifdef MYDDAS_STATS + end = myddas_stats_walltime(); + + MYDDAS_STATS_INITIALIZE_TIME_STRUCT(diff,time_copy); + myddas_stats_subtract_time(diff,end,start); + diff = myddas_stats_time_copy_to_final(diff); + + MYDDAS_FREE(end,struct myddas_stats_time_struct); + MYDDAS_FREE(start,struct myddas_stats_time_struct); + + MYDDAS_STATS_GET_DB_ROW_FUNCTION(total_time); + myddas_stats_add_time(total_time,diff,total_time); + MYDDAS_STATS_GET_DB_ROW_FUNCTION_COUNT(count); + MYDDAS_STATS_SET_DB_ROW_FUNCTION_COUNT(++count); + + MYDDAS_FREE(diff,struct myddas_stats_time_struct); +#endif /* MYDDAS_STATS */ + cut_fail(); /* This macro already does a return FALSE */ + + } else if (res == SQLITE_ROW) { + list = arg_list_args; + Term tf; + + for (i = 0; i < arity; i++) + { + /* convert data types here */ + head = HeadOfTerm(list); + list = TailOfTerm(list); + + int type = sqlite3_column_type(res_set->stmt, i); + switch(type) { + case SQLITE_INTEGER: + tf = Yap_Mk64IntegerTerm( sqlite3_column_int64(res_set->stmt, i)); + break; + case SQLITE_FLOAT: + tf = MkFloatTerm( sqlite3_column_double(res_set->stmt, i)); + break; + case SQLITE_TEXT: + tf = MkAtomTerm( Yap_LookupAtom((const char *)sqlite3_column_text(res_set->stmt, i) )); + break; + case SQLITE_BLOB: + { + size_t bytes = sqlite3_column_bytes(res_set->stmt, i); + tf = Yap_AllocExternalDataInStack(EXTERNAL_BLOB, bytes); + memcpy( ExternalBlobFromTerm( tf ), sqlite3_column_blob(res_set->stmt, i), bytes ); + } + break; + case SQLITE_NULL: + null_atom[0] = MkIntegerTerm(null_id++); + tf = Yap_MkApplTerm(Yap_MkFunctor(Yap_LookupAtom("null"),1),1,null_atom); + break; + } + if (!Yap_unify(head, tf)) + rc = FALSE; + } +#ifdef MYDDAS_STATS + end = myddas_stats_walltime(); + + myddas_stats_subtract_time(diff,end,start); + diff = myddas_stats_time_copy_to_final(diff); + + MYDDAS_FREE(end,struct myddas_stats_time_struct); + MYDDAS_FREE(start,struct myddas_stats_time_struct); + + MYDDAS_STATS_GET_DB_ROW_FUNCTION(total_time); + myddas_stats_add_time(total_time,diff,total_time); + MYDDAS_STATS_GET_DB_ROW_FUNCTION_COUNT(count); + MYDDAS_STATS_SET_DB_ROW_FUNCTION_COUNT(++count); + + MYDDAS_FREE(diff,struct myddas_stats_time_struct); +#endif /* MYDDAS_STATS */ + } else + { + Yap_Error(SYSTEM_ERROR_INTERNAL, TermNil, "sqlite3: %s", sqlite3_errmsg( db ) ); + } + return rc; +} + +#else + + +void Yap_InitMYDDAS_SQLITE3Preds(void); +void Yap_InitBackMYDDAS_SQLITE3Preds(void); + + +void Yap_InitMYDDAS_SQLITE3Preds(void) +{} +void Yap_InitBackMYDDAS_SQLITE3Preds(void) +{} + + +#endif