pgmoon

一个用纯 Lua(MoonScript)编写的 PostgreSQL 客户端库

$ opm get GoCodeIT-Inc/pgmoon

pgmoon

!测试

> 注意:您是否从旧版本的 OpenResty 升级?由于 Lua 模式兼容性的更改,您必须更新到 pgmoon 1.12 或更高版本,以避免查询返回受影响行时出现错误的结果。

pgmoon 是一个用纯 Lua(MoonScript)编写的 PostgreSQL 客户端库。

pgmoon 最初是为在 [OpenResty][] 中使用而设计的,利用 [cosocket api](https://github.com/openresty/lua-nginx-module#ngxsockettcp) 提供异步查询,但它也可以在 [LuaSocket][] 或 [cqueues][] 可用的任何常规 Lua 环境中工作。

它是运行查询的完美选择,无论是在 OpenResty 的环境中,还是在命令行(例如测试)中,以及在像 [Lapis][] 这样的 Web 框架中。

安装

    $ luarocks install pgmoon

<details> <summary>使用 <a href="https://opm.openresty.org.cn/">OpenResty 的 OPM</a></summary>

    $ opm get leafo/pgmoon

</details>

依赖

pgmoon 支持各种环境和库,因此可能需要安装其他依赖项,具体取决于您打算如何与数据库通信。

> 提示:如果您使用的是 OpenResty,则不需要其他依赖项(通常,某些身份验证方法可能需要加密库)。

使用 pgmoon **需要** 套接字实现,根据环境您可以选择其中一个。

  • [OpenResty][] — 使用内置套接字,无需其他依赖项。

  • [LuaSocket][] — luarocks install luasocket

  • [cqueues][] — luarocks install cqueues

如果您使用的是 PUC Lua 5.1 或 5.2,则需要一个 bit 库(LuaJIT 不需要)。

    $ luarocks install luabitop

如果您想使用 JSON 类型,则需要 lua-cjson。

    $ luarocks install lua-cjson

SSL 连接可能需要其他依赖项。

  • OpenResty — 无需其他依赖项。

  • LuaSocket — luarocks install luasec

  • cqueues — luarocks install luaossl

密码身份验证可能需要加密库,例如 [luaossl][]。

    $ luarocks install luaossl

> 注意:[LuaCrypto][] 可以用作备选方案,但该库已被废弃,不建议使用。

> 注意:在 [OpenResty][] 中使用时,如果可能,将优先使用内置函数。

示例

    local pgmoon = require("pgmoon")
    local pg = pgmoon.new({
      host = "127.0.0.1",
      port = "5432",
      database = "mydb",
      user = "postgres"
    })
    
    assert(pg:connect())
    
    local res = assert(pg:query("select * from users where username = " ..
      pg:escape_literal("leafo")))

如果您使用的是 OpenResty,则在完成连接后,您可以将套接字释放到连接池,以便在将来的请求中重用。

    pg:keepalive()

参考

require("pgmoon") 返回的表中的函数

new(options={})

从配置对象创建一个新的 Postgres 对象。除非另有说明,否则所有字段都是可选的。新创建的对象不会自动连接,您必须在创建对象后调用 conect

可用选项

  • "database":要连接到的数据库名称 **必需**

  • "host":要连接到的主机(默认:"127.0.0.1"

  • "port":要连接到的端口(默认:"5432"

  • "user":用于身份验证的数据库用户名(默认:"postgres"

  • "password":身份验证密码,根据服务器配置可能需要。

  • "ssl":启用 ssl(默认:false

  • "ssl_verify":验证服务器证书(默认:nil

  • "ssl_required":如果服务器不支持 SSL 连接,则中止连接(默认:nil

  • "socket_type":要使用的套接字类型,可以是:"nginx""luasocket"cqueues(默认:如果在 nginx 中则为 "nginx",否则为 "luasocket"

  • "application_name":设置在 pg_stat_activity 中显示的连接名称。(默认:"pgmoon"

  • "pool":(仅限 OpenResty)使用 OpenResty cosocket 时要使用的池名称(默认:"#{host}:#{port}:#{database}"

  • "pool_size":(仅限 OpenResty)直接传递给 OpenResty cosocket connect 函数,请参阅文档

  • "backlog":(仅限 OpenResty)直接传递给 OpenResty cosocket connect 函数,请参阅文档

  • "cqueues_openssl_context":在创建 cqueues SSL 连接时要使用的手动创建的 opensssl.ssl.context

  • "luasec_opts":在使用 LuaSec SSL 连接时要使用的手动创建的选项对象。

new 返回的 Postgres 对象上的方法

success, err = postgres:connect()

使用在调用 new 时指定的凭据连接到 Postgres 服务器。成功时返回 true,失败时返回 nil 和错误消息。

postgres:settimeout(time)

设置所有后续套接字操作(连接、写入、接收)的超时值(以毫秒为单位)。此函数没有任何返回值。

success, err = postgres:disconnect()

如果套接字已打开,则关闭与服务器的套接字。在此之后,不应在对象上调用其他方法,除了另一个连接调用。

success, err = postgres:keepalive(...)

通过 setkeepalive 方法将套接字释放到 OpenResty 套接字池。此处传递的任何参数也会传递到 setkeepalive

result, num_queries = postgres:query(query_string)

result, err, partial, num_queries = postgres:query(query_string)

将查询发送到服务器。失败时返回 nil 和错误消息。

成功时,根据发送的查询类型返回结果。

SELECT 查询、带有 returningINSERT 或任何其他返回结果集的查询将返回结果的数组表。每个结果都是一个哈希表,其中键是列的名称,值是该结果行对应列的结果。

    local res = pg:query("select id, name from users")

可能返回

    {
      {
        id = 123,
        name = "Leafo"
      },
      {
        id = 234,
        name = "Lee"
      }
    }

任何影响行的查询,如 UPDATEDELETEINSERT,都会返回一个表结果,其中 affected_rows 字段设置为受影响的行数。

    local res = pg:query("delete from users")

可能返回

    {
      affected_rows = 2
    }

任何没有结果集或更新行的查询都将返回 true

此方法还支持通过用 ; 分隔来一次发送多个查询。执行的查询数作为第二个返回值在结果对象之后返回。当执行多个查询时,结果对象会略有变化。它会变成一个数组表,包含所有单独的结果。

    local res, num_queries = pg:query([[
      select id, name from users;
      select id, title from posts
    ]])

可能返回

    num_queries = 2
    
    res = {
      {
        {
          id = 123,
          name = "Leafo"
        },
        {
          id = 234,
          name = "Lee"
        }
      },
      {
        {
          id = 546,
          title = "My first post"
        }
      }
    }

类似地,对于返回受影响行或仅返回 true 的查询,当有多个查询时,它们将被包装在一个额外的数组表中。您还可以根据需要混合不同的查询类型。

由于 Postgres 一次执行每个查询,因此较早的查询可能会成功,而后续的查询可能会失败。如果在多个查询中出现故障,则错误消息后将返回部分结果和已执行的部分查询数。

escaped = postgres:escape_literal(val)

转义 Lua 值以用作 Postgres 值,并插入到查询字符串中。当将用户提供的数据发送到查询时,您应该使用此方法来防止 SQL 注入攻击。

escaped = postgres:escape_identifier(val)

转义 Lua 值以用作 Postgres 标识符。这包括表名或列名等。这并不包括常规值,您应该为此使用 escape_literal。当名称与内置语言关键字冲突时,需要标识符转义。

str = tostring(postgres)

返回 Postgres 对象当前状态的字符串表示形式。

SSL 连接

pgmoon 可以建立到 Postgres 服务器的 SSL 连接。如果服务器不支持 SSL,它也可以拒绝连接到它。就像 pgmoon 在 OpenResty 之外使用时依赖 LuaSocket 一样,它在这样的上下文中依赖 luaossl/LuaSec 进行 SSL 连接。

    local pgmoon = require("pgmoon")
    local pg = pgmoon.new({
      host = "127.0.0.1",
      ssl = true, -- enable SSL
      ssl_verify = true, -- verify server certificate
      ssl_required = true, -- abort if the server does not support SSL connections
      ssl_version = "tlsv1_2", -- e.g., defaults to highest available, no less than TLS v1.1 (LuaSec only)
      cafile = "...", -- certificate authority (LuaSec only)
      cert = "...", -- client certificate (LuaSec only)
      key = "...", -- client key (LuaSec only)
    })
    
    assert(pg:connect())

> 注意:在 Postgres 12 及更高版本中,客户端连接接受的最小 SSL 版本为 1.2。当使用 LuaSocket + LuaSec 连接到 SSL 服务器时,如果您未指定 ssl_version,则将使用 tlsv1_2

在 OpenResty 中,如果您希望验证服务器证书,请确保配置 lua_ssl_trusted_certificate 指令。

身份验证类型

Postgres 有几种身份验证类型。pgmoon 目前支持 Trust 和 MD5 身份验证。

类型转换

Postgres 内置了一套非常丰富的类型。pgmoon 会尽力将任何 Postgres 类型转换为相应的 Lua 类型。

所有整数、浮点数和数字类型都转换为 Lua 的 number 类型。布尔类型转换为 Lua 布尔值。JSON 类型使用 Lua CJSON 解码为 Lua 表。Lua 表可以像下面描述的那样编码为 JSON。

任何数组类型都会自动转换为 Lua 数组表。如果您需要将 Lua 中的数组编码为 Postgres 的数组语法,则可以使用 pgmoon.arrays 模块。请参阅下文。

任何其他类型都将作为 Lua 字符串返回。

处理数组

从查询返回时,数组会自动解码。数字、字符串和布尔类型会相应地自动加载。嵌套数组也受支持。

使用 encode_array 将 Lua 表编码为查询的数组语法。

    local pgmoon = require("pgmoon")
    local pg = pgmoon.new(auth)
    pg:connect()
    
    local encode_array = require("pgmoon.arrays").encode_array
    local my_array = {1,2,3,4,5}
    pg:query("insert into some_table (some_arr_col) values(" .. encode_array(my_array) .. ")")

空数组

尝试编码空数组时,将抛出错误。Postgres 在使用数组时需要一个类型。当数组中有值时,Postgres 可以推断类型,但当数组中没有值时,无法推断类型。这在 Postgres 提供的错误中有所说明。

    postgres=# select ARRAY[];
    ERROR:  cannot determine type of empty array
    LINE 1: select ARRAY[];
                   ^
    HINT:  Explicitly cast to the desired type, for example ARRAY[]::integer[].

处理 JSON

从查询返回时,jsonjsonb 类型会自动解码。

使用 encode_json 将 Lua 表编码为查询的 JSON 语法。

    local pgmoon = require("pgmoon")
    local pg = pgmoon.new(auth)
    pg:connect()
    
    local encode_json = require("pgmoon.json").encode_json
    local my_tbl = {hello = "world"}
    pg:query("insert into some_table (some_json_col) values(" .. encode_json(my_tbl) .. ")")

处理 hstore

由于 hstore 是扩展类型,因此需要一个查询才能在 pgmoon 自动解码之前找到类型 ID。连接后,在连接对象上调用 setup_hstore 方法进行设置。

    local pgmoon = require("pgmoon")
    local pg = pgmoon.new(auth)
    pg:connect()
    pg:setup_hstore()

使用 encode_hstore 将 Lua 表编码为 hstore 语法,用于更新和插入。

    local encode_hstore = require("pgmoon.hstore").encode_hstore
    local tbl = {foo = "bar"}
    pg:query("insert into some_table (hstore_col) values(" .. encode_hstore(tbl) .. ")")

您可以使用 decode_hstore 函数手动从字符串解码 hstore 值。仅当您没有调用 setup_hstore 时才需要这样做。

    local decode_hstore = require("pgmoon.hstore").decode_hstore
    local res = pg:query("select * from some_table")
    local hstore_tbl = decode_hstore(res[1].hstore_col)

转换 NULL

默认情况下,Postgres 中的 NULL 值转换为 nil,这意味着它们在结果表中不可见。如果您想将 NULL 值转换为某个可见值,请在 Postgres 对象上将 convert_null 设置为 true

    local pgmoon = require("pgmoon")
    local pg = pgmoon.new(auth)
    pg:connect()
    
    pg.convert_null = true
    local res = pg:query("select NULL the_null")
    
    assert(pg.NULL == res[1].the_null)

如上所示,NULL 值设置为 pg.NULL。您可以更改此值以使 pgmoon 使用其他内容作为 NULL。例如,如果您使用的是 OpenResty,您可能希望重用 ngx.null

联系方式

作者:Leaf Corcoran (leafo) (@moonscript) 邮箱:leafot@gmail.com 主页:<http://leafo.net>

更新日志

  • 1.13.0 — 2021-10-13 - 添加对 scram_sha_256_auth (@murillopaula) 的支持,在使用 ngx.socket 时添加 'backlog' 和 'pool_size' 选项 (@xiaocang),更新 LuaSec ssl_protocol 默认选项 (@jeremymv2),application_name 选项 (@mecampbellsoup)

  • 1.12.0 — 2021-01-06 - Lua 模式兼容性修复,支持 Lua 5.1 到 5.4 (@jprjr)。修复 SSL 版本未传递的错误。在使用 LuaSec 时默认为 TLS v1.2。Luabitop 不再作为依赖项自动安装。新的测试套件。

  • 1.11.0 — 2020-03-26 - 使用 LuaSec 时允许 TLS v1.2(Miles Elam)

  • 1.10.0 — 2019-04-15 - 支持 luaossl 用于加密函数,在缺少加密库时添加更好的错误提示。

  • 1.9.0 — 2018-04-02 - nginx 池名称包含用户,连接报告名称为 pgmoon

  • 1.8.0 — 2016-11-07 — 添加 cqueues 支持,Nginx cosocket 的 SSL 调用修复 (@thibaultCha)

  • 1.7.0 — 2016-09-21 — 添加到 opm,添加对 openresty 池的支持,更好的默认池,支持 hstore (@edan)

  • 1.6.0 — 2016-07-21 — 添加对 json 和 jsonb 数组解码的支持。

  • 1.5.0 — 2016-07-12 — 添加 SSL 支持 (@thibaultCha),添加 UUID 数组类型 (@edan),添加对通知的支持 (@starius)

  • 1.4.0 — 2016-02-18 — 添加对解码 jsonb 的支持,添加 JSON 序列化器 (@thibaultCha)

  • 1.3.0 — 2016-02-11 — 修复解析看起来像数字的字符串失败的错误,添加在初始化上下文中使用 ngx 的支持(@thibaultCha),添加明文密码认证,修复 md5 认证的警告

  • 1.2.0 — 2015-07-10 — 添加对 PostgreSQL 数组的支持

  • 1.1.1 — 2014-08-12 — 修复 md5 认证的一个错误

  • 1.1.0 — 2014-05-21 — 添加对在一个调用中使用多个查询的支持

  • 1.0.0 — 2014-05-19 — 初始版本

许可证 (MIT)

版权所有 (C) 2021 Leaf Corcoran

特此免费授予获得此软件及相关文档文件(“软件”)副本的任何人,在不限制条件下处理软件的权利,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或出售软件副本的权利,并允许向其提供软件的人员这样做,但须符合以下条件

上述版权声明和本许可声明应包含在所有副本或软件的任何主要部分中。

该软件按“原样”提供,不提供任何形式的明示或暗示保证,包括但不限于适销性、特定用途适用性和非侵权的保证。在任何情况下,作者或版权持有人均不对任何索赔、损害或其他责任承担责任,无论是在合同、侵权或其他方面,均源于或与软件或软件的使用或其他交易有关。

[luaossl]: https://github.com/wahern/luaossl [LuaCrypto]: https://luarocks.org/modules/starius/luacrypto [LuaSec]: https://github.com/brunoos/luasec [Lapis]: http://leafo.net/lapis [OpenResty]: https://openresty.org.cn/ [LuaSocket]: http://w3.impa.br/~diego/software/luasocket/ [cqueues]: http://25thandclement.com/~william/projects/cqueues.html

作者

Leaf Corcoran (leafo)

许可证

mit

版本