数据库实战
四个数据库客户端,全是纯 PuXian 实现、不依赖任何 C 驱动:mysql(文本+预处理协议)、pg(v3 wire,含 SCRAM)、mongodb(OP_MSG)、redis(RESP)。本页给连接方式与三条最容易踩的坑。
MySQL
import mysql def main(): var c = mysql_connect("127.0.0.1", 3306, "app", "密码", "appdb") if c.is_err(): print("连不上:" + str(c.err())) return var conn = c.ok() var r = mysql_query(conn, "SELECT id, name FROM users LIMIT 3") if r.is_ok(): for row in r.ok()["rows"]: print(row[0] + " " + row[1]) # 带参数的查询:用预处理,不要拼字符串 var one = mysql_query_params(conn, "SELECT name FROM users WHERE id = ?", [1001]) print(one.ok()["rows"]) mysql_close(conn)
| 函数 | 说明 |
|---|---|
mysql_connect(host, port, user, password, dbname, opts) | → Ok(conn) / Err |
mysql_query(conn, sql) | 文本协议,一次性简单查询 |
mysql_query_params(conn, sql, [..]) | 预处理绑定,防注入用这个 |
mysql_begin / mysql_commit / mysql_rollback | 事务 |
mysql_select_db / mysql_ping / mysql_close | 切库、探活、关闭 |
坑 1:mysql_escape(s) 只收一个参数
写成 mysql_escape(conn, s) 会报参数不匹配。要防注入,正确做法不是 escape,而是 mysql_query_params——把值交给协议绑定,别自己拼 SQL。
坑 2:连接里 NOW() 是 UTC
DSN 带 time_zone='+00:00' 时,连接内的 NOW() 返回 UTC,而表字段 DEFAULT CURRENT_TIMESTAMP 用的是服务器时区(东八区)。表现是「刚插进去的行,时间显示在未来 8 小时」或「队列永远取不到任务,且不报错」。
SET time_zone='+08:00',要么所有时间列显式写 UTC_TIMESTAMP(),别让「连接时区」和「表默认值时区」各说各话。坑 3:首次连库在解释轨要 0.4 秒
实测:tcp_connect 0 ms、SELECT 1 25 ms、SET time_zone 8 ms,剩下约 420–450 ms 全是纯解释开销(协议帧的字节级拼装)。每次请求新建连接的接口,在解释轨上首包就慢半秒——编译轨或连接复用能消掉。
PostgreSQL
import pg def main(): var c = pg_connect("127.0.0.1", 5432, "app", "密码", "appdb", {"tls": false}) var conn = c.ok() var r = pg_query_params(conn, "SELECT id, name FROM users WHERE id = $1", [1001]) print(r.ok()["columns"]) # [{name: id, type_oid: 23}, …] print(r.ok()["rows"]) # [["1001", "张三"]] ← 文本格式,类型自己转 pg_close(conn)
认证支持 md5 / SCRAM-SHA-256 / cleartext / trust;结果统一是文本格式(NULL → null),所以数字也是字符串,要自己 int()。
MongoDB
import mongodb def main(): var c = mg_connect("127.0.0.1", 27017, 3000) var conn = c.ok() print(mg_ping(conn)) mg_insert(conn, "appdb", "users", {"name": "张三", "age": 28}) print(mg_find(conn, "appdb", "users", {"age": {"$gt": 18}}, 10)) print(mg_count(conn, "appdb", "users", {"age": {"$gt": 18}})) mg_close(conn)
函数前缀是 mg_,参数依次带 db 与 coll(Mongo 一个连接可以跨库)。OP_MSG 协议子集:ping、insert、find、count、update_one/many、delete_one/many、distinct、create_index、aggregate、drop。find 的 limit 是位置参数,不是 opts 字典。
Redis
连接与全套命令见 Redis 专页:redis_connect(host, port, opts),SET 返回 Ok(true),mset 收 [[k,v],…]。这三条是初学最容易写错的地方。
选型建议
| 场景 | 包 | 理由 |
|---|---|---|
| 业务主库、事务 | mysql / pg | 预处理绑定 + 事务齐全 |
| 缓存、限流、会话 | redis | 单命令原子;SET NX EX 一把梭 |
| 文档型、无固定结构 | mongodb | 免迁移,但弱事务 |