[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/pq 76f81f5ca5 05/63: Replace low-level functions with a
From: |
ELPA Syncer |
Subject: |
[elpa] externals/pq 76f81f5ca5 05/63: Replace low-level functions with a higher level one. |
Date: |
Mon, 14 Feb 2022 23:24:18 -0500 (EST) |
branch: externals/pq
commit 76f81f5ca50020831ee82c5926f6b86b24e760b9
Author: Andreas Seltenreich <andreas+git@ansel.ydns.eu>
Commit: Andreas Seltenreich <andreas+git@ansel.ydns.eu>
Replace low-level functions with a higher level one.
Now that all the building blocks are in place, we can formulate a
higher level function pq:query that avoids exposing the PQresult
pointers to lisp. This is more convenient to deal with and probably
also more efficient since less emacs-module interface round-trips are
neccessary.
---
pq.c | 322 ++++++++++------------------------------------------------------
test.el | 26 +++---
2 files changed, 64 insertions(+), 284 deletions(-)
diff --git a/pq.c b/pq.c
index a6eb78824c..9ecda93aa3 100644
--- a/pq.c
+++ b/pq.c
@@ -17,38 +17,11 @@ static emacs_value Qnil;
static emacs_value Qt;
static emacs_value Qpq_error;
-/* We pass different kinds of libpq pointers to lisp. We wrap them up
- with type information so we don't crash if the user mixes them up. */
-struct pq_pointer {
- enum pq_pointer_type {
- type_conn,
- type_res
- } type;
- union {
- PGconn *conn;
- PGresult *res;
- } p;
-};
-
void pq_finalize_pointer(void *user_ptr)
{
- struct pq_pointer *ptr = user_ptr;
-
- /* Do libpq cleanup */
- switch (ptr->type) {
- case type_conn: {
- PQfinish(ptr->p.conn);
- fprintf(stderr, "PQfinish(%p)\n", ptr->p.conn);
- break;
- }
- case type_res:
- fprintf(stderr, "PQclear(%p)\n", ptr->p.res);
- PQclear(ptr->p.res);
- break;
- }
-
- /* Free our wrapper */
- free(user_ptr);
+ PGconn *conn = user_ptr;
+ fprintf(stderr, "PQfinish(%p)\n", conn);
+ PQfinish(conn);
}
/* Raise error unless a PGresult is ok. */
@@ -65,7 +38,6 @@ bool result_ok(emacs_env *env, PGresult *res)
default:
{
char *errmsg = PQresultErrorMessage(res);
-/* char *errmsg = PQresStatus(status); */
emacs_value errstring = env->make_string(env, errmsg, strlen(errmsg));
PQclear(res);
@@ -102,42 +74,35 @@ Fpq_connectdb (emacs_env *env, int nargs, emacs_value
args[], void *data)
fprintf(stderr, "PQconnectdb(%s) -> %p\n", conninfo, conn);
free(conninfo);
- struct pq_pointer *p = malloc(sizeof(struct pq_pointer));
- p->type = type_conn;
- p->p.conn = conn;
- return env->make_user_ptr(env, pq_finalize_pointer, p);
+ return env->make_user_ptr(env, pq_finalize_pointer, conn);
}
static emacs_value
-Fpq_exec (emacs_env *env, int nargs, emacs_value args[], void *data)
+pq_getvalue_internal(emacs_env *env, PGresult *res, int row, int column)
{
- if (!env->is_not_nil(env, args[0]))
- return Qnil;
-
- struct pq_pointer *arg0 = env->get_user_ptr(env, args[0]);
- assert(type_conn == arg0->type);
-
- char *command = my_string_to_c(env, args[1]);
- PGresult *res = PQexec(arg0->p.conn, command);
- free(command);
-
- if (!result_ok(env, res))
- return Qnil;
+ char *result = PQgetvalue(res, row, column);
- struct pq_pointer *p = malloc(sizeof(struct pq_pointer));
- p->type = type_res;
- p->p.res = res;
- return env->make_user_ptr(env, pq_finalize_pointer, p);
+ switch(PQftype(res, column)) {
+ case INT2OID:
+ case INT4OID:
+ case OIDOID:
+ return env->make_integer(env, atol(result));
+ case INT8OID:
+ case FLOAT4OID:
+ case FLOAT8OID:
+ case NUMERICOID:
+ return env->make_float(env, atof(result));
+ default:
+ return env->make_string(env, result, strlen(result));
+ }
}
static emacs_value
-Fpq_execParams (emacs_env *env, int nargs, emacs_value args[], void *data)
+Fpq_query (emacs_env *env, int nargs, emacs_value args[], void *data)
{
if (!env->is_not_nil(env, args[0]))
return Qnil;
-
- struct pq_pointer *arg0 = env->get_user_ptr(env, args[0]);
- assert(type_conn == arg0->type);
+ PGconn *conn = env->get_user_ptr(env, args[0]);
int nParams = nargs - 2;
const char *paramValues[nParams];
@@ -146,7 +111,7 @@ Fpq_execParams (emacs_env *env, int nargs, emacs_value
args[], void *data)
paramValues[i] = my_string_to_c(env, args[2+i]);
char *command = my_string_to_c(env, args[1]);
- PGresult *res = PQexecParams(arg0->p.conn, command, nParams,
+ PGresult *res = PQexecParams(conn, command, nParams,
NULL, paramValues, NULL, NULL, 0);
for (int i=0; i<nParams; i++)
@@ -157,147 +122,33 @@ Fpq_execParams (emacs_env *env, int nargs, emacs_value
args[], void *data)
if (!result_ok(env, res))
return Qnil;
- struct pq_pointer *p = malloc(sizeof(struct pq_pointer));
- p->type = type_res;
- p->p.res = res;
- return env->make_user_ptr(env, pq_finalize_pointer, p);
-}
-
-static emacs_value
-Fpq_prepare (emacs_env *env, int nargs, emacs_value args[], void *data)
-{
- if (!env->is_not_nil(env, args[0]))
- return Qnil;
-
- struct pq_pointer *arg0 = env->get_user_ptr(env, args[0]);
- assert(type_conn == arg0->type);
-
- char *name = my_string_to_c(env, args[1]);
- char *command = my_string_to_c(env, args[2]);
-
- PGresult *res = PQprepare(arg0->p.conn, name, command, 0, 0);
-
- free(name);
- free(command);
-
- if (!result_ok(env, res))
- return Qnil;
-
- return args[1];
-}
-
-static emacs_value
-Fpq_execPrepared (emacs_env *env, int nargs, emacs_value args[], void *data)
-{
- if (!env->is_not_nil(env, args[0]))
- return Qnil;
- struct pq_pointer *arg0 = env->get_user_ptr(env, args[0]);
- assert(type_conn == arg0->type);
-
- char *name = my_string_to_c(env, args[1]);
-
- int nParams = nargs - 2;
- const char *paramValues[nParams];
- for (int i=0; i<nParams; i++)
- paramValues[i] = my_string_to_c(env, args[2+i]);
-
- PGresult *res = PQexecParams(arg0->p.conn, name, nParams,
- NULL, paramValues, NULL, NULL, 0);
-
- for (int i=0; i<nParams; i++)
- free((void *)paramValues[i]);
-
- if (!result_ok(env, res))
- return Qnil;
-
- struct pq_pointer *p = malloc(sizeof(struct pq_pointer));
- p->type = type_res;
- p->p.res = res;
- return env->make_user_ptr(env, pq_finalize_pointer, p);
-}
-
-static emacs_value
-Fpq_ntuples (emacs_env *env, int nargs, emacs_value args[], void *data)
-{
- if (!env->is_not_nil(env, args[0]))
- return Qnil;
- struct pq_pointer *arg0 = env->get_user_ptr(env, args[0]);
- assert(type_res == arg0->type);
- return env->make_integer(env, PQntuples(arg0->p.res));
-}
-
-static emacs_value
-Fpq_nfields (emacs_env *env, int nargs, emacs_value args[], void *data)
-{
- if (!env->is_not_nil(env, args[0]))
- return Qnil;
- struct pq_pointer *arg0 = env->get_user_ptr(env, args[0]);
- assert(type_res == arg0->type);
- return env->make_integer(env, PQnfields(arg0->p.res));
-}
-
-static emacs_value
-Fpq_fname (emacs_env *env, int nargs, emacs_value args[], void *data)
-{
- if (!env->is_not_nil(env, args[0]))
- return Qnil;
- struct pq_pointer *arg0 = env->get_user_ptr(env, args[0]);
- int column = env->extract_integer(env, args[1]);
- assert(type_res == arg0->type);
- char *name = PQfname(arg0->p.res, column);
- return env->make_string(env, name, strlen(name));
-}
+ int ntuples = PQntuples(res);
+ int nfields = PQnfields(res);
-static emacs_value
-pq_getvalue_internal(emacs_env *env, PGresult *res, int row, int column)
-{
- char *result = PQgetvalue(res, row, column);
-
- switch(PQftype(res, column)) {
- case INT2OID:
- case INT4OID:
- case OIDOID:
- return env->make_integer(env, atol(result));
- case INT8OID:
- case FLOAT4OID:
- case FLOAT8OID:
- case NUMERICOID:
- return env->make_float(env, atof(result));
- default:
- return env->make_string(env, result, strlen(result));
- }
-}
-
-static emacs_value
-Fpq_getvalue(emacs_env *env, int nargs, emacs_value args[], void *data)
-{
- if (!env->is_not_nil(env, args[0]))
- return Qnil;
- struct pq_pointer *arg0 = env->get_user_ptr(env, args[0]);
- int row = env->extract_integer(env, args[1]);
- int column = env->extract_integer(env, args[2]);
- assert(type_res == arg0->type);
- if (PQgetisnull(arg0->p.res, row, column))
- return Qnil;
- return pq_getvalue_internal(env, arg0->p.res, row, column);
-}
+ emacs_value list = Qnil;
+ emacs_value Qvector = env->intern (env, "vector");
+ emacs_value Qcons = env->intern (env, "cons");
+
+ for (int t = ntuples-1; t >= 0; t--) {
+ emacs_value tuple;
+ if (1 == nfields) {
+ tuple = pq_getvalue_internal(env, res, t, 0);
+ } else if (0 == nfields) {
+ tuple = Qnil;
+ } else {
+ emacs_value *values = malloc((nfields + 1)*sizeof(emacs_value));
+ for (int i = 0; i < nfields; i++) {
+ values[i] = pq_getvalue_internal(env, res, t, i);
+ }
+ values[nfields] = Qnil;
+ tuple = env->funcall (env, Qvector, nfields, values);
+ }
-static emacs_value
-Fpq_getrow(emacs_env *env, int nargs, emacs_value args[], void *data)
-{
- struct pq_pointer *arg0 = env->get_user_ptr(env, args[0]);
- int row = env->extract_integer(env, args[1]);
- assert(type_res == arg0->type);
- int nfields = PQnfields(arg0->p.res);
- emacs_value *values = malloc((nfields + 1)*sizeof(emacs_value));
-
- for (int i = 0; i < nfields; i++) {
- values[i] = pq_getvalue_internal(env, arg0->p.res, row, i);
+ emacs_value args[2] = {tuple, list};
+ list = env->funcall (env, Qcons, 2, args);
}
- values[nfields] = Qnil;
- emacs_value Qvector = env->intern (env, "vector");
- return env->funcall (env, Qvector, nfields, values);
+ return list;
}
static emacs_value
@@ -305,12 +156,11 @@ Fpq_escape (emacs_env *env, int nargs, emacs_value
args[], void *data)
{
if (!env->is_not_nil(env, args[0]))
return Qnil;
- struct pq_pointer *arg0 = env->get_user_ptr(env, args[0]);
- assert(type_conn == arg0->type);
+ PGconn *conn = env->get_user_ptr(env, args[0]);
char *value = my_string_to_c(env, args[1]);
char *(*escaper)(PGconn *, const char *, size_t) = data;
- char *quoted = escaper(arg0->p.conn, value, strlen(value));
+ char *quoted = escaper(conn, value, strlen(value));
emacs_value result = env->make_string(env, quoted, strlen(quoted));
PQfreemem(quoted);
return result;
@@ -362,77 +212,14 @@ emacs_module_init (struct emacs_runtime *init_ert)
);
bind_function("pq:connectdb", fun1);
- emacs_value fun2 = env->make_function (env,
- 2, /* min. number of arguments */
- 2, /* max. number of arguments */
- Fpq_exec, /* actual function pointer */
- "Execute STATEMENT using CONNECTION.", /* docstring */
- NULL /* user pointer of your choice (data param in
Fmymod_test) */
- );
- bind_function("pq:exec", fun2);
-
- emacs_value fun3 = env->make_function (env,
- 1, /* min. number of arguments */
- 1, /* max. number of arguments */
- Fpq_ntuples, /* actual function pointer */
- "Return number of rows in result.", /* docstring */
- NULL /* user pointer of your choice (data param in
Fmymod_test) */
- );
- bind_function("pq:ntuples", fun3);
-
- emacs_value fun4 = env->make_function (env,
- 1, /* min. number of arguments */
- 1, /* max. number of arguments */
- Fpq_nfields, /* actual function pointer */
- "Return number of fields in rows of result.", /*
docstring */
- NULL /* user pointer of your choice (data param in
Fmymod_test) */
- );
- bind_function("pq:nfields", fun4);
-
- emacs_value fun5 = env->make_function (env,
- 2, /* min. number of arguments */
- 2, /* max. number of arguments */
- Fpq_fname, /* actual function pointer */
- "Return name in RESULT of column NUMBER.", /* docstring */
- NULL /* user pointer of your choice (data param in
Fmymod_test) */
- );
- bind_function("pq:fname", fun5);
-
- emacs_value fun6 = env->make_function (env,
- 3, /* min. number of arguments */
- 3, /* max. number of arguments */
- Fpq_getvalue, /* actual function pointer */
- "Return value for RESULT at ROW and COLUMN", /* docstring
*/
- NULL /* user pointer of your choice (data param in
Fmymod_test) */
- );
- bind_function("pq:getvalue", fun6);
-
emacs_value fun7 = env->make_function (env,
2, /* min. number of arguments */
2+MAX_PARAMS, /* max. number of arguments */
- Fpq_execParams, /* actual function pointer */
- "Return value for RESULT at ROW and COLUMN", /* docstring
*/
- NULL /* user pointer of your choice (data param in
Fmymod_test) */
- );
- bind_function("pq:execParams", fun7);
-
- emacs_value fun8 = env->make_function (env,
- 3, /* min. number of arguments */
- 3, /* max. number of arguments */
- Fpq_prepare, /* actual function pointer */
- "Prepare statement NAME with STATEMENT ", /* docstring */
- NULL /* user pointer of your choice (data param in
Fmymod_test) */
- );
- bind_function("pq:prepare", fun8);
-
- emacs_value fun9 = env->make_function (env,
- 2, /* min. number of arguments */
- 2+MAX_PARAMS, /* max. number of arguments */
- Fpq_execPrepared, /* actual function pointer */
- "Execute prepared statement NAME with ARGS...", /*
docstring */
+ Fpq_query, /* actual function pointer */
+ "Execute QUERY on CONNECTION.", /* docstring */
NULL /* user pointer of your choice (data param in
Fmymod_test) */
);
- bind_function("pq:execPrepared", fun9);
+ bind_function("pq:query", fun7);
emacs_value fun10 = env->make_function (env,
2, /* min. number of arguments */
@@ -452,15 +239,6 @@ emacs_module_init (struct emacs_runtime *init_ert)
);
bind_function("pq:escapeIdentifier", fun11);
- emacs_value fun12 = env->make_function (env,
- 2, /* min. number of arguments */
- 2, /* max. number of arguments */
- Fpq_getrow, /* actual function pointer */
- "Fetch ROW from RESULT as a vector.", /* docstring */
- PQescapeIdentifier /* user pointer of your choice (data param
in Fmymod_test) */
- );
- bind_function("pq:getrow", fun12);
-
Qnil = env->intern (env, "nil");
Qt = env->intern (env, "t");
Qpq_error = env->intern (env, "pq:error");
diff --git a/test.el b/test.el
index a9637f64c9..8dd6d0ec3c 100644
--- a/test.el
+++ b/test.el
@@ -1,16 +1,18 @@
(load-library "~/src/emacs-module-postgres/pq.so")
(setq con (pq:connectdb "port=5433 dbname=smith"))
-(setq result (pq:exec con "select version()"))
-(pq:ntuples result)
-(pq:nfields result)
-(pq:getvalue result 0 0)
-(setq result (pq:exec con "select version(),now()"))
-(pq:getvalue result 0 1)
-(pq:getvalue (pq:execParams con "select 'Hello, ' || $1::text"
(user-login-name)) 0 0)
-(pq:escapeLiteral con "moo'oo")
-(pq:exec con (concat "set application_name to " (pq:escapeLiteral con
(emacs-version))))
-(setq result (pq:exec con "select version()"))
-(setq result (pq:exec con "select * from pg_stat_activity"))
-(pq:getrow result 0)
+(pq:query con "select version()")
+;; ("PostgreSQL 9.4.8 on i686-pc-linux-gnu, compiled by gcc (Debian 4.9.2-10)
4.9.2, 32-bit")
+(pq:query con "select 1 union select 2")
+;; (1 2)
+(pq:query con "select 1,2")
+;; ([1 2])
+(pq:query con "select 1,2 union select 3,4")
+;; ([1 2] [3 4])
+(pq:query con "select 'Hello, ' || $1::text" (user-login-name))
+;; ("Hello, andreas")
+(pq:escapeLiteral con "mo'oo\"oo")
+;; "'mo''oo\"oo'"
+(pq:escapeIdentifier con "moo'oo\"oo")
+;; "\"moo'oo\"\"oo\""
(setq con nil)
(garbage-collect)
- [elpa] externals/pq e1d1b77b88 34/63: test: Notice receiver., (continued)
- [elpa] externals/pq e1d1b77b88 34/63: test: Notice receiver., ELPA Syncer, 2022/02/14
- [elpa] externals/pq 499dc3b50e 57/63: Re-introduce custom error signal pq:error with SQLSTATE., ELPA Syncer, 2022/02/14
- [elpa] externals/pq d738d21b6a 52/63: Add checks for non-local exit., ELPA Syncer, 2022/02/14
- [elpa] externals/pq 8151e0bc99 60/63: Add function pq:notifies to support LISTEN., ELPA Syncer, 2022/02/14
- [elpa] externals/pq 46e38888e3 01/63: Initial commit., ELPA Syncer, 2022/02/14
- [elpa] externals/pq b72ee45524 02/63: Add .gitignore, flush tempfile., ELPA Syncer, 2022/02/14
- [elpa] externals/pq dfa4ad04bb 03/63: README: clarify., ELPA Syncer, 2022/02/14
- [elpa] externals/pq 8e13d07737 16/63: Update Makefile, ELPA Syncer, 2022/02/14
- [elpa] externals/pq e9e1998796 10/63: Handle booleans and fix some NULL handling., ELPA Syncer, 2022/02/14
- [elpa] externals/pq 09e320f63f 29/63: Initial travis ci configuration, ELPA Syncer, 2022/02/14
- [elpa] externals/pq 76f81f5ca5 05/63: Replace low-level functions with a higher level one.,
ELPA Syncer <=
- [elpa] externals/pq 1048d41b99 28/63: Use pg_config to detect postgresql include directory, ELPA Syncer, 2022/02/14
- [elpa] externals/pq fb79f059fb 19/63: Flush debug fprintf()s., ELPA Syncer, 2022/02/14
- [elpa] externals/pq 2c03e4aba4 42/63: Make garbage-collection test more robust., ELPA Syncer, 2022/02/14
- [elpa] externals/pq dc688341b0 35/63: test: Check more raised errors in different parts of the code., ELPA Syncer, 2022/02/14
- [elpa] externals/pq 33e626d5b8 11/63: README: It's no longer quick and dirty., ELPA Syncer, 2022/02/14
- [elpa] externals/pq 45a732a4c2 31/63: Add test for signal error, ELPA Syncer, 2022/02/14
- [elpa] externals/pq 6ab076af29 26/63: Fix memory leak., ELPA Syncer, 2022/02/14
- [elpa] externals/pq 191b842a56 21/63: Add missing static declarations., ELPA Syncer, 2022/02/14
- [elpa] externals/pq f896dabb85 22/63: Install notice receiver while fetching results as well., ELPA Syncer, 2022/02/14
- [elpa] externals/pq 8964028f11 25/63: Coerce non-string query parameters into strings., ELPA Syncer, 2022/02/14