如何在OCaml中使用Caqti序列化运行时PostgreSQL查询?
问题场景
- 接收用户提交的字符串形式SQL查询,执行后返回JSON格式结果
- 使用Caqti库(支持类型安全查询),但查询类型依赖具体查询内容,而查询内容在编译时未知
- 简化处理:假设所有列均为字符串,需要一个类型为
string -> string list list的函数——输入字符串查询,返回string list list结果(string list代表单行数据,string list list代表多行数据,因无法预知返回列数)
示例说明
我做了一个需要dune构建的示例,运行步骤:
- 用
dune init project foo初始化项目 - 将示例的.ml文件替换生成的
bin/main.ml - 用示例的dune文件替换
bin/dune文件
核心问题
我需要获取用户输入的SQL查询字符串:
let query: string = Sys.argv.(1) in (* 这是用户查询 *)
然后执行查询并得到string list list类型结果,期望的函数签名是:
val do_query (query : string) : (string list list, string) Lwt_result.t
Caqti要求必须声明查询类型,比如针对todos表(列定义(id int, content text)),查询SELECT id, content FROM todos的类型声明是:
Caqti_type.(unit ->* (tup2 int string)) "SELECT id, content FROM todos"
但用户输入的查询内容未知,我不知道在Caqti_type.( ??? ) user_query中该填写什么类型,示例中留下的函数:
let user_query q = Caqti_type.(unit ->* unit (* ??? 这里该填什么? *)) q
其中q是用户输入的查询字符串,替换第二个unit的类型就是当前的核心问题。
解决方案
我通过to_jsonb实现了需求,将user_query定义为:
let user_query q : (unit, string, [ `Many | `One | `Zero ]) Caqti_request.t = let cte = Format.sprintf {| WITH q AS (%s) SELECT to_jsonb(q.*)::text FROM q |} q in Caqti_type.(unit ->* string) cte
该方案将每一行结果输出为JSON格式,还支持逐行返回结果,无需将所有数据加载到内存中。
内容的提问来源于stack exchange,提问作者geckos
相关产品推荐
相关产品推荐

