pgmoon
用纯 Lua(MoonScript)编写的 PostgreSQL 客户端库
$ opm get xiangnanscu/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
对象。除非另有说明,否则所有字段都是可选的。新创建的对象不会自动连接,您必须在创建对象后调用 connect
。
可用选项
"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"
: 手动创建的opensssl.ssl.context
,用于创建 cqueues SSL 连接时"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 将二进制视为字符串,因此通常 > 比使用 > 二进制协议更快,后者需要在 Lua 中进行二进制到字符串的转换。
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
cafile = "...", -- certificate authority (LuaSec only)
cert = "...", -- client certificate
key = "...", -- client key
})
assert(pg:connect())
> 注意:在 Postgres 12 及更高版本中,客户端接受的最低 SSL 版本 > 连接为 1.2。当使用 LuaSocket + LuaSec 连接到 SSL 时 > 服务器,如果您没有指定 ssl_version
,则使用 tlsv1_2
。
在 OpenResty 中,如果您希望验证服务器证书,请确保配置 lua_ssl_trusted_certificate 指令。
身份验证类型
Postgres 有几种身份验证类型。pgmoon 目前支持使用 scram-sha-256-auth 或 md5 的 trust、peer 和 password 身份验证。
类型转换
Postgres 内置了一套非常丰富的类型。pgmoon 将尽力将任何 Postgres 类型转换为适当的 Lua 类型。
所有整数、浮点数和数字类型都转换为 Lua 的 number 类型。布尔类型转换为 Lua 布尔值。JSON 类型使用 Lua CJSON 解码为 Lua 表格。Lua 表格可以像下面描述的那样编码为 JSON。
任何数组类型都会自动转换为 Lua 数组表格。如果您需要将 Lua 中的数组编码为 Postgres 的数组语法,可以使用 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 由数组的第一个元素确定。
空数组
尝试编码空数组时将抛出错误。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[].
您可以通过始终在使用的任何值中包含类型转换来解决此错误,这将允许您传递空数组并继续使用数组中的值,假设类型匹配。
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
的结果是一个有效的 Postgres 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 必须尝试从 postgres 中解释类型并将其映射到 Lua 中可用的东西。默认情况下,会为数字、布尔值、字符串和 JSON 等基本类型提供实现。
如果您想为 PostgreSQL 返回的某些类型的值添加自定义行为,可以提供自己的类型反序列化器。
您必须了解类型和类型 OID。PostgreSQL 中的每种类型都存储在 pg_type
目录表中。每种类型都有一个 OID(存储为 32 位正整数)来唯一标识它。Postgres 提供的核心类型具有固定的类型 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
一个函数,它接收来自 Postgres 的原始字符串值并将其转换为更实用的东西(可选)。任何具有相同名称的现有反序列化器函数都将被覆盖。
自定义类型序列化器
使用带参数的 query 方法(也称为扩展查询协议)时,传递到参数中的值必须序列化为该值的字符串版本和类型 OID。
pgmoon 为 Lua 的基本类型提供了实现:字符串、布尔值、数字和 postgres.NULL
(表示 NULL
值)。
如果您想支持自定义类型,例如 JSON,那么您将需要提供自己的序列化器。
> 序列化与转义:pgmoon 有两种方法可以准备要发送到查询中的数据。转义 用于将某个值转换为可以安全地连接到查询中的 SQL 片段。这使用 postgres:escape_literal()
完成,适用于简单的查询协议。另一方面,序列化 用于将值转换为可以使用扩展查询协议由 Postgres 解析的字符串表示形式。例如,转义 的字符串将是 'hello'
(注意引号,这是一个有效的 SQL 语法片段,而序列化字符串将只是字符串:hello
(通常与类型 OID 配合使用,对于文本通常为 25
)。序列化是反序列化的反向操作,如上所述。
> 注意: 序列化不同于转义。您不能直接将序列化值连接到查询中。但是,您可以将序列化值作为字符串进行转义,然后尝试在查询中将其转换为适当的类型。
要为对象提供自己的序列化器,您可以在元表中添加一个名为 pgmoon_serialize
的方法。此方法接受两个参数:要序列化的值和正在执行序列化的 Postgres
的当前实例。该方法应返回两个值:类型 OID(作为整数)和该值的字符串表示形式。
> 注意:类型 OID 0 可以用于“未知”,Postgres 将尝试根据上下文推断值的类型。如果可能,您应该始终尝试提供特定的类型 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()
方法返回。
> 注意:Postgres 在使用扩展查询协议时支持值的二进制表示形式,但目前 pgmoon 不支持它。
转换 NULL
默认情况下,Postgres 中的 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) 邮箱:[email protected] 主页:<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
作者
南翔 (xiangnanscu)
许可证
mit
版本
-
xiangnanscu/pgmoon 1.16.0用纯 Lua (MoonScript) 编写的 PostgreSQL 客户端库 2022-12-02 13:01:04
-
xiangnanscu/pgmoon 1.15.0用纯 Lua (MoonScript) 编写的 PostgreSQL 客户端库 2022-11-11 15:43:44
-
xiangnanscu/pgmoon 1.14.1用纯 Lua (MoonScript) 编写的 PostgreSQL 客户端库 2022-03-18 14:52:55
-
xiangnanscu/pgmoon 1.14.0用纯 Lua (MoonScript) 编写的 PostgreSQL 客户端库 2022-03-18 14:31:31
-
xiangnanscu/pgmoon 1.13.0用纯 Lua (MoonScript) 编写的 PostgreSQL 客户端库 2022-01-09 05:50:28
-
xiangnanscu/pgmoon 1.12.0用纯 Lua (MoonScript) 编写的 PostgreSQL 客户端库 2021-10-03 13:59:37
-
xiangnanscu/pgmoon 1.10.0用纯 Lua (MoonScript) 编写的 PostgreSQL 客户端库 2020-02-16 14:33:11