emacs-elpa-diffs
[Top][All Lists]
Advanced

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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]