Re: CommitFest 2009-09, two weeks on - Mailing list pgsql-hackers

From Joe Conway
Subject Re: CommitFest 2009-09, two weeks on
Date
Msg-id 4AC80CC4.9010105@joeconway.com
Whole thread Raw
In response to Re: CommitFest 2009-09, two weeks on  (Itagaki Takahiro <itagaki.takahiro@oss.ntt.co.jp>)
Responses dblink memory leak  (Itagaki Takahiro <itagaki.takahiro@oss.ntt.co.jp>)
List pgsql-hackers
Itagaki Takahiro wrote:
> The point is *memory leak* in dblink when a query is canceled or
> become time-out. I think it is a bug, and my patch could fix it.

Please see if this works for you.

Joe

Index: dblink.c
===================================================================
RCS file: /opt/src/cvs/pgsql/contrib/dblink/dblink.c,v
retrieving revision 1.84
diff -c -r1.84 dblink.c
*** dblink.c    12 Sep 2009 23:20:52 -0000    1.84
--- dblink.c    4 Oct 2009 02:19:17 -0000
***************
*** 97,109 ****
  static char *generate_relation_name(Oid relid);
  static void dblink_connstr_check(const char *connstr);
  static void dblink_security_check(PGconn *conn, remoteConn *rconn);
! static void dblink_res_error(const char *conname, PGresult *res, const char *dblink_context_msg, bool fail);
  static char *get_connect_string(const char *servername);
  static char *escape_param_str(const char *from);

  /* Global */
  static remoteConn *pconn = NULL;
  static HTAB *remoteConnHash = NULL;

  /*
   *    Following is list that holds multiple remote connections.
--- 97,111 ----
  static char *generate_relation_name(Oid relid);
  static void dblink_connstr_check(const char *connstr);
  static void dblink_security_check(PGconn *conn, remoteConn *rconn);
! static void dblink_res_error(const char *conname, const char *dblink_context_msg, bool fail);
  static char *get_connect_string(const char *servername);
  static char *escape_param_str(const char *from);
+ static void dblink_error_callback(void *arg);

  /* Global */
  static remoteConn *pconn = NULL;
  static HTAB *remoteConnHash = NULL;
+ static PGresult *res = NULL;

  /*
   *    Following is list that holds multiple remote connections.
***************
*** 143,149 ****
--- 145,154 ----
      do { \
              msg = pstrdup(PQerrorMessage(conn)); \
              if (res) \
+             { \
                  PQclear(res); \
+                 res = NULL; \
+             } \
              elog(ERROR, "%s: %s", p2, msg); \
      } while (0)

***************
*** 212,217 ****
--- 217,238 ----
              } \
      } while (0)

+ #define ERRORCONTEXTCALLBACK \
+     ErrorContextCallback    dberrcontext
+
+ #define PUSH_DBERRCONTEXT(_error_callback_) \
+     do { \
+         dberrcontext.callback = _error_callback_; \
+         dberrcontext.arg = (void *) NULL; \
+         dberrcontext.previous = error_context_stack; \
+         error_context_stack = &dberrcontext; \
+     } while (0)
+
+ #define POP_DBERRCONTEXT \
+     do { \
+         error_context_stack = dberrcontext.previous; \
+     } while (0)
+
  /*
   * Create a persistent connection to another database
   */
***************
*** 325,331 ****
  dblink_open(PG_FUNCTION_ARGS)
  {
      char       *msg;
-     PGresult   *res = NULL;
      PGconn       *conn = NULL;
      char       *curname = NULL;
      char       *sql = NULL;
--- 346,351 ----
***************
*** 333,339 ****
--- 353,361 ----
      StringInfoData buf;
      remoteConn *rconn = NULL;
      bool        fail = true;    /* default to backward compatible behavior */
+     ERRORCONTEXTCALLBACK;

+     PUSH_DBERRCONTEXT(dblink_error_callback);
      DBLINK_INIT;
      initStringInfo(&buf);

***************
*** 381,389 ****
      if (PQtransactionStatus(conn) == PQTRANS_IDLE)
      {
          res = PQexec(conn, "BEGIN");
!         if (PQresultStatus(res) != PGRES_COMMAND_OK)
              DBLINK_RES_INTERNALERROR("begin error");
          PQclear(res);
          rconn->newXactForCursor = TRUE;

          /*
--- 403,412 ----
      if (PQtransactionStatus(conn) == PQTRANS_IDLE)
      {
          res = PQexec(conn, "BEGIN");
!         if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
              DBLINK_RES_INTERNALERROR("begin error");
          PQclear(res);
+         res = NULL;
          rconn->newXactForCursor = TRUE;

          /*
***************
*** 402,412 ****
      res = PQexec(conn, buf.data);
      if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
      {
!         dblink_res_error(conname, res, "could not open cursor", fail);
          PG_RETURN_TEXT_P(cstring_to_text("ERROR"));
      }
-
      PQclear(res);
      PG_RETURN_TEXT_P(cstring_to_text("OK"));
  }

--- 425,437 ----
      res = PQexec(conn, buf.data);
      if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
      {
!         dblink_res_error(conname, "could not open cursor", fail);
          PG_RETURN_TEXT_P(cstring_to_text("ERROR"));
      }
      PQclear(res);
+     res = NULL;
+
+     POP_DBERRCONTEXT;
      PG_RETURN_TEXT_P(cstring_to_text("OK"));
  }

***************
*** 418,431 ****
  dblink_close(PG_FUNCTION_ARGS)
  {
      PGconn       *conn = NULL;
-     PGresult   *res = NULL;
      char       *curname = NULL;
      char       *conname = NULL;
      StringInfoData buf;
      char       *msg;
      remoteConn *rconn = NULL;
      bool        fail = true;    /* default to backward compatible behavior */
!
      DBLINK_INIT;
      initStringInfo(&buf);

--- 443,457 ----
  dblink_close(PG_FUNCTION_ARGS)
  {
      PGconn       *conn = NULL;
      char       *curname = NULL;
      char       *conname = NULL;
      StringInfoData buf;
      char       *msg;
      remoteConn *rconn = NULL;
      bool        fail = true;    /* default to backward compatible behavior */
!     ERRORCONTEXTCALLBACK;
!
!     PUSH_DBERRCONTEXT(dblink_error_callback);
      DBLINK_INIT;
      initStringInfo(&buf);

***************
*** 471,481 ****
      res = PQexec(conn, buf.data);
      if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
      {
!         dblink_res_error(conname, res, "could not close cursor", fail);
          PG_RETURN_TEXT_P(cstring_to_text("ERROR"));
      }
-
      PQclear(res);

      /* if we started a transaction, decrement cursor count */
      if (rconn->newXactForCursor)
--- 497,507 ----
      res = PQexec(conn, buf.data);
      if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
      {
!         dblink_res_error(conname, "could not close cursor", fail);
          PG_RETURN_TEXT_P(cstring_to_text("ERROR"));
      }
      PQclear(res);
+     res = NULL;

      /* if we started a transaction, decrement cursor count */
      if (rconn->newXactForCursor)
***************
*** 488,499 ****
              rconn->newXactForCursor = FALSE;

              res = PQexec(conn, "COMMIT");
!             if (PQresultStatus(res) != PGRES_COMMAND_OK)
                  DBLINK_RES_INTERNALERROR("commit error");
              PQclear(res);
          }
      }

      PG_RETURN_TEXT_P(cstring_to_text("OK"));
  }

--- 514,527 ----
              rconn->newXactForCursor = FALSE;

              res = PQexec(conn, "COMMIT");
!             if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
                  DBLINK_RES_INTERNALERROR("commit error");
              PQclear(res);
+             res = NULL;
          }
      }

+     POP_DBERRCONTEXT;
      PG_RETURN_TEXT_P(cstring_to_text("OK"));
  }

***************
*** 509,519 ****
      int            call_cntr;
      int            max_calls;
      AttInMetadata *attinmeta;
-     PGresult   *res = NULL;
      MemoryContext oldcontext;
      char       *conname = NULL;
      remoteConn *rconn = NULL;
!
      DBLINK_INIT;

      /* stuff done only on the first call of the function */
--- 537,548 ----
      int            call_cntr;
      int            max_calls;
      AttInMetadata *attinmeta;
      MemoryContext oldcontext;
      char       *conname = NULL;
      remoteConn *rconn = NULL;
!     ERRORCONTEXTCALLBACK;
!
!     PUSH_DBERRCONTEXT(dblink_error_callback);
      DBLINK_INIT;

      /* stuff done only on the first call of the function */
***************
*** 585,597 ****
              (PQresultStatus(res) != PGRES_COMMAND_OK &&
               PQresultStatus(res) != PGRES_TUPLES_OK))
          {
!             dblink_res_error(conname, res, "could not fetch from cursor", fail);
              SRF_RETURN_DONE(funcctx);
          }
          else if (PQresultStatus(res) == PGRES_COMMAND_OK)
          {
              /* cursor does not exist - closed already or bad name */
              PQclear(res);
              ereport(ERROR,
                      (errcode(ERRCODE_INVALID_CURSOR_NAME),
                       errmsg("cursor \"%s\" does not exist", curname)));
--- 614,627 ----
              (PQresultStatus(res) != PGRES_COMMAND_OK &&
               PQresultStatus(res) != PGRES_TUPLES_OK))
          {
!             dblink_res_error(conname, "could not fetch from cursor", fail);
              SRF_RETURN_DONE(funcctx);
          }
          else if (PQresultStatus(res) == PGRES_COMMAND_OK)
          {
              /* cursor does not exist - closed already or bad name */
              PQclear(res);
+             res = NULL;
              ereport(ERROR,
                      (errcode(ERRCODE_INVALID_CURSOR_NAME),
                       errmsg("cursor \"%s\" does not exist", curname)));
***************
*** 635,640 ****
--- 665,671 ----
          if (funcctx->max_calls < 1)
          {
              PQclear(res);
+             res = NULL;
              SRF_RETURN_DONE(funcctx);
          }

***************
*** 690,701 ****
--- 721,736 ----
          /* make the tuple into a datum */
          result = HeapTupleGetDatum(tuple);

+         POP_DBERRCONTEXT;
          SRF_RETURN_NEXT(funcctx, result);
      }
      else
      {
          /* do when there is no more left */
          PQclear(res);
+         res = NULL;
+
+         POP_DBERRCONTEXT;
          SRF_RETURN_DONE(funcctx);
      }
  }
***************
*** 755,766 ****
      int            max_calls;
      AttInMetadata *attinmeta;
      char       *msg;
-     PGresult   *res = NULL;
      bool        is_sql_cmd = false;
      char       *sql_cmd_status = NULL;
      MemoryContext oldcontext;
      bool        freeconn = false;
!
      DBLINK_INIT;

      /* stuff done only on the first call of the function */
--- 790,802 ----
      int            max_calls;
      AttInMetadata *attinmeta;
      char       *msg;
      bool        is_sql_cmd = false;
      char       *sql_cmd_status = NULL;
      MemoryContext oldcontext;
      bool        freeconn = false;
!     ERRORCONTEXTCALLBACK;
!
!     PUSH_DBERRCONTEXT(dblink_error_callback);
      DBLINK_INIT;

      /* stuff done only on the first call of the function */
***************
*** 857,863 ****
          {
              if (freeconn)
                  PQfinish(conn);
!             dblink_res_error(conname, res, "could not execute query", fail);
              MemoryContextSwitchTo(oldcontext);
              SRF_RETURN_DONE(funcctx);
          }
--- 893,899 ----
          {
              if (freeconn)
                  PQfinish(conn);
!             dblink_res_error(conname, "could not execute query", fail);
              MemoryContextSwitchTo(oldcontext);
              SRF_RETURN_DONE(funcctx);
          }
***************
*** 926,932 ****
--- 962,971 ----
          if (funcctx->max_calls < 1)
          {
              if (res)
+             {
                  PQclear(res);
+                 res = NULL;
+             }
              MemoryContextSwitchTo(oldcontext);
              SRF_RETURN_DONE(funcctx);
          }
***************
*** 984,995 ****
--- 1023,1038 ----
          /* make the tuple into a datum */
          result = HeapTupleGetDatum(tuple);

+         POP_DBERRCONTEXT;
          SRF_RETURN_NEXT(funcctx, result);
      }
      else
      {
          /* do when there is no more left */
          PQclear(res);
+         res = NULL;
+
+         POP_DBERRCONTEXT;
          SRF_RETURN_DONE(funcctx);
      }
  }
***************
*** 1119,1125 ****
  dblink_exec(PG_FUNCTION_ARGS)
  {
      char       *msg;
-     PGresult   *res = NULL;
      text       *sql_cmd_status = NULL;
      TupleDesc    tupdesc = NULL;
      PGconn       *conn = NULL;
--- 1162,1167 ----
***************
*** 1129,1135 ****
      remoteConn *rconn = NULL;
      bool        freeconn = false;
      bool        fail = true;    /* default to backward compatible behavior */
!
      DBLINK_INIT;

      if (PG_NARGS() == 3)
--- 1171,1179 ----
      remoteConn *rconn = NULL;
      bool        freeconn = false;
      bool        fail = true;    /* default to backward compatible behavior */
!     ERRORCONTEXTCALLBACK;
!
!     PUSH_DBERRCONTEXT(dblink_error_callback);
      DBLINK_INIT;

      if (PG_NARGS() == 3)
***************
*** 1172,1178 ****
          (PQresultStatus(res) != PGRES_COMMAND_OK &&
           PQresultStatus(res) != PGRES_TUPLES_OK))
      {
!         dblink_res_error(conname, res, "could not execute command", fail);

          /* need a tuple descriptor representing one TEXT column */
          tupdesc = CreateTemplateTupleDesc(1, false);
--- 1216,1222 ----
          (PQresultStatus(res) != PGRES_COMMAND_OK &&
           PQresultStatus(res) != PGRES_TUPLES_OK))
      {
!         dblink_res_error(conname, "could not execute command", fail);

          /* need a tuple descriptor representing one TEXT column */
          tupdesc = CreateTemplateTupleDesc(1, false);
***************
*** 1198,1207 ****
--- 1242,1253 ----
           */
          sql_cmd_status = cstring_to_text(PQcmdStatus(res));
          PQclear(res);
+         res = NULL;
      }
      else
      {
          PQclear(res);
+         res = NULL;
          ereport(ERROR,
                  (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
                   errmsg("statement returning results not allowed")));
***************
*** 1211,1216 ****
--- 1257,1263 ----
      if (freeconn)
          PQfinish(conn);

+     POP_DBERRCONTEXT;
      PG_RETURN_TEXT_P(sql_cmd_status);
  }

***************
*** 2418,2424 ****
  }

  static void
! dblink_res_error(const char *conname, PGresult *res, const char *dblink_context_msg, bool fail)
  {
      int            level;
      char       *pg_diag_sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
--- 2465,2471 ----
  }

  static void
! dblink_res_error(const char *conname, const char *dblink_context_msg, bool fail)
  {
      int            level;
      char       *pg_diag_sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
***************
*** 2453,2459 ****
--- 2500,2509 ----
      xpstrdup(message_context, pg_diag_context);

      if (res)
+     {
          PQclear(res);
+         res = NULL;
+     }

      if (conname)
          dblink_context_conname = conname;
***************
*** 2550,2552 ****
--- 2600,2615 ----

      return buf->data;
  }
+
+ /*
+  * error context callback to let us free resources
+  */
+ static void
+ dblink_error_callback(void *arg)
+ {
+     if (res)
+     {
+         PQclear(res);
+         res = NULL;
+     }
+ }

Attachment

pgsql-hackers by date:

Previous
From: Andrew Dunstan
Date:
Subject: Re: CREATE LIKE INCLUDING COMMENTS and STORAGES
Next
From: Noah Misch
Date:
Subject: Review of "SQLDA support for ECPG"