]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/extra/m_pgsql.cpp
Okay, this is getting towards working now. It just needs the API finishing...everythi...
[user/henk/code/inspircd.git] / src / modules / extra / m_pgsql.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd is copyright (C) 2002-2004 ChatSpike-Dev.
6  *                       E-mail:
7  *                <brain@chatspike.net>
8  *                <Craig@chatspike.net>
9  *                <omster@gmail.com>
10  *     
11  * Written by Craig Edwards, Craig McLure, and others.
12  * This program is free but copyrighted software; see
13  *            the file COPYING for details.
14  *
15  * ---------------------------------------------------
16  */
17
18 #include <sstream>
19 #include <string>
20 #include <deque>
21 #include <map>
22 #include <libpq-fe.h>
23
24 #include "users.h"
25 #include "channels.h"
26 #include "modules.h"
27 #include "helperfuncs.h"
28 #include "inspircd.h"
29 #include "configreader.h"
30
31 #include "m_sqlv2.h"
32
33 /* $ModDesc: PostgreSQL Service Provider module for all other m_sql* modules, uses v2 of the SQL API */
34 /* $CompileFlags: -I`pg_config --includedir` */
35 /* $LinkerFlags: -L`pg_config --libdir` -lpq */
36
37 /* UGH, UGH, UGH, UGH, UGH, UGH
38  * I'm having trouble seeing how I
39  * can avoid this. The core-defined
40  * constructors for InspSocket just
41  * aren't suitable...and if I'm
42  * reimplementing them I need this so
43  * I can access the socket engine :\
44  */
45 extern InspIRCd* ServerInstance;
46 InspSocket* socket_ref[MAX_DESCRIPTORS];
47
48 /* Forward declare, so we can have the typedef neatly at the top */
49 class SQLConn;
50 /* Also needs forward declaration, as it's used inside SQLconn */
51 class ModulePgSQL;
52
53 typedef std::map<std::string, SQLConn*> ConnMap;
54
55 /* CREAD,       Connecting and wants read event
56  * CWRITE,      Connecting and wants write event
57  * WREAD,       Connected/Working and wants read event
58  * WWRITE,      Connected/Working and wants write event
59  */
60 enum SQLstatus { CREAD, CWRITE, WREAD, WWRITE };
61
62 /** QueryQueue, a queue of queries waiting to be executed.
63  * This maintains two queues internally, one for 'priority'
64  * queries and one for less important ones. Each queue has
65  * new queries appended to it and ones to execute are popped
66  * off the front. This keeps them flowing round nicely and no
67  * query should ever get 'stuck' for too long. If there are
68  * queries in the priority queue they will be executed first,
69  * 'unimportant' queries will only be executed when the
70  * priority queue is empty.
71  *
72  * We store lists of SQLrequest's here, by value as we want to avoid storing
73  * any data allocated inside the client module (in case that module is unloaded
74  * while the query is in progress).
75  *
76  * Because we want to work on the current SQLrequest in-situ, we need a way
77  * of accessing the request we are currently processing, QueryQueue::front(),
78  * but that call needs to always return the same request until that request
79  * is removed from the queue, this is what the 'which' variable is. New queries are
80  * always added to the back of one of the two queues, but if when front()
81  * is first called then the priority queue is empty then front() will return
82  * a query from the normal queue, but if a query is then added to the priority
83  * queue then front() must continue to return the front of the *normal* queue
84  * until pop() is called.
85  */
86
87 class QueryQueue : public classbase
88 {
89 private:
90         std::deque<SQLrequest> priority;        /* The priority queue */
91         std::deque<SQLrequest> normal;  /* The 'normal' queue */
92         enum { PRI, NOR, NON } which;   /* Which queue the currently active element is at the front of */
93
94 public:
95         QueryQueue()
96         : which(NON)
97         {
98         }
99         
100         void push(const SQLrequest &q)
101         {
102                 log(DEBUG, "QueryQueue::push(): Adding %s query to queue: %s", ((q.pri) ? "priority" : "non-priority"), q.query.c_str());
103                 
104                 if(q.pri)
105                         priority.push_back(q);
106                 else
107                         normal.push_back(q);
108         }
109         
110         void pop()
111         {
112                 if((which == PRI) && priority.size())
113                 {
114                         priority.pop_front();
115                 }
116                 else if((which == NOR) && normal.size())
117                 {
118                         normal.pop_front();
119                 }
120                 
121                 /* Reset this */
122                 which = NON;
123                 
124                 /* Silently do nothing if there was no element to pop() */
125         }
126         
127         SQLrequest& front()
128         {
129                 switch(which)
130                 {
131                         case PRI:
132                                 return priority.front();
133                         case NOR:
134                                 return normal.front();
135                         default:
136                                 if(priority.size())
137                                 {
138                                         which = PRI;
139                                         return priority.front();
140                                 }
141                                 
142                                 if(normal.size())
143                                 {
144                                         which = NOR;
145                                         return normal.front();
146                                 }
147                                 
148                                 /* This will probably result in a segfault,
149                                  * but the caller should have checked totalsize()
150                                  * first so..meh - moron :p
151                                  */
152                                 
153                                 return priority.front();
154                 }
155         }
156         
157         std::pair<int, int> size()
158         {
159                 return std::make_pair(priority.size(), normal.size());
160         }
161         
162         int totalsize()
163         {
164                 return priority.size() + normal.size();
165         }
166 };
167
168 /** PgSQLresult is a subclass of the mostly-pure-virtual class SQLresult.
169  * All SQL providers must create their own subclass and define it's methods using that
170  * database library's data retriveal functions. The aim is to avoid a slow and inefficient process
171  * of converting all data to a common format before it reaches the result structure. This way
172  * data is passes to the module nearly as directly as if it was using the API directly itself.
173  */
174
175 class PgSQLresult : public SQLresult
176 {
177         PGresult* res;
178 public:
179         PgSQLresult(Module* self, Module* to, PGresult* result)
180         : SQLresult(self, to), res(result)
181         {
182                 int rows = PQntuples(res);
183                 int cols = PQnfields(res);
184                 
185                 log(DEBUG, "Created new PgSQL result; %d rows, %d columns", rows, cols);
186                 
187                 for (int r = 0; r < rows; r++)
188                 {
189                         log(DEBUG, "Row %d:", r);
190                                         
191                         for(int i = 0; i < cols; i++)
192                         {
193                                 log(DEBUG, "\t[%s]: %s", PQfname(result, i), PQgetvalue(result, r, i));
194                         }
195                 }
196         }
197         
198         ~PgSQLresult()
199         {
200                 PQclear(res);
201         }
202         
203         virtual int Rows()
204         {
205                 return PQntuples(res);
206         }
207 };
208
209 /** SQLConn represents one SQL session.
210  * Each session has its own persistent connection to the database.
211  * This is a subclass of InspSocket so it can easily recieve read/write events from the core socket
212  * engine, unlike the original MySQL module this module does not block. Ever. It gets a mild stabbing
213  * if it dares to.
214  */
215
216 class SQLConn : public InspSocket
217 {
218 private:
219         ModulePgSQL* us;                /* Pointer to the SQL provider itself */
220         Server* Srv;                    /* Server* for..uhm..something, maybe */
221         std::string     dbhost; /* Database server hostname */
222         unsigned int    dbport; /* Database server port */
223         std::string     dbname; /* Database name */
224         std::string     dbuser; /* Database username */
225         std::string     dbpass; /* Database password */
226         bool                    ssl;    /* If we should require SSL */
227         PGconn*                 sql;    /* PgSQL database connection handle */
228         SQLstatus               status; /* PgSQL database connection status */
229         bool                    qinprog;/* If there is currently a query in progress */
230         QueryQueue              queue;  /* Queue of queries waiting to be executed on this connection */
231
232 public:
233
234         /* This class should only ever be created inside this module, using this constructor, so we don't have to worry about the default ones */
235
236         SQLConn(ModulePgSQL* self, Server* srv, const std::string &h, unsigned int p, const std::string &d, const std::string &u, const std::string &pwd, bool s);
237
238         ~SQLConn();
239
240         bool DoResolve();
241
242         bool DoConnect();
243
244         virtual void Close();
245         
246         bool DoPoll();
247         
248         bool DoConnectedPoll();
249         
250         void ShowStatus();      
251         
252         virtual bool OnDataReady();
253
254         virtual bool OnWriteReady();
255         
256         virtual bool OnConnected();
257         
258         bool DoEvent();
259         
260         std::string MkInfoStr();
261         
262         const char* StatusStr();
263         
264         SQLerror DoQuery(const SQLrequest &req);
265         
266         SQLerror Query(const SQLrequest &req);
267 };
268
269 class ModulePgSQL : public Module
270 {
271 private:
272         Server* Srv;
273         ConnMap connections;
274         unsigned long currid;
275         char* sqlsuccess;
276
277 public:
278         ModulePgSQL(Server* Me)
279         : Module::Module(Me), Srv(Me), currid(0)
280         {
281                 log(DEBUG, "%s 'SQL' feature", Srv->PublishFeature("SQL", this) ? "Published" : "Couldn't publish");
282                 log(DEBUG, "%s 'PgSQL' feature", Srv->PublishFeature("PgSQL", this) ? "Published" : "Couldn't publish");
283                 
284                 sqlsuccess = new char[strlen(SQLSUCCESS)+1];
285                 
286                 strcpy(sqlsuccess, SQLSUCCESS);
287
288                 OnRehash("");
289         }
290
291         void Implements(char* List)
292         {
293                 List[I_OnRequest] = List[I_OnRehash] = List[I_OnUserRegister] = List[I_OnCheckReady] = List[I_OnUserDisconnect] = 1;
294         }
295
296         virtual void OnRehash(const std::string &parameter)
297         {
298                 ConfigReader conf;
299                 
300                 /* Delete all the SQLConn objects in the connection lists,
301                  * this will call their destructors where they can handle
302                  * closing connections and such.
303                  */
304                 for(ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
305                 {
306                         DELETE(iter->second);
307                 }
308                 
309                 /* Empty out our list of connections */
310                 connections.clear();
311
312                 for(int i = 0; i < conf.Enumerate("database"); i++)
313                 {
314                         std::string id;
315                         SQLConn* newconn;
316                         
317                         id = conf.ReadValue("database", "id", i);
318                         newconn = new SQLConn(this, Srv,
319                                                                                 conf.ReadValue("database", "hostname", i),
320                                                                                 conf.ReadInteger("database", "port", i, true),
321                                                                                 conf.ReadValue("database", "name", i),
322                                                                                 conf.ReadValue("database", "username", i),
323                                                                                 conf.ReadValue("database", "password", i),
324                                                                                 conf.ReadFlag("database", "ssl", i));
325                         
326                         connections.insert(std::make_pair(id, newconn));
327                 }       
328         }
329         
330         virtual char* OnRequest(Request* request)
331         {
332                 if(strcmp(SQLREQID, request->GetData()) == 0)
333                 {
334                         SQLrequest* req = (SQLrequest*)request;
335                         ConnMap::iterator iter;
336                 
337                         log(DEBUG, "Got query: '%s' on id '%s'", req->query.c_str(), req->dbid.c_str());
338
339                         if((iter = connections.find(req->dbid)) != connections.end())
340                         {
341                                 /* Execute query */
342                                 req->error = iter->second->Query(*req);
343                                 req->id = NewID();
344                                 
345                                 return (req->error.Id() == NO_ERROR) ? sqlsuccess : NULL;
346                         }
347                         else
348                         {
349                                 req->error.Id(BAD_DBID);
350                                 return NULL;
351                         }
352                 }
353
354                 log(DEBUG, "Got unsupported API version string: %s", request->GetData());
355                 
356                 return NULL;
357         }
358         
359         unsigned long NewID()
360         {
361                 if (currid+1 == 0)
362                         currid++;
363                 
364                 return ++currid;
365         }
366                 
367         virtual Version GetVersion()
368         {
369                 return Version(1, 0, 0, 0, VF_VENDOR|VF_SERVICEPROVIDER);
370         }
371         
372         virtual ~ModulePgSQL()
373         {
374                 DELETE(sqlsuccess);
375         }       
376 };
377
378 SQLConn::SQLConn(ModulePgSQL* self, Server* srv, const std::string &h, unsigned int p, const std::string &d, const std::string &u, const std::string &pwd, bool s)
379 : InspSocket::InspSocket(), us(self), Srv(srv), dbhost(h), dbport(p), dbname(d), dbuser(u), dbpass(pwd), ssl(s), sql(NULL), status(CWRITE), qinprog(false)
380 {
381         log(DEBUG, "Creating new PgSQL connection to database %s on %s:%u (%s/%s)", dbname.c_str(), dbhost.c_str(), dbport, dbuser.c_str(), dbpass.c_str());
382
383         /* Some of this could be reviewed, unsure if I need to fill 'host' etc...
384          * just copied this over from the InspSocket constructor.
385          */
386         strlcpy(this->host, dbhost.c_str(), MAXBUF);
387         this->port = dbport;
388         
389         this->ClosePending = false;
390         
391         if(!inet_aton(this->host, &this->addy))
392         {
393                 /* Its not an ip, spawn the resolver.
394                  * PgSQL doesn't do nonblocking DNS 
395                  * lookups, so we do it for it.
396                  */
397                 
398                 log(DEBUG,"Attempting to resolve %s", this->host);
399                 
400                 this->dns.SetNS(Srv->GetConfig()->DNSServer);
401                 this->dns.ForwardLookupWithFD(this->host, fd);
402                 
403                 this->state = I_RESOLVING;
404                 socket_ref[this->fd] = this;
405                 
406                 return;
407         }
408         else
409         {
410                 log(DEBUG,"No need to resolve %s", this->host);
411                 strlcpy(this->IP, this->host, MAXBUF);
412                 
413                 if(!this->DoConnect())
414                 {
415                         throw ModuleException("Connect failed");
416                 }
417         }
418 }
419
420 SQLConn::~SQLConn()
421 {
422         Close();
423 }
424
425 bool SQLConn::DoResolve()
426 {       
427         log(DEBUG, "Checking for DNS lookup result");
428         
429         if(this->dns.HasResult())
430         {
431                 std::string res_ip = dns.GetResultIP();
432                 
433                 if(res_ip.length())
434                 {
435                         log(DEBUG, "Got result: %s", res_ip.c_str());
436                         
437                         strlcpy(this->IP, res_ip.c_str(), MAXBUF);
438                         dbhost = res_ip;
439                         
440                         socket_ref[this->fd] = NULL;
441                         
442                         return this->DoConnect();
443                 }
444                 else
445                 {
446                         log(DEBUG, "DNS lookup failed, dying horribly");
447                         Close();
448                         return false;
449                 }
450         }
451         else
452         {
453                 log(DEBUG, "No result for lookup yet!");
454                 return true;
455         }
456 }
457
458 bool SQLConn::DoConnect()
459 {
460         log(DEBUG, "SQLConn::DoConnect()");
461         
462         if(!(sql = PQconnectStart(MkInfoStr().c_str())))
463         {
464                 log(DEBUG, "Couldn't allocate PGconn structure, aborting: %s", PQerrorMessage(sql));
465                 Close();
466                 return false;
467         }
468         
469         if(PQstatus(sql) == CONNECTION_BAD)
470         {
471                 log(DEBUG, "PQconnectStart failed: %s", PQerrorMessage(sql));
472                 Close();
473                 return false;
474         }
475         
476         ShowStatus();
477         
478         if(PQsetnonblocking(sql, 1) == -1)
479         {
480                 log(DEBUG, "Couldn't set connection nonblocking: %s", PQerrorMessage(sql));
481                 Close();
482                 return false;
483         }
484         
485         /* OK, we've initalised the connection, now to get it hooked into the socket engine
486          * and then start polling it.
487          */
488         
489         log(DEBUG, "Old DNS socket: %d", this->fd);
490         this->fd = PQsocket(sql);
491         log(DEBUG, "New SQL socket: %d", this->fd);
492         
493         if(this->fd <= -1)
494         {
495                 log(DEBUG, "PQsocket says we have an invalid FD: %d", this->fd);
496                 Close();
497                 return false;
498         }
499         
500         this->state = I_CONNECTING;
501         ServerInstance->SE->AddFd(this->fd,false,X_ESTAB_MODULE);
502         socket_ref[this->fd] = this;
503         
504         /* Socket all hooked into the engine, now to tell PgSQL to start connecting */
505         
506         return DoPoll();
507 }
508
509 void SQLConn::Close()
510 {
511         log(DEBUG,"SQLConn::Close");
512         
513         if(this->fd > 01)
514                 socket_ref[this->fd] = NULL;
515         this->fd = -1;
516         this->state = I_ERROR;
517         this->OnError(I_ERR_SOCKET);
518         this->ClosePending = true;
519         
520         if(sql)
521         {
522                 PQfinish(sql);
523                 sql = NULL;
524         }
525         
526         return;
527 }
528
529 bool SQLConn::DoPoll()
530 {
531         switch(PQconnectPoll(sql))
532         {
533                 case PGRES_POLLING_WRITING:
534                         log(DEBUG, "PGconnectPoll: PGRES_POLLING_WRITING");
535                         WantWrite();
536                         status = CWRITE;
537                         return DoPoll();
538                 case PGRES_POLLING_READING:
539                         log(DEBUG, "PGconnectPoll: PGRES_POLLING_READING");
540                         status = CREAD;
541                         break;
542                 case PGRES_POLLING_FAILED:
543                         log(DEBUG, "PGconnectPoll: PGRES_POLLING_FAILED: %s", PQerrorMessage(sql));
544                         return false;
545                 case PGRES_POLLING_OK:
546                         log(DEBUG, "PGconnectPoll: PGRES_POLLING_OK");
547                         status = WWRITE;
548                         return DoConnectedPoll();
549                 default:
550                         log(DEBUG, "PGconnectPoll: wtf?");
551                         break;
552         }
553         
554         return true;
555 }
556
557 bool SQLConn::DoConnectedPoll()
558 {
559         if(!qinprog && queue.totalsize())
560         {
561                 /* There's no query currently in progress, and there's queries in the queue. */
562                 SQLrequest& query = queue.front();
563                 DoQuery(query);
564         }
565         
566         if(PQconsumeInput(sql))
567         {
568                 log(DEBUG, "PQconsumeInput succeeded");
569                         
570                 if(PQisBusy(sql))
571                 {
572                         log(DEBUG, "Still busy processing command though");
573                 }
574                 else if(qinprog)
575                 {
576                         log(DEBUG, "Looks like we have a result to process!");
577                         
578                         /* Grab the request we're processing */
579                         SQLrequest& query = queue.front();
580                         
581                         /* Get a pointer to the module we're about to return the result to */
582                         Module* to = query.GetSource();
583                         
584                         /* Fetch the result.. */
585                         PGresult* result = PQgetResult(sql);
586                         
587                         /* PgSQL would allow a query string to be sent which has multiple
588                          * queries in it, this isn't portable across database backends and
589                          * we don't want modules doing it. But just in case we make sure we
590                          * drain any results there are and just use the last one.
591                          * If the module devs are behaving there will only be one result.
592                          */
593                         while (PGresult* temp = PQgetResult(sql))
594                         {
595                                 PQclear(result);
596                                 result = temp;
597                         }
598                         
599                         if(to)
600                         {
601                                 /* ..and the result */
602                                 log(DEBUG, "Got result, status code: %s; error message: %s", PQresStatus(PQresultStatus(result)), PQresultErrorMessage(result));
603                                         
604                                 PgSQLresult reply(us, to, result);
605                                 
606                                 reply.Send();
607                                 
608                                 /* PgSQLresult's destructor will free the PGresult */
609                         }
610                         else
611                         {
612                                 /* If the client module is unloaded partway through a query then the provider will set
613                                  * the pointer to NULL. We cannot just cancel the query as the result will still come
614                                  * through at some point...and it could get messy if we play with invalid pointers...
615                                  */
616                                 log(DEBUG, "Looks like we're handling a zombie query from a module which unloaded before it got a result..fun. ID: %lu", query.id);
617                                 PQclear(result);
618                         }
619                         
620                         qinprog = false;
621                         queue.pop();                            
622                         DoConnectedPoll();
623                 }
624                 
625                 return true;
626         }
627         
628         log(DEBUG, "PQconsumeInput failed: %s", PQerrorMessage(sql));
629         return false;
630 }
631
632 void SQLConn::ShowStatus()
633 {
634         switch(PQstatus(sql))
635         {
636                 case CONNECTION_STARTED:
637                         log(DEBUG, "PQstatus: CONNECTION_STARTED: Waiting for connection to be made.");
638                         break;
639
640                 case CONNECTION_MADE:
641                         log(DEBUG, "PQstatus: CONNECTION_MADE: Connection OK; waiting to send.");
642                         break;
643                 
644                 case CONNECTION_AWAITING_RESPONSE:
645                         log(DEBUG, "PQstatus: CONNECTION_AWAITING_RESPONSE: Waiting for a response from the server.");
646                         break;
647                 
648                 case CONNECTION_AUTH_OK:
649                         log(DEBUG, "PQstatus: CONNECTION_AUTH_OK: Received authentication; waiting for backend start-up to finish.");
650                         break;
651                 
652                 case CONNECTION_SSL_STARTUP:
653                         log(DEBUG, "PQstatus: CONNECTION_SSL_STARTUP: Negotiating SSL encryption.");
654                         break;
655                 
656                 case CONNECTION_SETENV:
657                         log(DEBUG, "PQstatus: CONNECTION_SETENV: Negotiating environment-driven parameter settings.");
658                         break;
659                 
660                 default:
661                         log(DEBUG, "PQstatus: ???");
662         }
663 }
664
665 bool SQLConn::OnDataReady()
666 {
667         /* Always return true here, false would close the socket - we need to do that ourselves with the pgsql API */
668         log(DEBUG, "OnDataReady(): status = %s", StatusStr());
669         
670         return DoEvent();
671 }
672
673 bool SQLConn::OnWriteReady()
674 {
675         /* Always return true here, false would close the socket - we need to do that ourselves with the pgsql API */
676         log(DEBUG, "OnWriteReady(): status = %s", StatusStr());
677         
678         return DoEvent();
679 }
680
681 bool SQLConn::OnConnected()
682 {
683         log(DEBUG, "OnConnected(): status = %s", StatusStr());
684         
685         return DoEvent();
686 }
687
688 bool SQLConn::DoEvent()
689 {
690         bool ret;
691         
692         if((status == CREAD) || (status == CWRITE))
693         {
694                 ret = DoPoll();
695         }
696         else
697         {
698                 ret = DoConnectedPoll();
699         }
700         
701         switch(PQflush(sql))
702         {
703                 case -1:
704                         log(DEBUG, "Error flushing write queue: %s", PQerrorMessage(sql));
705                         break;
706                 case 0:
707                         log(DEBUG, "Successfully flushed write queue (or there was nothing to write)");
708                         break;
709                 case 1:
710                         log(DEBUG, "Not all of the write queue written, triggering write event so we can have another go");
711                         WantWrite();
712                         break;
713         }
714
715         return ret;
716 }
717
718 std::string SQLConn::MkInfoStr()
719 {                       
720         std::ostringstream conninfo("connect_timeout = '2'");
721         
722         if(dbhost.length())
723                 conninfo << " hostaddr = '" << dbhost << "'";
724         
725         if(dbport)
726                 conninfo << " port = '" << dbport << "'";
727         
728         if(dbname.length())
729                 conninfo << " dbname = '" << dbname << "'";
730         
731         if(dbuser.length())
732                 conninfo << " user = '" << dbuser << "'";
733         
734         if(dbpass.length())
735                 conninfo << " password = '" << dbpass << "'";
736         
737         if(ssl)
738                 conninfo << " sslmode = 'require'";
739         
740         return conninfo.str();
741 }
742
743 const char* SQLConn::StatusStr()
744 {
745         if(status == CREAD) return "CREAD";
746         if(status == CWRITE) return "CWRITE";
747         if(status == WREAD) return "WREAD";
748         if(status == WWRITE) return "WWRITE";
749         return "Err...what, erm..BUG!";
750 }
751
752 SQLerror SQLConn::DoQuery(const SQLrequest &req)
753 {
754         if((status == WREAD) || (status == WWRITE))
755         {
756                 if(!qinprog)
757                 {
758                         if(PQsendQuery(sql, req.query.c_str()))
759                         {
760                                 log(DEBUG, "Dispatched query: %s", req.query.c_str());
761                                 qinprog = true;
762                                 return SQLerror();
763                         }
764                         else
765                         {
766                                 log(DEBUG, "Failed to dispatch query: %s", PQerrorMessage(sql));
767                                 return SQLerror(QSEND_FAIL, PQerrorMessage(sql));
768                         }
769                 }
770         }
771
772         log(DEBUG, "Can't query until connection is complete");
773         return SQLerror(BAD_CONN, "Can't query until connection is complete");
774 }
775
776 SQLerror SQLConn::Query(const SQLrequest &req)
777 {
778         queue.push(req);
779         
780         if(!qinprog && queue.totalsize())
781         {
782                 /* There's no query currently in progress, and there's queries in the queue. */
783                 SQLrequest& query = queue.front();
784                 return DoQuery(query);
785         }
786         else
787         {
788                 return SQLerror();
789         }
790 }
791
792 class ModulePgSQLFactory : public ModuleFactory
793 {
794  public:
795         ModulePgSQLFactory()
796         {
797         }
798         
799         ~ModulePgSQLFactory()
800         {
801         }
802         
803         virtual Module * CreateModule(Server* Me)
804         {
805                 return new ModulePgSQL(Me);
806         }
807 };
808
809
810 extern "C" void * init_module( void )
811 {
812         return new ModulePgSQLFactory;
813 }