ECPG dynamic cursor, SQLDA support

Поиск
Список
Период
Сортировка
От Boszormenyi Zoltan
Тема ECPG dynamic cursor, SQLDA support
Дата
Msg-id 4A41F73D.7020502@cybertec.at
обсуждение исходный текст
Ответы Re: ECPG dynamic cursor, SQLDA support  (Michael Meskes <meskes@postgresql.org>)
Re: ECPG dynamic cursor, SQLDA support  (Jaime Casanova <jcasanov@systemguards.com.ec>)
Список pgsql-hackers
Hi,

attached is our latest patch extending ECPG:

1. Support computed cursorname (DECLARE :cursorname ..., etc)
    No change in that part, it's purely a parser-only change, there's no
    ECPGdo() change you criticized or any other bad juju. Because of
    adding support for :cursorname and the dynamically generated
    ECPG grammar, we needed to modify the main gram.y as well,
    to make way for computed cursor names. The fetch_direction and
    FetchStmt change was needed in gram.y because otherwise
    "FETCH fetch_direction from_in cursor_name" and
    "MOVE fetch_direction from_in cursor_name" rules caused
    shift/reduce conflicts in the "fetch_direction" rule in ECPG grammar.
    This modification was a little sacrifice in gram.y but it allowed
    keeping the ECPG grammar clean and little change in the automatic
    grammar generation.

2. Support SQLDA structure in ECPG, both in USING DESCRIPTOR
    and INTO DESCRIPTOR, if Informix-compatible mode is set.
    This means the following changes
    - introduce the pg_sqlda_t and pg_sqlvar_t structures, ECPGt_sqlda type
      and SQLNNN contants in sqltypes.h
    - make a distinction between DESCRIPTOR and SQL DESCRIPTOR
      in Informix mode
    - Support FETCH ... USING DESCRIPTOR in Informix mode only,
      that works the same way as FETCH ... INTO DESCRIPTOR.
3. Support DESCRIBE OUTPUT
    The support was added as a new, exported ECPGdescribe2() function
    in descriptor.c, the old unsupported ECPGescribe() interface was
left intact
    so the libecpg.so major library version doesn't need to be changed.

Best regards,
Zoltán Böszörményi

--
Bible has answers for everything. Proof:
"But let your communication be, Yea, yea; Nay, nay: for whatsoever is more
than these cometh of evil." (Matthew 5:37) - basics of digital technology.
"May your kingdom come" - superficial description of plate tectonics

----------------------------------
Zoltán Böszörményi
Cybertec Schönig & Schönig GmbH
http://www.postgresql.at/

diff -dcrpN postgresql-8.4rc2/src/backend/parser/gram.y postgresql-8.4rc2-sqlda/src/backend/parser/gram.y
*** postgresql-8.4rc2/src/backend/parser/gram.y    2009-06-18 03:27:02.000000000 +0200
--- postgresql-8.4rc2-sqlda/src/backend/parser/gram.y    2009-06-24 10:33:27.000000000 +0200
*************** static TypeName *TableFuncTypeName(List
*** 250,256 ****

  %type <str>        relation_name copy_file_name
                  database_name access_method_clause access_method attr_name
!                 index_name name file_name cluster_index_specification

  %type <list>    func_name handler_name qual_Op qual_all_Op subquery_Op
                  opt_class opt_validator validator_clause
--- 250,256 ----

  %type <str>        relation_name copy_file_name
                  database_name access_method_clause access_method attr_name
!                 index_name name cursor_name file_name cluster_index_specification

  %type <list>    func_name handler_name qual_Op qual_all_Op subquery_Op
                  opt_class opt_validator validator_clause
*************** reloption_elem:
*** 1863,1869 ****
   *****************************************************************************/

  ClosePortalStmt:
!             CLOSE name
                  {
                      ClosePortalStmt *n = makeNode(ClosePortalStmt);
                      n->portalname = $2;
--- 1863,1869 ----
   *****************************************************************************/

  ClosePortalStmt:
!             CLOSE cursor_name
                  {
                      ClosePortalStmt *n = makeNode(ClosePortalStmt);
                      n->portalname = $2;
*************** comment_text:
*** 4056,4069 ****
   *
   *****************************************************************************/

! FetchStmt:    FETCH fetch_direction from_in name
                  {
                      FetchStmt *n = (FetchStmt *) $2;
                      n->portalname = $4;
                      n->ismove = FALSE;
                      $$ = (Node *)n;
                  }
!             | FETCH name
                  {
                      FetchStmt *n = makeNode(FetchStmt);
                      n->direction = FETCH_FORWARD;
--- 4056,4087 ----
   *
   *****************************************************************************/

! FetchStmt:        FETCH BACKWARD from_in cursor_name
!                 {
!                     FetchStmt *n = makeNode(FetchStmt);
!                     n->portalname = $4;
!                     n->ismove = FALSE;
!                     n->direction = FETCH_BACKWARD;
!                     n->howMany = 1;
!                     $$ = (Node *)n;
!                 }
!             | FETCH FORWARD from_in cursor_name
!                 {
!                     FetchStmt *n = makeNode(FetchStmt);
!                     n->portalname = $4;
!                     n->ismove = FALSE;
!                     n->direction = FETCH_FORWARD;
!                     n->howMany = 1;
!                     $$ = (Node *)n;
!                 }
!             | FETCH fetch_direction from_in cursor_name
                  {
                      FetchStmt *n = (FetchStmt *) $2;
                      n->portalname = $4;
                      n->ismove = FALSE;
                      $$ = (Node *)n;
                  }
!             | FETCH cursor_name
                  {
                      FetchStmt *n = makeNode(FetchStmt);
                      n->direction = FETCH_FORWARD;
*************** FetchStmt:    FETCH fetch_direction from_in
*** 4072,4085 ****
                      n->ismove = FALSE;
                      $$ = (Node *)n;
                  }
!             | MOVE fetch_direction from_in name
                  {
                      FetchStmt *n = (FetchStmt *) $2;
                      n->portalname = $4;
                      n->ismove = TRUE;
                      $$ = (Node *)n;
                  }
!             | MOVE name
                  {
                      FetchStmt *n = makeNode(FetchStmt);
                      n->direction = FETCH_FORWARD;
--- 4090,4121 ----
                      n->ismove = FALSE;
                      $$ = (Node *)n;
                  }
!             | MOVE BACKWARD from_in cursor_name
!                 {
!                     FetchStmt *n = makeNode(FetchStmt);
!                     n->portalname = $4;
!                     n->ismove = TRUE;
!                     n->direction = FETCH_BACKWARD;
!                     n->howMany = 1;
!                     $$ = (Node *)n;
!                 }
!             | MOVE FORWARD from_in cursor_name
!                 {
!                     FetchStmt *n = makeNode(FetchStmt);
!                     n->portalname = $4;
!                     n->ismove = TRUE;
!                     n->direction = FETCH_FORWARD;
!                     n->howMany = 1;
!                     $$ = (Node *)n;
!                 }
!             | MOVE fetch_direction from_in cursor_name
                  {
                      FetchStmt *n = (FetchStmt *) $2;
                      n->portalname = $4;
                      n->ismove = TRUE;
                      $$ = (Node *)n;
                  }
!             | MOVE cursor_name
                  {
                      FetchStmt *n = makeNode(FetchStmt);
                      n->direction = FETCH_FORWARD;
*************** fetch_direction:
*** 4154,4166 ****
                      n->howMany = FETCH_ALL;
                      $$ = (Node *)n;
                  }
-             | FORWARD
-                 {
-                     FetchStmt *n = makeNode(FetchStmt);
-                     n->direction = FETCH_FORWARD;
-                     n->howMany = 1;
-                     $$ = (Node *)n;
-                 }
              | FORWARD SignedIconst
                  {
                      FetchStmt *n = makeNode(FetchStmt);
--- 4190,4195 ----
*************** fetch_direction:
*** 4175,4187 ****
                      n->howMany = FETCH_ALL;
                      $$ = (Node *)n;
                  }
-             | BACKWARD
-                 {
-                     FetchStmt *n = makeNode(FetchStmt);
-                     n->direction = FETCH_BACKWARD;
-                     n->howMany = 1;
-                     $$ = (Node *)n;
-                 }
              | BACKWARD SignedIconst
                  {
                      FetchStmt *n = makeNode(FetchStmt);
--- 4204,4209 ----
*************** set_target_list:
*** 6770,6776 ****
   *                CURSOR STATEMENTS
   *
   *****************************************************************************/
! DeclareCursorStmt: DECLARE name cursor_options CURSOR opt_hold FOR SelectStmt
                  {
                      DeclareCursorStmt *n = makeNode(DeclareCursorStmt);
                      n->portalname = $2;
--- 6792,6798 ----
   *                CURSOR STATEMENTS
   *
   *****************************************************************************/
! DeclareCursorStmt: DECLARE cursor_name cursor_options CURSOR opt_hold FOR SelectStmt
                  {
                      DeclareCursorStmt *n = makeNode(DeclareCursorStmt);
                      n->portalname = $2;
*************** DeclareCursorStmt: DECLARE name cursor_o
*** 6781,6786 ****
--- 6803,6811 ----
                  }
          ;

+ cursor_name:    name                        { $$ = $1; }
+         ;
+
  cursor_options: /*EMPTY*/                    { $$ = 0; }
              | cursor_options NO SCROLL        { $$ = $1 | CURSOR_OPT_NO_SCROLL; }
              | cursor_options SCROLL            { $$ = $1 | CURSOR_OPT_SCROLL; }
diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/descriptor.c
postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/descriptor.c
*** postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/descriptor.c    2009-06-11 16:49:13.000000000 +0200
--- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/descriptor.c    2009-06-24 10:33:27.000000000 +0200
***************
*** 13,18 ****
--- 13,19 ----
  #include "ecpgerrno.h"
  #include "extern.h"
  #include "sqlca.h"
+ #include "sqlda.h"
  #include "sql3types.h"

  static void descriptor_free(struct descriptor * desc);
*************** get_char_item(int lineno, void *var, enu
*** 225,230 ****
--- 226,237 ----
      return (true);
  }

+ #define RETURN_IF_NO_DATA    if (ntuples < 1) \
+                 { \
+                     ecpg_raise(lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL); \
+                     return (false); \
+                 }
+
  bool
  ECPGget_desc(int lineno, const char *desc_name, int index,...)
  {
*************** ECPGget_desc(int lineno, const char *des
*** 243,253 ****
          return (false);

      ntuples = PQntuples(ECPGresult);
-     if (ntuples < 1)
-     {
-         ecpg_raise(lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL);
-         return (false);
-     }

      if (index < 1 || index > PQnfields(ECPGresult))
      {
--- 250,255 ----
*************** ECPGget_desc(int lineno, const char *des
*** 282,287 ****
--- 284,290 ----
          switch (type)
          {
              case (ECPGd_indicator):
+                 RETURN_IF_NO_DATA;
                  data_var.ind_type = vartype;
                  data_var.ind_pointer = var;
                  data_var.ind_varcharsize = varcharsize;
*************** ECPGget_desc(int lineno, const char *des
*** 294,299 ****
--- 297,303 ----
                  break;

              case ECPGd_data:
+                 RETURN_IF_NO_DATA;
                  data_var.type = vartype;
                  data_var.pointer = var;
                  data_var.varcharsize = varcharsize;
*************** ECPGget_desc(int lineno, const char *des
*** 376,381 ****
--- 380,386 ----
              case ECPGd_ret_length:
              case ECPGd_ret_octet:

+                 RETURN_IF_NO_DATA;
                  /*
                   * this is like ECPGstore_result
                   */
*************** ECPGget_desc(int lineno, const char *des
*** 479,484 ****
--- 484,490 ----
      sqlca->sqlerrd[2] = ntuples;
      return (true);
  }
+ #undef RETURN_IF_NO_DATA

  bool
  ECPGset_desc_header(int lineno, const char *desc_name, int count)
*************** ecpg_find_desc(int line, const char *nam
*** 721,729 ****
--- 727,846 ----
      return NULL;                /* not found */
  }

+ static pg_sqlda_t*
+ build_sqlda(int lineno, bool input, const char *connection_name, const char *stmt_name)
+ {
+     struct connection *con;
+     PGresult    *res;
+     pg_sqlda_t    *sqlda = NULL;
+
+     con = ecpg_get_connection(connection_name);
+     res = PQdescribePrepared(con->connection, stmt_name);
+     if (!ecpg_check_PQresult(res, lineno, con->connection, ECPG_COMPAT_INFORMIX))
+         return NULL;
+
+     sqlda = ecpg_build_sqlda_for_PGresult(lineno, res);
+
+     PQclear(res);
+     return sqlda;
+ }
+
+ /* Old, unsupported interface */
  bool
  ECPGdescribe(int line, bool input, const char *statement,...)
  {
      ecpg_log("ECPGdescribe called on line %d for %s: %s\n", line, input ? "input" : "output", statement);
      return false;
  }
+
+ /* New interface for DESCRIBE [INPUT|OUTPUT], both for named descriptor and Informix-like SQLDA */
+ bool
+ ECPGdescribe2(int line, bool input, const char *connection_name, const char *stmt_name, ...)
+ {
+     bool        ret = false;
+     va_list        args;
+
+     /* DESCRIBE INPUT is not yet supported */
+     if (input)
+         return false;
+
+     va_start(args, stmt_name);
+
+     for (;;)
+     {
+         enum ECPGttype    type, dummy_type;
+         void        *ptr, *dummy_ptr;
+         long        dummy;
+
+         /* variable type */
+         type = va_arg(args, enum ECPGttype);
+
+         if (type == ECPGt_EORT)
+             break;
+
+         /* rest of variable parameters*/
+         ptr = va_arg(args, void *);
+         dummy = va_arg(args, long);
+         dummy = va_arg(args, long);
+         dummy = va_arg(args, long);
+
+         /* variable indicator */
+         dummy_type = va_arg(args, enum ECPGttype);
+         dummy_ptr = va_arg(args, void *);
+         dummy = va_arg(args, long);
+         dummy = va_arg(args, long);
+         dummy = va_arg(args, long);
+
+         switch (type)
+         {
+             case ECPGt_descriptor:
+             {
+                 char    *name = ptr;
+                 struct connection *con = ecpg_get_connection(connection_name);
+                 struct descriptor *desc = ecpg_find_desc(line, name);
+                 PGresult    *res;
+                 ExecStatusType  ret;
+
+                 if (con == NULL)
+                     break;
+                 if (desc == NULL)
+                     break;
+
+                 res = PQdescribePrepared(con->connection, stmt_name);
+                 ret = PQresultStatus(res);
+                 if (ecpg_check_PQresult(res, line, con->connection, ECPG_COMPAT_PGSQL))
+                 {
+                     if (desc->result != NULL)
+                         PQclear(desc->result);
+                     desc->result = res;
+                     ret = true;
+                 }
+                 break;
+             }
+             case ECPGt_sqlda:
+             {
+                 pg_sqlda_t **sqlda_ptr = ptr;
+                 pg_sqlda_t *sqlda_new;
+
+                 sqlda_new = build_sqlda(line, input, connection_name, stmt_name);
+                 if (sqlda_new)
+                 {
+                     /* slight leak */
+ #if 0
+                     pg_sqlda_t *sqlda_old = *sqlda_ptr;
+                     if (sqlda_old)
+                         free(sqlda_old); */
+ #endif
+                     *sqlda_ptr = sqlda_new;
+                     ret = true;
+                 }
+                 break;
+             }
+             default:
+                 /* nothing else may come */
+                 ;
+         }
+     }
+
+     return ret;
+ }
diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/execute.c
postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/execute.c
*** postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/execute.c    2009-06-11 16:49:13.000000000 +0200
--- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/execute.c    2009-06-24 10:33:27.000000000 +0200
***************
*** 25,30 ****
--- 25,31 ----
  #include "ecpgerrno.h"
  #include "extern.h"
  #include "sqlca.h"
+ #include "sqlda.h"
  #include "sql3types.h"
  #include "pgtypes_numeric.h"
  #include "pgtypes_date.h"
*************** ecpg_store_input(const int lineno, const
*** 1033,1038 ****
--- 1034,1042 ----
              case ECPGt_descriptor:
                  break;

+             case ECPGt_sqlda:
+                 break;
+
              default:
                  /* Not implemented yet */
                  ecpg_raise(lineno, ECPG_UNSUPPORTED, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, (char *)
ecpg_type_name(var->type));
*************** ecpg_execute(struct statement * stmt)
*** 1170,1175 ****
--- 1174,1235 ----
              if (desc->count == desc_counter)
                  desc_counter = 0;
          }
+         else if (var->type == ECPGt_sqlda)
+         {
+             pg_sqlda_t      **_sqlda = (pg_sqlda_t **)var->pointer;
+             pg_sqlda_t       *sqlda = *_sqlda;
+             struct variable    desc_inlist;
+             int        i;
+
+             if (sqlda == NULL)
+                 return false;
+
+             desc_counter++;
+             for (i = 0; i < sqlda->sqld; i++)
+             {
+                 if (i + 1 == desc_counter)
+                 {
+                     desc_inlist.type = ecpg_sqlda_type(sqlda->sqlvar[i].sqltype);
+                     desc_inlist.value = sqlda->sqlvar[i].sqldata;
+                     desc_inlist.pointer = &(sqlda->sqlvar[i].sqldata);
+                     switch (desc_inlist.type)
+                     {
+                         case ECPGt_char:
+                         case ECPGt_varchar:
+                             desc_inlist.varcharsize = strlen(sqlda->sqlvar[i].sqldata);
+                             break;
+                         default:
+                             desc_inlist.varcharsize = 0;
+                             break;
+                     }
+                     desc_inlist.arrsize = 1;
+                     desc_inlist.offset = 0;
+                     if (sqlda->sqlvar[i].sqlind)
+                     {
+                         desc_inlist.ind_type = ECPGt_short;
+                         /* ECPG expects indicator value < 0 */
+                         if (*(sqlda->sqlvar[i].sqlind))
+                             *(sqlda->sqlvar[i].sqlind) = -1;
+                         desc_inlist.ind_value = sqlda->sqlvar[i].sqlind;
+                         desc_inlist.ind_pointer = &(sqlda->sqlvar[i].sqlind);
+                         desc_inlist.ind_varcharsize = desc_inlist.ind_arrsize = 1;
+                         desc_inlist.ind_offset = 0;
+                     }
+                     else
+                     {
+                         desc_inlist.ind_type = ECPGt_NO_INDICATOR;
+                         desc_inlist.ind_value = desc_inlist.ind_pointer = NULL;
+                         desc_inlist.ind_varcharsize = desc_inlist.ind_arrsize = desc_inlist.ind_offset = 0;
+                     }
+                     if (!ecpg_store_input(stmt->lineno, stmt->force_indicator, &desc_inlist, &tobeinserted, false))
+                         return false;
+
+                     break;
+                 }
+             }
+             if (sqlda->sqld == desc_counter)
+                 desc_counter = 0;
+         }
          else
          {
              if (!ecpg_store_input(stmt->lineno, stmt->force_indicator, var, &tobeinserted, false))
*************** ecpg_execute(struct statement * stmt)
*** 1351,1356 ****
--- 1411,1437 ----
                  }
                  var = var->next;
              }
+             else if (var != NULL && var->type == ECPGt_sqlda)
+             {
+                 pg_sqlda_t      **_sqlda = (pg_sqlda_t **)var->pointer;
+                 pg_sqlda_t       *sqlda = *_sqlda;
+
+                 if (!sqlda)
+                 {
+                     sqlda = ecpg_build_sqlda_for_PGresult(stmt->lineno, results);
+                     if (!sqlda)
+                         status = false;
+                     else
+                         *_sqlda = sqlda;
+                 }
+                 else if (!ecpg_compare_sqlda_with_PGresult(sqlda, results))
+                     status = false;
+
+                 if (status == true)
+                     ecpg_set_sqlda_from_PGresult(stmt->lineno, _sqlda, results);
+
+                 var = var->next;
+             }
              else
                  for (act_field = 0; act_field < nfields && status; act_field++)
                  {
diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/exports.txt
postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/exports.txt
*** postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/exports.txt    2008-03-25 13:45:25.000000000 +0100
--- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/exports.txt    2009-06-24 10:33:27.000000000 +0200
*************** ECPGstatus                       23
*** 26,28 ****
--- 26,29 ----
  ECPGtrans                        24
  sqlprint                         25
  ECPGget_PGconn             26
+ ECPGdescribe2             27
diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/extern.h
postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/extern.h
*** postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/extern.h    2009-05-20 18:13:18.000000000 +0200
--- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/extern.h    2009-06-24 10:33:27.000000000 +0200
***************
*** 6,11 ****
--- 6,12 ----
  #include "postgres_fe.h"
  #include "libpq-fe.h"
  #include "sqlca.h"
+ #include "sqlda.h"
  #include "ecpg_config.h"
  #ifndef CHAR_BIT
  #include <limits.h>
*************** bool        ecpg_init(const struct connection
*** 129,134 ****
--- 130,137 ----
  char       *ecpg_strdup(const char *, int);
  const char *ecpg_type_name(enum ECPGttype);
  int            ecpg_dynamic_type(Oid);
+ int            ecpg_sqlda_type(int);
+ int            ecpg_to_sqlda_type(Oid);
  void        ecpg_free_auto_mem(void);
  void        ecpg_clear_auto_mem(void);

*************** void        ecpg_log(const char *format,...);
*** 149,154 ****
--- 152,161 ----
  bool        ecpg_auto_prepare(int, const char *, const int, char **, const char *);
  void        ecpg_init_sqlca(struct sqlca_t * sqlca);

+ pg_sqlda_t *ecpg_build_sqlda_for_PGresult(int, PGresult *);
+ bool        ecpg_compare_sqlda_with_PGresult(pg_sqlda_t *sqlda, const PGresult *results);
+ void        ecpg_set_sqlda_from_PGresult(int, pg_sqlda_t **, const PGresult *);
+
  /* SQLSTATE values generated or processed by ecpglib (intentionally
   * not exported -- users should refer to the codes directly) */

diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/Makefile
postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/Makefile
*** postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/Makefile    2009-02-07 18:17:34.000000000 +0100
--- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/Makefile    2009-06-24 10:33:27.000000000 +0200
*************** include $(top_builddir)/src/Makefile.glo
*** 15,21 ****

  NAME= ecpg
  SO_MAJOR_VERSION= 6
! SO_MINOR_VERSION= 1

  override CPPFLAGS := -I../include -I$(top_srcdir)/src/interfaces/ecpg/include \
      -I$(libpq_srcdir) -I$(top_builddir)/src/port $(CPPFLAGS)
--- 15,21 ----

  NAME= ecpg
  SO_MAJOR_VERSION= 6
! SO_MINOR_VERSION= 2

  override CPPFLAGS := -I../include -I$(top_srcdir)/src/interfaces/ecpg/include \
      -I$(libpq_srcdir) -I$(top_builddir)/src/port $(CPPFLAGS)
*************** override CFLAGS += $(PTHREAD_CFLAGS)
*** 24,30 ****
  # Need to recompile any libpgport object files
  LIBS := $(filter-out -lpgport, $(LIBS))

! OBJS= execute.o typename.o descriptor.o data.o error.o prepare.o memory.o \
      connect.o misc.o path.o pgstrcasecmp.o \
      $(filter snprintf.o strlcpy.o, $(LIBOBJS))

--- 24,30 ----
  # Need to recompile any libpgport object files
  LIBS := $(filter-out -lpgport, $(LIBS))

! OBJS= execute.o typename.o descriptor.o sqlda.o data.o error.o prepare.o memory.o \
      connect.o misc.o path.o pgstrcasecmp.o \
      $(filter snprintf.o strlcpy.o, $(LIBOBJS))

*************** ifneq ($(PORTNAME), win32)
*** 33,39 ****
  OBJS += thread.o
  endif

! SHLIB_LINK = -L../pgtypeslib -lpgtypes $(libpq) $(filter -lintl -lm, $(LIBS)) $(PTHREAD_LIBS)

  SHLIB_EXPORTS = exports.txt

--- 33,39 ----
  OBJS += thread.o
  endif

! SHLIB_LINK = -L../pgtypeslib -lpgtypes $(libpq) $(filter -lintl -lm -ldl, $(LIBS)) $(PTHREAD_LIBS)

  SHLIB_EXPORTS = exports.txt

diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/sqlda.c
postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/sqlda.c
*** postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/sqlda.c    1970-01-01 01:00:00.000000000 +0100
--- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/sqlda.c    2009-06-24 10:33:27.000000000 +0200
***************
*** 0 ****
--- 1,271 ----
+ /*
+  * Crude SQLDA support routines
+  * Only supports fetching 1 record at a time
+  *
+  * The allocated memory area pointed by an sqlda pointer
+  * contains both the metadata and the data, so freeing up
+  * is a simple free(sqlda) as expected by the ESQL/C examples.
+  *
+  * (C) 2009 Cybertec GmbH
+  *     Zoltán Böszörményi <zb@cybertec.at>
+  *     Hans-Jürgen Schönig <hs@cybertec.at>
+  */
+
+ #define POSTGRES_ECPG_INTERNAL
+ #include "postgres_fe.h"
+ #include "pg_type.h"
+
+ #include <inttypes.h>
+ #include <dlfcn.h>
+
+ #include "ecpg-pthread-win32.h"
+ #include "decimal.h"
+ #include "ecpgtype.h"
+ #include "ecpglib.h"
+ #include "ecpgerrno.h"
+ #include "extern.h"
+ #include "sqlca.h"
+ #include "sqlda.h"
+ #include "sqltypes.h"
+
+ /*
+  * Build pg_sqlda_t (metadata only) from PGresult
+  */
+ pg_sqlda_t *
+ ecpg_build_sqlda_for_PGresult(int line, PGresult *res)
+ {
+     pg_sqlda_t *sqlda;
+     pg_sqlvar_t*sqlvar;
+     char       *fname;
+     long        size;
+     int        i;
+
+     size = sizeof(pg_sqlda_t) + PQnfields(res) * sizeof(pg_sqlvar_t);
+     for (i = 0; i < PQnfields(res); i++)
+         size += strlen(PQfname(res, i)) + 1;
+     /* round allocated size up to the next multiple of 8 */
+     if (size % 8)
+         size += 8 - (size % 8);
+
+     sqlda = (pg_sqlda_t *)ecpg_alloc(size, line);
+     if (!sqlda)
+     {
+         ecpg_raise(line, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
+         return NULL;
+     }
+     memset(sqlda, 0, size);
+     sqlvar = (pg_sqlvar_t *)(sqlda + 1);
+     fname = (char *)(sqlvar + PQnfields(res));
+
+     sqlda->sqld = PQnfields(res);
+     sqlda->desc_occ = size; /* cheat here, keep the full allocated size */
+     sqlda->sqlvar = sqlvar;
+
+     for (i = 0; i < sqlda->sqld; i++)
+     {
+         sqlda->sqlvar[i].sqltype = ecpg_to_sqlda_type(PQftype(res, i));
+         strcpy(fname, PQfname(res, i));
+         sqlda->sqlvar[i].sqlname = fname;
+         fname += strlen(sqlda->sqlvar[i].sqlname) + 1;
+         sqlda->sqlvar[i].sqlformat = (char *)(long)PQfformat(res, i);
+         sqlda->sqlvar[i].sqlxid = PQftype(res, i);
+         sqlda->sqlvar[i].sqltypelen = PQfsize(res, i);
+     }
+
+     return sqlda;
+ }
+
+ /*
+  * Check whether the supplied sqlda and PGresult
+  * both has the same metadata
+  */
+ bool
+ ecpg_compare_sqlda_with_PGresult(pg_sqlda_t *sqlda, const PGresult *res)
+ {
+     int    i;
+
+     if (sqlda->sqld != PQnfields(res))
+         return false;
+
+     for (i = 0; i < sqlda->sqld; i++)
+     {
+         if (sqlda->sqlvar[i].sqltype != ecpg_dynamic_type(PQftype(res, i)))
+             return false;
+         if (strcmp(sqlda->sqlvar[i].sqlname, PQfname(res, i)))
+             return false;
+         if (sqlda->sqlvar[i].sqlformat != (char *)(long)PQfformat(res, i))
+             return false;
+         if (sqlda->sqlvar[i].sqlxid != PQftype(res, i))
+             return false;
+         if (sqlda->sqlvar[i].sqltypelen != PQfsize(res, i))
+             return false;
+     }
+
+     return true;
+ }
+
+ static long
+ ecpg_sqlda_size_round_align(long size, int alignment, int round)
+ {
+     if (size % alignment)
+         size += alignment - (size % alignment);
+     size += round;
+     return size;
+ }
+
+ static long
+ ecpg_sqlda_size_align(long size, int alignment)
+ {
+     if (size % alignment)
+         size += alignment - (size % alignment);
+     return size;
+ }
+
+ /*
+  * Sets values from PGresult.
+  * Reallocates the memory area pointed by *_sqlda if needed
+  */
+ void
+ ecpg_set_sqlda_from_PGresult(int lineno, pg_sqlda_t **_sqlda, const PGresult *res)
+ {
+     pg_sqlda_t *sqlda = (*_sqlda);
+     int        i;
+     long        size;
+     static    int2    value_is_null = 1;
+     static    int2    value_is_not_null = 0;
+
+     /* Compute new structure size for allocation */
+     size = sizeof(pg_sqlda_t) + sqlda->sqld * sizeof(pg_sqlvar_t);
+     for (i = 0; i < PQnfields(res); i++)
+         size += strlen(PQfname(res, i)) + 1;
+
+     for (i = 0; i < sqlda->sqld; i++)
+     {
+         switch (sqlda->sqlvar[i].sqltype)
+         {
+             case SQLSMINT:
+                 size = ecpg_sqlda_size_round_align(size, sizeof(short), sizeof(short));
+                 break;
+             case SQLINT:
+             case SQLSERIAL:
+                 size = ecpg_sqlda_size_round_align(size, sizeof(int), sizeof(int));
+                 break;
+             case SQLFLOAT:
+                 size = ecpg_sqlda_size_round_align(size, sizeof(double), sizeof(double));
+                 break;
+             case SQLSMFLOAT:
+                 size = ecpg_sqlda_size_round_align(size, sizeof(float), sizeof(float));
+                 break;
+             case SQLDECIMAL:
+                 size = ecpg_sqlda_size_round_align(size, sizeof(int), sizeof(decimal));
+                 break;
+             case SQLINT8:
+             case SQLSERIAL8:
+                 size = ecpg_sqlda_size_round_align(size, sizeof(int64_t), sizeof(int64_t));
+                 break;
+
+             /*
+              * These types will be passed as character strings
+              * until we know what to do with them.
+              */
+             case SQLCHAR:
+             case SQLTEXT:
+             case SQLVCHAR:
+             case SQLNCHAR:
+             case SQLNVCHAR:
+             case SQLMONEY:
+             case SQLDATE:
+             case SQLDTIME:
+             case SQLINTERVAL:
+             default:
+                 break;
+         }
+     }
+
+     if (sqlda->desc_occ < size)
+     {
+         sqlda = realloc(sqlda, size);
+         *_sqlda = sqlda;
+         sqlda->desc_occ = size;
+     }
+
+     /*
+      * Set sqlvar[i]->sqldata pointers and convert values to correct format
+      */
+     size = sizeof(pg_sqlda_t) + sqlda->sqld * sizeof(pg_sqlvar_t);
+     for (i = 0; i < PQnfields(res); i++)
+         size += strlen(PQfname(res, i)) + 1;
+
+     for (i = 0; i < sqlda->sqld; i++)
+     {
+         switch (sqlda->sqlvar[i].sqltype)
+         {
+             case SQLSMINT:
+                 size = ecpg_sqlda_size_align(size, sizeof(short));
+                 sscanf(PQgetvalue(res, 0, i), "%hd", (short *)((char *)sqlda + size));
+                 sqlda->sqlvar[i].sqldata = (char *)sqlda + size;
+                 size += sizeof(short);
+                 break;
+             case SQLINT:
+             case SQLSERIAL:
+                 size = ecpg_sqlda_size_align(size, sizeof(int));
+                 sscanf(PQgetvalue(res, 0, i), "%d", (int *)((char *)sqlda + size));
+                 sqlda->sqlvar[i].sqldata = (char *)sqlda + size;
+                 size += sizeof(int);
+                 break;
+             case SQLFLOAT:
+                 size = ecpg_sqlda_size_align(size, sizeof(double));
+                 sscanf(PQgetvalue(res, 0, i), "%lf", (double *)((char *)sqlda + size));
+                 sqlda->sqlvar[i].sqldata = (char *)sqlda + size;
+                 size += sizeof(double);
+                 break;
+             case SQLSMFLOAT:
+                 size = ecpg_sqlda_size_align(size, sizeof(float));
+                 sscanf(PQgetvalue(res, 0, i), "%f", (float *)((char *)sqlda + size));
+                 sqlda->sqlvar[i].sqldata = (char *)sqlda + size;
+                 size += sizeof(float);
+                 break;
+             case SQLDECIMAL:
+             {
+                 size = ecpg_sqlda_size_align(size, sizeof(int));
+                 sqlda->sqlvar[i].sqldata = (char *)sqlda + size;
+
+                 ecpg_get_data(res, 0, i, lineno,
+                         ECPGt_decimal, ECPGt_NO_INDICATOR,
+                         sqlda->sqlvar[i].sqldata, NULL, 0, 0, 0,
+                         ECPG_ARRAY_NONE, ECPG_COMPAT_INFORMIX, false);
+
+                 size += sizeof(decimal);
+                 break;
+             }
+             case SQLINT8:
+             case SQLSERIAL8:
+                 size = ecpg_sqlda_size_align(size, sizeof(int64_t));
+                 sscanf(PQgetvalue(res, 0, i), "%" PRId64, (int64_t *)((char *)sqlda + size));
+                 sqlda->sqlvar[i].sqldata = (char *)sqlda + size;
+                 size += sizeof(int64_t);
+                 break;
+
+             /*
+              * These types will be passed as character strings until
+              * it's known what to do with them. We use sqlvar->sqldata
+              * in all cases regardless of length, don't care about
+              * sqlvar->sqlilongdata.
+              */
+             case SQLCHAR:
+             case SQLTEXT:
+             case SQLVCHAR:
+             case SQLNCHAR:
+             case SQLNVCHAR:
+             case SQLMONEY:
+             case SQLDATE:
+             case SQLDTIME:
+             case SQLINTERVAL:
+             default:
+                 sqlda->sqlvar[i].sqldata = PQgetvalue(res, 0, i);
+                 break;
+         }
+
+         sqlda->sqlvar[i].sqlind = PQgetisnull(res, 0, i) ? &value_is_null : &value_is_not_null;
+     }
+ }
diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/typename.c
postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/typename.c
*** postgresql-8.4rc2/src/interfaces/ecpg/ecpglib/typename.c    2007-11-15 22:14:45.000000000 +0100
--- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/ecpglib/typename.c    2009-06-24 10:33:27.000000000 +0200
***************
*** 7,12 ****
--- 7,13 ----
  #include "ecpgtype.h"
  #include "ecpglib.h"
  #include "extern.h"
+ #include "sqltypes.h"
  #include "sql3types.h"
  #include "pg_type.h"

*************** ecpg_dynamic_type(Oid type)
*** 99,101 ****
--- 100,189 ----
              return -(int) type;
      }
  }
+
+ int
+ ecpg_sqlda_type(int type)
+ {
+     switch (type)
+     {
+         case SQLCHAR:
+         case SQLNCHAR:
+             return ECPGt_char;
+         case SQLSMINT:
+             return ECPGt_short;
+         case SQLINT:
+             return ECPGt_int;
+         case SQLFLOAT:
+             return ECPGt_double;
+         case SQLSMFLOAT:
+             return ECPGt_float;
+         case SQLDECIMAL:
+             return ECPGt_decimal;
+         case SQLSERIAL:
+             return ECPGt_int;
+         case SQLDATE:
+             return ECPGt_date;
+ #if 0
+         case SQLMONEY:
+             return ???;
+         case SQLNULL:
+             return ???;
+ #endif
+         case SQLDTIME:
+             return ECPGt_timestamp;
+ #if 0
+         case SQLBYTES:
+             return ???;
+ #endif
+         case SQLTEXT:
+             return ECPGt_char;
+         case SQLVCHAR:
+         case SQLNVCHAR:
+             return ECPGt_varchar;
+         case SQLINTERVAL:
+             return ECPGt_interval;
+         case SQLINT8:
+         case SQLSERIAL8:
+             return ECPGt_long_long;
+         default:
+             return (-type);
+     }
+ }
+
+ int
+ ecpg_to_sqlda_type(Oid type)
+ {
+     switch (type)
+     {
+         case CHAROID:
+         case BPCHAROID:
+             return SQLCHAR;
+         case INT2OID:
+             return SQLSMINT;
+         case INT4OID:
+             return SQLINT;
+         case FLOAT8OID:
+             return SQLFLOAT;
+         case FLOAT4OID:
+             return SQLSMFLOAT;
+         case NUMERICOID:
+             return SQLDECIMAL;
+         case DATEOID:
+             return SQLDATE;
+         case CASHOID:
+             return SQLMONEY;
+         case TIMESTAMPOID:
+         case TIMESTAMPTZOID:
+             return SQLDTIME;
+         case TEXTOID:
+             return SQLTEXT;
+         case VARCHAROID:
+             return SQLVCHAR;
+         case INTERVALOID:
+             return SQLINTERVAL;
+         case INT8OID:
+             return SQLINT8;
+         default:
+             return (-type);
+     }
+ }
diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/include/ecpglib.h
postgresql-8.4rc2-sqlda/src/interfaces/ecpg/include/ecpglib.h
*** postgresql-8.4rc2/src/interfaces/ecpg/include/ecpglib.h    2009-06-11 16:49:13.000000000 +0200
--- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/include/ecpglib.h    2009-06-24 10:33:27.000000000 +0200
*************** bool        ECPGset_desc(int, const char *, in
*** 84,89 ****
--- 84,90 ----
  void        ECPGset_noind_null(enum ECPGttype, void *);
  bool        ECPGis_noind_null(enum ECPGttype, void *);
  bool        ECPGdescribe(int, bool, const char *,...);
+ bool        ECPGdescribe2(int, bool, const char *, const char *, ...);

  /* dynamic result allocation */
  void        ECPGfree_auto_mem(void);
diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/include/ecpgtype.h
postgresql-8.4rc2-sqlda/src/interfaces/ecpg/include/ecpgtype.h
*** postgresql-8.4rc2/src/interfaces/ecpg/include/ecpgtype.h    2007-08-14 12:01:52.000000000 +0200
--- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/include/ecpgtype.h    2009-06-24 10:33:27.000000000 +0200
*************** enum ECPGttype
*** 61,67 ****
      ECPGt_const,                /* a constant is needed sometimes */
      ECPGt_EOIT,                    /* End of insert types. */
      ECPGt_EORT,                    /* End of result types. */
!     ECPGt_NO_INDICATOR            /* no indicator */
  };

   /* descriptor items */
--- 61,68 ----
      ECPGt_const,                /* a constant is needed sometimes */
      ECPGt_EOIT,                    /* End of insert types. */
      ECPGt_EORT,                    /* End of result types. */
!     ECPGt_NO_INDICATOR,            /* no indicator */
!     ECPGt_sqlda                /* INFORMIX-compatible sqlda_t descriptor */
  };

   /* descriptor items */
diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/include/sqlda.h
postgresql-8.4rc2-sqlda/src/interfaces/ecpg/include/sqlda.h
*** postgresql-8.4rc2/src/interfaces/ecpg/include/sqlda.h    2009-06-11 16:49:13.000000000 +0200
--- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/include/sqlda.h    2009-06-24 10:33:27.000000000 +0200
***************
*** 1,3 ****
--- 1,74 ----
  /*
   * $PostgreSQL: pgsql/src/interfaces/ecpg/include/sqlda.h,v 1.4 2009/06/11 14:49:13 momjian Exp $
   */
+
+ #ifndef POSTGRES_SQLDA_H
+ #define POSTGRES_SQLDA_H
+
+ /* Define Informix "standard" types */
+ #ifndef C_H
+ typedef int        int4;
+ typedef    short        int2;
+ #endif
+ typedef    char        int1;
+
+ typedef    int        mint;
+ typedef    long        mlong;
+
+ typedef    short        MSHORT;
+ typedef    char        MCHAR;
+
+ typedef    unsigned int    uint4;
+ typedef    unsigned short    uint2;
+ typedef    unsigned char    uint1;
+
+ typedef    unsigned int    muint;
+ typedef    unsigned long    mulong;
+
+ typedef    unsigned short    MUSHORT;
+ typedef    unsigned char    MUCHAR;
+
+ #define MI_INT_SIZE     (sizeof(int)    * 8)
+ #define MI_LONG_SIZE    (sizeof(long)   * 8)
+ #define MI_PTR_SIZE     (sizeof(char *) * 8)
+
+ typedef struct sqlvar_struct
+ {
+     int2    sqltype;        /* variable type                */
+     int4    sqllen;            /* length in bytes              */
+     char       *sqldata;        /* pointer to data              */
+     int2       *sqlind;        /* pointer to indicator         */
+     char       *sqlname;        /* variable name                */
+     char       *sqlformat;        /* reserved for future use      */
+     int2    sqlitype;        /* ind variable type            */
+     int2    sqlilen;        /* ind length in bytes          */
+     char       *sqlidata;        /* ind data pointer             */
+     int4    sqlxid;            /* extended id type             */
+     char       *sqltypename;    /* extended type name           */
+     int2    sqltypelen;        /* length of extended type name */
+     int2    sqlownerlen;        /* length of owner name         */
+     int2    sqlsourcetype;        /* source type for distinct of built-ins */
+     char       *sqlownername;    /* owner name                   */
+     int4    sqlsourceid;        /* extended id of source type   */
+
+     /*
+      * sqlilongdata is new.  It supports data that exceeds the 32k
+      * limit.  sqlilen and sqlidata are for backward compatibility
+      * and they have maximum value of <32K.
+      */
+     char       *sqlilongdata;    /* for data field beyond 32K    */
+     int4    sqlflags;        /* for internal use only        */
+     void       *sqlreserved;    /* reserved for future use      */
+ } pg_sqlvar_t;
+
+ typedef struct sqlda
+ {
+     int2        sqld;
+     pg_sqlvar_t       *sqlvar;
+     char        desc_name[19];    /* descriptor name              */
+     int2        desc_occ;    /* size of sqlda structure      */
+     struct sqlda       *desc_next;    /* pointer to next sqlda struct */
+     void           *reserved;    /* reserved for future use */
+ } pg_sqlda_t;
+
+ #endif /* POSTGRES_SQLDA_H */
diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/include/sqltypes.h
postgresql-8.4rc2-sqlda/src/interfaces/ecpg/include/sqltypes.h
*** postgresql-8.4rc2/src/interfaces/ecpg/include/sqltypes.h    2009-06-11 16:49:13.000000000 +0200
--- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/include/sqltypes.h    2009-06-24 10:33:27.000000000 +0200
***************
*** 30,33 ****
--- 30,63 ----
  #define CLVCHARPTRTYPE    124
  #define CTYPEMAX    25

+ #define    SQLCHAR        0
+ #define    SQLSMINT    1
+ #define    SQLINT        2
+ #define    SQLFLOAT    3
+ #define    SQLSMFLOAT    4
+ #define    SQLDECIMAL    5
+ #define    SQLSERIAL    6
+ #define    SQLDATE        7
+ #define    SQLMONEY    8
+ #if 0
+ #define    SQLNULL        9
+ #endif
+ #define    SQLDTIME    10
+ #define    SQLBYTES    11
+ #define    SQLTEXT        12
+ #define    SQLVCHAR    13
+ #define    SQLINTERVAL    14
+ #define    SQLNCHAR    15
+ #define    SQLNVCHAR    16
+ #define    SQLINT8        17
+ #define    SQLSERIAL8    18
+ #if 0
+ #define    SQLSET        19
+ #define    SQLMULTISET    20
+ #define    SQLLIST        21
+ #define    SQLROW        22
+ #define    SQLCOLLECTION    23
+ #define    SQLROWREF    24
+ #endif
+
  #endif   /* ndef ECPG_SQLTYPES_H */
diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/preproc/descriptor.c
postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/descriptor.c
*** postgresql-8.4rc2/src/interfaces/ecpg/preproc/descriptor.c    2009-01-23 13:43:32.000000000 +0100
--- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/descriptor.c    2009-06-24 10:33:27.000000000 +0200
*************** descriptor_variable(const char *name, in
*** 326,328 ****
--- 326,347 ----
      strlcpy(descriptor_names[input], name, sizeof(descriptor_names[input]));
      return (struct variable *) & varspace[input];
  }
+
+ struct variable *
+ sqlda_variable(const char *name)
+ {
+     struct variable *p = (struct variable *) mm_alloc(sizeof(struct variable));
+
+     p->name = mm_strdup(name);
+     p->type = (struct ECPGtype *) mm_alloc(sizeof(struct ECPGtype));
+     p->type->type = ECPGt_sqlda;
+     p->type->size = NULL;
+     p->type->struct_sizeof = NULL;
+     p->type->u.element = NULL;
+     p->type->lineno = 0;
+     p->brace_level = 0;
+     p->next = NULL;
+
+     return p;
+ }
+
diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/preproc/ecpg.addons
postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/ecpg.addons
*** postgresql-8.4rc2/src/interfaces/ecpg/preproc/ecpg.addons    2009-01-30 13:53:43.000000000 +0100
--- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/ecpg.addons    2009-06-24 10:33:27.000000000 +0200
*************** ECPG: stmtViewStmt rule
*** 84,90 ****
      }
      | ECPGDescribe
      {
!         fprintf(yyout, "{ ECPGdescribe(__LINE__, %s,", $1);
          dump_variables(argsresult, 1);
          fputs("ECPGt_EORT);", yyout);
          fprintf(yyout, "}");
--- 84,90 ----
      }
      | ECPGDescribe
      {
!         fprintf(yyout, "{ ECPGdescribe2(__LINE__, %s,", $1);
          dump_variables(argsresult, 1);
          fputs("ECPGt_EORT);", yyout);
          fprintf(yyout, "}");
*************** ECPG: fetch_directionBACKWARDSignedIcons
*** 221,226 ****
--- 221,235 ----
              free($2);
              $2 = make_str("$0");
          }
+ ECPG: cursor_namename rule
+     | char_civar
+         {
+             char *curname = mm_alloc(strlen($1) + 2);
+             sprintf(curname, ":%s", $1);
+             free($1);
+             $1 = curname;
+             $$ = $1;
+         }
  ECPG: PrepareStmtPREPAREprepared_nameprep_type_clauseASPreparableStmt block
      {
          $$.name = $2;
*************** ECPG: PrepareStmtPREPAREprepared_namepre
*** 235,243 ****
      }
  ECPG: ExecuteStmtEXECUTEprepared_nameexecute_param_clauseexecute_rest block
      { $$ = $2; }
! ECPG: DeclareCursorStmtDECLAREnamecursor_optionsCURSORopt_holdFORSelectStmt block
      {
          struct cursor *ptr, *this;

          for (ptr = cur; ptr != NULL; ptr = ptr->next)
          {
--- 244,253 ----
      }
  ECPG: ExecuteStmtEXECUTEprepared_nameexecute_param_clauseexecute_rest block
      { $$ = $2; }
! ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectStmt block
      {
          struct cursor *ptr, *this;
+         char *cursor_marker = $2[0] == ':' ? make_str("$0") : mm_strdup($2);

          for (ptr = cur; ptr != NULL; ptr = ptr->next)
          {
*************** ECPG: DeclareCursorStmtDECLAREnamecursor
*** 251,257 ****
          this->name = $2;
          this->connection = connection;
          this->opened = false;
!         this->command =  cat_str(7, make_str("declare"), mm_strdup($2), $3, make_str("cursor"), $5, make_str("for"),
$7);
          this->argsinsert = argsinsert;
          this->argsresult = argsresult;
          argsinsert = argsresult = NULL;
--- 261,267 ----
          this->name = $2;
          this->connection = connection;
          this->opened = false;
!         this->command =  cat_str(7, make_str("declare"), cursor_marker, $3, make_str("cursor"), $5, make_str("for"),
$7);
          this->argsinsert = argsinsert;
          this->argsresult = argsresult;
          argsinsert = argsresult = NULL;
*************** ECPG: DeclareCursorStmtDECLAREnamecursor
*** 262,267 ****
--- 272,282 ----
          else
              $$ = cat_str(3, make_str("/*"), mm_strdup(this->command), make_str("*/"));
      }
+ ECPG: ClosePortalStmtCLOSEcursor_name block
+     {
+         char *cursor_marker = $2[0] == ':' ? make_str("$0") : $2;
+         $$ = cat2_str(make_str("close"), cursor_marker);
+     }
  ECPG: opt_hold block
      {
          if (compat == ECPG_COMPAT_INFORMIX_SE && autocommit == true)
*************** ECPG: VariableShowStmtSHOWALL block
*** 326,371 ****
          mmerror(PARSE_ERROR, ET_ERROR, "SHOW ALL is not implemented");
          $$ = EMPTY;
      }
! ECPG: FetchStmtFETCHfetch_directionfrom_inname block
      {
          add_additional_variables($4, false);
!         $$ = cat_str(4, make_str("fetch"), $2, $3, $4);
      }
! ECPG: FetchStmtFETCHname block
      {
          add_additional_variables($2, false);
!         $$ = cat_str(2, make_str("fetch"), $2);
      }
! ECPG: FetchStmtMOVEname rule
!     | FETCH fetch_direction from_in name ecpg_into
          {
              add_additional_variables($4, false);
!             $$ = cat_str(4, make_str("fetch"), $2, $3, $4);
          }
!     | FETCH fetch_direction name ecpg_into
          {
              add_additional_variables($3, false);
!             $$ = cat_str(4, make_str("fetch"), $2, make_str("from"), $3);
          }
!     | FETCH from_in name ecpg_into
          {
              add_additional_variables($3, false);
!             $$ = cat_str(3, make_str("fetch"), $2, $3);
          }
!     | FETCH name ecpg_into
          {
              add_additional_variables($2, false);
!             $$ = cat2_str(make_str("fetch"), $2);
          }
!     | FETCH fetch_direction name
          {
              add_additional_variables($3, false);
!             $$ = cat_str(4, make_str("fetch"), $2, make_str("from"), $3);
          }
!     | FETCH from_in name
          {
              add_additional_variables($3, false);
!             $$ = cat_str(3, make_str("fetch"), $2, $3);
          }
  ECPG: SpecialRuleRelationOLD addon
          if (!QueryIsRule)
--- 341,442 ----
          mmerror(PARSE_ERROR, ET_ERROR, "SHOW ALL is not implemented");
          $$ = EMPTY;
      }
! ECPG: FetchStmtFETCHBACKWARDfrom_incursor_name block
      {
+         char *cursor_marker = $4[0] == ':' ? make_str("$0") : $4;
          add_additional_variables($4, false);
!         $$ = cat_str(3, make_str("fetch backward"), $3, cursor_marker);
      }
! ECPG: FetchStmtFETCHFORWARDfrom_incursor_name block
      {
+         char *cursor_marker = $4[0] == ':' ? make_str("$0") : $4;
+         add_additional_variables($4, false);
+         $$ = cat_str(3, make_str("fetch forward"), $3, cursor_marker);
+     }
+ ECPG: FetchStmtFETCHfetch_directionfrom_incursor_name block
+     {
+         char *cursor_marker = $4[0] == ':' ? make_str("$0") : $4;
+         add_additional_variables($4, false);
+         $$ = cat_str(4, make_str("fetch"), $2, $3, cursor_marker);
+     }
+ ECPG: FetchStmtFETCHcursor_name block
+     {
+         char *cursor_marker = $2[0] == ':' ? make_str("$0") : $2;
          add_additional_variables($2, false);
!         $$ = cat_str(2, make_str("fetch"), cursor_marker);
      }
! ECPG: FetchStmtMOVEcursor_name rule
!     | FETCH BACKWARD from_in cursor_name ecpg_fetch_into
          {
+             char *cursor_marker = $4[0] == ':' ? make_str("$0") : $4;
              add_additional_variables($4, false);
!             $$ = cat_str(3, make_str("fetch backward"), $3, cursor_marker);
          }
!     | FETCH FORWARD from_in cursor_name ecpg_fetch_into
          {
+             char *cursor_marker = $4[0] == ':' ? make_str("$0") : $4;
+             add_additional_variables($4, false);
+             $$ = cat_str(3, make_str("fetch forward"), $3, cursor_marker);
+         }
+     | FETCH fetch_direction from_in cursor_name ecpg_fetch_into
+         {
+             char *cursor_marker = $4[0] == ':' ? make_str("$0") : $4;
+             add_additional_variables($4, false);
+             $$ = cat_str(4, make_str("fetch"), $2, $3, cursor_marker);
+         }
+     | FETCH BACKWARD cursor_name ecpg_fetch_into
+         {
+             char *cursor_marker = $3[0] == ':' ? make_str("$0") : $3;
              add_additional_variables($3, false);
!             $$ = cat2_str(make_str("fetch backward from"), cursor_marker);
          }
!     | FETCH FORWARD cursor_name ecpg_fetch_into
!         {
!             char *cursor_marker = $3[0] == ':' ? make_str("$0") : $3;
!             add_additional_variables($3, false);
!             $$ = cat2_str(make_str("fetch forward from"), cursor_marker);
!         }
!     | FETCH fetch_direction cursor_name ecpg_fetch_into
          {
+             char *cursor_marker = $3[0] == ':' ? make_str("$0") : $3;
              add_additional_variables($3, false);
!             $$ = cat_str(4, make_str("fetch"), $2, make_str("from"), cursor_marker);
          }
!     | FETCH from_in cursor_name ecpg_fetch_into
          {
+             char *cursor_marker = $3[0] == ':' ? make_str("$0") : $3;
+             add_additional_variables($3, false);
+             $$ = cat_str(3, make_str("fetch"), $2, cursor_marker);
+         }
+     | FETCH cursor_name ecpg_fetch_into
+         {
+             char *cursor_marker = $2[0] == ':' ? make_str("$0") : $2;
              add_additional_variables($2, false);
!             $$ = cat2_str(make_str("fetch"), cursor_marker);
          }
!     | FETCH BACKWARD cursor_name
          {
+             char *cursor_marker = $3[0] == ':' ? make_str("$0") : $3;
              add_additional_variables($3, false);
!             $$ = cat2_str(make_str("fetch backward from"), cursor_marker);
          }
!     | FETCH FORWARD cursor_name
          {
+             char *cursor_marker = $3[0] == ':' ? make_str("$0") : $3;
              add_additional_variables($3, false);
!             $$ = cat2_str(make_str("fetch forward from"), cursor_marker);
!         }
!     | FETCH fetch_direction cursor_name
!         {
!             char *cursor_marker = $3[0] == ':' ? make_str("$0") : $3;
!             add_additional_variables($3, false);
!             $$ = cat_str(4, make_str("fetch"), $2, make_str("from"), cursor_marker);
!         }
!     | FETCH from_in cursor_name
!         {
!             char *cursor_marker = $3[0] == ':' ? make_str("$0") : $3;
!             add_additional_variables($3, false);
!             $$ = cat_str(3, make_str("fetch"), $2, cursor_marker);
          }
  ECPG: SpecialRuleRelationOLD addon
          if (!QueryIsRule)
diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/preproc/ecpg.trailer
postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/ecpg.trailer
*** postgresql-8.4rc2/src/interfaces/ecpg/preproc/ecpg.trailer    2009-06-11 01:11:52.000000000 +0200
--- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/ecpg.trailer    2009-06-24 10:33:27.000000000 +0200
*************** prepared_name: name             {
*** 284,292 ****
   * Declare a prepared cursor. The syntax is different from the standard
   * declare statement, so we create a new rule.
   */
! ECPGCursorStmt:  DECLARE name cursor_options CURSOR opt_hold FOR prepared_name
          {
              struct cursor *ptr, *this;
              struct variable *thisquery = (struct variable *)mm_alloc(sizeof(struct variable));
              const char *con = connection ? connection : "NULL";

--- 284,293 ----
   * Declare a prepared cursor. The syntax is different from the standard
   * declare statement, so we create a new rule.
   */
! ECPGCursorStmt:  DECLARE cursor_name cursor_options CURSOR opt_hold FOR prepared_name
          {
              struct cursor *ptr, *this;
+             char *cursor_marker = $2[0] == ':' ? make_str("$0") : mm_strdup($2);
              struct variable *thisquery = (struct variable *)mm_alloc(sizeof(struct variable));
              const char *con = connection ? connection : "NULL";

*************** ECPGCursorStmt:  DECLARE name cursor_opt
*** 303,309 ****
              this->next = cur;
              this->name = $2;
              this->connection = connection;
!             this->command =  cat_str(6, make_str("declare"), mm_strdup($2), $3, make_str("cursor"), $5, make_str("for
$1"));
              this->argsresult = NULL;

              thisquery->type = &ecpg_query;
--- 304,310 ----
              this->next = cur;
              this->name = $2;
              this->connection = connection;
!             this->command =  cat_str(6, make_str("declare"), cursor_marker, $3, make_str("cursor"), $5, make_str("for
$1"));
              this->argsresult = NULL;

              thisquery->type = &ecpg_query;
*************** ECPGCursorStmt:  DECLARE name cursor_opt
*** 313,320 ****
              sprintf(thisquery->name, "ECPGprepared_statement(%s, %s, __LINE__)", con, $7);

              this->argsinsert = NULL;
              add_variable_to_head(&(this->argsinsert), thisquery, &no_indicator);
-
              cur = this;

              $$ = cat_str(3, make_str("/*"), mm_strdup(this->command), make_str("*/"));
--- 314,326 ----
              sprintf(thisquery->name, "ECPGprepared_statement(%s, %s, __LINE__)", con, $7);

              this->argsinsert = NULL;
+             if ($2[0] == ':')
+             {
+                 struct variable *var = find_variable($2 + 1);
+                 remove_variable_from_list(&argsinsert, var);
+                 add_variable_to_head(&(this->argsinsert), var, &no_indicator);
+             }
              add_variable_to_head(&(this->argsinsert), thisquery, &no_indicator);
              cur = this;

              $$ = cat_str(3, make_str("/*"), mm_strdup(this->command), make_str("*/"));
*************** ECPGFree:    SQL_FREE name    { $$ = $2; }
*** 944,950 ****
  /*
   * open is an open cursor, at the moment this has to be removed
   */
! ECPGOpen: SQL_OPEN name opt_ecpg_using { $$ = $2; };

  opt_ecpg_using: /*EMPTY*/    { $$ = EMPTY; }
          | ecpg_using        { $$ = $1; }
--- 950,965 ----
  /*
   * open is an open cursor, at the moment this has to be removed
   */
! ECPGOpen: SQL_OPEN cursor_name opt_ecpg_using
!         {
!             if ($2[0] == ':')
!             {
!                 struct variable *var = find_variable($2 + 1);
!                 remove_variable_from_list(&argsinsert, var);
!             }
!             $$ = $2;
!         }
!         ;

  opt_ecpg_using: /*EMPTY*/    { $$ = EMPTY; }
          | ecpg_using        { $$ = $1; }
*************** ecpg_using:    USING using_list     { $$ = EMP
*** 956,974 ****

  using_descriptor: USING opt_sql SQL_DESCRIPTOR quoted_ident_stringvar
          {
!             add_variable_to_head(&argsinsert, descriptor_variable($4,0), &no_indicator);
              $$ = EMPTY;
          }
          ;

  into_descriptor: INTO opt_sql SQL_DESCRIPTOR quoted_ident_stringvar
          {
!             add_variable_to_head(&argsresult, descriptor_variable($4,1), &no_indicator);
              $$ = EMPTY;
          }
          ;

! opt_sql: /*EMPTY*/ | SQL_SQL;

  using_list: UsingValue | UsingValue ',' using_list;

--- 971,1025 ----

  using_descriptor: USING opt_sql SQL_DESCRIPTOR quoted_ident_stringvar
          {
!             if (strlen($2) || !(INFORMIX_MODE))
!                 add_variable_to_head(&argsinsert, descriptor_variable($4,0), &no_indicator);
!             else
!             {
!                 if ($4[0] == '\"')
!                 {
!                     char *pos;
!
!                     $4[0] = ' ';
!                     for (pos = $4; *pos; pos++)
!                         if (*pos == '\"')
!                             *pos = ' ';
!                 }
!                 add_variable_to_head(&argsinsert, sqlda_variable($4), &no_indicator);
!             }
              $$ = EMPTY;
          }
          ;

  into_descriptor: INTO opt_sql SQL_DESCRIPTOR quoted_ident_stringvar
          {
!             if (strlen($2) || !(INFORMIX_MODE))
!                 add_variable_to_head(&argsresult, descriptor_variable($4,1), &no_indicator);
!             else
!             {
!                 if ($4[0] == '\"')
!                 {
!                     char *pos;
!
!                     $4[0] = ' ';
!                     for (pos = $4; *pos; pos++)
!                         if (*pos == '\"')
!                             *pos = ' ';
!                 }
!                 add_variable_to_head(&argsresult, sqlda_variable($4), &no_indicator);
!             }
              $$ = EMPTY;
          }
          ;

! into_sqlda:    INTO name
!         {
!             add_variable_to_head(&argsresult, sqlda_variable($2), &no_indicator);
!             $$ = EMPTY;
!         }
!
! opt_sql: /*EMPTY*/        { $$ = EMPTY; }
!         | SQL_SQL        { $$ = make_str("sql"); }
!         ;

  using_list: UsingValue | UsingValue ',' using_list;

*************** ECPGDescribe: SQL_DESCRIBE INPUT_P name
*** 1001,1022 ****
      {
          const char *con = connection ? connection : "NULL";
          mmerror(PARSE_ERROR, ET_WARNING, "using unsupported DESCRIBE statement");
!         $$ = (char *) mm_alloc(sizeof("1, ECPGprepared_statement(, \"\", __LINE__)") + strlen(con) + strlen($3));
!         sprintf($$, "1, ECPGprepared_statement(%s, \"%s\", __LINE__)", con, $3);
      }
      | SQL_DESCRIBE opt_output name using_descriptor
      {
          const char *con = connection ? connection : "NULL";
!         mmerror(PARSE_ERROR, ET_WARNING, "using unsupported DESCRIBE statement");
!         $$ = (char *) mm_alloc(sizeof("0, ECPGprepared_statement(, \"\", __LINE__)") + strlen(con) + strlen($3));
!         sprintf($$, "0, ECPGprepared_statement(%s, \"%s\", __LINE__)", con, $3);
      }
      | SQL_DESCRIBE opt_output name into_descriptor
      {
          const char *con = connection ? connection : "NULL";
          mmerror(PARSE_ERROR, ET_WARNING, "using unsupported DESCRIBE statement");
!         $$ = (char *) mm_alloc(sizeof("0, ECPGprepared_statement(, \"\", __LINE__)") + strlen(con) + strlen($3));
!         sprintf($$, "0, ECPGprepared_statement(%s, \"%s\", __LINE__)", con, $3);
      }
      ;

--- 1052,1084 ----
      {
          const char *con = connection ? connection : "NULL";
          mmerror(PARSE_ERROR, ET_WARNING, "using unsupported DESCRIBE statement");
!         $$ = (char *) mm_alloc(sizeof("1, , \"\"") + strlen(con) + strlen($3));
!         sprintf($$, "1, %s, \"%s\"", con, $3);
      }
      | SQL_DESCRIBE opt_output name using_descriptor
      {
          const char *con = connection ? connection : "NULL";
!         $$ = (char *) mm_alloc(sizeof("0, , \"\"") + strlen(con) + strlen($3));
!         sprintf($$, "0, %s, \"%s\"", con, $3);
      }
      | SQL_DESCRIBE opt_output name into_descriptor
      {
          const char *con = connection ? connection : "NULL";
+         $$ = (char *) mm_alloc(sizeof("0, , \"\"") + strlen(con) + strlen($3));
+         sprintf($$, "0, %s, \"%s\"", con, $3);
+     }
+     | SQL_DESCRIBE INPUT_P name into_sqlda
+     {
+         const char *con = connection ? connection : "NULL";
          mmerror(PARSE_ERROR, ET_WARNING, "using unsupported DESCRIBE statement");
!         $$ = (char *) mm_alloc(sizeof("1, , \"\"") + strlen(con) + strlen($3));
!         sprintf($$, "1, %s, \"%s\"", con, $3);
!     }
!     | SQL_DESCRIBE opt_output name into_sqlda
!     {
!         const char *con = connection ? connection : "NULL";
!         $$ = (char *) mm_alloc(sizeof("0, , \"\"") + strlen(con) + strlen($3));
!         sprintf($$, "0, %s, \"%s\"", con, $3);
      }
      ;

*************** civarind: cvariable indicator
*** 1768,1773 ****
--- 1830,1845 ----
          }
          ;

+ char_civar: char_variable
+         {
+             char *qm; /* dummy questionmark, we have to pass the real variable name */
+             add_variable_to_head(&argsinsert, find_variable($1), &no_indicator);
+             qm = create_questionmarks($1, false);
+             free(qm);
+             $$ = $1;
+         }
+         ;
+
  civar: cvariable
          {
              add_variable_to_head(&argsinsert, find_variable($1), &no_indicator);
*************** ecpg_into: INTO into_list    { $$ = EMPTY;
*** 1983,1988 ****
--- 2055,2075 ----
          | into_descriptor    { $$ = $1; }
      ;

+ ecpg_fetch_into: ecpg_into    { $$ = $1; }
+     | using_descriptor
+     {
+         struct variable *var;
+
+         if (!INFORMIX_MODE)
+             mmerror(PARSE_ERROR, ET_ERROR, "Not in Informix compatibility mode");
+
+         var = argsinsert->variable;
+         remove_variable_from_list(&argsinsert, var);
+         add_variable_to_head(&argsresult, var, &no_indicator);
+         $$ = $1;
+     }
+     ;
+
  %%

  void base_yyerror(const char *error)
diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/preproc/ecpg.type
postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/ecpg.type
*** postgresql-8.4rc2/src/interfaces/ecpg/preproc/ecpg.type    2008-11-14 11:03:33.000000000 +0100
--- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/ecpg.type    2009-06-24 10:33:27.000000000 +0200
***************
*** 43,48 ****
--- 43,49 ----
  %type <str> c_term
  %type <str> c_thing
  %type <str> char_variable
+ %type <str> char_civar
  %type <str> civar
  %type <str> civarind
  %type <str> ColLabel
***************
*** 60,65 ****
--- 61,67 ----
  %type <str> ecpg_ident
  %type <str> ecpg_interval
  %type <str> ecpg_into
+ %type <str> ecpg_fetch_into
  %type <str> ecpg_param
  %type <str> ecpg_sconst
  %type <str> ecpg_using
***************
*** 70,75 ****
--- 72,78 ----
  %type <str> execute_rest
  %type <str> indicator
  %type <str> into_descriptor
+ %type <str> into_sqlda
  %type <str> Iresult
  %type <str> on_off
  %type <str> opt_bit_field
***************
*** 84,89 ****
--- 87,93 ----
  %type <str> opt_reference
  %type <str> opt_scale
  %type <str> opt_server
+ %type <str> opt_sql
  %type <str> opt_user
  %type <str> opt_opt_value
  %type <str> ora_user
diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/preproc/extern.h
postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/extern.h
*** postgresql-8.4rc2/src/interfaces/ecpg/preproc/extern.h    2009-06-11 16:49:13.000000000 +0200
--- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/extern.h    2009-06-24 10:33:27.000000000 +0200
*************** extern void add_descriptor(char *, char
*** 89,96 ****
--- 89,98 ----
  extern void drop_descriptor(char *, char *);
  extern struct descriptor *lookup_descriptor(char *, char *);
  extern struct variable *descriptor_variable(const char *name, int input);
+ extern struct variable *sqlda_variable(const char *name);
  extern void add_variable_to_head(struct arguments **, struct variable *, struct variable *);
  extern void add_variable_to_tail(struct arguments **, struct variable *, struct variable *);
+ extern void remove_variable_from_list(struct arguments ** list, struct variable * var);
  extern void dump_variables(struct arguments *, int);
  extern struct typedefs *get_typedef(char *);
  extern void adjust_array(enum ECPGttype, char **, char **, char *, char *, int, bool);
diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/preproc/type.c
postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/type.c
*** postgresql-8.4rc2/src/interfaces/ecpg/preproc/type.c    2009-06-11 16:49:13.000000000 +0200
--- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/type.c    2009-06-24 10:33:27.000000000 +0200
*************** ECPGdump_a_simple(FILE *o, const char *n
*** 325,330 ****
--- 325,332 ----
      else if (type == ECPGt_descriptor)
          /* remember that name here already contains quotes (if needed) */
          fprintf(o, "\n\tECPGt_descriptor, %s, 0L, 0L, 0L, ", name);
+     else if (type == ECPGt_sqlda)
+         fprintf(o, "\n\tECPGt_sqlda, &%s, 0L, 0L, 0L, ", name);
      else
      {
          char       *variable = (char *) mm_alloc(strlen(name) + ((prefix == NULL) ? 0 : strlen(prefix)) + 4);
diff -dcrpN postgresql-8.4rc2/src/interfaces/ecpg/preproc/variable.c
postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/variable.c
*** postgresql-8.4rc2/src/interfaces/ecpg/preproc/variable.c    2009-06-11 16:49:13.000000000 +0200
--- postgresql-8.4rc2-sqlda/src/interfaces/ecpg/preproc/variable.c    2009-06-24 10:33:27.000000000 +0200
*************** add_variable_to_tail(struct arguments **
*** 401,406 ****
--- 401,430 ----
          *list = new;
  }

+ void
+ remove_variable_from_list(struct arguments ** list, struct variable * var)
+ {
+     struct arguments *p, *prev = NULL;
+     bool    found = false;
+
+     for (p = *list; p; p = p->next)
+     {
+         if (p->variable == var)
+         {
+             found = true;
+             break;
+         }
+         prev = p;
+     }
+     if (found)
+     {
+         if (prev)
+             prev->next = p->next;
+         else
+             *list = p->next;
+     }
+ }
+
  /* Dump out a list of all the variable on this list.
     This is a recursive function that works from the end of the list and
     deletes the list as we go on.

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Brendan Jurd
Дата:
Сообщение: Re: [BUGS] BUG #4862: different results in to_date() between 8.3.7 & 8.4.RC1
Следующее
От: Peter Eisentraut
Дата:
Сообщение: Re: dblink for 8.4 should work without user-mappings