PostgreSQL StringInfoData 数据结构详解
摘要
StringInfoData 是 PostgreSQL 中广泛使用的动态字符串缓冲区数据结构,用于高效地构建和管理可变长度的字符串。本文基于 PostgreSQL 源码(src/include/lib/stringinfo.h 和 src/common/stringinfo.c),详细剖析 StringInfo 的设计原理、内部实现、使用模式和最佳实践。
1. 设计背景与特点
1.1 为什么需要 StringInfo?
传统的 C 字符串处理方式存在以下问题:
c
// 传统方式的问题
char buffer[1024];
sprintf(buffer, "%s", str1);
strcat(buffer, str2); // 需要计算长度
strcat(buffer, str3); // 容易缓冲区溢出StringInfoData 提供了:
- 自动内存管理: 根据需要自动扩展缓冲区
- 长度跟踪: 始终知道当前字符串长度
- 安全追加: 不会发生缓冲区溢出
- 二进制支持: 可以存储任意二进制数据
- 内存上下文集成: 与 PostgreSQL 内存管理系统无缝集成
1.2 核心特性
| 特性 | 说明 |
|---|---|
| 最大长度 | 1GB (MaxAllocSize) |
| 内存分配 | 使用 palloc() (后端) 或 malloc() (前端) |
| 字符串类型 | 可存储 C 字符串或二进制数据 |
| 终止符 | data[len] 始终为 '\0' |
| 游标支持 | 内置游标用于顺序读取 |
2. 数据结构详解
2.1 StringInfoData 定义
c
// src/include/lib/stringinfo.h:46-54
typedef struct StringInfoData
{
char *data; // 字符串缓冲区指针
int len; // 当前字符串长度 (不含终止符)
int maxlen; // 已分配缓冲区大小 (含终止符)
int cursor; // 游标位置,用于顺序读取
} StringInfoData;
typedef StringInfoData *StringInfo; // 指针类型别名2.2 内存布局图解
┌─────────────────────────────────────────────────────────────────────┐
│ StringInfoData 内存布局 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ StringInfo str = initStringInfo(); │
│ │
│ 初始状态 (1024 字节): │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ data ──────► ┌──────────────────────────────────────────────┐ │ │
│ │ │ [0] [len] [maxlen-1] │ │ │
│ │ │ '\0' │ │ │
│ │ │ ▲ │ │ │
│ │ │ └─ len = 0, data[0] = '\0' │ │ │
│ │ └──────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ len = 0 maxlen = 1024 │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ 追加 "hello" 后: │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ data ──────► ┌──────────────────────────────────────────────┐ │ │
│ │ │ 'h' 'e' 'l' 'l' 'o' '\0' │ │ │
│ │ │ [0] [1] [2] [3] [4] [5] │ │ │
│ │ │ ▲ │ │ │
│ │ │ └─ len = 5 │ │ │
│ │ └──────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ len = 5 maxlen = 1024 │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ 追加更多数据后需要扩展: │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ data ──────► ┌──────────────────────────────────────────────┐ │ │
│ │ (旧) │ 'h' 'e' 'l' 'l' 'o' ... │ │ │
│ │ └──────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ │ repalloc (可能移动) │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────────────┐ │ │
│ │ │ 'h' 'e' 'l' 'l' 'o' ... '\0' │ │ │
│ │ │ [0] [1] [2] [3] [4] ... [2047] │ │ │
│ │ └──────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ len = 5 maxlen = 2048 (扩展后) │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘2.3 字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
data | char * | 指向字符串缓冲区的指针 |
len | int | 当前字符串长度 (不包括终止符 \0) |
maxlen | int | 缓冲区总大小 (包括终止符空间) |
cursor | int | 游标位置,用于顺序读取操作 |
2.4 重要约束
c
// 始终满足的不变式:
// 1. data != NULL
// 2. maxlen > len (除非是只读 StringInfo)
// 3. data[len] == '\0' (终止符)
// 4. 0 <= len <= maxlen <= MaxAllocSize3. 创建与初始化
3.1 四种创建方式
c
// 方式 1: makeStringInfo()
// 数据缓冲区和结构体都使用 palloc 分配
StringInfo makeStringInfo(void);
// 方式 2: initStringInfo()
// 结构体是栈变量,仅数据缓冲区使用 palloc 分配
void initStringInfo(StringInfo str);
// 方式 3: initReadOnlyStringInfo()
// 只读模式,不复制数据,不对缓冲区负责
void initReadOnlyStringInfo(StringInfo str, char *data, int len);
// 方式 4: initStringInfoFromString()
// 从已存在的 palloc 缓冲区初始化
void initStringInfoFromString(StringInfo str, char *data, int len);3.2 创建示例
c
// 方式 1: makeStringInfo()
// 推荐用于需要返回 StringInfo 的场景
StringInfo
build_query(void)
{
StringInfo result = makeStringInfo();
appendStringInfoString(result, "SELECT * FROM users");
return result; // 调用者负责释放
}
// 方式 2: initStringInfo()
// 推荐用于局部使用
void
process_data(void)
{
StringInfoData buf;
initStringInfo(&buf);
appendStringInfo(&buf, "Hello, %s!", "World");
printf("%s\n", buf.data);
// 栈变量,自动释放
}
// 方式 3: 只读模式
void
parse_buffer(char *input, int len)
{
StringInfoData buf;
initReadOnlyStringInfo(&buf, input, len);
// 只能读取,不能追加或重置
// 不会复制数据,直接使用原缓冲区
// 适用于性能敏感的只读场景
}
// 方式 4: 从现有缓冲区
void
wrap_existing_buffer(char *existing)
{
StringInfoData buf;
int len = strlen(existing);
initStringInfoFromString(&buf, existing, len);
// 可以追加,但会影响原始缓冲区
// 适用于复用已分配内存的场景
}3.3 内部实现
c
// makeStringInfo 实现
StringInfo makeStringInfo(void)
{
StringInfo res;
res = (StringInfo) palloc(sizeof(StringInfoData));
initStringInfo(res);
return res;
}
// initStringInfo 实现
void initStringInfo(StringInfo str)
{
int size = 1024; // 初始缓冲区大小
str->data = (char *) palloc(size);
str->maxlen = size;
resetStringInfo(str); // 设置 len=0, data[0]='\0'
}4. 字符串追加操作
4.1 追加函数一览
| 函数 | 说明 | 性能 |
|---|---|---|
appendStringInfo() | 格式化追加 | 较慢 |
appendStringInfoString() | 追加字符串 | 快 |
appendStringInfoChar() | 追加单个字符 | 快 |
appendStringInfoCharMacro() | 追加单个字符(宏) | 最快 |
appendStringInfoSpaces() | 追加空格 | 快 |
appendBinaryStringInfo() | 追加二进制数据 | 快 |
appendBinaryStringInfoNT() | 追加二进制(无终止符) | 最快 |
4.2 格式化追加
c
// 类似于 sprintf + strcat,但更安全
void appendStringInfo(StringInfo str, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
// 使用示例
StringInfo buf = makeStringInfo();
appendStringInfo(buf, "SELECT %s FROM %s WHERE ",
"\"name\"", "\"users\"");
appendStringInfo(buf, "id = %d", 42);
// 结果: SELECT "name" FROM "users" WHERE id = 424.3 字符串追加
c
// 追加 null 终止的字符串,比 appendStringInfo 更高效
void appendStringInfoString(StringInfo str, const char *s);
// 使用示例
appendStringInfoString(buf, "Hello");
appendStringInfoString(buf, " ");
appendStringInfoString(buf, "World");
// 等价于: appendStringInfo(buf, "%s %s %s", "Hello", "", "World");
// 但性能更好4.4 字符追加
c
// 追加单个字符
void appendStringInfoChar(StringInfo str, char ch);
// 高性能宏版本 (关键路径使用)
#define appendStringInfoCharMacro(str, ch) \
(((str)->len + 1 >= (str)->maxlen) ? \
appendStringInfoChar(str, ch) : \
(void)((str)->data[(str)->len] = (ch), \
(str)->data[++(str)->len] = '\0'))
// 使用示例
for (int i = 0; i < 10; i++)
{
appendStringInfoChar(buf, 'a' + i);
appendStringInfoChar(buf, ',');
}4.5 二进制追加
c
// 追加任意二进制数据 (含终止符)
void appendBinaryStringInfo(StringInfo str,
const void *data, int datalen);
// 追加二进制数据 (不含终止符)
void appendBinaryStringInfoNT(StringInfo str,
const void *data, int datalen);
// 使用示例
// 处理二进制数据
const char binary_data[] = {0x01, 0x02, 0x03, 0x00, 0xFF};
appendBinaryStringInfo(buf, binary_data, sizeof(binary_data));
// 注意: 如果数据可能包含 '\0',使用此函数
// 处理带长度的文本
const char *text = get_string_ptr();
int len = get_string_length();
appendBinaryStringInfo(buf, text, len);4.6 空格追加
c
// 追加指定数量的空格
void appendStringInfoSpaces(StringInfo str, int count);
// 使用示例
appendStringInfoString(buf, "SELECT");
appendStringInfoSpaces(buf, 1); // 1 个空格
appendStringInfoString(buf, "name");
appendStringInfoSpaces(buf, 2); // 2 个空格
appendStringInfoString(buf, "FROM users");
// 结果: "SELECT name FROM users"5. 内存管理与重置
5.1 重置 StringInfo
c
// 清空内容但保留缓冲区
void resetStringInfo(StringInfo str);
// 内部实现
void resetStringInfo(StringInfo str)
{
Assert(str->maxlen != 0); // 不能重置只读 StringInfo
str->data[0] = '\0';
str->len = 0;
str->cursor = 0;
}
// 使用示例
void process_multiple_items(void)
{
StringInfo buf = makeStringInfo();
for (int i = 0; i < 1000; i++)
{
resetStringInfo(buf); // 清空,复用缓冲区
appendStringInfo(buf, "Item %d processed", i);
process_item(buf->data);
}
destroyStringInfo(buf);
}5.2 预扩展缓冲区
c
// 确保缓冲区有足够空间 (提前分配)
void enlargeStringInfo(StringInfo str, int needed);
// 使用示例
void build_large_result(void)
{
StringInfo buf = makeStringInfo();
// 预估需要 100KB,提前分配避免多次扩展
enlargeStringInfo(buf, 100 * 1024);
// 频繁追加操作
for (int i = 0; i < 10000; i++)
{
appendStringInfo(buf, "data item %d\n", i);
}
destroyStringInfo(buf);
}5.3 销毁 StringInfo
c
// 释放 StringInfo 及其缓冲区 (makeStringInfo 创建的)
void destroyStringInfo(StringInfo str);
// 使用示例
StringInfo buf = makeStringInfo();
// ... 使用 buf ...
destroyStringInfo(buf); // 必须调用!
// 如果使用 initStringInfo (栈变量),不需要销毁
void foo(void)
{
StringInfoData buf;
initStringInfo(&buf);
// ... 使用 buf ...
// 函数返回时 buf 自动销毁
}5.4 内存管理注意事项
c
// 重要: 内存上下文行为
/*
* enlargeStringInfo 使用 repalloc(),这意味着:
* - 缓冲区保持在 initStringInfo 调用时的内存上下文中
* - 即使当前上下文改变,缓冲区仍然有效
* - 这对于 PostgreSQL 的内存管理至关重要!
*/
// 正确做法: 在正确的上下文中初始化
void correct_usage(MemoryContext ctx)
{
MemoryContext old = MemoryContextSwitchTo(ctx);
StringInfo buf = makeStringInfo(); // 在 ctx 中分配
// ... 使用 buf ...
MemoryContextSwitchTo(old);
destroyStringInfo(buf); // 在同一个 ctx 中释放
}6. 缓冲区扩展机制
6.1 扩展策略
c
// enlargeStringInfo 实现分析
void enlargeStringInfo(StringInfo str, int needed)
{
int newlen;
// 验证请求
if (needed < 0)
ereport(ERROR, ...); // 负数请求无效
if (needed >= MaxAllocSize - str->len)
ereport(ERROR, ...); // 超出最大限制
needed += str->len + 1; // 总需求空间
// 如果已有空间足够,直接返回
if (needed <= str->maxlen)
return;
// 倍增策略: 2x 扩展
newlen = 2 * str->maxlen;
while (needed > newlen)
newlen = 2 * newlen;
// 限制在最大允许大小
if (newlen > MaxAllocSize)
newlen = MaxAllocSize;
// 重新分配
str->data = (char *) repalloc(str->data, newlen);
str->maxlen = newlen;
}6.2 扩展示例
初始状态: maxlen = 1024, len = 1000
追加 50 字节后:
needed = 50 + 1000 + 1 = 1051
needed <= maxlen (1024)? 否
newlen = 2 * 1024 = 2048
1051 <= 2048? 是,扩展到 2048
追加 1500 字节后:
needed = 1500 + 1050 + 1 = 2551
2551 <= 2048? 否
newlen = 2 * 2048 = 4096
2551 <= 4096? 是,扩展到 40966.3 性能优化建议
c
// 1. 如果知道最终大小,提前扩展
StringInfo buf = makeStringInfo();
enlargeStringInfo(buf, expected_size);
// 2. 批量追加优于多次小追加
// 慢:
for (int i = 0; i < n; i++)
appendStringInfo(buf, "%s", items[i]);
// 快:
appendStringInfo(buf, "%s%s%s...", items[0], items[1], ...);
// 3. 使用最合适的追加函数
appendStringInfo(buf, "%s", str); // 较慢
appendStringInfoString(buf, str); // 快
appendBinaryStringInfo(buf, str, len); // 最快7. 游标操作
7.1 游标概念
StringInfo 内置游标支持,用于顺序读取:
c
typedef struct StringInfoData
{
char *data;
int len;
int maxlen;
int cursor; // 游标位置
} StringInfoData;
// 游标不会自动移动,需要手动管理7.2 游标使用示例
c
// 解析逗号分隔的值
void parse_csv(const char *csv)
{
StringInfoData buf;
initStringInfo(&buf);
appendStringInfoString(&buf, csv);
buf.cursor = 0;
while (buf.cursor < buf.len)
{
// 找到下一个逗号
int start = buf.cursor;
while (buf.cursor < buf.len && buf.data[buf.cursor] != ',')
buf.cursor++;
// 提取字段
int field_len = buf.cursor - start;
char *field = palloc(field_len + 1);
memcpy(field, buf.data + start, field_len);
field[field_len] = '\0';
printf("Field: %s\n", field);
pfree(field);
// 跳过逗号
if (buf.cursor < buf.len && buf.data[buf.cursor] == ',')
buf.cursor++;
}
pfree(buf.data);
}8. 高级使用模式
8.1 构建 SQL 查询
c
StringInfo
build_select_query(const char *table, List *columns,
List *conditions)
{
StringInfo buf = makeStringInfo();
// SELECT
appendStringInfoString(buf, "SELECT ");
// 列名
if (columns == NIL)
appendStringInfoString(buf, "*");
else
{
bool first = true;
ListCell *lc;
foreach (lc, columns)
{
if (!first)
appendStringInfoString(buf, ", ");
appendStringInfoString(buf, (const char *) lfirst(lc));
first = false;
}
}
// FROM
appendStringInfo(buf, " FROM %s", table);
// WHERE
if (conditions != NIL)
{
appendStringInfoString(buf, " WHERE ");
bool first = true;
foreach (lc, conditions)
{
if (!first)
appendStringInfoString(buf, " AND ");
appendStringInfoString(buf, (const char *) lfirst(lc));
first = false;
}
}
return buf;
}
// 使用示例
List *cols = list_make2("id", "name");
List *conds = list_make2("id > 10", "active = true");
StringInfo query = build_select_query("users", cols, conds);
printf("%s\n", query->data);
// 输出: SELECT id, name FROM users WHERE id > 10 AND active = true
destroyStringInfo(query);8.2 JSON 构建
c
void
append_json_pair(StringInfo buf, const char *key, const char *value)
{
appendStringInfoChar(buf, '"');
appendStringInfoString(buf, key);
appendStringInfo(buf, "\": \"%s\"", value);
appendStringInfoChar(buf, '"');
}
StringInfo
build_json_object(List *pairs)
{
StringInfo buf = makeStringInfo();
ListCell *lc;
bool first = true;
appendStringInfoChar(buf, '{');
foreach (lc, pairs)
{
if (!first)
appendStringInfoString(buf, ", ");
append_json_pair(buf, pairs[lc].key, pairs[lc].value);
first = false;
}
appendStringInfoChar(buf, '}');
return buf;
}8.3 SQL 标识符转义
c
// PostgreSQL 标识符需要双引号转义
void
appendQualifiedRelation(StringInfo buf, const char *schema,
const char *table)
{
if (schema != NULL && schema[0] != '\0')
{
appendStringInfoChar(buf, '"');
appendBinaryStringInfo(buf, schema, strlen(schema));
appendStringInfo(buf, "\".\"%s\"", table);
}
else
{
appendStringInfoChar(buf, '"');
appendStringInfoString(buf, table);
appendStringInfoChar(buf, '"');
}
}
// 使用示例
appendQualifiedRelation(buf, "public", "users table");
// 输出: "public"."users table"8.4 缓冲区分块处理
c
// 处理大字符串,分块输出
void
output_in_chunks(StringInfo buf, void (*output_func)(const char *, int))
{
const char *p = buf->data;
int remaining = buf->len;
int chunk_size = 8192;
while (remaining > 0)
{
int to_write = (remaining > chunk_size) ? chunk_size : remaining;
output_func(p, to_write);
p += to_write;
remaining -= to_write;
}
}8.5 字符串累积器
c
// 模拟 StringBuffer 的用法
typedef struct
{
StringInfoData buffer;
int count;
} StringAccum;
void
init_string_accum(StringAccum *acc)
{
initStringInfo(&acc->buffer);
acc->count = 0;
}
void
accum_append(StringAccum *acc, const char *str)
{
if (acc->count > 0)
appendStringInfoChar(&acc->buffer, ',');
appendStringInfoString(&acc->buffer, str);
acc->count++;
}
char *
accum_result(StringAccum *acc)
{
char *result = palloc(acc->buffer.len + 1);
memcpy(result, acc->buffer.data, acc->buffer.len);
result[acc->buffer.len] = '\0';
return result;
}
// 使用示例
StringAccum acc;
init_string_accum(&acc);
accum_append(&acc, "apple");
accum_append(&acc, "banana");
accum_append(&acc, "cherry");
char *result = accum_result(&acc);
// result = "apple,banana,cherry"9. 只读 StringInfo
9.1 只读模式特点
c
// 初始化只读 StringInfo
void initReadOnlyStringInfo(StringInfo str, char *data, int len);
// 只读特性:
// 1. maxlen = 0 (标记只读)
// 2. 不能调用 appendStringInfo*
// 3. 不能调用 resetStringInfo()
// 4. 调用 enlargeStringInfo 会失败9.2 使用场景
c
// 场景 1: 包装外部缓冲区
void
process_external_buffer(char *external, int len)
{
StringInfoData buf;
// 不复制数据,直接使用外部缓冲区
initReadOnlyStringInfo(&buf, external, len);
// 只能读取 len 和 cursor
// 不能追加
printf("Buffer length: %d\n", buf.len);
printf("Content: %.*s\n", buf.len, buf.data);
// 函数返回,外部缓冲区由调用者负责
}
// 场景 2: 性能敏感的数据传递
void
wrap_message_data(const char *msg_data, int msg_len)
{
StringInfoData buf;
initReadOnlyStringInfo(&buf, (char *)msg_data, msg_len);
// 快速包装,无内存复制
parse_message(&buf);
}9.3 只读检测
c
// 通过 maxlen 判断是否只读
bool is_readonly_string_info(StringInfo str)
{
return str->maxlen == 0;
}
// 只读 StringInfo 不能追加
void safe_append(StringInfo str, const char *data)
{
if (str->maxlen == 0)
{
// 只读,不能追加
ereport(ERROR,
errmsg("cannot append to read-only StringInfo"));
}
appendStringInfoString(str, data);
}10. 常见错误与调试
10.1 常见错误
c
// 错误 1: 忘记销毁 makeStringInfo 创建的 StringInfo
StringInfo leak = makeStringInfo();
appendStringInfoString(leak, "leaked");
// 忘记 destroyStringInfo(leak); // 内存泄漏!
// 错误 2: 使用已释放的缓冲区
StringInfo buf = makeStringInfo();
appendStringInfoString(buf, "test");
char *copy = pstrdup(buf->data);
destroyStringInfo(buf);
// printf("%s\n", buf->data); // 使用已释放内存!
// 错误 3: 混合使用 pfree 和 destroyStringInfo
StringInfoData stack_buf;
initStringInfo(&stack_buf);
// pfree(stack_buf.data); // 错误!
// destroyStringInfo(&stack_buf); // 错误!
pfree(stack_buf.data); // 只释放数据缓冲区
// 正确: initStringInfo 的栈变量不需要显式销毁10.2 调试技巧
c
// 1. 打印 StringInfo 状态
void
debug_string_info(StringInfo str, const char *name)
{
elog(DEBUG1, "%s: len=%d, maxlen=%d, cursor=%d, data=\"%s\"",
name, str->len, str->maxlen, str->cursor, str->data);
}
// 2. 断言检查
Assert(str->maxlen > 0); // 不是只读
Assert(str->len < str->maxlen); // 有空间
Assert(str->data[str->len] == '\0'); // 正确终止
// 3. Valgrind 支持
// StringInfo 使用 palloc,Valgrind 可以检测内存问题11. pqexpbuffer 对比
PostgreSQL 还有另一个类似的数据结构 pqexpbuffer(在 src/interfaces/libpq/pqexpbuffer.h):
c
/*
* pqexpbuffer.h 说明:
* This module is essentially the same as the backend's StringInfo data type,
* but is provided for use by frontend code that doesn't link the backend library.
*/| 特性 | StringInfo | pqexpbuffer |
|---|---|---|
| 使用场景 | 后端代码 | 前端代码 |
| 内存分配 | palloc | malloc |
| API | 完全相同 | 完全相同 |
| 头文件 | lib/stringinfo.h | libpq/pqexpbuffer.h |
注意: 两者的 API 完全兼容,可以根据链接需求选择。
12. 源码文件索引
| 文件路径 | 内容描述 |
|---|---|
src/include/lib/stringinfo.h | StringInfo 头文件: 结构、函数声明 |
src/common/stringinfo.c | StringInfo 实现: 所有函数实现 |
src/include/libpq/pqexpbuffer.h | 前端版 StringInfo 头文件 |
src/interfaces/libpq/pqexpbuffer.c | 前端版 StringInfo 实现 |
12.1 函数速查
| 函数 | 用途 |
|---|---|
makeStringInfo() | 创建并初始化 |
initStringInfo() | 初始化栈变量 |
initReadOnlyStringInfo() | 只读初始化 |
initStringInfoFromString() | 从现有缓冲区初始化 |
appendStringInfo() | 格式化追加 |
appendStringInfoString() | 字符串追加 |
appendStringInfoChar() | 字符追加 |
appendStringInfoSpaces() | 空格追加 |
appendBinaryStringInfo() | 二进制追加 |
enlargeStringInfo() | 预扩展缓冲区 |
resetStringInfo() | 重置内容 |
destroyStringInfo() | 销毁 (makeStringInfo 创建的) |
13. 总结
13.1 核心要点
动态字符串缓冲区: 自动扩展,无需手动管理大小
四种初始化方式:
makeStringInfo(): 完整动态分配initStringInfo(): 栈变量 + 动态缓冲区initReadOnlyStringInfo(): 只读包装initStringInfoFromString(): 现有缓冲区
追加函数选择:
appendBinaryStringInfo(): 最通用appendStringInfoString(): 字符串最快appendStringInfo(): 格式化最方便appendStringInfoCharMacro(): 单字符最高效
内存管理:
- 内存保持在初始化时的上下文
destroyStringInfo()释放makeStringInfo()resetStringInfo()清空内容复用缓冲区
扩展策略: 2 倍扩展,避免频繁 realloc
13.2 使用口诀
创建用 make 或 init,
追加用 append 函数,
格式化用 appendStringInfo,
字符串用 appendStringInfoString,
字符用 appendStringInfoChar,
重置用 resetStringInfo,
销毁用 destroyStringInfo。13.3 性能提示
- 如果知道大小,提前
enlargeStringInfo() - 批量追加优于多次小追加
- 避免在循环中使用
makeStringInfo(),改用resetStringInfo() - 单字符追加用宏版本
appendStringInfoCharMacro()
本文档基于 PostgreSQL 源码 (v17) 分析编写,详细阐述了 PostgreSQL StringInfoData 数据结构的设计、实现和使用方法。