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