diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt index 21dd772..729941f 100644 --- a/src/interfaces/libpq/exports.txt +++ b/src/interfaces/libpq/exports.txt @@ -171,3 +171,4 @@ PQsslAttributeNames 168 PQsslAttribute 169 PQsetErrorContextVisibility 170 PQresultVerboseErrorMessage 171 +PQsetRowProcessor 172 diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index b551875..1b3fbd8 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -1036,6 +1036,15 @@ pqRowProcessor(PGconn *conn, const char **errmsgp) int i; /* + * If we have a row processor set, just return the row processor. + */ + if (conn->row_processor_set) + return conn->row_processor(res, + columns, + nfields, + conn->row_processor_data); + + /* * In single-row mode, make a new PGresult that will hold just this one * row; the original conn->result is left unchanged so that it can be used * again as the template for future rows. @@ -1391,6 +1400,11 @@ PQsendQueryStart(PGconn *conn) /* reset single-row processing mode */ conn->singleRowMode = false; + /* Clean up the row processor */ + conn->row_processor_set = false; + conn->row_processor = 0; + conn->row_processor_data = 0; + /* ready to send command message */ return true; } @@ -1623,6 +1637,45 @@ PQsetSingleRowMode(PGconn *conn) } /* + * Sets a row processor, allowing the caller to bypass the standard + * pqRowProcessor with their own. + */ +int +PQsetRowProcessor(PGconn *conn, PQrowProcessor rp, void *data) +{ + if (!conn) + return 0; + + if (conn->asyncStatus != PGASYNC_BUSY) + return 0; + + if (conn->result) + return 0; + + /* + * Setup the row processor + */ + conn->row_processor_set = true; + conn->row_processor = rp; + conn->row_processor_data = data; + + return 1; +} + +/* + * Clears the row processor + */ +int +PQclearRowProcessor(PGconn *conn) +{ + conn->row_processor_set = false; + conn->row_processor = NULL; + conn->row_processor_data = NULL; + + return 1; +} + +/* * Consume any available input from the backend * 0 return: some kind of trouble * 1 return: no problem diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h index 1b53d0e..af32fff 100644 --- a/src/interfaces/libpq/libpq-fe.h +++ b/src/interfaces/libpq/libpq-fe.h @@ -241,6 +241,47 @@ typedef struct pgresAttDesc int atttypmod; /* type-specific modifier info */ } PGresAttDesc; +/* + * Data for a single attribute of a single tuple + * + * We use char* for Attribute values. + * + * The value pointer always points to a null-terminated area; we add a + * null (zero) byte after whatever the backend sends us. This is only + * particularly useful for text values ... with a binary value, the + * value might have embedded nulls, so the application can't use C string + * operators on it. But we add a null anyway for consistency. + * Note that the value itself does not contain a length word. + * + * A NULL attribute is a special case in two ways: its len field is NULL_LEN + * and its value field points to null_field in the owning PGresult. All the + * NULL attributes in a query result point to the same place (there's no need + * to store a null string separately for each one). + */ + +#define PG_NULL_LEN (-1) /* pg_result len for NULL value */ + +/* PGdataValue represents a data field value being passed to a row processor. + * It could be either text or binary data; text data is not zero-terminated. + * A SQL NULL is represented by len < 0; then value is still valid but there + * are no data bytes there. + */ +typedef struct pgDataValue +{ + int len; /* data length in bytes, or <0 if NULL */ + const char *value; /* data value, without zero-termination */ +} PGdataValue; + +/* + * When PQsetRowProcessor sets a PQrowProcessor, libpq will call the + * function for each row received from the back end. It's the users + * responsibilty to COPY the data for each column to memory. PQrowProcessor + * should return 1 in the event of an error and 0 for sucessfull processing. + */ +typedef int (*PQrowProcessor)(PGresult *res, const PGdataValue *cols, + int col_count, void *data); + + /* ---------------- * Exported functions of libpq * ---------------- @@ -418,6 +459,8 @@ extern int PQsendQueryPrepared(PGconn *conn, int resultFormat); extern int PQsetSingleRowMode(PGconn *conn); extern PGresult *PQgetResult(PGconn *conn); +extern int PQsetRowProcessor(PGconn *conn, PQrowProcessor rp, void *data); +extern int PQclearRowProcessor(PGconn *conn); /* Routines for managing an asynchronous query */ extern int PQisBusy(PGconn *conn); diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 7289bd1..d4ce72d 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -281,16 +281,6 @@ typedef struct pgLobjfuncs Oid fn_lo_write; /* OID of backend function LOwrite */ } PGlobjfuncs; -/* PGdataValue represents a data field value being passed to a row processor. - * It could be either text or binary data; text data is not zero-terminated. - * A SQL NULL is represented by len < 0; then value is still valid but there - * are no data bytes there. - */ -typedef struct pgDataValue -{ - int len; /* data length in bytes, or <0 if NULL */ - const char *value; /* data value, without zero-termination */ -} PGdataValue; typedef enum pg_conn_host_type { @@ -389,6 +379,11 @@ struct pg_conn char copy_is_binary; /* 1 = copy binary, 0 = copy text */ int copy_already_done; /* # bytes already returned in COPY * OUT */ + + bool row_processor_set; + PQrowProcessor row_processor; /* pqRowProcessor call row_processor if */ + void *row_processor_data; /* row_processor_set */ + PGnotify *notifyHead; /* oldest unreported Notify msg */ PGnotify *notifyTail; /* newest unreported Notify msg */