Skip to content

Commit e7b89ea

Browse files
committed
Update README.md
1 parent 9767099 commit e7b89ea

File tree

1 file changed

+225
-2
lines changed

1 file changed

+225
-2
lines changed

README.md

+225-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,225 @@
1-
# async_postgres
2-
PostgreSQL binary module for Garry's Mod
1+
![gmsv_async_postgres](https://socialify.git.ci/Pika-Software/gmsv_async_postgres/image?custom_description=Async+PostgreSQL+binary+for+Garry%27s+Mod&description=1&font=Raleway&issues=1&language=1&name=1&pattern=Formal+Invitation&pulls=1&stargazers=1&theme=Auto)
2+
3+
Simple PostgreSQL connector module for Garry's Mod
4+
using [libpq] with asynchronousity in mind.
5+
6+
Besides binary module you'll probably need to add [`async_postgres.lua`][lua module] to your project.
7+
8+
## Features
9+
* Fully asynchronous, yet allows to wait a query to finish
10+
* Provides full simplified [libpq] interface
11+
* Simple, robust and efficient
12+
* Flexible [lua module] which extends functionality
13+
* [Type friendly][LuaLS] [lua module] with documentatio
14+
15+
## Installation
16+
1. Go to [releases](https://github.com/Pika-Software/gmsv_async_postgres/releases)
17+
2. Download `async_postgres.lua` and `gmsv_async_postgres_xxx.dll` files
18+
> [!NOTE]
19+
> If are unsure which binary to download, you can run this command inside console of your server
20+
> ```lua
21+
> lua_run print("gmsv_async_postgres_" .. (system.IsWindows() and "win" or system.IsOSX() and "osx" or "linux") .. (jit.arch == "x64" and "64" or not system.IsLinux() and "32" or "") .. ".dll")
22+
> ```
23+
3. Put `gmsv_async_postgres_xxx.dll` inside `garrysmod/lua/bin/` folder (if folder does not exists, create it)
24+
4. Put `async_postgres.lua` inside `garrysmod/lua/autorun/server/` or inside your project folder
25+
5. Profit 🎉
26+
27+
## Caveats
28+
* when `queryParams` is used and parameters is `string`, then string will be sent as bytes!<br>
29+
You'll need to convert numbers **exclipitly** to `number` type, otherwise
30+
PostgreSQL will interpent parameter as binary integer, and will return error
31+
or unexpected results may happend.
32+
33+
* Result rows are returned as strings, you'll need to convert them to numbers if needed.
34+
* You'll need to use `Client:unescapeBytea(...)` to convert bytea data to string from reuslt.
35+
36+
## Usage
37+
`async_postgres.Client` usage example
38+
```lua
39+
-- Lua module will require the binary module automatically, and will provide Client and Pool classes
40+
include("async_postgres.lua")
41+
42+
-- See https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING for connection string format
43+
local client = async_postgres.Client("postgresql://postgres:postgres@localhost")
44+
45+
-- Do not forget to connect to the server
46+
client:connect(function(ok, err)
47+
assert(ok, err)
48+
print("Connected to " .. client:host() .. ":" .. client:port())
49+
end)
50+
51+
-- PostgreSQL can only process one query at a time,
52+
-- but async_postgres.Client has internal queue for queries
53+
-- so you can queue up as many queries as you want
54+
-- and they will be executed one by one when possible
55+
--
56+
-- For example this query will be executed after the connection is established
57+
client:query("select now()", function(ok, res)
58+
assert(ok, res)
59+
print("Current time is " .. res.rows[1].now)
60+
end)
61+
62+
-- You can also pass parameters to the query without the need of using client:escape(...)
63+
client:queryParams("select $1 as a, $2 as b", { 5, 10 }, function(ok, res)
64+
assert(ok, res)
65+
PrintTable(res.rows) -- will output [1] = { ["a"] = "5", ["b"] = "10" }
66+
end)
67+
68+
client:prepare("test", "select $1 as value", function(ok, err)
69+
assert(ok, err)
70+
end)
71+
72+
client:queryPrepared("test", { "foobar" }, function(ok, res)
73+
assert(ok, res)
74+
print("Value is: " .. res.rows[1].value) -- will output "Value is foobar"
75+
end)
76+
77+
-- You can also wait for all queries to finish
78+
while client:isBusy() do
79+
-- Will wait for the next query/reset to finish
80+
client:wait()
81+
end
82+
83+
-- And :close() will close the connection to the server
84+
client:close() -- passing true will wait for all queries to finish, but we did waiting in the loop above
85+
```
86+
87+
`async_postgres.Pool` usage example
88+
```lua
89+
local pool = async_postgres.Pool("postgresql://postgres:postgres@localhost")
90+
91+
-- You can make same queries as with Client
92+
-- but with Pool you don't need to worry about connection
93+
-- Pool will manage connections for you
94+
pool:query("select now()", function(ok, res)
95+
assert(ok, res)
96+
print("Current time is " .. res.rows[1].now)
97+
end)
98+
99+
-- With Pool you also can create transactions
100+
-- callback will be called in coroutine with ctx
101+
-- which has query methods that don't need callback
102+
-- and will return results directly
103+
pool:transaction(function(ctx)
104+
-- you can also use ctx.client for methods like :db() if you want
105+
106+
local res = ctx:query("select now()")
107+
print("Time in transaction is: " .. res.rows[1].now)
108+
109+
local res = ctx:query("select $1 as value", { "barfoo" })
110+
print("Value in transaction is: " .. res.rows[1].value) -- will output "Value in transaction is barfoo"
111+
112+
-- If error happens in transaction, it will be rolled back
113+
error("welp something went wrong :p)
114+
115+
-- if no error happens, transaction will be commited
116+
end)
117+
118+
-- Or you can use :connect() to create your own transactions or for smth else
119+
-- :connect() will acquire first available connected Client from the Pool
120+
-- and you'll need to call client:release() when you're done
121+
pool:connect(function(client)
122+
client:query("select now()", function(ok, res)
123+
client:release() -- don't forget to release the client!
124+
125+
assert(ok, res)
126+
print("Current time is " .. res.rows[1].now)
127+
end)
128+
end)
129+
```
130+
131+
## Reference
132+
### Enums
133+
- `async_postgres.VERSION`: string, e.g., "1.0.0"
134+
- `async_postgres.BRANCH`: string, e.g., "main"
135+
- `async_postgres.URL`: string
136+
- `async_postgres.PQ_VERSION`: number, e.g., 160004
137+
- `async_postgres.LUA_API_VERSION`: number, e.g., 1
138+
- `async_postgres.CONNECTION_OK`: number
139+
- `async_postgres.CONNECTION_BAD`: number
140+
- `async_postgres.PQTRANS_IDLE`: number
141+
- `async_postgres.PQTRANS_ACTIVE`: number
142+
- `async_postgres.PQTRANS_INTRANS`: number
143+
- `async_postgres.PQTRANS_INERROR`: number
144+
- `async_postgres.PQTRANS_UNKNOWN`: number
145+
- `async_postgres.PQERRORS_TERSE`: number
146+
- `async_postgres.PQERRORS_DEFAULT`: number
147+
- `async_postgres.PQERRORS_VERBOSE`: number
148+
- `async_postgres.PQERRORS_SQLSTATE`: number
149+
- `async_postgres.PQSHOW_CONTEXT_NEVER`: number
150+
- `async_postgres.PQSHOW_CONTEXT_ERRORS`: number
151+
- `async_postgres.PQSHOW_CONTEXT_ALWAYS`: number
152+
153+
### `async_postgres.Client` Class
154+
- `async_postgres.Client(conninfo)`: Creates a new client instance
155+
156+
#### Methods
157+
- `Client:connect(callback)`: Connects to the database (or reconnects if was connected)
158+
- `Client:reset(callback)`: Reconnects to the database
159+
- `Client:query(query, callback)`: Sends a query to the server
160+
- `Client:queryParams(query, params, callback)`: Sends a query with parameters to the server
161+
- `Client:prepare(name, query, callback)`: Creates a prepared statement
162+
- `Client:queryPrepared(name, params, callback)`: Executes a prepared statement
163+
- `Client:describePrepared(name, callback)`: Describes a prepared statement
164+
- `Client:describePortal(name, callback)`: Describes a portal
165+
- `Client:close(wait)`: Closes the connection to the database
166+
- `Client:pendingQueries()`: Returns the number of queued queries (excludes currently executing query)
167+
- `Client:db()`: Returns the database name
168+
- `Client:user()`: Returns the user name
169+
- `Client:pass()`: Returns the password
170+
- `Client:host()`: Returns the host name
171+
- `Client:hostaddr()`: Returns the server IP address
172+
- `Client:port()`: Returns the port
173+
- `Client:transactionStatus()`: Returns the transaction status (see `PQTRANS` enums)
174+
- `Client:parameterStatus(paramName)`: Looks up a current parameter setting
175+
- `Client:protocolVersion()`: Interrogates the frontend/backend protocol being used
176+
- `Client:serverVersion()`: Returns the server version as integer
177+
- `Client:errorMessage()`: Returns last error message
178+
- `Client:backendPID()`: Returns the backend process ID
179+
- `Client:sslInUse()`: Returns true if SSL is used
180+
- `Client:sslAttribute(name)`: Returns SSL-related information
181+
- `Client:encryptPassword(user, password, algorithm)`: Prepares the encrypted form of a password
182+
- `Client:escape(str)`: Escapes a string for use within an SQL command
183+
- `Client:escapeIdentifier(str)`: Escapes a string for use as an SQL identifier
184+
- `Client:escapeBytea(str)`: Escapes binary data for use within an SQL command
185+
- `Client:unescapeBytea(str)`: Converts an escaped bytea data into binary data
186+
- `Client:release(suppress)`: Releases the client back to the pool
187+
188+
#### Events
189+
- `Client:onNotify(channel, payload, backendPID)`: Called when a NOTIFY message is received
190+
- `Client:onNotice(message, errdata)`: Called when the server sends a notice/warning message during a query
191+
- `Client:onError(message)`: Called whenever an error occurs inside connect/query callback
192+
- `Client:onEnd()`: Called whenever connection to the server is lost/closed
193+
194+
### `async_postgres.Pool` Class
195+
- `async_postgres.Pool(conninfo)`: Creates a new pool instance
196+
197+
#### Methods
198+
- `Pool:connect(callback)`: Acquires a client from the pool
199+
- `Pool:query(query, callback)`: Sends a query to the server
200+
- `Pool:queryParams(query, params, callback)`: Sends a query with parameters to the server
201+
- `Pool:prepare(name, query, callback)`: Creates a prepared statement
202+
- `Pool:queryPrepared(name, params, callback)`: Executes a prepared statement
203+
- `Pool:describePrepared(name, callback)`: Describes a prepared statement
204+
- `Pool:describePortal(name, callback)`: Describes a portal
205+
- `Pool:transaction(callback)`: Begins a transaction and runs the callback with a transaction context
206+
207+
#### Events
208+
- `Pool:onConnect(client)`: Called when a new client connection is established
209+
- `Pool:onAcquire(client)`: Called when a client is acquired
210+
- `Pool:onError(message, client)`: Called when an error occurs
211+
- `Pool:onRelease(client)`: Called when a client is released back to the pool
212+
213+
### I need more documentation!
214+
Please check [`async_postgres.lua`][lua module] for full interface documentation.
215+
216+
And you also can check [libpq documentation][libpq] for more information on method behavior.
217+
218+
## Credit
219+
* [goobie-mysql](https://github.com/Srlion/goobie-mysql) for inspiring with many good ideas
220+
* @unknown-gd for being a good friend
221+
222+
223+
[libpq]: https://www.postgresql.org/docs/16/libpq.html
224+
[lua module]: ./async_postgres.lua
225+
[LuaLS]: https://luals.github.io/

0 commit comments

Comments
 (0)