]>
jfr.im git - irc/quakenet/newserv.git/blob - pqsql/pqsql.c
687cdee9159c12589563668e0755cfb1fd4e1c46
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"
14 #include "../lib/strlfunc.h"
24 /* It's possible that we might want to do a very long query, longer than the
25 * IRC-oriented SSTRING_MAX value. One option would be to increase
26 * SSTRING_MAX, but the whole purpose of sstring's is to efficiently deal
27 * with situations where the malloc() padding overhead is large compared to
28 * string length and strings are frequently recycled. Since neither of
29 * these are necessarily true for longer strings it makes more sense to use
32 * So, query always points at the query string. If it fitted in a sstring,
33 * query_ss will point at the sstring for freeing purposes. If query_ss is
34 * NULL then it was malloc'd so should be free()'d directly.
36 typedef struct pqasyncquery_s
{
40 PQQueryHandler handler
;
42 struct pqasyncquery_s
*next
;
45 typedef struct pqtableloaderinfo_s
48 PQQueryHandler init
, data
, fini
;
49 } pqtableloaderinfo_s
;
51 pqasyncquery_s
*queryhead
= NULL
, *querytail
= NULL
;
56 void dbhandler(int fd
, short revents
);
57 void pqstartloadtable(PGconn
*dbconn
, void *arg
);
58 void dbstatus(int hooknum
, void *arg
);
59 void disconnectdb(void);
70 void connectdb(void) {
71 sstring
*dbhost
, *dbusername
, *dbpassword
, *dbdatabase
, *dbport
;
72 char connectstr
[1024];
77 /* stolen from chanserv as I'm lazy */
78 dbhost
= getcopyconfigitem("pqsql", "host", "UNIX", HOSTLEN
);
79 dbusername
= getcopyconfigitem("pqsql", "username", "newserv", 20);
80 dbpassword
= getcopyconfigitem("pqsql", "password", "moo", 20);
81 dbdatabase
= getcopyconfigitem("pqsql", "database", "newserv", 20);
82 dbport
= getcopyconfigitem("pqsql", "port", "431", 8);
84 if(!dbhost
|| !dbusername
|| !dbpassword
|| !dbdatabase
|| !dbport
) {
85 /* freesstring allows NULL */
87 freesstring(dbusername
);
88 freesstring(dbpassword
);
89 freesstring(dbdatabase
);
94 if (!strcmp(dbhost
->content
,"UNIX")) {
95 snprintf(connectstr
, sizeof(connectstr
), "dbname=%s user=%s password=%s", dbdatabase
->content
, dbusername
->content
, dbpassword
->content
);
97 snprintf(connectstr
, sizeof(connectstr
), "host=%s port=%s dbname=%s user=%s password=%s", dbhost
->content
, dbport
->content
, dbdatabase
->content
, dbusername
->content
, dbpassword
->content
);
101 freesstring(dbusername
);
102 freesstring(dbpassword
);
103 freesstring(dbdatabase
);
106 Error("pqsql", ERR_INFO
, "Attempting database connection: %s", connectstr
);
108 /* Blocking connect for now.. */
109 dbconn
= PQconnectdb(connectstr
);
111 if (!dbconn
|| (PQstatus(dbconn
) != CONNECTION_OK
)) {
112 Error("pqsql", ERR_ERROR
, "Unable to connect to db: %s", pqlasterror(dbconn
));
115 Error("pqsql", ERR_INFO
, "Connected!");
119 PQsetnonblocking(dbconn
, 1);
121 /* this kicks ass, thanks splidge! */
122 registerhandler(PQsocket(dbconn
), POLLIN
, dbhandler
);
123 registerhook(HOOK_CORE_STATSREQUEST
, dbstatus
);
126 void dbhandler(int fd
, short revents
) {
130 if(revents
& POLLIN
) {
131 PQconsumeInput(dbconn
);
133 if(!PQisBusy(dbconn
)) { /* query is complete */
134 if(queryhead
->handler
)
135 (queryhead
->handler
)(dbconn
, queryhead
->tag
);
137 while((res
= PQgetResult(dbconn
))) {
138 switch(PQresultStatus(res
)) {
139 case PGRES_TUPLES_OK
:
140 Error("pqsql", ERR_WARNING
, "Unhandled tuples output (query: %s)", queryhead
->query
);
143 case PGRES_NONFATAL_ERROR
:
144 case PGRES_FATAL_ERROR
:
145 /* if a create query returns an error assume it went ok, paul will winge about this */
146 if(!(queryhead
->flags
& QH_CREATE
))
147 Error("pqsql", ERR_WARNING
, "Unhandled error response (query: %s)", queryhead
->query
);
157 /* Free the query and advance */
159 if(queryhead
== querytail
)
162 queryhead
= queryhead
->next
;
165 freesstring(qqp
->query_ss
);
168 } else if (qqp
->query
) {
174 if(queryhead
) { /* Submit the next query */
175 PQsendQuery(dbconn
, queryhead
->query
);
183 void pqasyncqueryf(PQQueryHandler handler
, void *tag
, int flags
, char *format
, ...) {
192 va_start(va
, format
);
193 len
= vsnprintf(querybuf
, sizeof(querybuf
), format
, va
);
196 /* PPA: no check here... */
197 qp
= (pqasyncquery_s
*)malloc(sizeof(pqasyncquery_s
));
200 Error("pqsql",ERR_STOP
,"malloc() failed in pqsql.c");
202 /* Use sstring or allocate (see above rant) */
203 if (len
> SSTRING_MAX
) {
204 qp
->query
= (char *)malloc(len
+1);
205 strcpy(qp
->query
,querybuf
);
208 qp
->query_ss
= getsstring(querybuf
, len
);
209 qp
->query
= qp
->query_ss
->content
;
212 qp
->handler
= handler
;
213 qp
->next
= NULL
; /* shove them at the end */
217 querytail
->next
= qp
;
220 querytail
= queryhead
= qp
;
221 PQsendQuery(dbconn
, qp
->query
);
226 void pqloadtable(char *tablename
, PQQueryHandler init
, PQQueryHandler data
, PQQueryHandler fini
)
228 pqtableloaderinfo_s
*tli
;
230 tli
=(pqtableloaderinfo_s
*)malloc(sizeof(pqtableloaderinfo_s
));
231 tli
->tablename
=getsstring(tablename
, 100);
235 pqasyncquery(pqstartloadtable
, tli
, "SELECT COUNT(*) FROM %s", tli
->tablename
->content
);
238 void pqstartloadtable(PGconn
*dbconn
, void *arg
)
241 unsigned long i
, count
, tablecrc
;
242 pqtableloaderinfo_s
*tli
= arg
;
244 res
= PQgetResult(dbconn
);
246 if (PQresultStatus(res
) != PGRES_TUPLES_OK
&& PQresultStatus(res
) != PGRES_COMMAND_OK
) {
247 Error("pqsql", ERR_ERROR
, "Error getting row count for %s.", tli
->tablename
->content
);
251 if (PQnfields(res
) != 1) {
252 Error("pqsql", ERR_ERROR
, "Count query format error for %s.", tli
->tablename
->content
);
256 tablecrc
=crc32(tli
->tablename
->content
);
257 count
=strtoul(PQgetvalue(res
, 0, 0), NULL
, 10);
260 Error("pqsql", ERR_INFO
, "Found %lu entries in table %s, scheduling load.", count
, tli
->tablename
->content
);
262 pqasyncquery(tli
->init
, NULL
, "BEGIN");
263 pqasyncquery(NULL
, NULL
, "DECLARE table%lx%lx CURSOR FOR SELECT * FROM %s", tablecrc
, count
, tli
->tablename
->content
);
265 for (i
=0;(count
- i
) > 1000; i
+=1000)
266 pqasyncquery(tli
->data
, NULL
, "FETCH 1000 FROM table%lx%lx", tablecrc
, count
);
268 pqasyncquery(tli
->data
, NULL
, "FETCH ALL FROM table%lx%lx", tablecrc
, count
);
270 pqasyncquery(NULL
, NULL
, "CLOSE table%lx%lx", tablecrc
, count
);
271 pqasyncquery(tli
->fini
, NULL
, "COMMIT");
273 freesstring(tli
->tablename
);
277 void disconnectdb(void) {
278 pqasyncquery_s
*qqp
= queryhead
, *nqqp
;
283 /* do this first else we may get conflicts */
284 deregisterhandler(PQsocket(dbconn
), 0);
286 /* Throw all the queued queries away, beware of data malloc()ed inside the query item.. */
290 freesstring(qqp
->query_ss
);
293 } else if (qqp
->query
) {
301 deregisterhook(HOOK_CORE_STATSREQUEST
, dbstatus
);
303 dbconn
= NULL
; /* hmm? */
308 /* more stolen code from Q9 */
309 void dbstatus(int hooknum
, void *arg
) {
310 if ((long)arg
> 10) {
316 for(qqp
=queryhead
;qqp
;qqp
=qqp
->next
)
319 snprintf(message
, sizeof(message
), "PQSQL : %6d queries queued.",i
);
321 triggerhook(HOOK_CORE_STATSREPLY
, message
);
325 int pqconnected(void) {
329 char* pqlasterror(PGconn
* pgconn
) {
330 static char errormsg
[PQ_ERRORMSG_LENGTH
+ 1];
333 return "PGCONN NULL";
334 strlcpy(errormsg
, PQerrorMessage(pgconn
), PQ_ERRORMSG_LENGTH
);
335 for(i
=0;i
<errormsg
[i
];i
++) {
336 if((errormsg
[i
] == '\r') || (errormsg
[i
] == '\n'))