From 4dbd2761f68b377c81f7d97ed0139eddeb49aa39 Mon Sep 17 00:00:00 2001 From: SunBeau Date: Fri, 17 May 2024 09:33:34 +0800 Subject: [PATCH 1/4] merge qlibpq.h into qdatabase.c --- src/extensions/qdatabase.c | 152 +++++++++++++++++++++++++++++- src/internal/qlibpq.h | 183 ------------------------------------- 2 files changed, 151 insertions(+), 184 deletions(-) delete mode 100644 src/internal/qlibpq.h diff --git a/src/extensions/qdatabase.c b/src/extensions/qdatabase.c index 29895baf..a2a562ee 100644 --- a/src/extensions/qdatabase.c +++ b/src/extensions/qdatabase.c @@ -89,7 +89,157 @@ #endif /* ENABLE_MYSQL */ #ifdef ENABLE_PGSQL -#include "qlibpq.h" +#include "libpq-fe.h" + +/* +Wrap the low-level functions of the libpq library. +Provide interfaces with naming convention similar to mysql_xyz(). +*/ + +#define qtype_cast(_type, _src) ((_type)_src) + +typedef enum { + PQ_UNKNOWN, + PQ_WRITE, + PQ_READ, + PQ_PING, +} pgquery_t; + +typedef struct { + char *emsg; + PGconn *pgconn; + + PGresult *pgresult; + int rows; + int cols; + int cursor; +} pgsql_t; + +static inline pgsql_t* pgsql_init(void) +{ + pgsql_t* pgsql = (pgsql_t *)calloc(1, sizeof(pgsql_t)); + return pgsql; +} + +static inline const char* pgsql_set_emsg(pgsql_t* pgsql, const char* emsg) +{ + if (pgsql == NULL || emsg == NULL) { + return NULL; + } + + if (pgsql->emsg) { + free(pgsql->emsg); + } + + pgsql->emsg = strdup(emsg); + return pgsql->emsg; +} + +static inline void pgsql_close(pgsql_t* pgsql) +{ + if (pgsql == NULL) { + return; + } + + if (pgsql->emsg) { + free(pgsql->emsg); + pgsql->emsg = NULL; + } + + if (pgsql->pgresult) { + PQclear(pgsql->pgresult); + pgsql->pgresult = NULL; + } + + if (pgsql->pgconn) { + PQfinish(pgsql->pgconn); + pgsql->pgconn = NULL; + } + + free(pgsql); +} + +static inline void pgsql_query_result(pgsql_t* pgsql) +{ + if (pgsql->pgresult) { + PQclear(pgsql->pgresult); + pgsql->pgresult = NULL; + } +} + +static inline int pgsql_query(pgsql_t* pgsql, const char *query, pgquery_t type) +{ + if (pgsql == NULL || pgsql->pgconn == NULL) { + return -1; /* error */ + } + + /* free previous query results */ + pgsql_query_result(pgsql); + + if (type == PQ_PING) { + query = ""; + } + + PGresult *result = PQexec(pgsql->pgconn, query); + ExecStatusType status = PQresultStatus(result); + + switch (type) { + case PQ_PING: { + if (status != PGRES_EMPTY_QUERY) { + return -1; /* error */ + } + break; + } + + case PQ_READ: { + if (status != PGRES_TUPLES_OK) { + return -1; /* error */ + } + break; + } + + default: { + if (status != PGRES_COMMAND_OK) { + return -1; /* error */ + } + break; + } + } + + pgsql->pgresult = result; + + return 0; /* ok */ +} + +static inline int pgsql_affected_rows(pgsql_t* pgsql) +{ + if (pgsql && pgsql->pgresult) { + return atoi(PQcmdTuples(pgsql->pgresult)); + } + + /* error */ + return -1; +} + +static inline int pgsql_num_rows(pgsql_t* pgsql) +{ + if (pgsql && pgsql->pgresult) { + return PQntuples(pgsql->pgresult); + } + + /* error */ + return -1; +} + +static inline int pgsql_num_fields(pgsql_t* pgsql) +{ + if (pgsql && pgsql->pgresult) { + return PQnfields(pgsql->pgresult); + } + + /* error */ + return -1; +} #endif /* ENABLE_PGSQL */ #include diff --git a/src/internal/qlibpq.h b/src/internal/qlibpq.h deleted file mode 100644 index 60353cf6..00000000 --- a/src/internal/qlibpq.h +++ /dev/null @@ -1,183 +0,0 @@ -/****************************************************************************** - * qLibc - * - * Copyright (c) 2024 SunBeau. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -/* Wrap the low-level functions of the libpq library */ - -#ifndef QLIBPQ_H -#define QLIBPQ_H - -#include "libpq-fe.h" -#include -#include - -#define qtype_cast(_type, _src) ((_type)_src) - -typedef enum { - PQ_UNKNOWN, - PQ_WRITE, - PQ_READ, - PQ_PING, -} pgquery_t; - -typedef struct { - char *emsg; - PGconn *pgconn; - - PGresult *pgresult; - int rows; - int cols; - int cursor; -} pgsql_t; - -static inline pgsql_t* pgsql_init(void) -{ - pgsql_t* pgsql = (pgsql_t *)calloc(1, sizeof(pgsql_t)); - return pgsql; -} - -static inline const char* pgsql_set_emsg(pgsql_t* pgsql, const char* emsg) -{ - if (pgsql == NULL || emsg == NULL) { - return NULL; - } - - if (pgsql->emsg) { - free(pgsql->emsg); - } - - pgsql->emsg = strdup(emsg); - return pgsql->emsg; -} - -static inline void pgsql_close(pgsql_t* pgsql) -{ - if (pgsql == NULL) { - return; - } - - if (pgsql->emsg) { - free(pgsql->emsg); - pgsql->emsg = NULL; - } - - if (pgsql->pgresult) { - PQclear(pgsql->pgresult); - pgsql->pgresult = NULL; - } - - if (pgsql->pgconn) { - PQfinish(pgsql->pgconn); - pgsql->pgconn = NULL; - } - - free(pgsql); -} - -static inline void pgsql_query_result(pgsql_t* pgsql) -{ - if (pgsql->pgresult) { - PQclear(pgsql->pgresult); - pgsql->pgresult = NULL; - } -} - -static inline int pgsql_query(pgsql_t* pgsql, const char *query, pgquery_t type) -{ - if (pgsql == NULL || pgsql->pgconn == NULL) { - return -1; /* error */ - } - - /* free previous query results */ - pgsql_query_result(pgsql); - - if (type == PQ_PING) { - query = ""; - } - - PGresult *result = PQexec(pgsql->pgconn, query); - ExecStatusType status = PQresultStatus(result); - - switch (type) { - case PQ_PING: { - if (status != PGRES_EMPTY_QUERY) { - return -1; /* error */ - } - break; - } - - case PQ_READ: { - if (status != PGRES_TUPLES_OK) { - return -1; /* error */ - } - break; - } - - default: { - if (status != PGRES_COMMAND_OK) { - return -1; /* error */ - } - break; - } - } - - pgsql->pgresult = result; - - return 0; /* ok */ -} - -static inline int pgsql_affected_rows(pgsql_t* pgsql) -{ - if (pgsql && pgsql->pgresult) { - return atoi(PQcmdTuples(pgsql->pgresult)); - } - - /* error */ - return -1; -} - -static inline int pgsql_num_rows(pgsql_t* pgsql) -{ - if (pgsql && pgsql->pgresult) { - return PQntuples(pgsql->pgresult); - } - - /* error */ - return -1; -} - -static inline int pgsql_num_fields(pgsql_t* pgsql) -{ - if (pgsql && pgsql->pgresult) { - return PQnfields(pgsql->pgresult); - } - - /* error */ - return -1; -} - -#endif /* QLIBPQ_H */ From 510bdfd8e271aa305e6576c5f141e99a3fdbb1b0 Mon Sep 17 00:00:00 2001 From: SunBeau Date: Fri, 17 May 2024 12:48:31 +0800 Subject: [PATCH 2/4] reduce code duplication --- include/qlibc/extensions/qdatabase.h | 4 + src/extensions/qdatabase.c | 293 +++++++++++++++------------ 2 files changed, 165 insertions(+), 132 deletions(-) diff --git a/include/qlibc/extensions/qdatabase.h b/include/qlibc/extensions/qdatabase.h index 3da36d06..c6abccd8 100644 --- a/include/qlibc/extensions/qdatabase.h +++ b/include/qlibc/extensions/qdatabase.h @@ -55,6 +55,10 @@ extern "C" { #define Q_ENABLE_PGSQL (1) #endif /* LIBPQ_FE_H */ +#if defined(Q_ENABLE_MYSQL) && defined(Q_ENABLE_PGSQL) +#error "only can enable one in the same time" +#endif + /* types */ typedef struct qdbresult_s qdbresult_t; typedef struct qdb_s qdb_t; diff --git a/src/extensions/qdatabase.c b/src/extensions/qdatabase.c index a2a562ee..2d76431e 100644 --- a/src/extensions/qdatabase.c +++ b/src/extensions/qdatabase.c @@ -79,6 +79,14 @@ #if defined(ENABLE_MYSQL) || defined(ENABLE_PGSQL) ||defined( _DOXYGEN_SKIP) +#include +#include +#include +#include +#include + +#define qtype_cast(_type, _src) ((_type)_src) + #ifdef ENABLE_MYSQL #include "mysql.h" /* mysql specific connector options */ @@ -96,8 +104,6 @@ Wrap the low-level functions of the libpq library. Provide interfaces with naming convention similar to mysql_xyz(). */ -#define qtype_cast(_type, _src) ((_type)_src) - typedef enum { PQ_UNKNOWN, PQ_WRITE, @@ -242,11 +248,6 @@ static inline int pgsql_num_fields(pgsql_t* pgsql) } #endif /* ENABLE_PGSQL */ -#include -#include -#include -#include -#include #include "qinternal.h" #include "extensions/qdatabase.h" @@ -554,30 +555,28 @@ static bool close_(qdb_t *db) { if (db == NULL) return false; -#ifdef Q_ENABLE_MYSQL +#if defined(Q_ENABLE_MYSQL) || defined(Q_ENABLE_PGSQL) Q_MUTEX_ENTER(db->qmutex); +#if defined(Q_ENABLE_MYSQL) if (db->mysql != NULL) { mysql_close(db->mysql); db->mysql = NULL; mysql_library_end(); } - db->connected = false; - - Q_MUTEX_LEAVE(db->qmutex); - return true; -#elif defined(Q_ENABLE_PGSQL) - Q_MUTEX_ENTER(db->qmutex); +#endif /* Q_ENABLE_MYSQL */ +#if defined(Q_ENABLE_PGSQL) if (db->pgsql != NULL) { pgsql_close(db->pgsql); db->pgsql = NULL; } - db->connected = false; +#endif /* Q_ENABLE_PGSQL */ + db->connected = false; Q_MUTEX_LEAVE(db->qmutex); return true; -#else +#else /* Q_ENABLE_MYSQL || Q_ENABLE_PGSQL */ return false; #endif } @@ -594,35 +593,30 @@ static int execute_update(qdb_t *db, const char *query) { if (db == NULL || db->connected == false) return -1; -#ifdef Q_ENABLE_MYSQL +#if defined(Q_ENABLE_MYSQL) || defined(Q_ENABLE_PGSQL) Q_MUTEX_ENTER(db->qmutex); int affected = -1; // query DEBUG("%s", query); +#if defined(Q_ENABLE_MYSQL) if (mysql_query(db->mysql, query) == 0) { /* get affected rows */ if ((affected = mysql_affected_rows(db->mysql)) < 0) affected = -1; } +#endif /* Q_ENABLE_MYSQL */ - Q_MUTEX_LEAVE(db->qmutex); - return affected; -#elif defined(Q_ENABLE_PGSQL) - Q_MUTEX_ENTER(db->qmutex); - - int affected = -1; - - // query - DEBUG("%s", query); +#if defined(Q_ENABLE_PGSQL) if (pgsql_query(db->pgsql, query, PQ_WRITE) == 0) { /* get affected rows */ if ((affected = pgsql_affected_rows(db->pgsql)) < 0) affected = -1; } +#endif /* Q_ENABLE_PGSQL */ Q_MUTEX_LEAVE(db->qmutex); return affected; -#else +#else /* Q_ENABLE_MYSQL || Q_ENABLE_PGSQL */ return -1; #endif } @@ -659,9 +653,12 @@ static qdbresult_t *execute_query(qdb_t *db, const char *query) { if (db == NULL || db->connected == false) return NULL; -#ifdef Q_ENABLE_MYSQL - // query +#if defined(Q_ENABLE_MYSQL) || defined(Q_ENABLE_PGSQL) + DEBUG("%s", query); + +#if defined(Q_ENABLE_MYSQL) + // query if (mysql_query(db->mysql, query)) return NULL; // store @@ -684,24 +681,10 @@ static qdbresult_t *execute_query(qdb_t *db, const char *query) result->row = NULL; result->cols = mysql_num_fields(result->rs); result->cursor = 0; +#endif /* Q_ENABLE_MYSQL */ - /* assign methods */ - result->get_str = result_get_str; - result->get_str_at = result_get_str_at; - result->get_int = result_get_int; - result->get_int_at = result_get_int_at; - result->get_next = result_get_next; - - result->get_cols = result_get_cols; - result->get_rows = result_get_rows; - result->get_row = result_get_row; - - result->free = result_free; - - return result; -#elif defined(Q_ENABLE_PGSQL) +#if defined(Q_ENABLE_PGSQL) // query - DEBUG("%s", query); if (pgsql_query(db->pgsql, query, PQ_READ)) return NULL; // store @@ -714,6 +697,7 @@ static qdbresult_t *execute_query(qdb_t *db, const char *query) qtype_cast(pgsql_t*, result->pgsql)->rows = pgsql_num_rows(result->pgsql); qtype_cast(pgsql_t*, result->pgsql)->cols = pgsql_num_fields(result->pgsql); qtype_cast(pgsql_t*, result->pgsql)->cursor = 0; +#endif /* Q_ENABLE_PGSQL */ /* assign methods */ result->get_str = result_get_str; @@ -729,7 +713,7 @@ static qdbresult_t *execute_query(qdb_t *db, const char *query) result->free = result_free; return result; -#else +#else /* Q_ENABLE_MYSQL || Q_ENABLE_PGSQL */ return NULL; #endif } @@ -770,13 +754,14 @@ static bool begin_tran(qdb_t *db) { if (db == NULL) return false; -#ifdef Q_ENABLE_MYSQL +#if defined(Q_ENABLE_MYSQL) || defined(Q_ENABLE_PGSQL) Q_MUTEX_ENTER(db->qmutex); - if (db->qmutex.count != 1) { + if (qtype_cast(qmutex_t*, db->qmutex)->count != 1) { Q_MUTEX_LEAVE(db->qmutex); return false; } +#if defined(Q_ENABLE_MYSQL) qdbresult_t *result; result = db->execute_query(db, "START TRANSACTION"); if (result == NULL) { @@ -785,19 +770,17 @@ static bool begin_tran(qdb_t *db) } result->free(result); return true; -#elif defined(Q_ENABLE_PGSQL) - Q_MUTEX_ENTER(db->qmutex); - if (qtype_cast(qmutex_t*, db->qmutex)->count != 1) { - Q_MUTEX_LEAVE(db->qmutex); - return false; - } +#endif /* Q_ENABLE_MYSQL */ +#if defined(Q_ENABLE_PGSQL) if (pgsql_query(db->pgsql, "START TRANSACTION", PQ_WRITE) < 0) { Q_MUTEX_LEAVE(db->qmutex); return false; } return true; -#else +#endif /* Q_ENABLE_PGSQL */ + +#else /* Q_ENABLE_MYSQL || Q_ENABLE_PGSQL */ return false; #endif } @@ -813,27 +796,26 @@ static bool commit(qdb_t *db) { if (db == NULL) return false; -#ifdef Q_ENABLE_MYSQL +#if defined(Q_ENABLE_MYSQL) || defined(Q_ENABLE_PGSQL) bool ret = false; + +#if defined(Q_ENABLE_MYSQL) if (mysql_commit(db->mysql) == 0) { ret = true; } +#endif /* Q_ENABLE_MYSQL */ - if (db->qmutex.count > 0) { - Q_MUTEX_LEAVE(db->qmutex); - } - return ret; -#elif defined(Q_ENABLE_PGSQL) - bool ret = false; +#if defined(Q_ENABLE_PGSQL) if (pgsql_query(db->pgsql, "COMMIT", PQ_WRITE) == 0) { ret = true; } +#endif /* Q_ENABLE_PGSQL */ if (qtype_cast(qmutex_t*, db->qmutex)->count > 0) { Q_MUTEX_LEAVE(db->qmutex); } return ret; -#else +#else /* Q_ENABLE_MYSQL || Q_ENABLE_PGSQL */ return false; #endif } @@ -849,27 +831,26 @@ static bool rollback(qdb_t *db) { if (db == NULL) return false; -#ifdef Q_ENABLE_MYSQL +#if defined(Q_ENABLE_MYSQL) || defined(Q_ENABLE_PGSQL) bool ret = false; + +#if defined(Q_ENABLE_MYSQL) if (mysql_rollback(db->mysql) == 0) { ret = true; } +#endif /* Q_ENABLE_MYSQL */ - if (db->qmutex.count > 0) { - Q_MUTEX_LEAVE(db->qmutex); - } - return ret; -#elif defined(Q_ENABLE_PGSQL) - bool ret = false; +#if defined(Q_ENABLE_PGSQL) if (pgsql_query(db->pgsql, "ROLLBACK", PQ_WRITE) == 0) { ret = true; } +#endif /* Q_ENABLE_PGSQL */ if (qtype_cast(qmutex_t*, db->qmutex)->count > 0) { Q_MUTEX_LEAVE(db->qmutex); } return ret; -#else +#else /* Q_ENABLE_MYSQL || Q_ENABLE_PGSQL */ return 0; #endif } @@ -895,14 +876,20 @@ static bool set_fetchtype(qdb_t *db, bool fromdb) { if (db == NULL) return false; -#ifdef Q_ENABLE_MYSQL +#if defined(Q_ENABLE_MYSQL) || defined(Q_ENABLE_PGSQL) + +#if defined(Q_ENABLE_MYSQL) db->info.fetchtype = fromdb; return true; -#elif defined(Q_ENABLE_PGSQL) +#endif /* Q_ENABLE_MYSQL */ + +#if defined(Q_ENABLE_PGSQL) if (db->pgsql == NULL) return false; pgsql_set_emsg(db->pgsql, "unsupported operation"); return false; -#else +#endif /* Q_ENABLE_PGSQL */ + +#else /* Q_ENABLE_MYSQL || Q_ENABLE_PGSQL */ return false; #endif } @@ -939,21 +926,24 @@ static bool ping(qdb_t *db) { if (db == NULL) return false; -#ifdef Q_ENABLE_MYSQL +#if defined(Q_ENABLE_MYSQL) || defined(Q_ENABLE_PGSQL) + bool ping_failed = false; + +#if defined(Q_ENABLE_MYSQL) if (db->connected == true && mysql_ping(db->mysql) == 0) { return true; - } else { // ping test failed - if (open_(db) == true) { // try re-connect - DEBUG("Connection recovered."); - return true; - } } + ping_failed = true; +#endif /* Q_ENABLE_MYSQL */ - return false; -#elif defined(Q_ENABLE_PGSQL) +#if defined(Q_ENABLE_PGSQL) if (db->connected == true && pgsql_query(db->pgsql, NULL, PQ_PING) == 0) { return true; - } else { // ping test failed + } + ping_failed = true; +#endif /* Q_ENABLE_PGSQL */ + + if (ping_failed) { // ping test failed if (open_(db) == true) { // try re-connect DEBUG("Connection recovered."); return true; @@ -961,7 +951,7 @@ static bool ping(qdb_t *db) } return false; -#else +#else /* Q_ENABLE_MYSQL || Q_ENABLE_PGSQL */ return false; #endif } @@ -983,11 +973,15 @@ static const char *get_error(qdb_t *db, unsigned int *errorno) unsigned int eno = 0; const char *emsg; -#ifdef Q_ENABLE_MYSQL + +#if defined(Q_ENABLE_MYSQL) || defined(Q_ENABLE_PGSQL) +#if defined(Q_ENABLE_MYSQL) eno = mysql_errno(db->mysql); if (eno == 0) emsg = "(no error)"; else emsg = mysql_error(db->mysql); -#elif defined(Q_ENABLE_PGSQL) +#endif /* Q_ENABLE_MYSQL */ + +#if defined(Q_ENABLE_PGSQL) emsg = qtype_cast(pgsql_t*, db->pgsql)->emsg; if (emsg == NULL) { eno = 0; @@ -995,7 +989,8 @@ static const char *get_error(qdb_t *db, unsigned int *errorno) } else { eno = 1; } -#else +#endif /* Q_ENABLE_PGSQL */ +#else /* Q_ENABLE_MYSQL || Q_ENABLE_PGSQL */ emsg = "(not implemented)"; #endif @@ -1042,8 +1037,11 @@ static void free_(qdb_t *db) */ static const char *result_get_str(qdbresult_t *result, const char *field) { -#ifdef Q_ENABLE_MYSQL - if (result == NULL || result->rs == NULL || result->cols <= 0) return NULL; + if (result == NULL) return NULL; + +#if defined(Q_ENABLE_MYSQL) || defined(Q_ENABLE_PGSQL) +#if defined(Q_ENABLE_MYSQL) + if (result->rs == NULL || result->cols <= 0) return NULL; if (result->fields == NULL) result->fields = mysql_fetch_fields(result->rs); @@ -1055,10 +1053,10 @@ static const char *result_get_str(qdbresult_t *result, const char *field) } return NULL; -#elif defined(Q_ENABLE_PGSQL) - if (result == NULL - || result->pgsql == NULL - || qtype_cast(pgsql_t*, result->pgsql)->cols <= 0) { +#endif /* Q_ENABLE_MYSQL */ + +#if defined(Q_ENABLE_PGSQL) + if (result->pgsql == NULL || qtype_cast(pgsql_t*, result->pgsql)->cols <= 0) { return NULL; } @@ -1083,7 +1081,8 @@ static const char *result_get_str(qdbresult_t *result, const char *field) val = PQgetvalue(qtype_cast(pgsql_t*, result->pgsql)->pgresult, cur, row); return val; -#else +#endif /* Q_ENABLE_PGSQL */ +#else /* Q_ENABLE_MYSQL || Q_ENABLE_PGSQL */ return NULL; #endif } @@ -1098,18 +1097,21 @@ static const char *result_get_str(qdbresult_t *result, const char *field) */ static const char *result_get_str_at(qdbresult_t *result, int idx) { -#ifdef Q_ENABLE_MYSQL - if (result == NULL - || result->rs == NULL - || result->cursor == 0 - || idx <= 0 - || idx > result->cols ) { + if (result == NULL) return NULL; + +#if defined(Q_ENABLE_MYSQL) || defined(Q_ENABLE_PGSQL) +#if defined(Q_ENABLE_MYSQL) + if (result->rs == NULL + || result->cursor == 0 + || idx <= 0 + || idx > result->cols ) { return NULL; } return result->row[idx-1]; -#elif defined(Q_ENABLE_PGSQL) - if (result == NULL - || result->pgsql == NULL +#endif /* Q_ENABLE_MYSQL */ + +#if defined(Q_ENABLE_PGSQL) + if (result->pgsql == NULL || qtype_cast(pgsql_t*, result->pgsql)->rows == 0 || qtype_cast(pgsql_t*, result->pgsql)->cols == 0 || idx <= 0 @@ -1120,7 +1122,8 @@ static const char *result_get_str_at(qdbresult_t *result, int idx) int cur = qtype_cast(pgsql_t*, result->pgsql)->cursor; const char *val = PQgetvalue(qtype_cast(pgsql_t*, result->pgsql)->pgresult, cur, idx - 1); return val; -#else +#endif /* Q_ENABLE_PGSQL */ +#else /* Q_ENABLE_MYSQL || Q_ENABLE_PGSQL */ return NULL; #endif } @@ -1164,16 +1167,20 @@ static int result_get_int_at(qdbresult_t *result, int idx) */ static bool result_get_next(qdbresult_t *result) { -#ifdef Q_ENABLE_MYSQL - if (result == NULL || result->rs == NULL) return false; + if (result == NULL) return false; + +#if defined(Q_ENABLE_MYSQL) || defined(Q_ENABLE_PGSQL) +#if defined(Q_ENABLE_MYSQL) + if (result->rs == NULL) return false; if ((result->row = mysql_fetch_row(result->rs)) == NULL) return false; result->cursor++; return true; -#elif defined(Q_ENABLE_PGSQL) - if (result == NULL - || result->pgsql == NULL +#endif /* Q_ENABLE_MYSQL */ + +#if defined(Q_ENABLE_PGSQL) + if (result->pgsql == NULL || qtype_cast(pgsql_t*, result->pgsql)->pgresult == NULL) { return false; } @@ -1185,7 +1192,8 @@ static bool result_get_next(qdbresult_t *result) qtype_cast(pgsql_t*, result->pgsql)->cursor = cursor; return true; -#else +#endif /* Q_ENABLE_PGSQL */ +#else /* Q_ENABLE_MYSQL || Q_ENABLE_PGSQL */ return false; #endif } @@ -1199,13 +1207,19 @@ static bool result_get_next(qdbresult_t *result) */ static int result_get_cols(qdbresult_t *result) { -#ifdef Q_ENABLE_MYSQL - if (result == NULL || result->rs == NULL) return 0; + if (result == NULL) return 0; + +#if defined(Q_ENABLE_MYSQL) || defined(Q_ENABLE_PGSQL) +#if defined(Q_ENABLE_MYSQL) + if (result->rs == NULL) return 0; return result->cols; -#elif defined(Q_ENABLE_PGSQL) - if (result == NULL || result->pgsql == NULL) return 0; +#endif /* Q_ENABLE_MYSQL */ + +#if defined(Q_ENABLE_PGSQL) + if (result->pgsql == NULL) return 0; return qtype_cast(pgsql_t*, result->pgsql)->cols; -#else +#endif /* Q_ENABLE_PGSQL */ +#else /* Q_ENABLE_MYSQL || Q_ENABLE_PGSQL */ return 0; #endif } @@ -1219,13 +1233,19 @@ static int result_get_cols(qdbresult_t *result) */ static int result_get_rows(qdbresult_t *result) { -#ifdef Q_ENABLE_MYSQL - if (result == NULL || result->rs == NULL) return 0; + if (result == NULL) return 0; + +#if defined(Q_ENABLE_MYSQL) || defined(Q_ENABLE_PGSQL) +#if defined(Q_ENABLE_MYSQL) + if (result->rs == NULL) return 0; return mysql_num_rows(result->rs); -#elif defined(Q_ENABLE_PGSQL) - if (result == NULL || result->pgsql == NULL) return 0; +#endif /* Q_ENABLE_MYSQL */ + +#if defined(Q_ENABLE_PGSQL) + if (result->pgsql == NULL) return 0; return qtype_cast(pgsql_t*, result->pgsql)->rows; -#else +#endif /* Q_ENABLE_PGSQL */ +#else /* Q_ENABLE_MYSQL || Q_ENABLE_PGSQL */ return 0; #endif } @@ -1242,13 +1262,19 @@ static int result_get_rows(qdbresult_t *result) */ static int result_get_row(qdbresult_t *result) { -#ifdef Q_ENABLE_MYSQL - if (result == NULL || result->rs == NULL) return 0; + if (result == NULL) return 0; + +#if defined(Q_ENABLE_MYSQL) || defined(Q_ENABLE_PGSQL) +#if defined(Q_ENABLE_MYSQL) + if (result->rs == NULL) return 0; return result->cursor; -#elif defined(Q_ENABLE_PGSQL) - if (result == NULL || result->pgsql == NULL) return 0; +#endif /* Q_ENABLE_MYSQL */ + +#if defined(Q_ENABLE_PGSQL) + if (result->pgsql == NULL) return 0; return qtype_cast(pgsql_t*, result->pgsql)->cursor; -#else +#endif /* Q_ENABLE_PGSQL */ +#else /* Q_ENABLE_MYSQL || Q_ENABLE_PGSQL */ return 0; #endif } @@ -1260,8 +1286,10 @@ static int result_get_row(qdbresult_t *result) */ static void result_free(qdbresult_t *result) { -#ifdef Q_ENABLE_MYSQL if (result == NULL) return; + +#if defined(Q_ENABLE_MYSQL) || defined(Q_ENABLE_PGSQL) +#if defined(Q_ENABLE_MYSQL) if (result->rs != NULL) { if (result->fetchtype == true) { while (mysql_fetch_row(result->rs) != NULL); @@ -1271,13 +1299,14 @@ static void result_free(qdbresult_t *result) } free(result); return; -#elif defined(Q_ENABLE_PGSQL) - if (result) { - result->pgsql = NULL; - free(result); - } +#endif /* Q_ENABLE_MYSQL */ + +#if defined(Q_ENABLE_PGSQL) + result->pgsql = NULL; + free(result); return; -#else +#endif /* Q_ENABLE_PGSQL */ +#else /* Q_ENABLE_MYSQL || Q_ENABLE_PGSQL */ return; #endif } From f9dc1959f5cd1e3257595ccbf9cd88c7fd7dcd0e Mon Sep 17 00:00:00 2001 From: SunBeau Date: Sat, 18 May 2024 19:47:57 +0800 Subject: [PATCH 3/4] change a little code --- src/extensions/qdatabase.c | 14 ++++++++++---- tests/test_qdatabase_pgsql.c | 6 +----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/extensions/qdatabase.c b/src/extensions/qdatabase.c index 2d76431e..e8c7da5e 100644 --- a/src/extensions/qdatabase.c +++ b/src/extensions/qdatabase.c @@ -322,7 +322,16 @@ static void result_free(qdbresult_t *result); * return -1; * } * @endcode - + * + * @note + * The pgsql server and libpq library does not provide a setting for autocommit.
+ * The following instructions are from the official manual(https://www.postgresql.org/docs/15/sql-begin.html):
+ * BEGIN initiates a transaction block, that is, all statements after a BEGIN command + * will be executed in a single transaction until an explicit COMMIT or ROLLBACK is given. + * By default (without BEGIN), PostgreSQL executes transactions in “autocommit” mode, + * that is, each statement is executed in its own transaction + * and a commit is implicitly performed at the end of the statement + * (if execution was successful, otherwise a rollback is done). */ qdb_t *qdb(const char *dbtype, const char *addr, int port, const char *database, @@ -521,9 +530,6 @@ static bool open_(qdb_t *db) qtype_cast(pgsql_t*, db->pgsql)->pgconn = pgconn; - // set auto-commit - /* do nothing */ - // set flag db->connected = true; diff --git a/tests/test_qdatabase_pgsql.c b/tests/test_qdatabase_pgsql.c index 96605e9f..ffa18bf4 100644 --- a/tests/test_qdatabase_pgsql.c +++ b/tests/test_qdatabase_pgsql.c @@ -25,10 +25,6 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -/* This code is written and updated by following people and released under - * the same license as above qLibc license. - * Copyright (c) 2015 Zhenjiang Xie - https://github.com/Charles0429 - *****************************************************************************/ #include "libpq-fe.h" @@ -300,4 +296,4 @@ TEST("Test6: other") { default_db->free(default_db); } -QUNIT_END(); \ No newline at end of file +QUNIT_END(); From 69de9124594d28e5bb39e9fb47052d2aa89ee322 Mon Sep 17 00:00:00 2001 From: SunBeau Date: Sat, 18 May 2024 23:59:03 +0800 Subject: [PATCH 4/4] support fetchtype --- src/extensions/qdatabase.c | 145 ++++++++++++++++++++++++----------- tests/test_qdatabase_pgsql.c | 39 +++++++++- 2 files changed, 136 insertions(+), 48 deletions(-) diff --git a/src/extensions/qdatabase.c b/src/extensions/qdatabase.c index e8c7da5e..7542687b 100644 --- a/src/extensions/qdatabase.c +++ b/src/extensions/qdatabase.c @@ -106,9 +106,10 @@ Provide interfaces with naming convention similar to mysql_xyz(). typedef enum { PQ_UNKNOWN, - PQ_WRITE, - PQ_READ, PQ_PING, + PQ_QUERY, + PQ_UPDATE, + PQ_TRANSACTION, } pgquery_t; typedef struct { @@ -173,7 +174,8 @@ static inline void pgsql_query_result(pgsql_t* pgsql) } } -static inline int pgsql_query(pgsql_t* pgsql, const char *query, pgquery_t type) +static inline int pgsql_query(pgsql_t* pgsql, bool fetchtype, + const char *query, pgquery_t type) { if (pgsql == NULL || pgsql->pgconn == NULL) { return -1; /* error */ @@ -186,8 +188,28 @@ static inline int pgsql_query(pgsql_t* pgsql, const char *query, pgquery_t type) query = ""; } - PGresult *result = PQexec(pgsql->pgconn, query); - ExecStatusType status = PQresultStatus(result); + PGresult *result = NULL; + ExecStatusType status = -1; + + if (fetchtype && type == PQ_QUERY) { + if (PQsendQuery(pgsql->pgconn, query)) { + if (PQsetSingleRowMode(pgsql->pgconn)) { + result = PQgetResult(pgsql->pgconn); + } else { + return -1; /* error */ + } + } else { + return -1; /* error */ + } + } else { + result = PQexec(pgsql->pgconn, query); + } + + if (result == NULL) { + return -1; /* error */ + } + + status = PQresultStatus(result); switch (type) { case PQ_PING: { @@ -197,8 +219,9 @@ static inline int pgsql_query(pgsql_t* pgsql, const char *query, pgquery_t type) break; } - case PQ_READ: { - if (status != PGRES_TUPLES_OK) { + case PQ_QUERY: { + if (status != PGRES_TUPLES_OK && + status != PGRES_SINGLE_TUPLE) { return -1; /* error */ } break; @@ -246,6 +269,27 @@ static inline int pgsql_num_fields(pgsql_t* pgsql) /* error */ return -1; } + +static inline int pgsql_fetch_row(pgsql_t* pgsql) +{ + if (pgsql && pgsql->pgconn) { + PGresult *result = PQgetResult(pgsql->pgconn); + if (result) { + pgsql_query_result(pgsql); + pgsql->pgresult = result; + pgsql->rows = pgsql_num_rows(pgsql); + pgsql->cols = pgsql_num_fields(pgsql); + return 0; + } else { + /* error */ + return -1; + } + } + + /* error */ + return -1; +} + #endif /* ENABLE_PGSQL */ #include "qinternal.h" @@ -614,7 +658,7 @@ static int execute_update(qdb_t *db, const char *query) #endif /* Q_ENABLE_MYSQL */ #if defined(Q_ENABLE_PGSQL) - if (pgsql_query(db->pgsql, query, PQ_WRITE) == 0) { + if (pgsql_query(db->pgsql, db->info.fetchtype, query, PQ_UPDATE) == 0) { /* get affected rows */ if ((affected = pgsql_affected_rows(db->pgsql)) < 0) affected = -1; } @@ -691,7 +735,7 @@ static qdbresult_t *execute_query(qdb_t *db, const char *query) #if defined(Q_ENABLE_PGSQL) // query - if (pgsql_query(db->pgsql, query, PQ_READ)) return NULL; + if (pgsql_query(db->pgsql, db->info.fetchtype, query, PQ_QUERY)) return NULL; // store qdbresult_t *result = (qdbresult_t *)malloc(sizeof(qdbresult_t)); @@ -779,7 +823,7 @@ static bool begin_tran(qdb_t *db) #endif /* Q_ENABLE_MYSQL */ #if defined(Q_ENABLE_PGSQL) - if (pgsql_query(db->pgsql, "START TRANSACTION", PQ_WRITE) < 0) { + if (pgsql_query(db->pgsql, db->info.fetchtype, "START TRANSACTION", PQ_TRANSACTION) < 0) { Q_MUTEX_LEAVE(db->qmutex); return false; } @@ -812,7 +856,7 @@ static bool commit(qdb_t *db) #endif /* Q_ENABLE_MYSQL */ #if defined(Q_ENABLE_PGSQL) - if (pgsql_query(db->pgsql, "COMMIT", PQ_WRITE) == 0) { + if (pgsql_query(db->pgsql, db->info.fetchtype, "COMMIT", PQ_TRANSACTION) == 0) { ret = true; } #endif /* Q_ENABLE_PGSQL */ @@ -847,7 +891,7 @@ static bool rollback(qdb_t *db) #endif /* Q_ENABLE_MYSQL */ #if defined(Q_ENABLE_PGSQL) - if (pgsql_query(db->pgsql, "ROLLBACK", PQ_WRITE) == 0) { + if (pgsql_query(db->pgsql, db->info.fetchtype, "ROLLBACK", PQ_TRANSACTION) == 0) { ret = true; } #endif /* Q_ENABLE_PGSQL */ @@ -890,9 +934,8 @@ static bool set_fetchtype(qdb_t *db, bool fromdb) #endif /* Q_ENABLE_MYSQL */ #if defined(Q_ENABLE_PGSQL) - if (db->pgsql == NULL) return false; - pgsql_set_emsg(db->pgsql, "unsupported operation"); - return false; + db->info.fetchtype = fromdb; + return true; #endif /* Q_ENABLE_PGSQL */ #else /* Q_ENABLE_MYSQL || Q_ENABLE_PGSQL */ @@ -943,7 +986,7 @@ static bool ping(qdb_t *db) #endif /* Q_ENABLE_MYSQL */ #if defined(Q_ENABLE_PGSQL) - if (db->connected == true && pgsql_query(db->pgsql, NULL, PQ_PING) == 0) { + if (db->connected == true && pgsql_query(db->pgsql, db->info.fetchtype, NULL, PQ_PING) == 0) { return true; } ping_failed = true; @@ -1062,31 +1105,29 @@ static const char *result_get_str(qdbresult_t *result, const char *field) #endif /* Q_ENABLE_MYSQL */ #if defined(Q_ENABLE_PGSQL) - if (result->pgsql == NULL || qtype_cast(pgsql_t*, result->pgsql)->cols <= 0) { + pgsql_t* pgsql = result->pgsql; + if (pgsql == NULL || pgsql->cols <= 0) { return NULL; } - const char *val = NULL; - int rows = qtype_cast(pgsql_t*, result->pgsql)->rows; - int cols = qtype_cast(pgsql_t*, result->pgsql)->cols; - int cur = qtype_cast(pgsql_t*, result->pgsql)->cursor; - - /* get row num */ - int row = -1; - for (int i = 0; i < rows; i++) { - val = PQfname(qtype_cast(pgsql_t*, result->pgsql)->pgresult, i); - if (!strcasecmp(val, field)) { - row = i; - break; - } + /* get field num */ + const char *value = NULL; + int field_num = PQfnumber(pgsql->pgresult, field); + if (field_num == -1) { + return NULL; } - if (row == -1) { - return NULL; + /* get row num */ + int row_num = -1; + if (pgsql->rows == 1) { + row_num = 0; + } else { + row_num = pgsql->cursor; } - val = PQgetvalue(qtype_cast(pgsql_t*, result->pgsql)->pgresult, cur, row); - return val; + /* get value */ + value = PQgetvalue(pgsql->pgresult, row_num, field_num); + return value; #endif /* Q_ENABLE_PGSQL */ #else /* Q_ENABLE_MYSQL || Q_ENABLE_PGSQL */ return NULL; @@ -1117,17 +1158,26 @@ static const char *result_get_str_at(qdbresult_t *result, int idx) #endif /* Q_ENABLE_MYSQL */ #if defined(Q_ENABLE_PGSQL) + pgsql_t* pgsql = result->pgsql; if (result->pgsql == NULL - || qtype_cast(pgsql_t*, result->pgsql)->rows == 0 - || qtype_cast(pgsql_t*, result->pgsql)->cols == 0 + || pgsql->rows == 0 + || pgsql->cols == 0 || idx <= 0 - || idx > qtype_cast(pgsql_t*, result->pgsql)->cols) { + || idx > pgsql->cols) { return NULL; } - int cur = qtype_cast(pgsql_t*, result->pgsql)->cursor; - const char *val = PQgetvalue(qtype_cast(pgsql_t*, result->pgsql)->pgresult, cur, idx - 1); - return val; + /* get row num */ + int row_num = -1; + if (pgsql->rows == 1) { + row_num = 0; + } else { + row_num = pgsql->cursor; + } + + /* get value */ + const char *value = PQgetvalue(pgsql->pgresult, row_num, idx - 1); + return value; #endif /* Q_ENABLE_PGSQL */ #else /* Q_ENABLE_MYSQL || Q_ENABLE_PGSQL */ return NULL; @@ -1186,17 +1236,20 @@ static bool result_get_next(qdbresult_t *result) #endif /* Q_ENABLE_MYSQL */ #if defined(Q_ENABLE_PGSQL) - if (result->pgsql == NULL - || qtype_cast(pgsql_t*, result->pgsql)->pgresult == NULL) { + pgsql_t* pgsql = result->pgsql; + + if (pgsql == NULL || pgsql->pgresult == NULL) { return false; } - int cursor = qtype_cast(pgsql_t*, result->pgsql)->cursor; - if (++cursor == qtype_cast(pgsql_t*, result->pgsql)->rows) { - return false; + int cursor = pgsql->cursor; + if (++cursor >= pgsql->rows) { + if (pgsql_fetch_row(pgsql) != 0) { + return false; + } } - qtype_cast(pgsql_t*, result->pgsql)->cursor = cursor; + pgsql->cursor = cursor; return true; #endif /* Q_ENABLE_PGSQL */ #else /* Q_ENABLE_MYSQL || Q_ENABLE_PGSQL */ diff --git a/tests/test_qdatabase_pgsql.c b/tests/test_qdatabase_pgsql.c index ffa18bf4..78373457 100644 --- a/tests/test_qdatabase_pgsql.c +++ b/tests/test_qdatabase_pgsql.c @@ -273,6 +273,43 @@ TEST("Test5: autocommit") { ASSERT_NULL(db); } +TEST("Test6: fetchtype") { + qdb_t *default_db = test_connect_database(default_database); + ASSERT_TRUE(default_db->connected); + qdb_t *test_db = test_connect_testdb(default_db); + test_create_table_animals(test_db); + + /* test: set_fetchtype true */ + ASSERT_TRUE(test_db->set_fetchtype(test_db, true)); + + /* test: execute_queryf in fetchtype mode */ + qdbresult_t *qrst = test_db->execute_queryf(test_db, "SELECT * FROM %s;", "animals"); + ASSERT_EQUAL_INT(qrst->get_rows(qrst), 1); + ASSERT_EQUAL_INT(qrst->get_cols(qrst), 7); + ASSERT_EQUAL_INT(qrst->get_row(qrst), 0); + ASSERT_EQUAL_STR(qrst->get_str(qrst, "animal_name"), "Dog"); + ASSERT_EQUAL_STR(qrst->get_str_at(qrst, 3), "Canis lupus familiaris"); + ASSERT_TRUE(qrst->get_next(qrst)); + ASSERT_EQUAL_INT(qrst->get_row(qrst), 1); + ASSERT_TRUE(qrst->get_next(qrst)); + ASSERT_EQUAL_INT(qrst->get_row(qrst), 2); + + ASSERT_EQUAL_INT(qrst->get_int(qrst, "animal_id"), 3); + ASSERT_EQUAL_INT(qrst->get_int_at(qrst, 5), 1); + qrst->free(qrst); + + /* test: set_fetchtype false */ + ASSERT_TRUE(test_db->set_fetchtype(test_db, false)); + + /* disconnect */ + ASSERT_TRUE(test_db->close(test_db)); + ASSERT_TRUE(default_db->close(default_db)); + + /* free */ + test_db->free(test_db); + default_db->free(default_db); +} + TEST("Test6: other") { qdb_t *default_db = test_connect_database(default_database); ASSERT_TRUE(default_db->connected); @@ -281,8 +318,6 @@ TEST("Test6: other") { /* test:get_error */ ASSERT_EQUAL_STR(test_db->get_error(test_db, NULL), "(no error)"); - ASSERT_FALSE(test_db->set_fetchtype(test_db, true)); - ASSERT_EQUAL_STR(test_db->get_error(test_db, NULL), "unsupported operation"); /* test: ping */ ASSERT_TRUE(test_db->ping(test_db));