[Debian-iot-packaging] [hoel] 01/02: Import Upstream version 1.2.1
Thorsten Alteholz
alteholz at moszumanska.debian.org
Thu Oct 5 20:21:17 UTC 2017
This is an automated email from the git hooks/post-receive script.
alteholz pushed a commit to branch master
in repository hoel.
commit 1940cc85d9dd96e0c016fc728a1c834b78a002a0
Author: Thorsten Alteholz <debian at alteholz.de>
Date: Thu Oct 5 22:21:07 2017 +0200
Import Upstream version 1.2.1
---
.gitignore | 2 +
README.md | 54 +++++++-------
examples/Makefile | 13 +++-
examples/example_pgsql.c | 34 +++++++--
src/Makefile | 10 +--
src/hoel-pgsql.c | 184 ++++++++++++++++++++++++++++++++++++++++++++---
src/hoel-simple-json.c | 48 ++++---------
src/hoel.c | 10 +--
src/hoel.h | 9 ++-
test/core.c | 17 +++++
10 files changed, 294 insertions(+), 87 deletions(-)
diff --git a/.gitignore b/.gitignore
index b1d711a..c12118a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,5 @@ example_sqlite3
example_mariadb
example_pgsql
example_mariadb_json
+core
+valgrind.txt
diff --git a/README.md b/README.md
index 7fe53d1..91b5ffd 100644
--- a/README.md
+++ b/README.md
@@ -2,21 +2,22 @@
Database abstraction library written in C.
-Simple and easy to use database access library. Works with SQLite 3, MariaDB/Mysql and (barely) PostgreSQL databases. Uses a json-based language with `jansson` to execute simples queries based on one table.
+Simple and easy to use database access library. Works with SQLite 3, MariaDB/Mysql and PostgreSQL databases. Uses a JSON-based language with `jansson` to execute simples queries based on one table.
# Installation
-Clone, compile and install [yder](https://github.com/babelouest/yder) and [orcania](https://github.com/babelouest/orcania) librares.
+## Linux distribution package
-### Yder (simple logs library)
+Hoel is now available in Debian Buster (testing), Debian Sid, and soon in Ubuntu. To install the development package on your system, run the command as root:
```shell
-$ git clone https://github.com/babelouest/yder.git
-$ cd yder
-$ make
-$ sudo make install
+# apt install libhoel-dev
```
+## Install from the source
+
+Clone, compile and install [Orcania](https://github.com/babelouest/orcania) and [Yder](https://github.com/babelouest/yder) librares.
+
### Orcania (Miscellaneous functions)
```shell
@@ -25,7 +26,16 @@ $ cd orcania
$ make && sudo make install
```
-Install [Jansson](http://www.digip.org/jansson/) library for json manipulation. On a debian-based platform, run the following command:
+### Yder (simple logs library)
+
+```shell
+$ git clone https://github.com/babelouest/yder.git
+$ cd yder
+$ make
+$ sudo make install
+```
+
+Install [Jansson](http://www.digip.org/jansson/) library for JSON manipulation. On a debian-based platform, run the following command:
```shell
$ sudo apt-get install libjansson-dev
@@ -41,7 +51,7 @@ Compile hoel for the backend you need, go to hoel source folder, depending on yo
## SQLite 3
-Install libsqlite3-dev and uncomment the following lines in the `src/Makefile`
+Install `libsqlite3-dev` and uncomment the following lines in the `src/Makefile`
```Makefile
# HAS_SQLITE=-D_HOEL_SQLITE
@@ -50,7 +60,7 @@ Install libsqlite3-dev and uncomment the following lines in the `src/Makefile`
## MariaDB/Mysql
-Install libmysqlclient-dev and uncomment the following lines in the `src/Makefile`
+Install `libmysqlclient-dev` and uncomment the following lines in the `src/Makefile`
```Makefile
# FLAGS_MARIADB=-D_HOEL_MARIADB -I/usr/include/mysql/
@@ -59,17 +69,13 @@ Install libmysqlclient-dev and uncomment the following lines in the `src/Makefil
## Postgre SQL
-Install libpq-dev and uncomment the following lines in the `src/Makefile`
+Install `libpq-dev` and uncomment the following lines in the `src/Makefile`
```Makefile
# HAS_PGSQL=-D_HOEL_PGSQL -I/usr/include/postgresql/
# LIBS_PGSQL=-lpq
```
-### Postgre SQL limitations
-
-For some reasons, the Postgre SQL backend has some limitations. The `*_last_insert_id` functions don't work, and a select statement returns only string values. The reason is I couldn't find on the documentation how to implement those, if it's possible...
-
### Use different backends
You can use different backends at the same time, simply install the required libraries and uncomment all the required backend requirements in the `src/Makefile`.
@@ -357,9 +363,9 @@ int h_query_delete(const struct _h_connection * conn, const char * query);
int h_query_select(const struct _h_connection * conn, const char * query, struct _h_result * result);
```
-### Simple json queries
+### Simple JSON queries
-Hoel allows to use json objects for simple queries with `jansson` library. In the simple json queries, a json object called `json_t * j_query` is used to generate the query.
+Hoel allows to use JSON objects for simple queries with `jansson` library. In the simple JSON queries, a JSON object called `json_t * j_query` is used to generate the query.
All `json_t *` returned and updated values must be free after use.
@@ -371,19 +377,19 @@ A `j_query` has the following form:
"order_by": "col_name [asc|desc]" // String, available for h_select, specify the order by clause, optional
"limit": integer_value // Integer, available for h_select, specify the limit value, optional
"offset" // Integer, available for h_select, specify the limit value, optional but available only if limit is set
- "values": [{ // json object or json array of json objects, available for h_insert, mandatory, specify the values to update
+ "values": [{ // JSON object or JSON array of JSON objects, available for h_insert, mandatory, specify the values to update
"col1": "value1", // Generates col1='value1' for an update query
"col2": value_integer, // Generates col2=value_integer for an update query
"col3", "value3", // Generates col3='value3' for an update query
"col4", null // Generates col4=NULL for an update query
}]
- "set": { // json object, available for h_update, mandatory, specify the values to update
+ "set": { // JSON object, available for h_update, mandatory, specify the values to update
"col1": "value1", // Generates col1='value1' for an update query
"col2": value_integer, // Generates col2=value_integer for an update query
"col3", "value3", // Generates col3='value3' for an update query
"col4", null // Generates col4=NULL for an update query
}
- "where": { // json object, available for h_select, h_update and h_delete, mandatory, specify the where clause. All clauses are separated with an AND operator
+ "where": { // JSON object, available for h_select, h_update and h_delete, mandatory, specify the where clause. All clauses are separated with an AND operator
"col1": "value1", // Generates col1='value1'
"col2": value_integer, // Generates col2=value_integer
"col3": null, // Generates col3=NULL
@@ -404,7 +410,7 @@ A `j_query` has the following form:
#### Where clause construction
-A `where` clause is a json object containing a series of clauses. A clause can have 2 different forms:
+A `where` clause is a JSON object containing a series of clauses. A clause can have 2 different forms:
- `col_name: value`
- `col_name: {operator: "operator_value", value: value}`
@@ -418,7 +424,7 @@ In the second case, `col_name: {operator: "operator_value", value: value}`, depe
All clauses are separated by an `AND` operator.
-As en axample, here is a json object and its generated where clause:
+As en axample, here is a JSON object and its generated where clause:
JSON object:
```javascript
@@ -446,7 +452,7 @@ WHERE col1 = 'value1'
If you need less simple clauses, you can build it on your own and use the `h_execute_query` or the `h_execute_query_json` functions.
-The simple json queries functions are:
+The simple JSON queries functions are:
```c
/**
@@ -495,7 +501,7 @@ int h_update(const struct _h_connection * conn, const json_t * j_query, char **
int h_delete(const struct _h_connection * conn, const json_t * j_query, char ** generated_query);
```
-#### json last insert id
+#### JSON last insert id
The function `h_last_insert_id` returns the last inserted id in a `json_t *` format.
diff --git a/examples/Makefile b/examples/Makefile
index d44c9db..2c0100c 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -13,11 +13,10 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU GENERAL PUBLIC LICENSE for more details.
#
-PREFIX=/usr/local
CC=gcc
CFLAGS=-c -Wall -D_REENTRANT -I$(PREFIX)/include $(ADDITIONALFLAGS)
HOEL_LOCATION=../src
-LIBS=-lc -lhoel -ljansson -lyder -L$(PREFIX)/lib -L$(HOEL_LOCATION)
+LIBS=-lc -lhoel -ljansson -lyder -L$(HOEL_LOCATION)
ADDITIONALFLAGS=-O3
all: libhoel.so example_sqlite3 example_mariadb example_pgsql example_mariadb_json
@@ -48,8 +47,16 @@ example_mariadb_json: example_mariadb_json.c
$(CC) -c $(CFLAGS) example_mariadb_json.c
$(CC) -o example_mariadb_json example_mariadb_json.o $(LIBS)
-test: example_sqlite3 example_mariadb example_pgsql
+test: test_pgsql test_mariadb test_sqlite3 test_mariadb_json
+
+test_pgsql: example_pgsql
LD_LIBRARY_PATH=$(HOEL_LOCATION):${LD_LIBRARY_PATH} ./example_pgsql
+
+test_mariadb: example_mariadb
LD_LIBRARY_PATH=$(HOEL_LOCATION):${LD_LIBRARY_PATH} ./example_mariadb
+
+test_sqlite3: example_sqlite3
LD_LIBRARY_PATH=$(HOEL_LOCATION):${LD_LIBRARY_PATH} ./example_sqlite3
+
+test_mariadb_json: example_mariadb_json
LD_LIBRARY_PATH=$(HOEL_LOCATION):${LD_LIBRARY_PATH} ./example_mariadb_json
diff --git a/examples/example_pgsql.c b/examples/example_pgsql.c
index 6179e8b..601b2bc 100644
--- a/examples/example_pgsql.c
+++ b/examples/example_pgsql.c
@@ -1,12 +1,14 @@
#include <stdio.h>
#include <jansson.h>
+#include <yder.h>
+
#define _HOEL_PGSQL
#include "../src/hoel.h"
void print_result(struct _h_result result) {
int col, row, i;
char buf[64];
- printf("rows: %d, col: %d\n", result.nb_rows, result.nb_columns);
+ y_log_message(Y_LOG_LEVEL_DEBUG, "rows: %d, col: %d", result.nb_rows, result.nb_columns);
for (row = 0; row<result.nb_rows; row++) {
for (col=0; col<result.nb_columns; col++) {
switch(result.data[row][col].type) {
@@ -42,31 +44,51 @@ void print_result(struct _h_result result) {
int main(int argc, char ** argv) {
struct _h_result result;
struct _h_connection * conn;
- char * query = "select * from test", * connectionstring = "host=localhost dbname=test user=test password=test", * dump = NULL;
+ char * query = "select * from test",
+ * insert_query = "insert into test (name, age, birthdate) values ('bob', 21, '1997-05-09')",
+ * connectionstring = "host=girflet dbname=test user=test password=test",
+ * dump = NULL;
int res;
json_t * j_result;
+ y_init_logs("example_pgsql", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting example_pgsql");
+
conn = h_connect_pgsql(connectionstring);
-
+
+ res = h_query_insert(conn, insert_query);
+
+ if (res == H_OK) {
+ y_log_message(Y_LOG_LEVEL_DEBUG, "insert query executed");
+ j_result = h_last_insert_id(conn);
+ dump = json_dumps(j_result, JSON_ENCODE_ANY);
+ y_log_message(Y_LOG_LEVEL_DEBUG, "last id is %s", dump);
+ free(dump);
+ json_decref(j_result);
+ } else {
+ y_log_message(Y_LOG_LEVEL_ERROR, "Error executing query: %d", res);
+ }
+
res = h_query_select(conn, query, &result);
if (res == H_OK) {
print_result(result);
h_clean_result(&result);
} else {
- printf("Error executing query: %d\n", res);
+ y_log_message(Y_LOG_LEVEL_ERROR, "Error executing query: %d", res);
}
res = h_execute_query_json(conn, query, &j_result);
if (res == H_OK) {
dump = json_dumps(j_result, JSON_INDENT(2));
- printf("json result is\n%s\n", dump);
+ y_log_message(Y_LOG_LEVEL_DEBUG, "json result is\n%s", dump);
json_decref(j_result);
free(dump);
} else {
- printf("Error executing json query: %d\n", res);
+ y_log_message(Y_LOG_LEVEL_ERROR, "Error executing json query: %d", res);
}
h_close_db(conn);
+ y_close_logs();
+
return h_clean_connection(conn);
}
diff --git a/src/Makefile b/src/Makefile
index d275c4c..2ec14b2 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,5 +1,5 @@
#
-# Ulfius Framework
+# Hoel Framework
#
# Makefile used to build the software
#
@@ -26,17 +26,17 @@ LIBS_SQLITE=-lsqlite3
# comment/uncomment these lines to disable/enable mysql/mariadb support
FLAGS_MARIADB=-D_HOEL_MARIADB $(shell mysql_config --cflags)
-LIBS_MARIADB=$(shell mysql_config --libs)
+LIBS_MARIADB=$(shell mysql_config --libs_r)
# comment/uncomment these lines to disable/enable postgresql support
-# FLAGS_PGSQL=-D_HOEL_PGSQL -I/usr/include/postgresql/
-# LIBS_PGSQL=-lpq
+FLAGS_PGSQL=-D_HOEL_PGSQL -I/usr/include/postgresql/
+LIBS_PGSQL=-lpq
PREFIX=/usr/local
CFLAGS=-c -fPIC -Wall -I$(PREFIX)/include $(FLAGS_SQLITE) $(FLAGS_MARIADB) $(FLAGS_PGSQL) -D_REENTRANT $(ADDITIONALFLAGS)
LIBS=-L$(PREFIX)/lib -lc -ljansson -lyder -lorcania $(LIBS_SQLITE) $(LIBS_PGSQL) $(LIBS_MARIADB)
OUTPUT=libhoel.so
-VERSION=1.1.1
+VERSION=1.2
all: release
diff --git a/src/hoel-pgsql.c b/src/hoel-pgsql.c
index d591de3..e28e7c6 100644
--- a/src/hoel-pgsql.c
+++ b/src/hoel-pgsql.c
@@ -28,12 +28,19 @@
#include <libpq-fe.h>
#include <string.h>
+struct _h_pg_type {
+ Oid pg_type;
+ unsigned short h_type;
+};
+
/**
* Postgre SQL handle
*/
struct _h_pgsql {
- char * conninfo;
- PGconn * db_handle;
+ char * conninfo;
+ PGconn * db_handle;
+ unsigned int nb_type;
+ struct _h_pg_type * list_type;
};
/**
@@ -44,11 +51,15 @@ struct _h_pgsql {
struct _h_connection * h_connect_pgsql(char * conninfo) {
// TODO get oids and types
struct _h_connection * conn = NULL;
+ struct _h_result result_types;
+ int res_types, row;
+ char * cur_type_name, * endptr = NULL;
+ Oid cur_type_oid;
+
if (conninfo != NULL) {
conn = malloc(sizeof(struct _h_connection));
if (conn == NULL) {
y_log_message(Y_LOG_LEVEL_ERROR, "Hoel - Error allocating memory for conn");
- return NULL;
}
conn->type = HOEL_DB_TYPE_PGSQL;
@@ -56,9 +67,11 @@ struct _h_connection * h_connect_pgsql(char * conninfo) {
if (conn->connection == NULL) {
y_log_message(Y_LOG_LEVEL_ERROR, "Hoel - Error allocating memory for conn->connection");
free(conn);
- return NULL;
+ conn = NULL;
}
((struct _h_pgsql *)conn->connection)->db_handle = PQconnectdb(conninfo);
+ ((struct _h_pgsql *)conn->connection)->nb_type = 0;
+ ((struct _h_pgsql *)conn->connection)->list_type = NULL;
if (PQstatus(((struct _h_pgsql *)conn->connection)->db_handle) != CONNECTION_OK) {
y_log_message(Y_LOG_LEVEL_ERROR, "Hoel - Error connecting to PostgreSQL Database");
@@ -66,8 +79,57 @@ struct _h_connection * h_connect_pgsql(char * conninfo) {
PQfinish(((struct _h_pgsql *)conn->connection)->db_handle);
free(conn->connection);
free(conn);
- return NULL;
- }
+ conn = NULL;
+ } else {
+ res_types = h_execute_query_pgsql(conn, "select oid, typname from pg_type", &result_types);
+ if (res_types == H_OK) {
+ if (result_types.nb_columns == 2) {
+ for (row=0; row<result_types.nb_rows; row++) {
+ cur_type_oid = strtol(((struct _h_type_text *)result_types.data[row][0].t_data)->value, &endptr, 10);
+ cur_type_name = ((struct _h_type_text *)result_types.data[row][1].t_data)->value;
+ if (*endptr == '\0' && endptr != ((struct _h_type_text *)result_types.data[row][0].t_data)->value) {
+ ((struct _h_pgsql *)conn->connection)->list_type = o_realloc(((struct _h_pgsql *)conn->connection)->list_type, (((struct _h_pgsql *)conn->connection)->nb_type + 1) * sizeof(struct _h_pg_type));
+ if (((struct _h_pgsql *)conn->connection)->list_type != NULL) {
+ ((struct _h_pgsql *)conn->connection)->list_type[((struct _h_pgsql *)conn->connection)->nb_type].pg_type = cur_type_oid;
+ if (o_strcmp(cur_type_name, "bool") == 0) {
+ ((struct _h_pgsql *)conn->connection)->list_type[((struct _h_pgsql *)conn->connection)->nb_type].h_type = HOEL_COL_TYPE_BOOL;
+ } else if (o_strncmp(cur_type_name, "int", 3) == 0 || (o_strncmp(cur_type_name+1, "id", 2) == 0 && o_strlen(cur_type_name) == 3)) {
+ ((struct _h_pgsql *)conn->connection)->list_type[((struct _h_pgsql *)conn->connection)->nb_type].h_type = HOEL_COL_TYPE_INT;
+ } else if (o_strcmp(cur_type_name, "numeric") == 0 || o_strncmp(cur_type_name, "float", 5) == 0) {
+ ((struct _h_pgsql *)conn->connection)->list_type[((struct _h_pgsql *)conn->connection)->nb_type].h_type = HOEL_COL_TYPE_DOUBLE;
+ } else if (o_strcmp(cur_type_name, "date") == 0 || o_strncmp(cur_type_name, "time", 4) == 0) {
+ ((struct _h_pgsql *)conn->connection)->list_type[((struct _h_pgsql *)conn->connection)->nb_type].h_type = HOEL_COL_TYPE_DATE;
+ } else if (o_strcmp(cur_type_name, "bytea") == 0) {
+ ((struct _h_pgsql *)conn->connection)->list_type[((struct _h_pgsql *)conn->connection)->nb_type].h_type = HOEL_COL_TYPE_BLOB;
+ } else if (o_strcmp(cur_type_name, "bool") == 0) {
+ ((struct _h_pgsql *)conn->connection)->list_type[((struct _h_pgsql *)conn->connection)->nb_type].h_type = HOEL_COL_TYPE_BOOL;
+ } else {
+ ((struct _h_pgsql *)conn->connection)->list_type[((struct _h_pgsql *)conn->connection)->nb_type].h_type = HOEL_COL_TYPE_TEXT;
+ }
+ ((struct _h_pgsql *)conn->connection)->nb_type++;
+ } else {
+ y_log_message(Y_LOG_LEVEL_ERROR, "Hoel - Error allocating resources for list_type");
+ }
+ } else {
+ y_log_message(Y_LOG_LEVEL_ERROR, "Hoel - Error converting pg_type.oid to integer");
+ }
+ }
+ } else {
+ y_log_message(Y_LOG_LEVEL_ERROR, "Hoel - Error pg_type result");
+ PQfinish(((struct _h_pgsql *)conn->connection)->db_handle);
+ free(conn->connection);
+ free(conn);
+ conn = NULL;
+ }
+ h_clean_result(&result_types);
+ } else {
+ y_log_message(Y_LOG_LEVEL_ERROR, "Hoel - Error getting pg_type");
+ PQfinish(((struct _h_pgsql *)conn->connection)->db_handle);
+ free(conn->connection);
+ free(conn);
+ conn = NULL;
+ }
+ }
}
return conn;
}
@@ -77,6 +139,9 @@ struct _h_connection * h_connect_pgsql(char * conninfo) {
*/
void h_close_pgsql(struct _h_connection * conn) {
PQfinish(((struct _h_pgsql *)conn->connection)->db_handle);
+ o_free(((struct _h_pgsql *)conn->connection)->list_type);
+ ((struct _h_pgsql *)conn->connection)->list_type = NULL;
+ ((struct _h_pgsql *)conn->connection)->nb_type = 0;
}
/**
@@ -88,6 +153,21 @@ char * h_escape_string_pgsql(const struct _h_connection * conn, const char * uns
}
/**
+ * Return the hoel type of a column given its Oid
+ * If type is not found, return HOEL_COL_TYPE_TEXT
+ */
+static unsigned short h_get_type_from_oid(const struct _h_connection * conn, Oid pg_type) {
+ int i;
+
+ for (i = 0; i < ((struct _h_pgsql *)conn->connection)->nb_type; i++) {
+ if (((struct _h_pgsql *)conn->connection)->list_type[i].pg_type == pg_type) {
+ return ((struct _h_pgsql *)conn->connection)->list_type[i].h_type;
+ }
+ }
+ return HOEL_COL_TYPE_TEXT;
+}
+
+/**
* h_execute_query_pgsql
* Execute a query on a pgsql connection, set the result structure with the returned values
* Should not be executed by the user because all parameters are supposed to be correct
@@ -100,7 +180,7 @@ int h_execute_query_pgsql(const struct _h_connection * conn, const char * query,
struct _h_data * data, * cur_row = NULL;
res = PQexec(((struct _h_pgsql *)conn->connection)->db_handle, query);
- if (PQresultStatus(res) != PGRES_TUPLES_OK) {
+ if (PQresultStatus(res) != PGRES_TUPLES_OK && PQresultStatus(res) != PGRES_COMMAND_OK) {
y_log_message(Y_LOG_LEVEL_ERROR, "Error executing sql query");
y_log_message(Y_LOG_LEVEL_DEBUG, "Error message: \"%s\"", PQerrorMessage(((struct _h_pgsql *)conn->connection)->db_handle));
y_log_message(Y_LOG_LEVEL_DEBUG, "Query: \"%s\"", query);
@@ -117,10 +197,34 @@ int h_execute_query_pgsql(const struct _h_connection * conn, const char * query,
cur_row = NULL;
for(j = 0; j < nfields; j++) {
char * val = PQgetvalue(res, i, j);
- if (val == NULL || strlen(val) == 0) {
+ if (val == NULL) {
data = h_new_data_null();
} else {
- data = h_new_data_text(PQgetvalue(res, i, j));
+ switch (h_get_type_from_oid(conn, PQftype(res, j))) {
+ case HOEL_COL_TYPE_INT:
+ data = h_new_data_int(strtol(PQgetvalue(res, i, j), NULL, 10));
+ break;
+ case HOEL_COL_TYPE_DOUBLE:
+ data = h_new_data_double(strtod(PQgetvalue(res, i, j), NULL));
+ break;
+ case HOEL_COL_TYPE_BLOB:
+ data = h_new_data_blob(PQgetvalue(res, i, j), PQfsize(res, i));
+ break;
+ case HOEL_COL_TYPE_BOOL:
+ if (o_strcasecmp(PQgetvalue(res, i, j), "t") == 0) {
+ data = h_new_data_int(1);
+ } else if (o_strcasecmp(PQgetvalue(res, i, j), "f") == 0) {
+ data = h_new_data_int(0);
+ } else {
+ data = h_new_data_null();
+ }
+ break;
+ case HOEL_COL_TYPE_DATE:
+ case HOEL_COL_TYPE_TEXT:
+ default:
+ data = h_new_data_text(PQgetvalue(res, i, j));
+ break;
+ }
}
h_res = h_row_add_data(&cur_row, data, j);
h_clean_data_full(data);
@@ -162,7 +266,7 @@ int h_execute_query_json_pgsql(const struct _h_connection * conn, const char * q
}
res = PQexec(((struct _h_pgsql *)conn->connection)->db_handle, query);
- if (PQresultStatus(res) != PGRES_TUPLES_OK) {
+ if (PQresultStatus(res) != PGRES_TUPLES_OK && PQresultStatus(res) != PGRES_COMMAND_OK) {
y_log_message(Y_LOG_LEVEL_ERROR, "Error executing sql query");
y_log_message(Y_LOG_LEVEL_DEBUG, "Error message: \"%s\"", PQerrorMessage(((struct _h_pgsql *)conn->connection)->db_handle));
y_log_message(Y_LOG_LEVEL_DEBUG, "Query: \"%s\"", query);
@@ -183,7 +287,31 @@ int h_execute_query_json_pgsql(const struct _h_connection * conn, const char * q
if (val == NULL || strlen(val) == 0) {
json_object_set_new(j_data, PQfname(res, j), json_null());
} else {
- json_object_set_new(j_data, PQfname(res, j), json_string(PQgetvalue(res, i, j)));
+ switch (h_get_type_from_oid(conn, PQftype(res, j))) {
+ case HOEL_COL_TYPE_INT:
+ json_object_set_new(j_data, PQfname(res, j), json_integer(strtol(PQgetvalue(res, i, j), NULL, 10)));
+ break;
+ case HOEL_COL_TYPE_DOUBLE:
+ json_object_set_new(j_data, PQfname(res, j), json_real(strtod(PQgetvalue(res, i, j), NULL)));
+ break;
+ case HOEL_COL_TYPE_BLOB:
+ json_object_set_new(j_data, PQfname(res, j), json_stringn(PQgetvalue(res, i, j), PQfsize(res, i)));
+ break;
+ case HOEL_COL_TYPE_BOOL:
+ if (o_strcasecmp(PQgetvalue(res, i, j), "t") == 0) {
+ json_object_set_new(j_data, PQfname(res, j), json_integer(1));
+ } else if (o_strcasecmp(PQgetvalue(res, i, j), "f") == 0) {
+ json_object_set_new(j_data, PQfname(res, j), json_integer(0));
+ } else {
+ json_object_set_new(j_data, PQfname(res, j), json_null());
+ }
+ break;
+ case HOEL_COL_TYPE_DATE:
+ case HOEL_COL_TYPE_TEXT:
+ default:
+ json_object_set_new(j_data, PQfname(res, j), json_string(PQgetvalue(res, i, j)));
+ break;
+ }
}
}
json_array_append_new(*j_result, j_data);
@@ -192,4 +320,38 @@ int h_execute_query_json_pgsql(const struct _h_connection * conn, const char * q
PQclear(res);
return H_OK;
}
+
+/**
+ * Return the id of the last inserted value
+ * Assuming you use sequences for automatically generated ids
+ */
+int h_last_insert_id_pgsql(const struct _h_connection * conn) {
+ PGresult *res;
+ int int_res = 0;
+ char * str_res, * endptr = NULL;
+
+ res = PQexec(((struct _h_pgsql *)conn->connection)->db_handle, "SELECT lastval()");
+ if (PQresultStatus(res) != PGRES_TUPLES_OK && PQresultStatus(res) != PGRES_COMMAND_OK) {
+ y_log_message(Y_LOG_LEVEL_ERROR, "Error executing h_last_insert_id");
+ y_log_message(Y_LOG_LEVEL_DEBUG, "Error message: \"%s\"", PQerrorMessage(((struct _h_pgsql *)conn->connection)->db_handle));
+ return H_ERROR_QUERY;
+ }
+
+ if (PQnfields(res) && PQntuples(res)) {
+ str_res = PQgetvalue(res, 0, 0);
+ if (str_res != NULL) {
+ int_res = strtol(str_res, &endptr, 10);
+ if (*endptr != '\0' || endptr == str_res) {
+ y_log_message(Y_LOG_LEVEL_ERROR, "Error h_last_insert_id, returned value can't be converted to numeric");
+ int_res = 0;
+ }
+ } else {
+ y_log_message(Y_LOG_LEVEL_ERROR, "Error h_last_insert_id, returned value is NULL");
+ }
+ } else {
+ y_log_message(Y_LOG_LEVEL_ERROR, "Error h_last_insert_id, returned value has no data available");
+ }
+ PQclear(res);
+ return int_res;
+}
#endif
diff --git a/src/hoel-simple-json.c b/src/hoel-simple-json.c
index f8945c1..cbc3820 100644
--- a/src/hoel-simple-json.c
+++ b/src/hoel-simple-json.c
@@ -28,29 +28,6 @@
#include "h-private.h"
/**
- * trim_whitespace_and_double_quotes
- * Return the string without its beginning and ending whitespaces or double quotes
- */
-static char * trim_whitespace_and_double_quotes(char *str) {
- char *end;
-
- // Trim leading space
- while(isspace(*str) || *str == '"') str++;
-
- if(*str == 0) // All spaces?
- return str;
-
- // Trim trailing space
- end = str + strlen(str) - 1;
- while(end > str && (isspace(*end) || *end == '"')) end--;
-
- // Write new null terminator
- *(end+1) = 0;
-
- return str;
-}
-
-/**
* Builds an insert query from a json object and a table name
* Returned value must be o_free'd after use
*/
@@ -73,9 +50,7 @@ static char * h_get_insert_query_from_json_object(const struct _h_connection * c
}
break;
case JSON_INTEGER:
- tmp = json_dumps(value, JSON_ENCODE_ANY);
- new_data = msprintf("%s", tmp);
- o_free(tmp);
+ new_data = msprintf("%"JSON_INTEGER_FORMAT, json_integer_value(value));
break;
case JSON_REAL:
new_data = msprintf("%f", json_real_value(value));
@@ -221,16 +196,22 @@ static char * h_get_where_clause_from_json_object(const struct _h_connection * c
} else {
if (json_is_null(value)) {
clause = msprintf("%s IS NULL", key);
- } else {
- dump = json_dumps(value, JSON_ENCODE_ANY);
- escape = h_escape_string(conn, trim_whitespace_and_double_quotes(dump));
+ } else if (json_is_string(value)) {
+ escape = h_escape_string(conn, json_string_value(value));
if (escape == NULL) {
y_log_message(Y_LOG_LEVEL_ERROR, "Hoel - Error escape");
return NULL;
}
clause = msprintf("%s='%s'", key, escape);
- o_free(dump);
o_free(escape);
+ } else if (json_is_integer(value)) {
+ clause = msprintf("%s='%"JSON_INTEGER_FORMAT"'", key, json_integer_value(value));
+ } else if (json_is_real(value)) {
+ clause = msprintf("%s='%f'", key, json_real_value(value));
+ } else if (json_is_true(value)) {
+ clause = msprintf("%s=1");
+ } else if (json_is_false(value)) {
+ clause = msprintf("%s=0");
}
if (clause == NULL) {
y_log_message(Y_LOG_LEVEL_ERROR, "Hoel - Error allocating memory for clause");
@@ -582,9 +563,10 @@ json_t * h_last_insert_id(const struct _h_connection * conn) {
#endif
#ifdef _HOEL_PGSQL
} else if (conn->type == HOEL_DB_TYPE_PGSQL) {
- // TODO
- // Not possible ?
- y_log_message(Y_LOG_LEVEL_ERROR, "Hoel/h_last_insert_id Error feature not supported");
+ int last_id = h_last_insert_id_pgsql(conn);
+ if (last_id > 0) {
+ j_data = json_integer(last_id);
+ }
#endif
}
}
diff --git a/src/hoel.c b/src/hoel.c
index d4f10a9..52376a4 100644
--- a/src/hoel.c
+++ b/src/hoel.c
@@ -311,10 +311,12 @@ struct _h_data * h_query_last_insert_id(const struct _h_connection * conn) {
#endif
#ifdef _HOEL_PGSQL
} else if (conn->type == HOEL_DB_TYPE_PGSQL) {
- // TODO
- // Not possible ?
- y_log_message(Y_LOG_LEVEL_ERROR, "Error feature not supported");
- data = h_new_data_null();
+ int last_id = h_last_insert_id_pgsql(conn);
+ if (last_id > 0) {
+ data = h_new_data_int(last_id);
+ } else {
+ data = h_new_data_null();
+ }
#endif
} else {
data = h_new_data_null();
diff --git a/src/hoel.h b/src/hoel.h
index 3722b3f..5f181c0 100644
--- a/src/hoel.h
+++ b/src/hoel.h
@@ -24,7 +24,7 @@
#ifndef __HOEL_H__
#define __HOEL_H__
-#define HOEL_VERSION 1.1.1
+#define HOEL_VERSION 1.2
#include <jansson.h>
@@ -53,6 +53,7 @@
#define HOEL_COL_TYPE_TEXT 2
#define HOEL_COL_TYPE_DATE 3
#define HOEL_COL_TYPE_BLOB 4
+#define HOEL_COL_TYPE_BOOL 5
#define HOEL_COL_TYPE_NULL 5
#define H_OK 0 // No error
@@ -460,6 +461,12 @@ void h_close_pgsql(struct _h_connection * conn);
* returned value must be free'd after use
*/
char * h_escape_string_pgsql(const struct _h_connection * conn, const char * unsafe);
+
+/**
+ * Return the id of the last inserted value
+ * Assuming you use sequences for automatically generated ids
+ */
+int h_last_insert_id_pgsql(const struct _h_connection * conn);
#endif
#endif // __HOEL_H__
diff --git a/test/core.c b/test/core.c
index ab09122..d0336ca 100644
--- a/test/core.c
+++ b/test/core.c
@@ -15,6 +15,8 @@
#define DEFAULT_BD_PATH "/tmp/test.db"
#define WRONG_BD_PATH "nope.db"
+#define UNSAFE_STRING "un'safe' (\"string\")#!/$%*];"
+
#define SELECT_DATA_1 "SELECT `integer_col`, `string_col`, `date_col` FROM `test_table` WHERE `integer_col` = 1"
#define SELECT_DATA_2 "SELECT `integer_col`, `string_col`, `date_col` FROM `test_table` WHERE `integer_col` = 2"
#define SELECT_DATA_ERROR "SELECT `integer_col`, `string_col`, `date_col` FROM `test_table` WHERE `integer_col` = 'error'"
@@ -82,6 +84,9 @@ START_TEST(test_hoel_escape_string)
{
struct _h_connection * conn;
char * escaped;
+ json_t * j_query, * j_result;
+ int res;
+
conn = h_connect_sqlite(db_path);
ck_assert_ptr_ne(conn, NULL);
escaped = h_escape_string(conn, "value");
@@ -90,6 +95,18 @@ START_TEST(test_hoel_escape_string)
escaped = h_escape_string(conn, "`unsafe ' value\"!");
ck_assert_str_eq(escaped, "`unsafe '' value\"!");
h_free(escaped);
+
+ j_query = json_pack("{sss{siss}}", "table", "test_table", "values", "integer_col", 666, "string_col", UNSAFE_STRING);
+ res = h_insert(conn, j_query, NULL);
+ json_decref(j_query);
+ ck_assert_int_eq(res, H_OK);
+ j_query = json_pack("{sss[s]s{si}}", "table", "test_table", "columns", "string_col", "where", "integer_col", 666);
+ res = h_select(conn, j_query, &j_result, NULL);
+ json_decref(j_query);
+ ck_assert_int_eq(res, H_OK);
+ ck_assert_str_eq(UNSAFE_STRING, json_string_value(json_object_get(json_array_get(j_result, 0), "string_col")));
+ json_decref(j_result);
+
ck_assert_int_eq(h_close_db(conn), H_OK);
ck_assert_int_eq(h_clean_connection(conn), H_OK);
}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-iot/hoel.git
More information about the Debian-iot-packaging
mailing list