]> jfr.im git - irc/quakenet/newserv.git/blame - pqsql/pqsql.c
Added module support - UNTESTED, but I've got to commit before the Id tags update.
[irc/quakenet/newserv.git] / pqsql / pqsql.c
CommitLineData
e43481af
CP
1/*
2 * PQSQL module
3 *
4 * 99% of the handling is stolen from Q9.
5 */
6
7#include "../core/config.h"
8#include "../core/error.h"
9#include "../irc/irc_config.h"
10#include "../core/events.h"
11#include "../core/hooks.h"
87698d77 12#include "../lib/version.h"
e43481af
CP
13#include "pqsql.h"
14
15#include <stdlib.h>
16#include <sys/poll.h>
17#include <stdarg.h>
18
87698d77
CP
19MODULE_VERSION("$Id")
20
e43481af
CP
21typedef struct pqasyncquery_s {
22 sstring *query;
23 void *tag;
24 PQQueryHandler handler;
67545367 25 int flags;
e43481af
CP
26 struct pqasyncquery_s *next;
27} pqasyncquery_s;
28
29pqasyncquery_s *queryhead = NULL, *querytail = NULL;
30
31int dbconnected = 0;
32PGconn *dbconn;
33
34void dbhandler(int fd, short revents);
35void dbstatus(int hooknum, void *arg);
36void disconnectdb(void);
37void connectdb(void);
38
39void _init(void) {
40 connectdb();
41}
42
43void _fini(void) {
44 disconnectdb();
45}
46
47void connectdb(void) {
48 sstring *dbhost, *dbusername, *dbpassword, *dbdatabase, *dbport;
49 char connectstr[1024];
50
51 if(pqconnected())
52 return;
53
54 /* stolen from chanserv as I'm lazy */
55 dbhost = getcopyconfigitem("pqsql", "host", "localhost", HOSTLEN);
56 dbusername = getcopyconfigitem("pqsql", "username", "newserv", 20);
57 dbpassword = getcopyconfigitem("pqsql", "password", "moo", 20);
58 dbdatabase = getcopyconfigitem("pqsql", "database", "newserv", 20);
59 dbport = getcopyconfigitem("pqsql", "port", "431", 8);
60
61 if(!dbhost || !dbusername || !dbpassword || !dbdatabase || !dbport) {
62 /* freesstring allows NULL */
63 freesstring(dbhost);
64 freesstring(dbusername);
65 freesstring(dbpassword);
66 freesstring(dbdatabase);
67 freesstring(dbport);
68 return;
69 }
70
71 snprintf(connectstr, sizeof(connectstr), "dbname=%s user=%s password=%s", dbdatabase->content, dbusername->content, dbpassword->content);
72
73 freesstring(dbhost);
74 freesstring(dbusername);
75 freesstring(dbpassword);
76 freesstring(dbdatabase);
77 freesstring(dbport);
78
79 Error("pqsql", ERR_INFO, "Attempting database connection: %s", connectstr);
80
81 /* Blocking connect for now.. */
82 dbconn = PQconnectdb(connectstr);
83
7cc1a026
CP
84 if (!dbconn || (PQstatus(dbconn) != CONNECTION_OK)) {
85 Error("pqsql", ERR_ERROR, "Unable to connect to db.");
e43481af 86 return;
7cc1a026 87 }
4f981d28
P
88 Error("pqsql", ERR_INFO, "Connected!");
89
e43481af
CP
90 dbconnected = 1;
91
92 PQsetnonblocking(dbconn, 1);
93
94 /* this kicks ass, thanks splidge! */
95 registerhandler(PQsocket(dbconn), POLLIN, dbhandler);
96 registerhook(HOOK_CORE_STATSREQUEST, dbstatus);
97}
98
99void dbhandler(int fd, short revents) {
100 PGresult *res;
101 pqasyncquery_s *qqp;
102
103 if(revents & POLLIN) {
104 PQconsumeInput(dbconn);
105
106 if(!PQisBusy(dbconn)) { /* query is complete */
107 if(queryhead->handler)
108 (queryhead->handler)(dbconn, queryhead->tag);
109
110 while((res = PQgetResult(dbconn))) {
111 switch(PQresultStatus(res)) {
112 case PGRES_TUPLES_OK:
113 Error("pqsql", ERR_WARNING, "Unhandled tuples output (query: %s)", queryhead->query->content);
114 break;
115
116 case PGRES_NONFATAL_ERROR:
117 case PGRES_FATAL_ERROR:
67545367
CP
118 /* if a create query returns an error assume it went ok, paul will winge about this */
119 if(!(queryhead->flags & QH_CREATE))
120 Error("pqsql", ERR_WARNING, "Unhandled error response (query: %s)", queryhead->query->content);
e43481af
CP
121 break;
122
123 default:
124 break;
125 }
126
127 PQclear(res);
128 }
129
130 /* Free the query and advance */
131 qqp = queryhead;
132 if(queryhead == querytail)
133 querytail = NULL;
134
135 queryhead = queryhead->next;
136
137 freesstring(qqp->query);
138 free(qqp);
139
140 if(queryhead) { /* Submit the next query */
141 PQsendQuery(dbconn, queryhead->query->content);
142 PQflush(dbconn);
143 }
144 }
145 }
146}
147
148/* sorry Q9 */
67545367 149void pqasyncqueryf(PQQueryHandler handler, void *tag, int flags, char *format, ...) {
e43481af
CP
150 char querybuf[8192];
151 va_list va;
152 int len;
153 pqasyncquery_s *qp;
154
155 if(!pqconnected())
156 return;
157
158 va_start(va, format);
159 len = vsnprintf(querybuf, sizeof(querybuf), format, va);
160 va_end(va);
161
162 /* PPA: no check here... */
163 qp = (pqasyncquery_s *)malloc(sizeof(pqasyncquery_s));
67545367
CP
164 if(!qp)
165 return;
166
e43481af
CP
167 qp->query = getsstring(querybuf, len);
168 qp->tag = tag;
169 qp->handler = handler;
170 qp->next = NULL; /* shove them at the end */
67545367 171 qp->flags = flags;
e43481af
CP
172
173 if(querytail) {
174 querytail->next = qp;
175 querytail = qp;
176 } else {
177 querytail = queryhead = qp;
178 PQsendQuery(dbconn, qp->query->content);
179 PQflush(dbconn);
180 }
181}
182
e43481af
CP
183void disconnectdb(void) {
184 pqasyncquery_s *qqp = queryhead, *nqqp;
185
186 if(!pqconnected())
187 return;
188
189 /* do this first else we may get conflicts */
190 deregisterhandler(PQsocket(dbconn), 0);
191
192 /* Throw all the queued queries away, beware of data malloc()ed inside the query item.. */
193 while(qqp) {
194 nqqp = qqp->next;
195 freesstring(qqp->query);
196 free(qqp);
197 qqp = nqqp;
198 }
199
200 deregisterhook(HOOK_CORE_STATSREQUEST, dbstatus);
201 PQfinish(dbconn);
202 dbconn = NULL; /* hmm? */
203
204 dbconnected = 0;
205}
206
207/* more stolen code from Q9 */
208void dbstatus(int hooknum, void *arg) {
209 if ((int)arg > 10) {
210 int i = 0;
211 pqasyncquery_s *qqp;
212 char message[100];
213
214 if(queryhead)
215 for(qqp=queryhead;qqp;qqp=qqp->next)
216 i++;
217
6b5b3c37 218 snprintf(message, sizeof(message), "PQSQL : %6d queries queued.",i);
e43481af
CP
219
220 triggerhook(HOOK_CORE_STATSREPLY, message);
221 }
222}
223
224int pqconnected(void) {
225 return dbconnected;
226}