]>
jfr.im git - irc/quakenet/newserv.git/blob - pqsql/pqsql.c
4 * 99% of the handling is stolen from Q9.
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 "../lib/irc_string.h"
13 #include "../lib/version.h"
23 /* It's possible that we might want to do a very long query, longer than the
24 * IRC-oriented SSTRING_MAX value. One option would be to increase
25 * SSTRING_MAX, but the whole purpose of sstring's is to efficiently deal
26 * with situations where the malloc() padding overhead is large compared to
27 * string length and strings are frequently recycled. Since neither of
28 * these are necessarily true for longer strings it makes more sense to use
31 * So, query always points at the query string. If it fitted in a sstring,
32 * query_ss will point at the sstring for freeing purposes. If query_ss is
33 * NULL then it was malloc'd so should be free()'d directly.
35 typedef struct pqasyncquery_s
{
39 PQQueryHandler handler
;
41 struct pqasyncquery_s
*next
;
44 typedef struct pqtableloaderinfo_s
47 PQQueryHandler init
, data
, fini
;
48 } pqtableloaderinfo_s
;
50 pqasyncquery_s
*queryhead
= NULL
, *querytail
= NULL
;
55 void dbhandler(int fd
, short revents
);
56 void pqstartloadtable(PGconn
*dbconn
, void *arg
);
57 void dbstatus(int hooknum
, void *arg
);
58 void disconnectdb(void);
69 void connectdb(void) {
70 sstring
*dbhost
, *dbusername
, *dbpassword
, *dbdatabase
, *dbport
;
71 char connectstr
[1024];
76 /* stolen from chanserv as I'm lazy */
77 dbhost
= getcopyconfigitem("pqsql", "host", "localhost", HOSTLEN
);
78 dbusername
= getcopyconfigitem("pqsql", "username", "newserv", 20);
79 dbpassword
= getcopyconfigitem("pqsql", "password", "moo", 20);
80 dbdatabase
= getcopyconfigitem("pqsql", "database", "newserv", 20);
81 dbport
= getcopyconfigitem("pqsql", "port", "431", 8);
83 if(!dbhost
|| !dbusername
|| !dbpassword
|| !dbdatabase
|| !dbport
) {
84 /* freesstring allows NULL */
86 freesstring(dbusername
);
87 freesstring(dbpassword
);
88 freesstring(dbdatabase
);
93 snprintf(connectstr
, sizeof(connectstr
), "host=%s port=%s dbname=%s user=%s password=%s", dbhost
->content
, dbport
->content
, dbdatabase
->content
, dbusername
->content
, dbpassword
->content
);
96 freesstring(dbusername
);
97 freesstring(dbpassword
);
98 freesstring(dbdatabase
);
101 Error("pqsql", ERR_INFO
, "Attempting database connection: %s", connectstr
);
103 /* Blocking connect for now.. */
104 dbconn
= PQconnectdb(connectstr
);
106 if (!dbconn
|| (PQstatus(dbconn
) != CONNECTION_OK
)) {
107 Error("pqsql", ERR_ERROR
, "Unable to connect to db: %s", PQerrorMessage(dbconn
));
110 Error("pqsql", ERR_INFO
, "Connected!");
114 PQsetnonblocking(dbconn
, 1);
116 /* this kicks ass, thanks splidge! */
117 registerhandler(PQsocket(dbconn
), POLLIN
, dbhandler
);
118 registerhook(HOOK_CORE_STATSREQUEST
, dbstatus
);
121 void dbhandler(int fd
, short revents
) {
125 if(revents
& POLLIN
) {
126 PQconsumeInput(dbconn
);
128 if(!PQisBusy(dbconn
)) { /* query is complete */
129 if(queryhead
->handler
)
130 (queryhead
->handler
)(dbconn
, queryhead
->tag
);
132 while((res
= PQgetResult(dbconn
))) {
133 switch(PQresultStatus(res
)) {
134 case PGRES_TUPLES_OK
:
135 Error("pqsql", ERR_WARNING
, "Unhandled tuples output (query: %s)", queryhead
->query
);
138 case PGRES_NONFATAL_ERROR
:
139 case PGRES_FATAL_ERROR
:
140 /* if a create query returns an error assume it went ok, paul will winge about this */
141 if(!(queryhead
->flags
& QH_CREATE
))
142 Error("pqsql", ERR_WARNING
, "Unhandled error response (query: %s)", queryhead
->query
);
152 /* Free the query and advance */
154 if(queryhead
== querytail
)
157 queryhead
= queryhead
->next
;
160 freesstring(qqp
->query_ss
);
163 } else if (qqp
->query
) {
169 if(queryhead
) { /* Submit the next query */
170 PQsendQuery(dbconn
, queryhead
->query
);
178 void pqasyncqueryf(PQQueryHandler handler
, void *tag
, int flags
, char *format
, ...) {
187 va_start(va
, format
);
188 len
= vsnprintf(querybuf
, sizeof(querybuf
), format
, va
);
191 /* PPA: no check here... */
192 qp
= (pqasyncquery_s
*)malloc(sizeof(pqasyncquery_s
));
195 Error("pqsql",ERR_STOP
,"malloc() failed in pqsql.c");
197 /* Use sstring or allocate (see above rant) */
198 if (len
> SSTRING_MAX
) {
199 qp
->query
= (char *)malloc(len
+1);
200 strcpy(qp
->query
,querybuf
);
203 qp
->query_ss
= getsstring(querybuf
, len
);
204 qp
->query
= qp
->query_ss
->content
;
207 qp
->handler
= handler
;
208 qp
->next
= NULL
; /* shove them at the end */
212 querytail
->next
= qp
;
215 querytail
= queryhead
= qp
;
216 PQsendQuery(dbconn
, qp
->query
);
221 void pqloadtable(char *tablename
, PQQueryHandler init
, PQQueryHandler data
, PQQueryHandler fini
)
223 pqtableloaderinfo_s
*tli
;
225 tli
=(pqtableloaderinfo_s
*)malloc(sizeof(pqtableloaderinfo_s
));
226 tli
->tablename
=getsstring(tablename
, 100);
230 pqasyncquery(pqstartloadtable
, tli
, "SELECT COUNT(*) FROM %s", tli
->tablename
->content
);
233 void pqstartloadtable(PGconn
*dbconn
, void *arg
)
236 unsigned long i
, count
, tablecrc
;
237 pqtableloaderinfo_s
*tli
= arg
;
239 res
= PQgetResult(dbconn
);
241 if (PQresultStatus(res
) != PGRES_TUPLES_OK
&& PQresultStatus(res
) != PGRES_COMMAND_OK
) {
242 Error("pqsql", ERR_ERROR
, "Error getting row count for %s.", tli
->tablename
->content
);
246 if (PQnfields(res
) != 1) {
247 Error("pqsql", ERR_ERROR
, "Count query format error for %s.", tli
->tablename
->content
);
251 tablecrc
=crc32(tli
->tablename
->content
);
252 count
=strtoul(PQgetvalue(res
, 0, 0), NULL
, 10);
255 Error("pqsql", ERR_INFO
, "Found %lu entries in table %s, scheduling load.", count
, tli
->tablename
->content
);
257 pqasyncquery(tli
->init
, NULL
, "BEGIN");
258 pqasyncquery(NULL
, NULL
, "DECLARE table%lx%lx CURSOR FOR SELECT * FROM %s", tablecrc
, count
, tli
->tablename
->content
);
260 for (i
=0;(count
- i
) > 1000; i
+=1000)
261 pqasyncquery(tli
->data
, NULL
, "FETCH 1000 FROM table%lx%lx", tablecrc
, count
);
263 pqasyncquery(tli
->data
, NULL
, "FETCH ALL FROM table%lx%lx", tablecrc
, count
);
265 pqasyncquery(NULL
, NULL
, "CLOSE table%lx%lx", tablecrc
, count
);
266 pqasyncquery(tli
->fini
, NULL
, "COMMIT");
268 freesstring(tli
->tablename
);
272 void disconnectdb(void) {
273 pqasyncquery_s
*qqp
= queryhead
, *nqqp
;
278 /* do this first else we may get conflicts */
279 deregisterhandler(PQsocket(dbconn
), 0);
281 /* Throw all the queued queries away, beware of data malloc()ed inside the query item.. */
285 freesstring(qqp
->query_ss
);
288 } else if (qqp
->query
) {
296 deregisterhook(HOOK_CORE_STATSREQUEST
, dbstatus
);
298 dbconn
= NULL
; /* hmm? */
303 /* more stolen code from Q9 */
304 void dbstatus(int hooknum
, void *arg
) {
305 if ((long)arg
> 10) {
311 for(qqp
=queryhead
;qqp
;qqp
=qqp
->next
)
314 snprintf(message
, sizeof(message
), "PQSQL : %6d queries queued.",i
);
316 triggerhook(HOOK_CORE_STATSREPLY
, message
);
320 int pqconnected(void) {