Skip to content

Commit d97796e

Browse files
committed
Added support for notices and warnings
1 parent 2479e99 commit d97796e

File tree

7 files changed

+95
-43
lines changed

7 files changed

+95
-43
lines changed

async_postgres.lua

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@
9191
---@field unescapeBytea fun(self: PGconn, str: string): string
9292
---@field setNotifyCallback fun(self: PGconn, callback: fun(channel: string, payload: string, backendPID: number))
9393
---@field getNotifyCallback fun(self: PGconn): fun(channel: string, payload: string, backendPID: number)
94+
---@field setNoticeCallback fun(self: PGconn, callback: fun(message: string, errdata: table))
95+
---@field getNoticeCallback fun(self: PGconn): fun(message: string, errdata: table)
9496
---@field setArrayResult fun(self: PGconn, enabled: boolean)
9597
---@field getArrayResult fun(self: PGconn): boolean
9698

@@ -119,10 +121,6 @@ if async_postgres.LUA_API_VERSION ~= 1 then
119121
"expected 1, got " .. async_postgres.LUA_API_VERSION)
120122
end
121123

122-
123-
---@class async_postgres_module : async_postgres
124-
local module = setmetatable({}, { __index = async_postgres })
125-
126124
local Queue = {}
127125
Queue.__index = Queue
128126

@@ -244,6 +242,9 @@ function Client:connect(callback)
244242
self.conn:setNotifyCallback(function(channel, payload, backendPID)
245243
xpcall(self.onNotify, self.errorHandler, self, channel, payload, backendPID)
246244
end)
245+
self.conn:setNoticeCallback(function(message, errdata)
246+
xpcall(self.onNotice, self.errorHandler, self, message, errdata)
247+
end)
247248

248249
xpcall(callback, self.errorHandler, ok)
249250
self:processQueue()
@@ -663,6 +664,17 @@ end
663664
function Client:onNotify(channel, payload, backendPID)
664665
end
665666

667+
--- This **event** function is called when server sends a notice/warning message
668+
--- during a query
669+
---
670+
--- You can set it to your own function to handle notices
671+
--- By default this function calls `ErrorNoHalt`
672+
---@param message string notice message
673+
---@param errdata table additional data
674+
function Client:onNotice(message, errdata)
675+
ErrorNoHalt(tostring(self) .. " " .. message)
676+
end
677+
666678
--- This **event** function is called whenever an error occurs inside connect/query callback.
667679
---
668680
--- You can set it to your own function to handle errors.
@@ -696,7 +708,7 @@ end
696708
--- connectiong url format can be found at https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
697709
---@param url string connection url, see libpq documentation for more information
698710
---@return PGClient
699-
function module.Client(url)
711+
function async_postgres.Client(url)
700712
---@class PGClient
701713
local client = setmetatable({
702714
url = url,
@@ -891,7 +903,7 @@ function Pool:processQueue()
891903
local waiters = self.queue:size()
892904
local threshold = clients * self.threshold
893905
if clients < self.max and waiters > threshold then
894-
local client = module.Client(self.url)
906+
local client = async_postgres.Client(self.url)
895907
client.onError = function(client, message)
896908
return self:onError(message)
897909
end
@@ -1079,11 +1091,12 @@ end
10791091
--- Creates a new connection pool with given connection url,
10801092
--- then use :connect() to get available connection,
10811093
--- and then :release() to release it back to the pool
1082-
function module.Pool(url)
1094+
---@return PGPool
1095+
function async_postgres.Pool(url)
10831096
---@class PGPool
10841097
local pool = setmetatable({
10851098
url = url,
1086-
clients = { module.Client(url) },
1099+
clients = { async_postgres.Client(url) },
10871100
queue = Queue.new(),
10881101
max = 10,
10891102
threshold = 5,

pgsqloo.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/Users/retr0/Library/Application Support/Steam/steamapps/common/GarrysMod/garrysmod/addons/moon/lua/autorun/pgsqloo.lua

source/async_postgres.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
#include <GarrysMod/Lua/AutoReference.h>
33
#include <GarrysMod/Lua/Interface.h>
44
#include <GarrysMod/Lua/LuaInterface.h>
5-
#include <libpq-fe.h>
65

76
#include <algorithm>
87
#include <chrono>
@@ -109,10 +108,12 @@ namespace async_postgres {
109108
};
110109

111110
struct Connection {
111+
GLua::ILuaInterface* lua;
112112
pg::conn conn;
113113
std::shared_ptr<Query> query;
114114
std::shared_ptr<ResetEvent> reset_event;
115115
GLua::AutoReference on_notify;
116+
GLua::AutoReference on_notice;
116117
bool array_result = false;
117118

118119
Connection(GLua::ILuaInterface* lua, pg::conn&& conn);
@@ -142,6 +143,8 @@ namespace async_postgres {
142143
// result.cpp
143144
void create_result_table(GLua::ILuaInterface* lua, PGresult* result,
144145
bool array_result);
146+
void create_result_error_table(GLua::ILuaInterface* lua,
147+
const PGresult* result);
145148

146149
// misc.cpp
147150
void register_misc_connection_functions(GLua::ILuaInterface* lua);

source/connection.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ using namespace async_postgres;
55
std::vector<Connection*> async_postgres::connections = {};
66

77
Connection::Connection(GLua::ILuaInterface* lua, pg::conn&& conn)
8-
: conn(std::move(conn)) {
8+
: conn(std::move(conn)), lua(lua) {
99
// add connection to global list
1010
connections.push_back(this);
1111
}
@@ -35,6 +35,17 @@ inline bool socket_is_ready(PGconn* conn, PostgresPollingStatusType status) {
3535
return true;
3636
}
3737

38+
static void noticeReceiver(void* arg, const PGresult* res) {
39+
auto state = static_cast<Connection*>(arg);
40+
if (state->on_notice.IsValid()) {
41+
auto lua = state->lua;
42+
state->on_notice.Push();
43+
lua->PushString(PQresultErrorMessage(res));
44+
create_result_error_table(lua, res);
45+
pcall(lua, 2, 0);
46+
}
47+
}
48+
3849
void async_postgres::connect(GLua::ILuaInterface* lua, std::string_view url,
3950
GLua::AutoReference&& callback) {
4051
auto conn = pg::connectStart(url);
@@ -65,6 +76,8 @@ inline bool poll_pending_connection(GLua::ILuaInterface* lua,
6576
if (event.status == PGRES_POLLING_OK) {
6677
auto state = new Connection(lua, std::move(event.conn));
6778

79+
PQsetNoticeReceiver(state->conn.get(), noticeReceiver, state);
80+
6881
event.callback.Push();
6982
lua->PushBool(true);
7083
lua->PushUserType(state, connection_meta);

source/main.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,26 @@ namespace async_postgres::lua {
207207
return 1;
208208
}
209209

210+
lua_protected_fn(setNoticeCallback) {
211+
lua->CheckType(1, async_postgres::connection_meta);
212+
213+
auto state = lua_connection_state();
214+
if (lua->IsType(2, GLua::Type::Function)) {
215+
state->on_notice = GLua::AutoReference(lua, 2);
216+
}
217+
218+
return 0;
219+
}
220+
221+
lua_protected_fn(getNoticeCallback) {
222+
lua->CheckType(1, async_postgres::connection_meta);
223+
auto state = lua_connection_state();
224+
if (!state->on_notice.Push()) {
225+
lua->PushNil();
226+
}
227+
return 1;
228+
}
229+
210230
lua_protected_fn(wait) {
211231
lua->CheckType(1, async_postgres::connection_meta);
212232

@@ -309,6 +329,8 @@ void register_connection_mt(GLua::ILuaInterface* lua) {
309329
register_lua_fn(reset);
310330
register_lua_fn(setNotifyCallback);
311331
register_lua_fn(getNotifyCallback);
332+
register_lua_fn(setNoticeCallback);
333+
register_lua_fn(getNoticeCallback);
312334
register_lua_fn(wait);
313335
register_lua_fn(isBusy);
314336
register_lua_fn(querying);

source/query.cpp

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -60,39 +60,6 @@ inline bool bad_result(PGresult* result) {
6060
status == PGRES_FATAL_ERROR;
6161
}
6262

63-
#define set_error_field(name, field) \
64-
{ \
65-
const char* field_value = PQresultErrorField(result, field); \
66-
if (field_value) { \
67-
lua->PushString(field_value); \
68-
lua->SetField(-2, name); \
69-
} \
70-
}
71-
72-
inline void create_result_error_table(GLua::ILuaInterface* lua,
73-
PGresult* result) {
74-
lua->CreateTable();
75-
76-
lua->PushString(PQresStatus(PQresultStatus(result)));
77-
lua->SetField(-2, "status");
78-
79-
set_error_field("severity", PG_DIAG_SEVERITY_NONLOCALIZED);
80-
set_error_field("sqlState", PG_DIAG_SQLSTATE);
81-
set_error_field("messagePrimary", PG_DIAG_MESSAGE_PRIMARY);
82-
set_error_field("messageDetail", PG_DIAG_MESSAGE_DETAIL);
83-
set_error_field("messageHint", PG_DIAG_MESSAGE_HINT);
84-
set_error_field("statementPosition", PG_DIAG_STATEMENT_POSITION);
85-
set_error_field("context", PG_DIAG_CONTEXT);
86-
set_error_field("schemaName", PG_DIAG_SCHEMA_NAME);
87-
set_error_field("tableName", PG_DIAG_TABLE_NAME);
88-
set_error_field("columnName", PG_DIAG_COLUMN_NAME);
89-
set_error_field("dataType", PG_DIAG_DATATYPE_NAME);
90-
set_error_field("constraintName", PG_DIAG_CONSTRAINT_NAME);
91-
set_error_field("sourceFile", PG_DIAG_SOURCE_FILE);
92-
set_error_field("sourceLine", PG_DIAG_SOURCE_LINE);
93-
set_error_field("sourceFunction", PG_DIAG_SOURCE_FUNCTION);
94-
}
95-
9663
void query_result(GLua::ILuaInterface* lua, pg::result&& result,
9764
GLua::AutoReference& callback, bool array_result) {
9865
if (callback.Push()) {

source/result.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,36 @@ void async_postgres::create_result_table(GLua::ILuaInterface* lua,
6969
lua->PushNumber(PQoidValue(result));
7070
lua->SetField(-2, "oid");
7171
}
72+
73+
#define set_error_field(name, field) \
74+
{ \
75+
const char* field_value = PQresultErrorField(result, field); \
76+
if (field_value) { \
77+
lua->PushString(field_value); \
78+
lua->SetField(-2, name); \
79+
} \
80+
}
81+
82+
void async_postgres::create_result_error_table(GLua::ILuaInterface* lua,
83+
const PGresult* result) {
84+
lua->CreateTable();
85+
86+
lua->PushString(PQresStatus(PQresultStatus(result)));
87+
lua->SetField(-2, "status");
88+
89+
set_error_field("severity", PG_DIAG_SEVERITY_NONLOCALIZED);
90+
set_error_field("sqlState", PG_DIAG_SQLSTATE);
91+
set_error_field("messagePrimary", PG_DIAG_MESSAGE_PRIMARY);
92+
set_error_field("messageDetail", PG_DIAG_MESSAGE_DETAIL);
93+
set_error_field("messageHint", PG_DIAG_MESSAGE_HINT);
94+
set_error_field("statementPosition", PG_DIAG_STATEMENT_POSITION);
95+
set_error_field("context", PG_DIAG_CONTEXT);
96+
set_error_field("schemaName", PG_DIAG_SCHEMA_NAME);
97+
set_error_field("tableName", PG_DIAG_TABLE_NAME);
98+
set_error_field("columnName", PG_DIAG_COLUMN_NAME);
99+
set_error_field("dataType", PG_DIAG_DATATYPE_NAME);
100+
set_error_field("constraintName", PG_DIAG_CONSTRAINT_NAME);
101+
set_error_field("sourceFile", PG_DIAG_SOURCE_FILE);
102+
set_error_field("sourceLine", PG_DIAG_SOURCE_LINE);
103+
set_error_field("sourceFunction", PG_DIAG_SOURCE_FUNCTION);
104+
}

0 commit comments

Comments
 (0)