libpq_subst_storage_v1.patch
text/x-patch
Filename: libpq_subst_storage_v1.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v1
| File | + | − |
|---|---|---|
| src/interfaces/libpq/exports.txt | 4 | 0 |
| src/interfaces/libpq/fe-connect.c | 8 | 0 |
| src/interfaces/libpq/fe-exec.c | 80 | 6 |
| src/interfaces/libpq/fe-protocol2.c | 28 | 10 |
| src/interfaces/libpq/fe-protocol3.c | 27 | 11 |
| src/interfaces/libpq/libpq-fe.h | 65 | 0 |
| src/interfaces/libpq/libpq-int.h | 12 | 7 |
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index 1af8df6..a360d78 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -160,3 +160,7 @@ PQconnectStartParams 157
PQping 158
PQpingParams 159
PQlibVersion 160
+PQregisterTupleAdder 161
+PQgetAsCstring 162
+PQgetAddTupleParam 163
+PQsetAddTupleErrMes 164
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 50f3f83..437be26 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -2692,6 +2692,7 @@ makeEmptyPGconn(void)
conn->allow_ssl_try = true;
conn->wait_ssl_try = false;
#endif
+ conn->addTupleFunc = NULL;
/*
* We try to send at least 8K at a time, which is the usual size of pipe
@@ -5064,3 +5065,10 @@ PQregisterThreadLock(pgthreadlock_t newhandler)
return prev;
}
+
+void
+PQregisterTupleAdder(PGconn *conn, addTupleFunction func, void *param)
+{
+ conn->addTupleFunc = func;
+ conn->addTupleFuncParam = param;
+}
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 113aab0..c8ec9bd 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -48,7 +48,6 @@ char *const pgresStatus[] = {
static int static_client_encoding = PG_SQL_ASCII;
static bool static_std_strings = false;
-
static PGEvent *dupEvents(PGEvent *events, int count);
static bool PQsendQueryStart(PGconn *conn);
static int PQsendQueryGuts(PGconn *conn,
@@ -66,7 +65,9 @@ static PGresult *PQexecFinish(PGconn *conn);
static int PQsendDescribe(PGconn *conn, char desc_type,
const char *desc_target);
static int check_field_number(const PGresult *res, int field_num);
-
+static void *pqDefaultAddTupleFunc(PGresult *res, AddTupFunc func,
+ int id, size_t len);
+static void *pqAddTuple(PGresult *res, PGresAttValue *tup);
/* ----------------
* Space management for PGresult.
@@ -160,6 +161,9 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
result->curBlock = NULL;
result->curOffset = 0;
result->spaceLeft = 0;
+ result->addTupleFunc = pqDefaultAddTupleFunc;
+ result->addTupleFuncParam = NULL;
+ result->addTupleFuncErrMes = NULL;
if (conn)
{
@@ -194,6 +198,12 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
}
result->nEvents = conn->nEvents;
}
+
+ if (conn->addTupleFunc)
+ {
+ result->addTupleFunc = conn->addTupleFunc;
+ result->addTupleFuncParam = conn->addTupleFuncParam;
+ }
}
else
{
@@ -487,6 +497,33 @@ PQresultAlloc(PGresult *res, size_t nBytes)
return pqResultAlloc(res, nBytes, TRUE);
}
+void *
+pqDefaultAddTupleFunc(PGresult *res, AddTupFunc func, int id, size_t len)
+{
+ void *p;
+
+ switch (func)
+ {
+ case ADDTUP_ALLOC_TEXT:
+ return pqResultAlloc(res, len, TRUE);
+
+ case ADDTUP_ALLOC_BINARY:
+ p = pqResultAlloc(res, len, FALSE);
+
+ if (id == -1)
+ res->addTupleFuncParam = p;
+
+ return p;
+
+ case ADDTUP_ADD_TUPLE:
+ return pqAddTuple(res, res->addTupleFuncParam);
+
+ default:
+ /* Ignore */
+ break;
+ }
+ return NULL;
+}
/*
* pqResultAlloc -
* Allocate subsidiary storage for a PGresult.
@@ -830,9 +867,9 @@ pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...)
/*
* pqAddTuple
* add a row pointer to the PGresult structure, growing it if necessary
- * Returns TRUE if OK, FALSE if not enough memory to add the row
+ * Returns tup if OK, NULL if not enough memory to add the row.
*/
-int
+static void *
pqAddTuple(PGresult *res, PGresAttValue *tup)
{
if (res->ntups >= res->tupArrSize)
@@ -858,13 +895,13 @@ pqAddTuple(PGresult *res, PGresAttValue *tup)
newTuples = (PGresAttValue **)
realloc(res->tuples, newSize * sizeof(PGresAttValue *));
if (!newTuples)
- return FALSE; /* malloc or realloc failed */
+ return NULL; /* malloc or realloc failed */
res->tupArrSize = newSize;
res->tuples = newTuples;
}
res->tuples[res->ntups] = tup;
res->ntups++;
- return TRUE;
+ return tup;
}
/*
@@ -2822,6 +2859,43 @@ PQgetisnull(const PGresult *res, int tup_num, int field_num)
return 0;
}
+/* PQgetAsCString
+ * returns the field as C string.
+ */
+char *
+PQgetAsCstring(PGresAttValue *attval)
+{
+ return attval->len == NULL_LEN ? NULL : attval->value;
+}
+
+/* PQgetAddTupleParam
+ * Get the pointer to the contextual parameter from PGresult which is
+ * registered to PGconn by PQregisterTupleAdder
+ */
+void *
+PQgetAddTupleParam(const PGresult *res)
+{
+ if (!res)
+ return NULL;
+ return res->addTupleFuncParam;
+}
+
+/* PQsetAddTupleErrMes
+ * Set the error message pass back to the caller of addTupleFunc
+ * mes must be a malloc'ed memory block and it is released by the
+ * caller of addTupleFunc if set.
+ * You can replace the previous message by alternative mes, or clear
+ * it with NULL.
+ */
+void
+PQsetAddTupleErrMes(PGresult *res, char *mes)
+{
+ /* Free existing message */
+ if (res->addTupleFuncErrMes)
+ free(res->addTupleFuncErrMes);
+ res->addTupleFuncErrMes = mes;
+}
+
/* PQnparams:
* returns the number of input parameters of a prepared statement.
*/
diff --git a/src/interfaces/libpq/fe-protocol2.c b/src/interfaces/libpq/fe-protocol2.c
index 77c4d5a..c7f74ae 100644
--- a/src/interfaces/libpq/fe-protocol2.c
+++ b/src/interfaces/libpq/fe-protocol2.c
@@ -733,9 +733,10 @@ getAnotherTuple(PGconn *conn, bool binary)
if (conn->curTuple == NULL)
{
conn->curTuple = (PGresAttValue *)
- pqResultAlloc(result, nfields * sizeof(PGresAttValue), TRUE);
+ result->addTupleFunc(result, ADDTUP_ALLOC_BINARY, -1,
+ nfields * sizeof(PGresAttValue));
if (conn->curTuple == NULL)
- goto outOfMemory;
+ goto addTupleError;
MemSet(conn->curTuple, 0, nfields * sizeof(PGresAttValue));
/*
@@ -757,7 +758,7 @@ getAnotherTuple(PGconn *conn, bool binary)
{
bitmap = (char *) malloc(nbytes);
if (!bitmap)
- goto outOfMemory;
+ goto addTupleError;
}
if (pqGetnchar(bitmap, nbytes, conn))
@@ -787,9 +788,12 @@ getAnotherTuple(PGconn *conn, bool binary)
vlen = 0;
if (tup[i].value == NULL)
{
- tup[i].value = (char *) pqResultAlloc(result, vlen + 1, binary);
+ AddTupFunc func =
+ (binary ? ADDTUP_ALLOC_BINARY : ADDTUP_ALLOC_TEXT);
+ tup[i].value =
+ (char *) result->addTupleFunc(result, func, i, vlen + 1);
if (tup[i].value == NULL)
- goto outOfMemory;
+ goto addTupleError;
}
tup[i].len = vlen;
/* read in the value */
@@ -812,8 +816,9 @@ getAnotherTuple(PGconn *conn, bool binary)
}
/* Success! Store the completed tuple in the result */
- if (!pqAddTuple(result, tup))
- goto outOfMemory;
+ if (!result->addTupleFunc(result, ADDTUP_ADD_TUPLE, 0, 0))
+ goto addTupleError;
+
/* and reset for a new message */
conn->curTuple = NULL;
@@ -821,7 +826,7 @@ getAnotherTuple(PGconn *conn, bool binary)
free(bitmap);
return 0;
-outOfMemory:
+addTupleError:
/* Replace partially constructed result with an error result */
/*
@@ -829,8 +834,21 @@ outOfMemory:
* there's not enough memory to concatenate messages...
*/
pqClearAsyncResult(conn);
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory for query result\n"));
+ resetPQExpBuffer(&conn->errorMessage);
+
+ /*
+ * If error message is passed from addTupleFunc, set it into
+ * PGconn, assume out of memory if not.
+ */
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext(result->addTupleFuncErrMes ?
+ result->addTupleFuncErrMes :
+ "out of memory for query result\n"));
+ if (result->addTupleFuncErrMes)
+ {
+ free(result->addTupleFuncErrMes);
+ result->addTupleFuncErrMes = NULL;
+ }
/*
* XXX: if PQmakeEmptyPGresult() fails, there's probably not much we can
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index 45a84d8..d14b57a 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -634,9 +634,10 @@ getAnotherTuple(PGconn *conn, int msgLength)
if (conn->curTuple == NULL)
{
conn->curTuple = (PGresAttValue *)
- pqResultAlloc(result, nfields * sizeof(PGresAttValue), TRUE);
+ result->addTupleFunc(result, ADDTUP_ALLOC_BINARY, -1,
+ nfields * sizeof(PGresAttValue));
if (conn->curTuple == NULL)
- goto outOfMemory;
+ goto addTupleError;
MemSet(conn->curTuple, 0, nfields * sizeof(PGresAttValue));
}
tup = conn->curTuple;
@@ -673,11 +674,12 @@ getAnotherTuple(PGconn *conn, int msgLength)
vlen = 0;
if (tup[i].value == NULL)
{
- bool isbinary = (result->attDescs[i].format != 0);
-
- tup[i].value = (char *) pqResultAlloc(result, vlen + 1, isbinary);
+ AddTupFunc func = (result->attDescs[i].format != 0 ?
+ ADDTUP_ALLOC_BINARY : ADDTUP_ALLOC_TEXT);
+ tup[i].value =
+ (char *) result->addTupleFunc(result, func, i, vlen + 1);
if (tup[i].value == NULL)
- goto outOfMemory;
+ goto addTupleError;
}
tup[i].len = vlen;
/* read in the value */
@@ -689,22 +691,36 @@ getAnotherTuple(PGconn *conn, int msgLength)
}
/* Success! Store the completed tuple in the result */
- if (!pqAddTuple(result, tup))
- goto outOfMemory;
+ if (!result->addTupleFunc(result, ADDTUP_ADD_TUPLE, 0, 0))
+ goto addTupleError;
+
/* and reset for a new message */
conn->curTuple = NULL;
return 0;
-outOfMemory:
+addTupleError:
/*
* Replace partially constructed result with an error result. First
* discard the old result to try to win back some memory.
*/
pqClearAsyncResult(conn);
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory for query result\n"));
+ resetPQExpBuffer(&conn->errorMessage);
+
+ /*
+ * If error message is passed from addTupleFunc, set it into
+ * PGconn, assume out of memory if not.
+ */
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext(result->addTupleFuncErrMes ?
+ result->addTupleFuncErrMes :
+ "out of memory for query result\n"));
+ if (result->addTupleFuncErrMes)
+ {
+ free(result->addTupleFuncErrMes);
+ result->addTupleFuncErrMes = NULL;
+ }
pqSaveErrorResult(conn);
/* Discard the failed message by pretending we read it */
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index d13a5b9..bdce294 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -116,6 +116,16 @@ typedef enum
PQPING_NO_ATTEMPT /* connection not attempted (bad params) */
} PGPing;
+/* AddTupFunc is one of the parameters of addTupleFunc that decides
+ * the function of the addTupleFunction. See addTupleFunction for
+ * details */
+typedef enum
+{
+ ADDTUP_ALLOC_TEXT, /* Returns non-aligned memory for text value */
+ ADDTUP_ALLOC_BINARY, /* Returns aligned memory for binary value */
+ ADDTUP_ADD_TUPLE /* Adds tuple data into tuple storage */
+} AddTupFunc;
+
/* PGconn encapsulates a connection to the backend.
* The contents of this struct are not supposed to be known to applications.
*/
@@ -225,6 +235,12 @@ typedef struct pgresAttDesc
int atttypmod; /* type-specific modifier info */
} PGresAttDesc;
+typedef struct pgresAttValue
+{
+ int len; /* length in bytes of the value */
+ char *value; /* actual value, plus terminating zero byte */
+} PGresAttValue;
+
/* ----------------
* Exported functions of libpq
* ----------------
@@ -416,6 +432,52 @@ extern PGPing PQping(const char *conninfo);
extern PGPing PQpingParams(const char *const * keywords,
const char *const * values, int expand_dbname);
+/*
+ * Typedef for tuple storage function.
+ *
+ * This function pointer is used for tuple storage function in
+ * PGresult and PGconn.
+ *
+ * addTupleFunction is called for four types of function designated by
+ * the enum AddTupFunc.
+ *
+ * id is the identifier for allocated memory block. The caller sets -1
+ * for PGresAttValue array, and 0 to number of cols - 1 for each
+ * column.
+ *
+ * ADDTUP_ALLOC_TEXT requests the size bytes memory block for a text
+ * value which may not be alingned to the word boundary.
+ *
+ * ADDTUP_ALLOC_BINARY requests the size bytes memory block for a
+ * binary value which is aligned to the word boundary.
+ *
+ * ADDTUP_ADD_TUPLE requests to add tuple data into storage, and
+ * free the memory blocks allocated by this function if necessary.
+ * id and size are ignored.
+ *
+ * This function must return non-NULL value for success and must
+ * return NULL for failure and may set error message by
+ * PQsetAddTupleErrMes in malloc'ed memory. Assumed by caller as out
+ * of memory if the error message is NULL on failure. This function is
+ * assumed not to throw any exception.
+ */
+ typedef void *(*addTupleFunction)(PGresult *res, AddTupFunc func,
+ int id, size_t size);
+
+/*
+ * Register alternative tuple storage function to PGconn.
+ *
+ * By registering this function, pg_result disables its own tuple
+ * storage and calls it to append rows one by one.
+ *
+ * func is tuple store function. See addTupleFunction.
+ *
+ * addTupFuncParam is contextual storage that can be get with
+ * PQgetAddTupleParam in func.
+ */
+extern void PQregisterTupleAdder(PGconn *conn, addTupleFunction func,
+ void *addTupFuncParam);
+
/* Force the write buffer to be written (or at least try) */
extern int PQflush(PGconn *conn);
@@ -454,6 +516,9 @@ extern char *PQcmdTuples(PGresult *res);
extern char *PQgetvalue(const PGresult *res, int tup_num, int field_num);
extern int PQgetlength(const PGresult *res, int tup_num, int field_num);
extern int PQgetisnull(const PGresult *res, int tup_num, int field_num);
+extern char *PQgetAsCstring(PGresAttValue *attdesc);
+extern void *PQgetAddTupleParam(const PGresult *res);
+extern void PQsetAddTupleErrMes(PGresult *res, char *mes);
extern int PQnparams(const PGresult *res);
extern Oid PQparamtype(const PGresult *res, int param_num);
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 64dfcb2..45e4c93 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -134,12 +134,6 @@ typedef struct pgresParamDesc
#define NULL_LEN (-1) /* pg_result len for NULL value */
-typedef struct pgresAttValue
-{
- int len; /* length in bytes of the value */
- char *value; /* actual value, plus terminating zero byte */
-} PGresAttValue;
-
/* Typedef for message-field list entries */
typedef struct pgMessageField
{
@@ -209,6 +203,11 @@ struct pg_result
PGresult_data *curBlock; /* most recently allocated block */
int curOffset; /* start offset of free space in block */
int spaceLeft; /* number of free bytes remaining in block */
+
+ addTupleFunction addTupleFunc; /* Tuple storage function. See
+ * addTupleFunction for details. */
+ void *addTupleFuncParam; /* Contextual parameter for addTupleFunc */
+ char *addTupleFuncErrMes; /* Error message returned from addTupFunc */
};
/* PGAsyncStatusType defines the state of the query-execution state machine */
@@ -443,6 +442,13 @@ struct pg_conn
/* Buffer for receiving various parts of messages */
PQExpBufferData workBuffer; /* expansible string */
+
+ /* Tuple store function. The two fields below is copied to newly
+ * created PGresult if addTupleFunc is not NULL. Use default
+ * function if addTupleFunc is NULL. */
+ addTupleFunction addTupleFunc; /* Tuple storage function. See
+ * addTupleFunction for details. */
+ void *addTupleFuncParam; /* Contextual parameter for addTupFunc */
};
/* PGcancel stores all data necessary to cancel a connection. A copy of this
@@ -507,7 +513,6 @@ extern void
pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...)
/* This lets gcc check the format string for consistency. */
__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3)));
-extern int pqAddTuple(PGresult *res, PGresAttValue *tup);
extern void pqSaveMessageField(PGresult *res, char code,
const char *value);
extern void pqSaveParameterStatus(PGconn *conn, const char *name,