pgmoon
用纯 Lua(MoonScript)编写的 PostgreSQL 客户端库
$ opm get leafo/pgmoon
pgmoon
!测试
> 注意: 您是否从旧版本的 OpenResty 升级而来?您必须更新到 > pgmoon 1.12 或更高版本,因为 Lua 模式兼容性的更改是为了避免 > 从返回受影响行的查询中获取不正确的结果。
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 —
luarocks install lua-resty-openssl
LuaSocket —
luarocks install luasec
cqueues —
luarocks install luaossl
密码身份验证可能需要一个加密库,[luaossl][]。
$ luarocks install luaossl
> 注意: [LuaCrypto][] 可以用作后备,但该库已被放弃,不推荐使用。
> 注意: 在 [OpenResty][] 中使用将尽可能优先使用内置函数。
解析复杂类型(如数组和 HStore)需要安装 lpeg
。
示例
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 status = 'active' limit 20")
assert(pg:query("update users set name = $1 where id = $2", "leafo", 99))
如果您使用的是 OpenResty,您可以在完成使用套接字后将其释放回连接池,以便在将来的请求中重用。
pg:keepalive()
注意事项
PostgreSQL 允许结果使用相同的字段名多次。由于结果被提取到 Lua 表中,重复的字段将被覆盖,以前的值将不可用。
pg:query("select 1 as dog, 'hello' as dog") --> { { dog = "hello" } }
目前还没有解决这个问题的方法。如果您需要这样做,请打开一个 问题。
参考
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
对象上的方法
postgres:connect()
local success, err = postgres:connect()
使用调用 new
时指定的凭据连接到 Postgres 服务器。成功时返回 true
,失败时返回 nil
和错误消息。
postgres:settimeout(time)
postgres:settimeout(5000) -- 5 second timeout
设置所有后续套接字操作(连接、写入、接收)的超时值(以毫秒为单位)。此函数没有任何返回值。
默认超时取决于底层套接字实现,但通常对应于无超时。
postgres:disconnect()
local success, err = postgres:disconnect()
关闭套接字。如果套接字无法关闭,则返回 nil
。在大多数套接字类型上,可以再次调用 connect
以使用相同的 postgres 对象实例重新建立连接。
postgres:keepalive(...)
postgres:keepalive()
通过 setkeepalive
方法将套接字释放回 OpenResty 套接字池。这里传递的任何参数也会传递给 setkeepalive
。调用此方法后,套接字将不再可用于查询,应被视为已断开连接。
> 注意: 此方法仅在使用 nginx cosocket API 的 OpenResty 中有效。
postgres:query(query_string, ...)
-- return values for successful query
local result, err, num_queries = postgres:query("select name from users limit 2")
-- return value for failure (status is nil)
local status, err, partial_result, num_queries = postgres:query("select created_at from tags; select throw_error() from users")
向服务器发送查询(或多个查询)。失败时,第一个返回值是 nil
,然后是一个描述错误的字符串。由于对 postgres:query
的单个调用可以包含多个查询,因此在错误发生之前已成功执行的任何查询的结果将在错误消息之后返回。(注意: 查询是原子的,它们要么成功要么失败。部分结果将只包含成功的查询,而不是失败查询的部分数据)
<details>
<summary>附加返回值: 通知和提示</summary>
除了上面的返回值之外,pgmoon 还将在查询生成它们时返回另外两个值,通知和提示。
local result, err, num_queries, notifications, notices = postgres:query("drop table if exists some_table")
在这个例子中,如果表 some_table
不存在,那么 notices
将
是一个包含一条消息的数组,该消息表明该表不存在。
</details>
查询函数具有两种操作模式,这对应于 Postgres 服务器为将查询发送到数据库服务器提供的两种协议。
简单协议: 您只传入一个参数,查询字符串。
扩展协议: 您传入一个带有参数占位符(
$1
、$2
等)的查询,然后传入其他参数,这些参数将用作占位符的值。
有关差异和权衡的更多信息,请参阅 "扩展和简单查询协议"。
成功时,返回的结果取决于发送的查询类型。
SELECT
查询、带有 returning
的 INSERT
或其他返回结果集的查询将返回一个结果数组表。每个结果都是一个哈希表,其中键是列的名称,值是结果集中该行的结果。
local res = pg:query("select id, name from users")
可能返回
{
{
id = 123,
name = "Leafo"
},
{
id = 234,
name = "Lee"
}
}
影响行的任何查询,如 UPDATE
、DELETE
或 INSERT
,都将返回一个带有 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:escape_literal(val)
local sql_fragment = postgres:escape_literal(val)
local res = postgres:query("select created_at from users where id = " .. sql_fragment)
将 Lua 值转义为有效的 SQL 片段,该片段可以安全地连接到查询字符串中。永远不要在没有以某种方式转义的情况下将变量连接到查询中,否则您可能会受到 [SQL 注入攻击](https://en.wikipedia.org/wiki/SQL_injection)。
此函数知道以下 Lua 值类型
type(val) == "number"
→escape_literal(5.5) --> 5.5
type(val) == "string"
→escape_literal("your's") --> 'your''s'
type(val) == "boolean"
→escape_literal(true) --> TRUE
val == pgmoon.NULL
→escape_literal(pgmoon.NULL) --> NULL
任何其他类型都会抛出一个硬 error
,以确保您提供一个安全转义的值。
postgres:escape_identifier(val)
local sql_fragment = postgres:escape_identifier(some_table_name)`
local res = postgres:query("select * from " .. sql_fragment .. " limit 20)
转义 Lua 值以用作 Postgres 标识符。这包括表名或列名。这不包括常规值,您应该使用 escape_literal
来转义它们。当名称与内置语言关键字冲突时,需要标识符转义。
参数 val
必须是字符串。
tostring(postgres)
print(tostring(postgres)) --> "<Postgres socket: 0xffffff>"
返回 Postgres
对象当前状态的字符串表示形式。
扩展和简单查询协议
pgmoon 将使用简单或扩展协议向数据库服务器发出您的查询,具体取决于您是否在查询中提供参数和参数占位符。简单协议用于您的查询只是一个字符串时,扩展协议用于您提供其他参数作为 query
方法的参数时。
这些协议有一些权衡和差异。
扩展协议
local res, err = postgres:query("select name from users where id = $1 and status = $2", 12, "ready")
优点: 参数可以包含在查询中,而不会造成 SQL 注入攻击的风险,无需转义值和插值字符串。
优点: 支持
pgmoon_serialize
方法,允许自定义类型自动序列化为查询的参数。缺点: 一次只能发送一个查询。
缺点: 每个查询的开销显著增加。一个空操作查询可能慢 50% 到 100%。(注意,此开销可能微不足道,具体取决于查询本身的运行时间。)
缺点: 一些查询语法与参数不兼容(例如
where id in (...)
、动态表达式),因此您可能仍然需要使用字符串插值并承担相应的风险。
简单协议
local res, err = postgres:query("select name from users where id = " .. postgres:escape_literal(12) .." and status = " .. postgres:escape_literal("ready"))
优点: 性能更高。每个查询的开销很低,意味着每秒可以发送更多的查询,即使手动转义和插值参数也是如此。
优点: 可以将多个查询发送到单个请求中(用
;
分隔)。缺点: 查询的任何参数都必须手动转义并插值到查询字符串中。这可能容易出错,如果操作不当,会造成 SQL 注入攻击。
> 注意: 扩展协议还支持参数值和结果的二进制编码,但由于 Lua 将二进制视为字符串,因此通常使用字符串值从 Postgres 中获取数据比使用二进制协议更快,因为二进制协议需要在 Lua 中进行二进制到字符串的转换。
SSL 连接
pgmoon 可以建立与 PostgreSQL 服务器的 SSL 连接。如果服务器不支持 SSL,它也可以拒绝连接。就像 pgmoon 依赖 LuaSocket 在 OpenResty 之外使用一样,它也依赖 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
cafile = "...", -- certificate authority (LuaSec only)
cert = "...", -- client certificate
key = "...", -- client key
})
assert(pg:connect())
> 注意: 在 PostgreSQL 12 及更高版本中,客户端连接接受的最小 SSL 版本是 1.2。当使用 LuaSocket + LuaSec 连接到 SSL 服务器时,如果未指定 ssl_version
,则使用 tlsv1_2
。
在 OpenResty 中,如果要验证服务器证书,请确保配置 lua_ssl_trusted_certificate 指令。
身份验证类型
PostgreSQL 有几种身份验证类型。pgmoon 当前支持信任、对等和密码身份验证,使用 scram-sha-256-auth 或 md5。
类型转换
PostgreSQL 内置了非常丰富的类型集。pgmoon 将尽力将任何 PostgreSQL 类型转换为相应的 Lua 类型。
所有整数、浮点数和数值类型都转换为 Lua 的数字类型。布尔类型转换为 Lua 布尔值。JSON 类型使用 Lua CJSON 解码为 Lua 表。Lua 表可以像下面描述的那样编码为 JSON。
任何数组类型都自动转换为 Lua 数组表。如果需要将 Lua 中的数组编码为 PostgreSQL 的数组语法,可以使用 pgmoon.arrays
模块。见下文。
任何其他类型都作为 Lua 字符串返回。
处理数组
数组从查询返回时会自动反序列化为 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) .. ")")
从查询返回的数组的元表被配置为 PostgresArray
类型(在 require("pgmoon.arrays")
中定义)。
扩展协议
使用扩展查询协议(带参数的查询)时,使用 PostgresArray
创建的数组对象在作为参数传递时会自动序列化。
local PostgresArray = require("pgmoon.arrays").PostgresArray
postgres:query("update user set tags = $1 where id = 44", PostgresArray({1,2,4}))
请记住,调用 PostgresArray
会通过设置其元表来改变参数。如果您不想改变原始对象,请先复制一份。
此外,数组类型必须包含相同类型的值。不会对您传递的对象进行运行时检查。类型 OID 由数组的第一个条目确定。
空数组
尝试编码空数组时会抛出错误。PostgreSQL 在使用数组时需要一个类型。当数组中有值时,PostgreSQL 可以推断出类型,但是当数组中没有值时,就无法推断出类型。这在 PostgreSQL 提供的错误中得到了体现。
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[].
您可以通过始终在使用的任何值中包含类型转换来解决此错误,这样您就可以传递空数组,并继续使用数组的值,假设类型匹配。
local empty_tags = {}
pg:query("update posts set tags = " .. encode_array(empty_tags) .. "::text[]")
处理 JSON
json
和 jsonb
值在查询结果中会自动解码为 Lua 表(如果可用,则使用 cjson
库)。
要在查询中发送 JSON,您必须先将其转换为字符串文字,然后将其插到查询中。确保将其像其他参数一样对待,并对字符串调用 escape_literal
,使其适合安全地解析为 PostgreSQL 的值。
local pgmoon = require("pgmoon")
local pg = pgmoon.new(auth)
assert(pg:connect())
local my_tbl = { hello = "world" }
local json = require "cjson"
pg:query("update my_table set data = " .. db.escape_literal(json.encode(my_tbl)) .. " where id = 124"
处理 hstore
由于 hstore
是扩展类型,因此需要进行查询才能找到类型 ID,然后 pgmoon 才能自动解码它。连接后,在连接对象上调用 setup_hstore
方法来设置它。
local pgmoon = require("pgmoon")
local pg = pgmoon.new(auth)
pg:connect()
pg:setup_hstore()
使用 encode_hstore
将 Lua 表编码为适合插到查询中的 hstore 语法。
> 注意:encode_hstore
的结果是一个有效的 PostgreSQL SQL 片段,不需要对其调用 escape_literal。它可以安全地直接插入查询中。
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)
自定义类型反序列化器
PostgreSQL 有一个丰富的类型集。读取查询结果时,pgmoon 必须尝试解释 PostgreSQL 中的类型,并将它们映射到 Lua 中可用的类型。默认情况下,包括了用于数字、布尔值、字符串和 JSON 等基本类型的实现。
如果您想为 PostgreSQL 返回的某些类型的值添加自定义行为,可以提供自己的类型反序列化器。
您需要了解类型和类型 OID。PostgreSQL 中的每种类型都存储在 pg_type
目录表中。每个类型都有一个 OID(存储为 32 位正整数),用于唯一标识它。PostgreSQL 提供的核心类型具有固定的类型 OID(例如,布尔值始终为 16),但第三方类型可以添加,没有固定的 OID。
还要注意,任何现有类型的复合版本都有自己的 OID,例如,虽然单个布尔值类型 OID 为 16,但布尔值数组的类型 OID 为 1000。数组是同质的,每个值的类型必须相同。
可以使用 set_type_deserializer(oid, type_name, [deserializer])
方法在 pgmoon 中添加对新类型的支持。
local pgmoon = require("pgmoon")
local pg = pgmoon.new(config)
-- in this example we create a new deserializer called bignumber and provide
-- the function to deserialize (type OID 20 is an 8 byte integer)
pg:set_type_deserializer(20, "bignumber", function(val)
return "HUGENUMBER:" .. val
end)
-- in this example we point another OID to the "bignumber" deserializer we
-- provided above (type OID 701 is a 8 byte floating point number)
pg:set_type_deserializer(701, "bignumber")
参数如下:
oid
来自pg_type
的 OID,将被处理name
类型的本地名称。这是一个指向现有反序列化器的名称,或者如果deserializer
参数存在,则用于注册新的反序列化器。deserializer
一个函数,它接收来自 PostgreSQL 的原始字符串值,并将其转换为更有用的类型(可选)。任何具有相同名称的现有反序列化器函数都将被覆盖。
自定义类型序列化器
使用带参数的 query 方法(也称为扩展查询协议)时,传递到参数中的值必须序列化为该值的字符串版本和一个类型 OID。
pgmoon 为 Lua 的基本类型提供了实现:字符串、布尔值、数字和 postgres.NULL
(用于 NULL
值)。
如果您想支持自定义类型,例如 JSON,那么您需要提供自己的序列化器。
> 序列化与转义:pgmoon 有两种方法来准备要发送到查询中的数据。转义 用于将某个值转换为可以安全地连接到查询中的 SQL 片段。这是使用 postgres:escape_literal()
完成的,适用于简单的查询协议。另一方面,序列化 用于将值转换为字符串表示,该字符串表示可以由 PostgreSQL 解析为值,使用扩展查询协议。例如,一个转义的字符串将是 'hello'
(注意引号,这是一个有效的 SQL 语法片段,而一个序列化的字符串将只是字符串:hello
(通常与一个类型 OID 配对,对于文本通常为 25
)。序列化是反序列化的反向操作,如上所述。
> 注意: 序列化不与转义相同。您不能直接将序列化后的值连接到查询中。但是,您可以将序列化后的值作为字符串转义,然后尝试在查询中将其强制转换为适当的类型。
要为对象提供自己的序列化器,可以在元表中添加一个名为 pgmoon_serialize
的方法。此方法接受两个参数:要序列化的值和当前正在执行序列化的 Postgres
实例。此方法应返回两个值:类型 OID(作为整数)和该值的字符串表示。
> 注意:类型 OID 0 可用于“未知”,PostgreSQL 将尝试根据上下文推断值的类型。如果可能,您应该始终尝试提供特定的类型 OID。
-- this metatable will enable an object to be serialized as json for use as a
-- parameter in postgres:query()
local json_mt = {
pgmoon_serialize = function(v)
local cjson = require("cjson")
return 114, cjson.encode(v) -- 114 is oid from pg_type catalog
end
}
local data = {
age = 200,
color = "blue",
tags = {"one", "two"}
}
postgres:query("update user set data = $1 where id = 233", setmetatable(data, json_mt))
pgmoon_serialize
方法也可以返回 nil
和错误消息以中止序列化。这将完全阻止查询运行,并且错误将从 postgres:query()
方法返回。
> 注意:PostgreSQL 在使用扩展查询协议时支持值的二进制表示,但目前 pgmoon 不支持它。
转换 NULL
默认情况下,PostgreSQL 中的 NULL
转换为 nil
,这意味着它们在结果表中不可见。如果您想将 NULL
转换为某个可见值,请将 Postgres
对象的 convert_null
设置为 true
,postgres.NULL
对象将用于表示 NULL。
local pgmoon = require("pgmoon")
local config = {
database = "my_database",
convert_null = true
}
local postgres = pgmoon.new(config)
assert(postgres:connect())
local res = postgres:query("select NULL the_null")
assert(postgres.NULL == res[1].the_null)
如上所示,NULL
值被设置为 postgres.NULL
。可以更改此值,使 pgmoon 使用其他值作为 NULL
。例如,如果您使用 OpenResty,您可能希望重用 ngx.null
。
还要注意,您可以使用 postgres.NULL
作为扩展查询参数或在 escape_literal
中使用它来生成 NULL
的值。
联系
作者:Leaf Corcoran (leafo) (@moonscript) 电子邮件:leafot@gmail.com 主页:<http://leafo.net>
变更日志
注意:未来的更改说明将在 GitHub 版本页面上发布:https://github.com/leafo/pgmoon/releases
1.15.0 — 2022-6-3 - 扩展查询协议
1.14.0 — 2022-2-17 - 使用 OpenResty 加密函数,更好的空数组支持
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 — 修复了解析看起来像数字的字符串失败的错误,添加了在 init 上下文中使用 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
版本
-
leafo/pgmoon 1.16.0用纯 Lua (MoonScript) 编写的 PostgreSQL 客户端库 2022-11-22 22:50:08
-
leafo/pgmoon 1.15.0用纯 Lua (MoonScript) 编写的 PostgreSQL 客户端库 2022-06-03 19:03:35
-
leafo/pgmoon 1.14.0用纯 Lua (MoonScript) 编写的 PostgreSQL 客户端库 2022-02-17 18:11:25
-
leafo/pgmoon 1.13.0用纯 Lua (MoonScript) 编写的 PostgreSQL 客户端库 2021-10-14 23:12:40
-
leafo/pgmoon 1.9.0用纯 Lua (MoonScript) 编写的 PostgreSQL 客户端库 2018-04-05 02:58:36
-
leafo/pgmoon 1.7.0用纯 Lua (MoonScript) 编写的 PostgreSQL 客户端库 2016-10-21 22:01:09
-
leafo/pgmoon 1.6.0用纯 Lua (MoonScript) 编写的 PostgreSQL 客户端库 2016-10-21 21:21:19