ProxySQL DuckDB 插件协议兼容性指南:MySQL / PostgreSQL 前端协议下的能力边界与实测要点
发布时间:2026/10/8 14:11:32 作者:尧图编辑部 阅读量:1,286

后端数据库负载均衡【免费下载链接】proxysqlHigh-performance proxy for MySQL and PostgreSQL项目地址https://gitcode.com/gh_mirrors/pr/proxysql点击查看免费下载本指南以 doc/duckdb/protocol-compatibility.md 为核心系统梳理 ProxySQL v4.0 DuckDB 插件在 MySQL 与 PostgreSQL 两种前端协议下的支持矩阵、调用约束、兼容查询拦截、结果类型转换与错误码映射并结合plugins/duckdb/源码逐一印证实现原理。读完本文你将清楚哪些客户端 API 可以安全接入、哪些必须规避以及复杂 DuckDB 类型如何在纯文本协议下正确返回。定位传输层复用而非数据库实现DuckDB 插件使用 MySQL 与 PostgreSQL 的前端协议作为 DuckDB SQL 的传输通道见 plugins/duckdb/README.md 中对两个独立监听器的描述以及 index.md 中的架构说明它不是MySQL 或 PostgreSQL 服务器的实现它不追求完整的方言或 wire 层特性兼容查询认证完成后直接进入嵌入式 DuckDB 引擎不经过任何后端路由。因此用mysql、psql等既有客户端连接只是借用了它们熟悉的协议外壳SQL 语义始终是 DuckDB 的。默认监听端口MySQL 协议6031认证走mysql_users、PostgreSQL 协议6034认证走pgsql_users与 Admin 端口6032、常规 MySQL 代理端口6033相互独立。支持矩阵两种协议能力全览下表完整对应原文档的支持矩阵列明 MySQL 端点与 PostgreSQL 端点在各项能力上的差异能力项MySQL 端点PostgreSQL 端点认证mysql_userspgsql_users简单文本查询Simple query支持支持客户端预编译语句Client prepared statements不支持不支持扩展查询协议Extended-query protocol不适用拒绝SQLSTATE0A000单请求多条语句拒绝拒绝事务状态协议 OK / 状态行为ReadyForQuery的I/T/E结果元数据所有列均为字符串所有列均为TEXTOID两个端点同库不同协议插件在启动时打开一个duckdb_database每个连接线程各自持有自己的duckdb_connection见 duckdb_session.h 中DuckDBSessionState的注释所有会话看到的是同一份数据——默认:memory:时存活到 ProxySQL 停止配置duckdb-database_path后可落盘持久化。简单查询是唯一入口为什么不能用预编译 API插件明确要求客户端使用非预编译的 API这是两条协议共用的硬约束。PostgreSQL扩展查询被拒绝并正确恢复PostgreSQL 驱动若走常规扩展流程Parse、Bind、Describe、Execute会收到一个Feature not supported错误。随后插件按 PostgreSQL 错误再同步error-resynchronization规则处理丢弃前端消息直到遇到Sync发送一个ReadyForQuery恢复正常输入处理。这一逻辑在 duckdb_session.cpp 的duckdb_pgsql_message_action()中实现处于扩展错误状态时非SSync类型一律discardS触发send_ready而P/B/C/D/EParse/Bind/Close/Describe/Execute任一消息首次到达即进入扩展错误态并回复 SQLSTATE0A000。会话处理器duckdb_session_handler中同时将PG_PKT_STARTUP、PG_PKT_CANCEL、PG_PKT_SSLREQ等消息归类为Unsupported query type统一以0A000拒绝。也就是说插件不假装预编译执行可用而是诚实地拒绝并复位到文本查询状态。MySQL只用 COM_QUERY 系 APIMySQL 端点只响应COM_QUERY文本查询COM_STMT_PREPARE与COM_STMT_EXECUTE不在插件初始能力面内。处理器对 MySQL 报文先跳过 4 字节包头与 1 字节命令字节见duckdb_session_handler的constexpr分支随后直接取出 SQL 文本。单请求单语句为何必须逐条发送插件在执行前先 prepare每条请求而 DuckDB 会拒绝在单次 prepare 中携带多条语句。这是有意设计早期的直接执行路径虽能跑完所有语句但只向客户端暴露最后一条的结果集使得前面的副作用对客户端完全不可见。因此必须每条语句单独发送-- 第一次请求 CREATE TABLE t(i INTEGER); -- 第二次请求 INSERT INTO t VALUES (1);纯注释请求同样失败——因为其中没有可 prepare 的语句。源码层面duckdb_execute_effective()对每条语句统一走duckdb_prepare()→duckdb_execute_prepared()的恰好一次执行路径见 duckdb_session.cppDDL/DML/SELECT 的结果分发依据duckdb_result_return_type()判定DUCKDB_RESULT_TYPE_NOTHINGCREATE TABLE、SET 等返回 OKDUCKDB_RESULT_TYPE_CHANGED_ROWSINSERT/UPDATE/DELETE返回受影响行数DUCKDB_RESULT_TYPE_QUERY_RESULTSELECT返回结果集。兼容查询常用发现命令的拦截与改写下列常见发现命令会被插件拦截或改写避免把客户端生态里几乎必然出现的初始化查询直接丢给 DuckDB 报错SELECT versionSELECT VERSION()SELECT DATABASE()SELECT CURRENT_DATABASE()SHOW TABLESSHOW DATABASESSHOW SCHEMASSET autocommit0与SET autocommit1单条SET NAMES ...命令匹配规则见 duckdb_session.cpp 的duckdb_classify_query()为大小写不敏感、容忍常规空白、容忍结尾语句终止符但仅凭前缀不匹配——更长的标识符如SELECT version_comment或携带第二条语句的报文不会被误拦。duckdb_build_intercept_result()对版本与数据库类查询直接构造内建结果集版本取自duckdb_library_version()库名默认渲染为memoryNULL、空串与:memory:均归一化为此值。三类 SHOW 命令的处理策略是改写为 DuckDB SQL 返回实时数据而非固定行见 duckdb_session.cppSHOW TABLES→SELECT table_name FROM information_schema.tables WHERE table_schemamainSHOW DATABASES→SELECT database_name AS Database FROM duckdb_databases() WHERE NOT internal ORDER BY database_nameSHOW SCHEMAS→SELECT DISTINCT schema_name AS Schema FROM information_schema.schemata ORDER BY schema_name其余语句包括 DuckDB 原生的SET命令照常进入 DuckDB。注意两条边界客户端可能自动发送会话初始化 SQL只有上面这份窄清单保证被吸收或翻译SHOW DATABASES返回的是 DuckDB catalog与 MySQL 的数据库概念并不等同。SQL 方言连接协议≠SQL 方言使用 DuckDB SQL。即使连接走的是 MySQL 或 PostgreSQL 协议MySQL/PostgreSQL 特有的语法依然可能失败。对客户端库自动发出的会话初始化语句仅上文兼容清单内的内容会被吸收或翻译。因此在接入前应审查驱动默认执行的初始化 SQL 是否超出该清单。结果元数据与类型转换全文本列与安全改写全文本列两个协议序列化器都把每一列标记为文本MySQL 端为MYSQL_TYPE_VAR_STRINGPostgreSQL 端为TEXTOID见 duckdb_result.h 的转换契约注释。依赖数值、时间戳、数组或二进制类型元数据的应用要么自行解析返回文本要么等待未来的类型化结果实现。直接转换白名单直接转换路径支持常见标量布尔、有符号/无符号整数、浮点、双精度、日期、时间、时间戳、DECIMAL、INTERVAL、VARCHAR 与 BLOB。该白名单与 DuckDB 1.4.5 C APIGetInternalCValue的类型 switch逐项对应共 20 个类型BOOLEAN、TINYINT、SMALLINT、INTEGER、BIGINT、UTINYINT、USMALLINT、UINTEGER、UBIGINT、FLOAT、DOUBLE、DATE、TIME、TIMESTAMP、HUGEINT、UHUGEINT、DECIMAL、INTERVAL、VARCHAR、BLOB经实测确证例如DECIMAL(10,2)渲染为1.50、INTERVAL 3 DAY渲染为3 days。复杂类型的 VARCHAR 包装改写其他 DuckDB 类型会在执行前通过预编译语句的列类型被探测出来。一旦发现存在白名单之外的列插件尝试执行等价于下式的包装查询SELECT COLUMNS(*)::VARCHAR FROM (original query)这通常能把LIST、STRUCT、MAP、ARRAY、UNION、UUID、ENUM、BIT 及各类专门时间戳变体渲染为可读文本。关键设计源码注释C3决策在于决策发生在执行前——通过duckdb_prepared_statement_column_type()与duckdb_type_renders_as_text()duckdb_result.h 中的白名单谓词检查预编译语句的列类型而 prepare 只解析绑定、不执行因此易变表达式与副作用恰好只执行一次。历史上曾用词法门控做二次执行安全判断但SELECT [nextval(s)]这类查询会推进序列并返回 LIST词法判断无法区分只读外观与真实副作用现已彻底移除该门控改为结构性的一次 prepare 一次执行。实现细节同样值得注意duckdb_session.cpp包装查询以换行包裹子查询FROM (\nsql\n)而非直接拼接避免尾部-- 注释吞掉右括号先剥离尾部;几乎所有 CLI 客户端都会附带否则;在子查询内本身就是解析错误。RETURNING 的降级路径部分 DMLRETURNING语句无法放入上述包装DuckDB 语法不允许其在 FROM 子句子查询中出现。此时回退路径原语句只执行一次但不受支持的结果列可能以 NULL 返回。而 SQL NULL 本身在其他场景仍被如实保留为协议真 NULL——源码注释特别警告转换后的 NULL 字段本身是歧义的可能表示真实 SQL NULL或类型不在白名单调用方需在执行前用duckdb_result_has_unrenderable_column()加以区分。DML 的事务化结果物化对INSERT/UPDATE/DELETE ... RETURNING插件会在结果安全物化前开启内部事务autocommit 场景下BEGIN TRANSACTION物化成功再COMMIT失败则ROLLBACK避免变更已提交、报错却迟到的窗口见duckdb_execute_effective()中的owns_transaction逻辑。错误处理错误包与 SQLSTATE 映射MySQL 客户端收到 MySQL 错误包PostgreSQL 客户端收到ErrorResponse并携带映射过的 SQLSTATE。映射依据 DuckDB 暴露的无歧义错误类别见 duckdb_session.cpp覆盖语法 / 解析PG42601MySQL1064 / 42000数值越界、DECIMALPG22003MySQL1264 / 22003转换PG22018MySQL1366 / 22018除零PG22012MySQL1365 / 22012事务PG25000MySQL1180 / 25000未实现 / 缺扩展 / 自动加载PG0A000MySQL1235 / 0A000约束PG23000MySQL 依消息细分为1048NOT NULL/3819CHECK/1451外键被引用/1452外键/1062重复键、唯一约束否则1105连接 / 网络PG08000/08006MySQL2013 / 08S01I/OPG58030MySQL1028取消interruptPG57014MySQL1317 / 70100内存不足PG53200MySQL1037 / HY001权限PG42501MySQL1142 / 42000无效参数 / 设置类PG22023MySQL1525 / HY000未分类的 DuckDB 错误使用XX000PG与1105 / HY000MySQL而非一律标记为语法错误解析/语法类错误还保留了对duckdb_prepare_error()消息前缀Parser Error:、Syntax Error:的兜底识别。此外PostgreSQL 错误发射器duckdb_send_pgsql_error_response刻意不复用核心SQLite3_to_Postgres的错误分支——后者硬编码28000对语法错误/畸形报文/连接错误并不适用。事务BEGIN / COMMIT / ROLLBACK 与 ReadyForQuery 状态文本BEGIN、COMMIT、ROLLBACK命令直接在 DuckDB 中执行。PostgreSQL 的ReadyForQuery事务状态字节由duckdb_pgsql_transaction_status()返回跟踪在会话状态而非 DuckDB C 内部报告I—— 空闲 / 无活动事务T—— 活动有效事务E—— 活动但已失效invalidated事务状态迁移逻辑在apply_txn_outcome()BEGIN成功置TCOMMIT/ROLLBACK成功置I处于T时任何失败置E。事务动词识别classify_txn_verb同样容忍大小写与空白且能区分ROLLBACK TOsavepoint 回退不改变状态机与普通ROLLBACK并接受START TRANSACTION、END、ABORT等别名。会话初始化与 SET 策略引擎级配置的守护虽然这条不属于协议兼容清单的拦截项但直接影响客户端可用性插件对指向引擎级设置memory_limit、threads、enable_external_access、access_mode的客户端SET会拒绝并指引用户改用duckdb-*Admin 变量见duckdb_execute_managed_set()issue #6320 的安全考量。无作用域SET name会被改写为显式SET SESSION后再 prepareSET GLOBAL、配置类 PRAGMA如PRAGMA threads64以及EXPLAIN ... SET/RESET/PRAGMA一律拒绝EXPLAIN ANALYZE会真实执行被包裹语句故同样拦截。普通SET SESSION/RESET SESSION则限定在会话作用域内正常执行。这意味着连接池或 ORM 若习惯发送全局级 SET需要调整为会话级写法。当前明确缺失项能力边界清单原文档完整列出以下未实现能力接入前应据此评估无客户端可见的预编译语句无查询超时或duckdb_interrupt()策略——失控的 DuckDB 查询无法被中断无类型化结果元数据——所有列始终为文本无通用的 MySQL / PostgreSQL 方言翻译连接上限拒绝新接入 socket 时无结构化协议错误DuckDBEngine的连接计数拒绝发生在协议层之外客户端得不到规范的协议级错误报文。结合 index.md 的初始限制还可补充任一端点认证用户都共享同一 DuckDB 数据库没有按表的独立 DuckDB 授权层外部文件系统访问默认关闭除非信任所有端点用户拥有 ProxySQL 进程的文件系统权限否则应保持关闭。实用接入清单把上述规则落成可执行步骤选择驱动 APIMySQL 端用COM_QUERY系mysqlCLI、JDBC 默认文本路径等PostgreSQL 端务必使用简单查询Query消息关闭/避开扩展查询路径如 JDBCpreferQueryModesimple、libpq 的PQexec而非PQexecParams。逐条发语句避免多语句打包、避免纯注释请求。收敛初始化 SQL确认驱动自动发送的语句落在兼容清单内若依赖全局 SET改用SET SESSION或duckdb-*Admin 变量。文本化消费结果所有列均为文本数值/时间等需应用侧解析复杂类型LIST/STRUCT/MAP 等依赖自动 VARCHAR 包装注意RETURNING场景可能的 NULL 降级。按协议接错误依据上文的 SQLSTATE / errno 映射在应用侧做错误分类未分类错误以XX000/1105兜底。事务语义对齐PG 客户端依据ReadyForQuery的I/T/E判断事务状态MySQL 客户端依赖 OK 包。更完整的接入背景可继续阅读同一目录下的 quickstart.md五分钟上手、user-guide.md、configuration-reference.mdduckdb-database_path等引擎开关变量与 troubleshooting.md源码细节可对照 duckdb_session.cpp、duckdb_result.h 及 duckdb_listener.h。赞分享后端数据库负载均衡【免费下载链接】proxysqlHigh-performance proxy for MySQL and PostgreSQL项目地址https://gitcode.com/gh_mirrors/pr/proxysql点击查看免费下载相关推荐ProxySQL DuckDB 插件在 MySQL/PostgreSQL 协议下嵌入运行 DuckDB 分析引擎的完整指南ProxySQL DuckDB 插件在 MySQL/PostgreSQL 协议下嵌入运行 DuckDB 分析引擎的完整指南 本指南围绕 doc/duckdb/后端数据库负载均衡StarRocks group_concat 聚合函数完全指南语法、排序去重与底层实现解析StarRocks group_concat 聚合函数完全指南语法、排序去重与底层实现解析 GROUP_CONCAT 是 StarRocks 中把分组内多行非后端数据库负载均衡终极指南如何实现Vitess与MySQL协议的完全兼容性终极指南如何实现Vitess与MySQL协议的完全兼容性 Vitess是一个强大的数据库集群解决方案它能够帮助用户实现MySQL的水平扩展同时保持与MyS数据库分布式数据库云原生后端数据存储上一篇完整指南Navicat无限重置试用期脚本的3种高效方法下一篇Valetudo FAQ 深度解读离线优先的扫地机器人云替代方案其设计哲学与技术边界创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考