mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
Add more efficient functions to pqformat API.
There's three prongs to achieve greater efficiency here: 1) Allow reusing a stringbuffer across pq_beginmessage/endmessage, with the new pq_beginmessage_reuse/endmessage_reuse. This can be beneficial both because it avoids allocating the initial buffer, and because it's more likely to already have an correctly sized buffer. 2) Replacing pq_sendint() with pq_sendint$width() inline functions. Previously unnecessary and unpredictable branches in pq_sendint() were needed. Additionally the replacement functions are implemented more efficiently. pq_sendint is now deprecated, a separate commit will convert all in-tree callers. 3) Add pq_writeint$width(), pq_writestring(). These rely on sufficient space in the StringInfo's buffer, avoiding individual space checks & potential individual resizing. To allow this to be used for strings, expose mbutil.c's MAX_CONVERSION_GROWTH. Followup commits will make use of these facilities. Author: Andres Freund Discussion: https://postgr.es/m/20170914063418.sckdzgjfrsbekae4@alap3.anarazel.de
This commit is contained in:
parent
70c2d1be2b
commit
1de09ad8eb
@ -97,13 +97,24 @@ pq_beginmessage(StringInfo buf, char msgtype)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* --------------------------------
|
/* --------------------------------
|
||||||
* pq_sendbyte - append a raw byte to a StringInfo buffer
|
|
||||||
|
* pq_beginmessage_reuse - initialize for sending a message, reuse buffer
|
||||||
|
*
|
||||||
|
* This requires the buffer to be allocated in an sufficiently long-lived
|
||||||
|
* memory context.
|
||||||
* --------------------------------
|
* --------------------------------
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
pq_sendbyte(StringInfo buf, int byt)
|
pq_beginmessage_reuse(StringInfo buf, char msgtype)
|
||||||
{
|
{
|
||||||
appendStringInfoCharMacro(buf, byt);
|
resetStringInfo(buf);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We stash the message type into the buffer's cursor field, expecting
|
||||||
|
* that the pq_sendXXX routines won't touch it. We could alternatively
|
||||||
|
* make it the first byte of the buffer contents, but this seems easier.
|
||||||
|
*/
|
||||||
|
buf->cursor = msgtype;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --------------------------------
|
/* --------------------------------
|
||||||
@ -113,6 +124,7 @@ pq_sendbyte(StringInfo buf, int byt)
|
|||||||
void
|
void
|
||||||
pq_sendbytes(StringInfo buf, const char *data, int datalen)
|
pq_sendbytes(StringInfo buf, const char *data, int datalen)
|
||||||
{
|
{
|
||||||
|
/* use variant that maintains a trailing null-byte, out of caution */
|
||||||
appendBinaryStringInfo(buf, data, datalen);
|
appendBinaryStringInfo(buf, data, datalen);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,13 +149,13 @@ pq_sendcountedtext(StringInfo buf, const char *str, int slen,
|
|||||||
if (p != str) /* actual conversion has been done? */
|
if (p != str) /* actual conversion has been done? */
|
||||||
{
|
{
|
||||||
slen = strlen(p);
|
slen = strlen(p);
|
||||||
pq_sendint(buf, slen + extra, 4);
|
pq_sendint32(buf, slen + extra);
|
||||||
appendBinaryStringInfoNT(buf, p, slen);
|
appendBinaryStringInfoNT(buf, p, slen);
|
||||||
pfree(p);
|
pfree(p);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pq_sendint(buf, slen + extra, 4);
|
pq_sendint32(buf, slen + extra);
|
||||||
appendBinaryStringInfoNT(buf, str, slen);
|
appendBinaryStringInfoNT(buf, str, slen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -227,53 +239,6 @@ pq_send_ascii_string(StringInfo buf, const char *str)
|
|||||||
appendStringInfoChar(buf, '\0');
|
appendStringInfoChar(buf, '\0');
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --------------------------------
|
|
||||||
* pq_sendint - append a binary integer to a StringInfo buffer
|
|
||||||
* --------------------------------
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
pq_sendint(StringInfo buf, int i, int b)
|
|
||||||
{
|
|
||||||
unsigned char n8;
|
|
||||||
uint16 n16;
|
|
||||||
uint32 n32;
|
|
||||||
|
|
||||||
switch (b)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
n8 = (unsigned char) i;
|
|
||||||
appendBinaryStringInfoNT(buf, (char *) &n8, 1);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
n16 = pg_hton16((uint16) i);
|
|
||||||
appendBinaryStringInfoNT(buf, (char *) &n16, 2);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
n32 = pg_hton32((uint32) i);
|
|
||||||
appendBinaryStringInfoNT(buf, (char *) &n32, 4);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
elog(ERROR, "unsupported integer size %d", b);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------
|
|
||||||
* pq_sendint64 - append a binary 8-byte int to a StringInfo buffer
|
|
||||||
*
|
|
||||||
* It is tempting to merge this with pq_sendint, but we'd have to make the
|
|
||||||
* argument int64 for all data widths --- that could be a big performance
|
|
||||||
* hit on machines where int64 isn't efficient.
|
|
||||||
* --------------------------------
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
pq_sendint64(StringInfo buf, int64 i)
|
|
||||||
{
|
|
||||||
uint64 n64 = pg_hton64(i);
|
|
||||||
|
|
||||||
appendBinaryStringInfoNT(buf, (char *) &n64, sizeof(n64));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------
|
/* --------------------------------
|
||||||
* pq_sendfloat4 - append a float4 to a StringInfo buffer
|
* pq_sendfloat4 - append a float4 to a StringInfo buffer
|
||||||
*
|
*
|
||||||
@ -295,9 +260,7 @@ pq_sendfloat4(StringInfo buf, float4 f)
|
|||||||
} swap;
|
} swap;
|
||||||
|
|
||||||
swap.f = f;
|
swap.f = f;
|
||||||
swap.i = pg_hton32(swap.i);
|
pq_sendint32(buf, swap.i);
|
||||||
|
|
||||||
appendBinaryStringInfoNT(buf, (char *) &swap.i, 4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --------------------------------
|
/* --------------------------------
|
||||||
@ -341,6 +304,21 @@ pq_endmessage(StringInfo buf)
|
|||||||
buf->data = NULL;
|
buf->data = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --------------------------------
|
||||||
|
* pq_endmessage_reuse - send the completed message to the frontend
|
||||||
|
*
|
||||||
|
* The data buffer is *not* freed, allowing to reuse the buffer with
|
||||||
|
* pg_beginmessage_reuse.
|
||||||
|
--------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
pq_endmessage_reuse(StringInfo buf)
|
||||||
|
{
|
||||||
|
/* msgtype was saved in cursor field */
|
||||||
|
(void) pq_putmessage(buf->cursor, buf->data, buf->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------------
|
/* --------------------------------
|
||||||
* pq_begintypsend - initialize for constructing a bytea result
|
* pq_begintypsend - initialize for constructing a bytea result
|
||||||
|
@ -41,17 +41,6 @@
|
|||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
/*
|
|
||||||
* When converting strings between different encodings, we assume that space
|
|
||||||
* for converted result is 4-to-1 growth in the worst case. The rate for
|
|
||||||
* currently supported encoding pairs are within 3 (SJIS JIS X0201 half width
|
|
||||||
* kanna -> UTF8 is the worst case). So "4" should be enough for the moment.
|
|
||||||
*
|
|
||||||
* Note that this is not the same as the maximum character width in any
|
|
||||||
* particular encoding.
|
|
||||||
*/
|
|
||||||
#define MAX_CONVERSION_GROWTH 4
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We maintain a simple linked list caching the fmgr lookup info for the
|
* We maintain a simple linked list caching the fmgr lookup info for the
|
||||||
* currently selected conversion functions, as well as any that have been
|
* currently selected conversion functions, as well as any that have been
|
||||||
|
@ -14,20 +14,180 @@
|
|||||||
#define PQFORMAT_H
|
#define PQFORMAT_H
|
||||||
|
|
||||||
#include "lib/stringinfo.h"
|
#include "lib/stringinfo.h"
|
||||||
|
#include "mb/pg_wchar.h"
|
||||||
|
#include "port/pg_bswap.h"
|
||||||
|
|
||||||
extern void pq_beginmessage(StringInfo buf, char msgtype);
|
extern void pq_beginmessage(StringInfo buf, char msgtype);
|
||||||
extern void pq_sendbyte(StringInfo buf, int byt);
|
extern void pq_beginmessage_reuse(StringInfo buf, char msgtype);
|
||||||
|
extern void pq_endmessage(StringInfo buf);
|
||||||
|
extern void pq_endmessage_reuse(StringInfo buf);
|
||||||
|
|
||||||
extern void pq_sendbytes(StringInfo buf, const char *data, int datalen);
|
extern void pq_sendbytes(StringInfo buf, const char *data, int datalen);
|
||||||
extern void pq_sendcountedtext(StringInfo buf, const char *str, int slen,
|
extern void pq_sendcountedtext(StringInfo buf, const char *str, int slen,
|
||||||
bool countincludesself);
|
bool countincludesself);
|
||||||
extern void pq_sendtext(StringInfo buf, const char *str, int slen);
|
extern void pq_sendtext(StringInfo buf, const char *str, int slen);
|
||||||
extern void pq_sendstring(StringInfo buf, const char *str);
|
extern void pq_sendstring(StringInfo buf, const char *str);
|
||||||
extern void pq_send_ascii_string(StringInfo buf, const char *str);
|
extern void pq_send_ascii_string(StringInfo buf, const char *str);
|
||||||
extern void pq_sendint(StringInfo buf, int i, int b);
|
|
||||||
extern void pq_sendint64(StringInfo buf, int64 i);
|
|
||||||
extern void pq_sendfloat4(StringInfo buf, float4 f);
|
extern void pq_sendfloat4(StringInfo buf, float4 f);
|
||||||
extern void pq_sendfloat8(StringInfo buf, float8 f);
|
extern void pq_sendfloat8(StringInfo buf, float8 f);
|
||||||
extern void pq_endmessage(StringInfo buf);
|
|
||||||
|
extern void pq_sendfloat4(StringInfo buf, float4 f);
|
||||||
|
extern void pq_sendfloat8(StringInfo buf, float8 f);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Append a int8 to a StringInfo buffer, which already has enough space
|
||||||
|
* preallocated.
|
||||||
|
*
|
||||||
|
* The use of restrict allows the compiler to optimize the code based on the
|
||||||
|
* assumption that buf, buf->len, buf->data and *buf->data don't
|
||||||
|
* overlap. Without the annotation buf->len etc cannot be kept in a register
|
||||||
|
* over subsequent pq_writeint* calls.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
pq_writeint8(StringInfo restrict buf, int8 i)
|
||||||
|
{
|
||||||
|
int8 ni = i;
|
||||||
|
|
||||||
|
Assert(buf->len + sizeof(i) <= buf->maxlen);
|
||||||
|
memcpy((char *restrict) (buf->data + buf->len), &ni, sizeof(ni));
|
||||||
|
buf->len += sizeof(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Append a int16 to a StringInfo buffer, which already has enough space
|
||||||
|
* preallocated.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
pq_writeint16(StringInfo restrict buf, int16 i)
|
||||||
|
{
|
||||||
|
int16 ni = pg_hton16(i);
|
||||||
|
|
||||||
|
Assert(buf->len + sizeof(ni) <= buf->maxlen);
|
||||||
|
memcpy((char *restrict) (buf->data + buf->len), &ni, sizeof(i));
|
||||||
|
buf->len += sizeof(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Append a int32 to a StringInfo buffer, which already has enough space
|
||||||
|
* preallocated.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
pq_writeint32(StringInfo restrict buf, int32 i)
|
||||||
|
{
|
||||||
|
int32 ni = pg_hton32(i);
|
||||||
|
|
||||||
|
Assert(buf->len + sizeof(i) <= buf->maxlen);
|
||||||
|
memcpy((char *restrict) (buf->data + buf->len), &ni, sizeof(i));
|
||||||
|
buf->len += sizeof(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Append a int64 to a StringInfo buffer, which already has enough space
|
||||||
|
* preallocated.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
pq_writeint64(StringInfo restrict buf, int64 i)
|
||||||
|
{
|
||||||
|
int64 ni = pg_hton64(i);
|
||||||
|
|
||||||
|
Assert(buf->len + sizeof(i) <= buf->maxlen);
|
||||||
|
memcpy((char *restrict) (buf->data + buf->len), &ni, sizeof(i));
|
||||||
|
buf->len += sizeof(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Append a null-terminated text string (with conversion) to a buffer with
|
||||||
|
* preallocated space.
|
||||||
|
*
|
||||||
|
* NB: The pre-allocated space needs to be sufficient for the string after
|
||||||
|
* converting to client encoding.
|
||||||
|
*
|
||||||
|
* NB: passed text string must be null-terminated, and so is the data
|
||||||
|
* sent to the frontend.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
pq_writestring(StringInfo restrict buf, const char *restrict str)
|
||||||
|
{
|
||||||
|
int slen = strlen(str);
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
p = pg_server_to_client(str, slen);
|
||||||
|
if (p != str) /* actual conversion has been done? */
|
||||||
|
slen = strlen(p);
|
||||||
|
|
||||||
|
Assert(buf->len + slen + 1 <= buf->maxlen);
|
||||||
|
|
||||||
|
memcpy(((char *restrict) buf->data + buf->len), p, slen + 1);
|
||||||
|
buf->len += slen + 1;
|
||||||
|
|
||||||
|
if (p != str)
|
||||||
|
pfree(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* append a binary int8 to a StringInfo buffer */
|
||||||
|
static inline void
|
||||||
|
pq_sendint8(StringInfo buf, int8 i)
|
||||||
|
{
|
||||||
|
enlargeStringInfo(buf, sizeof(i));
|
||||||
|
pq_writeint8(buf, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* append a binary int16 to a StringInfo buffer */
|
||||||
|
static inline void
|
||||||
|
pq_sendint16(StringInfo buf, int16 i)
|
||||||
|
{
|
||||||
|
enlargeStringInfo(buf, sizeof(i));
|
||||||
|
pq_writeint16(buf, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* append a binary int32 to a StringInfo buffer */
|
||||||
|
static inline void
|
||||||
|
pq_sendint32(StringInfo buf, int32 i)
|
||||||
|
{
|
||||||
|
enlargeStringInfo(buf, sizeof(i));
|
||||||
|
pq_writeint32(buf, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* append a binary int64 to a StringInfo buffer */
|
||||||
|
static inline void
|
||||||
|
pq_sendint64(StringInfo buf, int64 i)
|
||||||
|
{
|
||||||
|
enlargeStringInfo(buf, sizeof(i));
|
||||||
|
pq_writeint64(buf, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* append a binary byte to a StringInfo buffer */
|
||||||
|
static inline void
|
||||||
|
pq_sendbyte(StringInfo buf, int8 byt)
|
||||||
|
{
|
||||||
|
pq_sendint8(buf, byt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Append a binary integer to a StringInfo buffer
|
||||||
|
*
|
||||||
|
* This function is deprecated.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
pq_sendint(StringInfo buf, int i, int b)
|
||||||
|
{
|
||||||
|
switch (b)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
pq_sendint8(buf, (int8) i);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
pq_sendint16(buf, (int16) i);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
pq_sendint32(buf, (int32) i);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
elog(ERROR, "unsupported integer size %d", b);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
extern void pq_begintypsend(StringInfo buf);
|
extern void pq_begintypsend(StringInfo buf);
|
||||||
extern bytea *pq_endtypsend(StringInfo buf);
|
extern bytea *pq_endtypsend(StringInfo buf);
|
||||||
|
@ -304,6 +304,17 @@ typedef enum pg_enc
|
|||||||
/* On FE are possible all encodings */
|
/* On FE are possible all encodings */
|
||||||
#define PG_VALID_FE_ENCODING(_enc) PG_VALID_ENCODING(_enc)
|
#define PG_VALID_FE_ENCODING(_enc) PG_VALID_ENCODING(_enc)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When converting strings between different encodings, we assume that space
|
||||||
|
* for converted result is 4-to-1 growth in the worst case. The rate for
|
||||||
|
* currently supported encoding pairs are within 3 (SJIS JIS X0201 half width
|
||||||
|
* kanna -> UTF8 is the worst case). So "4" should be enough for the moment.
|
||||||
|
*
|
||||||
|
* Note that this is not the same as the maximum character width in any
|
||||||
|
* particular encoding.
|
||||||
|
*/
|
||||||
|
#define MAX_CONVERSION_GROWTH 4
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Table for mapping an encoding number to official encoding name and
|
* Table for mapping an encoding number to official encoding name and
|
||||||
* possibly other subsidiary data. Be careful to check encoding number
|
* possibly other subsidiary data. Be careful to check encoding number
|
||||||
|
Loading…
x
Reference in New Issue
Block a user