]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/extra/m_mssql.cpp
Fix compilation of SQL modules, use GlobalCulls to clean up deleted modules
[user/henk/code/inspircd.git] / src / modules / extra / m_mssql.cpp
1 /*               +------------------------------------+
2  *               | Inspire Internet Relay Chat Daemon |
3  *               +------------------------------------+
4  *
5  *      InspIRCd: (C) 2002-2009 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/Credits
7  *
8  * This program is free but copyrighted software; see
9  *                        the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15 #include <tds.h>
16 #include <tdsconvert.h>
17 #include "users.h"
18 #include "channels.h"
19 #include "modules.h"
20
21 #include "m_sqlv2.h"
22
23 /* $ModDesc: MsSQL provider */
24 /* $CompileFlags: exec("grep VERSION_NO /usr/include/tdsver.h 2>/dev/null | perl -e 'print "-D_TDSVER=".((<> =~ /freetds v(\d+\.\d+)/i) ? $1*100 : 0);'") */
25 /* $LinkerFlags: -ltds */
26 /* $ModDep: m_sqlv2.h */
27
28 class SQLConn;
29 class MsSQLResult;
30 class ModuleMsSQL;
31
32 typedef std::map<std::string, SQLConn*> ConnMap;
33 typedef std::deque<MsSQLResult*> ResultQueue;
34
35 unsigned long count(const char * const str, char a)
36 {
37         unsigned long n = 0;
38         for (const char *p = str; *p; ++p)
39         {
40                 if (*p == '?')
41                         ++n;
42         }
43         return n;
44 }
45
46 ConnMap connections;
47 Mutex* ResultsMutex;
48 Mutex* LoggingMutex;
49
50 class QueryThread : public SocketThread
51 {
52   private:
53         ModuleMsSQL* Parent;
54   public:
55         QueryThread(ModuleMsSQL* mod)
56         : SocketThread(si), Parent(mod),        {
57         }
58         ~QueryThread() { }
59         virtual void Run();
60         virtual void OnNotify();
61 };
62
63 class MsSQLResult : public SQLresult
64 {
65  private:
66         int currentrow;
67         int rows;
68         int cols;
69
70         std::vector<std::string> colnames;
71         std::vector<SQLfieldList> fieldlists;
72         SQLfieldList emptyfieldlist;
73
74         SQLfieldList* fieldlist;
75         SQLfieldMap* fieldmap;
76
77  public:
78         MsSQLResult(Module* self, Module* to, unsigned int rid)
79         : SQLresult(self, to, rid), currentrow(0), rows(0), cols(0), fieldlist(NULL), fieldmap(NULL)
80         {
81         }
82
83         ~MsSQLResult()
84         {
85         }
86
87         void AddRow(int colsnum, char **dat, char **colname)
88         {
89                 colnames.clear();
90                 cols = colsnum;
91                 for (int i = 0; i < colsnum; i++)
92                 {
93                         fieldlists.resize(fieldlists.size()+1);
94                         colnames.push_back(colname[i]);
95                         SQLfield sf(dat[i] ? dat[i] : "", dat[i] ? false : true);
96                         fieldlists[rows].push_back(sf);
97                 }
98                 rows++;
99         }
100
101         void UpdateAffectedCount()
102         {
103                 rows++;
104         }
105
106         virtual int Rows()
107         {
108                 return rows;
109         }
110
111         virtual int Cols()
112         {
113                 return cols;
114         }
115
116         virtual std::string ColName(int column)
117         {
118                 if (column < (int)colnames.size())
119                 {
120                         return colnames[column];
121                 }
122                 else
123                 {
124                         throw SQLbadColName();
125                 }
126                 return "";
127         }
128
129         virtual int ColNum(const std::string &column)
130         {
131                 for (unsigned int i = 0; i < colnames.size(); i++)
132                 {
133                         if (column == colnames[i])
134                                 return i;
135                 }
136                 throw SQLbadColName();
137                 return 0;
138         }
139
140         virtual SQLfield GetValue(int row, int column)
141         {
142                 if ((row >= 0) && (row < rows) && (column >= 0) && (column < Cols()))
143                 {
144                         return fieldlists[row][column];
145                 }
146
147                 throw SQLbadColName();
148
149                 /* XXX: We never actually get here because of the throw */
150                 return SQLfield("",true);
151         }
152
153         virtual SQLfieldList& GetRow()
154         {
155                 if (currentrow < rows)
156                         return fieldlists[currentrow];
157                 else
158                         return emptyfieldlist;
159         }
160
161         virtual SQLfieldMap& GetRowMap()
162         {
163                 /* In an effort to reduce overhead we don't actually allocate the map
164                  * until the first time it's needed...so...
165                  */
166                 if(fieldmap)
167                 {
168                         fieldmap->clear();
169                 }
170                 else
171                 {
172                         fieldmap = new SQLfieldMap;
173                 }
174
175                 if (currentrow < rows)
176                 {
177                         for (int i = 0; i < Cols(); i++)
178                         {
179                                 fieldmap->insert(std::make_pair(ColName(i), GetValue(currentrow, i)));
180                         }
181                         currentrow++;
182                 }
183
184                 return *fieldmap;
185         }
186
187         virtual SQLfieldList* GetRowPtr()
188         {
189                 fieldlist = new SQLfieldList();
190
191                 if (currentrow < rows)
192                 {
193                         for (int i = 0; i < Rows(); i++)
194                         {
195                                 fieldlist->push_back(fieldlists[currentrow][i]);
196                         }
197                         currentrow++;
198                 }
199                 return fieldlist;
200         }
201
202         virtual SQLfieldMap* GetRowMapPtr()
203         {
204                 fieldmap = new SQLfieldMap();
205
206                 if (currentrow < rows)
207                 {
208                         for (int i = 0; i < Cols(); i++)
209                         {
210                                 fieldmap->insert(std::make_pair(colnames[i],GetValue(currentrow, i)));
211                         }
212                         currentrow++;
213                 }
214
215                 return fieldmap;
216         }
217
218         virtual void Free(SQLfieldMap* fm)
219         {
220                 delete fm;
221         }
222
223         virtual void Free(SQLfieldList* fl)
224         {
225                 delete fl;
226         }
227 };
228
229 class SQLConn : public classbase
230 {
231  private:
232         ResultQueue results;
233         Module* mod;
234         SQLhost host;
235         TDSLOGIN* login;
236         TDSSOCKET* sock;
237         TDSCONTEXT* context;
238
239  public:
240         QueryQueue queue;
241
242         SQLConn(Module* m, const SQLhost& hi)
243         : mod(m), host(hi), login(NULL), sock(NULL), context(NULL)
244         {
245                 if (OpenDB())
246                 {
247                         std::string query("USE " + host.name);
248                         if (tds_submit_query(sock, query.c_str()) == TDS_SUCCEED)
249                         {
250                                 if (tds_process_simple_query(sock) != TDS_SUCCEED)
251                                 {
252                                         LoggingMutex->Lock();
253                                         ServerInstance->Logs->Log("m_mssql",DEFAULT, "WARNING: Could not select database " + host.name + " for DB with id: " + host.id);
254                                         LoggingMutex->Unlock();
255                                         CloseDB();
256                                 }
257                         }
258                         else
259                         {
260                                 LoggingMutex->Lock();
261                                 ServerInstance->Logs->Log("m_mssql",DEFAULT, "WARNING: Could not select database " + host.name + " for DB with id: " + host.id);
262                                 LoggingMutex->Unlock();
263                                 CloseDB();
264                         }
265                 }
266                 else
267                 {
268                         LoggingMutex->Lock();
269                         ServerInstance->Logs->Log("m_mssql",DEFAULT, "WARNING: Could not connect to DB with id: " + host.id);
270                         LoggingMutex->Unlock();
271                         CloseDB();
272                 }
273         }
274
275         ~SQLConn()
276         {
277                 CloseDB();
278         }
279
280         SQLerror Query(SQLrequest &req)
281         {
282                 if (!sock)
283                         return SQLerror(SQL_BAD_CONN, "Socket was NULL, check if SQL server is running.");
284
285                 /* Pointer to the buffer we screw around with substitution in */
286                 char* query;
287
288                 /* Pointer to the current end of query, where we append new stuff */
289                 char* queryend;
290
291                 /* Total length of the unescaped parameters */
292                 unsigned long maxparamlen, paramcount;
293
294                 /* The length of the longest parameter */
295                 maxparamlen = 0;
296
297                 for(ParamL::iterator i = req.query.p.begin(); i != req.query.p.end(); i++)
298                 {
299                         if (i->size() > maxparamlen)
300                                 maxparamlen = i->size();
301                 }
302
303                 /* How many params are there in the query? */
304                 paramcount = count(req.query.q.c_str(), '?');
305
306                 /* This stores copy of params to be inserted with using numbered params 1;3B*/
307                 ParamL paramscopy(req.query.p);
308
309                 /* To avoid a lot of allocations, allocate enough memory for the biggest the escaped query could possibly be.
310                  * sizeofquery + (maxtotalparamlength*2) + 1
311                  *
312                  * The +1 is for null-terminating the string
313                  */
314
315                 query = new char[req.query.q.length() + (maxparamlen*paramcount*2) + 1];
316                 queryend = query;
317
318                 for(unsigned long i = 0; i < req.query.q.length(); i++)
319                 {
320                         if(req.query.q[i] == '?')
321                         {
322                                 /* We found a place to substitute..what fun.
323                                  * use mssql calls to escape and write the
324                                  * escaped string onto the end of our query buffer,
325                                  * then we "just" need to make sure queryend is
326                                  * pointing at the right place.
327                                  */
328
329                                 /* Is it numbered parameter?
330                                  */
331
332                                 bool numbered;
333                                 numbered = false;
334
335                                 /* Numbered parameter number :|
336                                  */
337                                 unsigned int paramnum;
338                                 paramnum = 0;
339
340                                 /* Let's check if it's a numbered param. And also calculate it's number.
341                                  */
342
343                                 while ((i < req.query.q.length() - 1) && (req.query.q[i+1] >= '0') && (req.query.q[i+1] <= '9'))
344                                 {
345                                         numbered = true;
346                                         ++i;
347                                         paramnum = paramnum * 10 + req.query.q[i] - '0';
348                                 }
349
350                                 if (paramnum > paramscopy.size() - 1)
351                                 {
352                                         /* index is out of range!
353                                          */
354                                         numbered = false;
355                                 }
356
357                                 if (numbered)
358                                 {
359                                         /* Custom escaping for this one. converting ' to '' should make SQL Server happy. Ugly but fast :]
360                                          */
361                                         char* escaped = new char[(paramscopy[paramnum].length() * 2) + 1];
362                                         char* escend = escaped;
363                                         for (std::string::iterator p = paramscopy[paramnum].begin(); p < paramscopy[paramnum].end(); p++)
364                                         {
365                                                 if (*p == '\'')
366                                                 {
367                                                         *escend = *p;
368                                                         escend++;
369                                                         *escend = *p;
370                                                 }
371                                                 *escend = *p;
372                                                 escend++;
373                                         }
374                                         *escend = 0;
375
376                                         for (char* n = escaped; *n; n++)
377                                         {
378                                                 *queryend = *n;
379                                                 queryend++;
380                                         }
381                                         delete[] escaped;
382                                 }
383                                 else if (req.query.p.size())
384                                 {
385                                         /* Custom escaping for this one. converting ' to '' should make SQL Server happy. Ugly but fast :]
386                                          */
387                                         char* escaped = new char[(req.query.p.front().length() * 2) + 1];
388                                         char* escend = escaped;
389                                         for (std::string::iterator p = req.query.p.front().begin(); p < req.query.p.front().end(); p++)
390                                         {
391                                                 if (*p == '\'')
392                                                 {
393                                                         *escend = *p;
394                                                         escend++;
395                                                         *escend = *p;
396                                                 }
397                                                 *escend = *p;
398                                                 escend++;
399                                         }
400                                         *escend = 0;
401
402                                         for (char* n = escaped; *n; n++)
403                                         {
404                                                 *queryend = *n;
405                                                 queryend++;
406                                         }
407                                         delete[] escaped;
408                                         req.query.p.pop_front();
409                                 }
410                                 else
411                                         break;
412                         }
413                         else
414                         {
415                                 *queryend = req.query.q[i];
416                                 queryend++;
417                         }
418                 }
419                 *queryend = 0;
420                 req.query.q = query;
421
422                 MsSQLResult* res = new MsSQLResult((Module*)mod, req.GetSource(), req.id);
423                 res->dbid = host.id;
424                 res->query = req.query.q;
425
426                 char* msquery = strdup(req.query.q.data());
427                 LoggingMutex->Lock();
428                 ServerInstance->Logs->Log("m_mssql",DEBUG,"doing Query: %s",msquery);
429                 LoggingMutex->Unlock();
430                 if (tds_submit_query(sock, msquery) != TDS_SUCCEED)
431                 {
432                         std::string error("failed to execute: "+std::string(req.query.q.data()));
433                         delete[] query;
434                         delete res;
435                         free(msquery);
436                         return SQLerror(SQL_QSEND_FAIL, error);
437                 }
438                 delete[] query;
439                 free(msquery);
440
441                 int tds_res;
442                 while (tds_process_tokens(sock, &tds_res, NULL, TDS_TOKEN_RESULTS) == TDS_SUCCEED)
443                 {
444                         //ServerInstance->Logs->Log("m_mssql",DEBUG,"<******> result type: %d", tds_res);
445                         //ServerInstance->Logs->Log("m_mssql",DEBUG,"AFFECTED ROWS: %d", sock->rows_affected);
446                         switch (tds_res)
447                         {
448                                 case TDS_ROWFMT_RESULT:
449                                         break;
450
451                                 case TDS_DONE_RESULT:
452                                         if (sock->rows_affected > -1)
453                                         {
454                                                 for (int c = 0; c < sock->rows_affected; c++)  res->UpdateAffectedCount();
455                                                 continue;
456                                         }
457                                         break;
458
459                                 case TDS_ROW_RESULT:
460                                         while (tds_process_tokens(sock, &tds_res, NULL, TDS_STOPAT_ROWFMT|TDS_RETURN_DONE|TDS_RETURN_ROW) == TDS_SUCCEED)
461                                         {
462                                                 if (tds_res != TDS_ROW_RESULT)
463                                                         break;
464
465                                                 if (!sock->current_results)
466                                                         continue;
467
468                                                 if (sock->res_info->row_count > 0)
469                                                 {
470                                                         int cols = sock->res_info->num_cols;
471                                                         char** name = new char*[MAXBUF];
472                                                         char** data = new char*[MAXBUF];
473                                                         for (int j=0; j<cols; j++)
474                                                         {
475                                                                 TDSCOLUMN* col = sock->current_results->columns[j];
476                                                                 name[j] = col->column_name;
477
478                                                                 int ctype;
479                                                                 int srclen;
480                                                                 unsigned char* src;
481                                                                 CONV_RESULT dres;
482                                                                 ctype = tds_get_conversion_type(col->column_type, col->column_size);
483 #if _TDSVER >= 82
484                                                                         src = col->column_data;
485 #else
486                                                                         src = &(sock->current_results->current_row[col->column_offset]);
487 #endif
488                                                                 srclen = col->column_cur_size;
489                                                                 tds_convert(sock->tds_ctx, ctype, (TDS_CHAR *) src, srclen, SYBCHAR, &dres);
490                                                                 data[j] = (char*)dres.ib;
491                                                         }
492                                                         ResultReady(res, cols, data, name);
493                                                 }
494                                         }
495                                         break;
496
497                                 default:
498                                         break;
499                         }
500                 }
501                 ResultsMutex->Lock();
502                 results.push_back(res);
503                 ResultsMutex->Unlock();
504                 return SQLerror();
505         }
506
507         static int HandleMessage(const TDSCONTEXT * pContext, TDSSOCKET * pTdsSocket, TDSMESSAGE * pMessage)
508         {
509                 SQLConn* sc = (SQLConn*)pContext->parent;
510                 LoggingMutex->Lock();
511                 sc->ServerInstance->Logs->Log("m_mssql", DEBUG, "Message for DB with id: %s -> %s", sc->host.id.c_str(), pMessage->message);
512                 LoggingMutex->Unlock();
513                 return 0;
514         }
515
516         static int HandleError(const TDSCONTEXT * pContext, TDSSOCKET * pTdsSocket, TDSMESSAGE * pMessage)
517         {
518                 SQLConn* sc = (SQLConn*)pContext->parent;
519                 LoggingMutex->Lock();
520                 sc->ServerInstance->Logs->Log("m_mssql", DEFAULT, "Error for DB with id: %s -> %s", sc->host.id.c_str(), pMessage->message);
521                 LoggingMutex->Unlock();
522                 return 0;
523         }
524
525         void ResultReady(MsSQLResult *res, int cols, char **data, char **colnames)
526         {
527                 res->AddRow(cols, data, colnames);
528         }
529
530         void AffectedReady(MsSQLResult *res)
531         {
532                 res->UpdateAffectedCount();
533         }
534
535         bool OpenDB()
536         {
537                 CloseDB();
538
539                 TDSCONNECTION* conn = NULL;
540
541                 login = tds_alloc_login();
542                 tds_set_app(login, "TSQL");
543                 tds_set_library(login,"TDS-Library");
544                 tds_set_host(login, "");
545                 tds_set_server(login, host.host.c_str());
546                 tds_set_server_addr(login, host.host.c_str());
547                 tds_set_user(login, host.user.c_str());
548                 tds_set_passwd(login, host.pass.c_str());
549                 tds_set_port(login, host.port);
550                 tds_set_packet(login, 512);
551
552                 context = tds_alloc_context(this);
553                 context->msg_handler = HandleMessage;
554                 context->err_handler = HandleError;
555
556                 sock = tds_alloc_socket(context, 512);
557                 tds_set_parent(sock, NULL);
558
559                 conn = tds_read_config_info(NULL, login, context->locale);
560
561                 if (tds_connect(sock, conn) == TDS_SUCCEED)
562                 {
563                         tds_free_connection(conn);
564                         return 1;
565                 }
566                 tds_free_connection(conn);
567                 return 0;
568         }
569
570         void CloseDB()
571         {
572                 if (sock)
573                 {
574                         tds_free_socket(sock);
575                         sock = NULL;
576                 }
577                 if (context)
578                 {
579                         tds_free_context(context);
580                         context = NULL;
581                 }
582                 if (login)
583                 {
584                         tds_free_login(login);
585                         login = NULL;
586                 }
587         }
588
589         SQLhost GetConfHost()
590         {
591                 return host;
592         }
593
594         void SendResults()
595         {
596                 while (results.size())
597                 {
598                         MsSQLResult* res = results[0];
599                         ResultsMutex->Lock();
600                         if (res->GetDest())
601                         {
602                                 res->Send();
603                         }
604                         else
605                         {
606                                 /* If the client module is unloaded partway through a query then the provider will set
607                                  * the pointer to NULL. We cannot just cancel the query as the result will still come
608                                  * through at some point...and it could get messy if we play with invalid pointers...
609                                  */
610                                 delete res;
611                         }
612                         results.pop_front();
613                         ResultsMutex->Unlock();
614                 }
615         }
616
617         void ClearResults()
618         {
619                 while (results.size())
620                 {
621                         MsSQLResult* res = results[0];
622                         delete res;
623                         results.pop_front();
624                 }
625         }
626
627         void DoLeadingQuery()
628         {
629                 SQLrequest& req = queue.front();
630                 req.error = Query(req);
631         }
632
633 };
634
635
636 class ModuleMsSQL : public Module
637 {
638  private:
639         unsigned long currid;
640         QueryThread* queryDispatcher;
641
642  public:
643         ModuleMsSQL()
644         : currid(0)
645         {
646                 LoggingMutex = new Mutex();
647                 ResultsMutex = new Mutex();
648
649                 ServerInstance->Modules->UseInterface("SQLutils");
650
651                 if (!ServerInstance->Modules->PublishFeature("SQL", this))
652                 {
653                         throw ModuleException("m_mssql: Unable to publish feature 'SQL'");
654                 }
655
656                 ReadConf();
657
658                 queryDispatcher = new QueryThread(this);
659                 ServerInstance->Threads->Start(queryDispatcher);
660
661                 ServerInstance->Modules->PublishInterface("SQL", this);
662                 Implementation eventlist[] = { I_OnRequest, I_OnRehash };
663                 ServerInstance->Modules->Attach(eventlist, this, 2);
664         }
665
666         virtual ~ModuleMsSQL()
667         {
668                 queryDispatcher->join();
669                 delete queryDispatcher;
670                 ClearQueue();
671                 ClearAllConnections();
672
673                 ServerInstance->Modules->UnpublishInterface("SQL", this);
674                 ServerInstance->Modules->UnpublishFeature("SQL");
675                 ServerInstance->Modules->DoneWithInterface("SQLutils");
676
677                 delete LoggingMutex;
678                 delete ResultsMutex;
679         }
680
681         void SendQueue()
682         {
683                 for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
684                 {
685                         iter->second->SendResults();
686                 }
687         }
688
689         void ClearQueue()
690         {
691                 for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
692                 {
693                         iter->second->ClearResults();
694                 }
695         }
696
697         bool HasHost(const SQLhost &host)
698         {
699                 for (ConnMap::iterator iter = connections.begin(); iter != connections.end(); iter++)
700                 {
701                         if (host == iter->second->GetConfHost())
702                                 return true;
703                 }
704                 return false;
705         }
706
707         bool HostInConf(const SQLhost &h)
708         {
709                 ConfigReader conf;
710                 for(int i = 0; i < conf.Enumerate("database"); i++)
711                 {
712                         SQLhost host;
713                         host.id         = conf.ReadValue("database", "id", i);
714                         host.host       = conf.ReadValue("database", "hostname", i);
715                         host.port       = conf.ReadInteger("database", "port", "1433", i, true);
716                         host.name       = conf.ReadValue("database", "name", i);
717                         host.user       = conf.ReadValue("database", "username", i);
718                         host.pass       = conf.ReadValue("database", "password", i);
719                         if (h == host)
720                                 return true;
721                 }
722                 return false;
723         }
724
725         void ReadConf()
726         {
727                 ClearOldConnections();
728
729                 ConfigReader conf;
730                 for(int i = 0; i < conf.Enumerate("database"); i++)
731                 {
732                         SQLhost host;
733
734                         host.id         = conf.ReadValue("database", "id", i);
735                         host.host       = conf.ReadValue("database", "hostname", i);
736                         host.port       = conf.ReadInteger("database", "port", "1433", i, true);
737                         host.name       = conf.ReadValue("database", "name", i);
738                         host.user       = conf.ReadValue("database", "username", i);
739                         host.pass       = conf.ReadValue("database", "password", i);
740
741                         if (HasHost(host))
742                                 continue;
743
744                         this->AddConn(host);
745                 }
746         }
747
748         void AddConn(const SQLhost& hi)
749         {
750                 if (HasHost(hi))
751                 {
752                         LoggingMutex->Lock();
753                         ServerInstance->Logs->Log("m_mssql",DEFAULT, "WARNING: A MsSQL connection with id: %s already exists. Aborting database open attempt.", hi.id.c_str());
754                         LoggingMutex->Unlock();
755                         return;
756                 }
757
758                 SQLConn* newconn;
759
760                 newconn = new SQLConn(this, hi);
761
762                 connections.insert(std::make_pair(hi.id, newconn));
763         }
764
765         void ClearOldConnections()
766         {
767                 ConnMap::iterator iter,safei;
768                 for (iter = connections.begin(); iter != connections.end(); iter++)
769                 {
770                         if (!HostInConf(iter->second->GetConfHost()))
771                         {
772                                 delete iter->second;
773                                 safei = iter;
774                                 --iter;
775                                 connections.erase(safei);
776                         }
777                 }
778         }
779
780         void ClearAllConnections()
781         {
782                 ConnMap::iterator i;
783                 while ((i = connections.begin()) != connections.end())
784                 {
785                         connections.erase(i);
786                         delete i->second;
787                 }
788         }
789
790         virtual void OnRehash(User* user)
791         {
792                 queryDispatcher->LockQueue();
793                 ReadConf();
794                 queryDispatcher->UnlockQueueWakeup();
795         }
796
797         virtual const char* OnRequest(Request* request)
798         {
799                 if(strcmp(SQLREQID, request->GetId()) == 0)
800                 {
801                         SQLrequest* req = (SQLrequest*)request;
802
803                         queryDispatcher->LockQueue();
804
805                         ConnMap::iterator iter;
806
807                         const char* returnval = NULL;
808
809                         if((iter = connections.find(req->dbid)) != connections.end())
810                         {
811                                 req->id = NewID();
812                                 iter->second->queue.push(*req);
813                                 returnval= SQLSUCCESS;
814                         }
815                         else
816                         {
817                                 req->error.Id(SQL_BAD_DBID);
818                         }
819
820                         queryDispatcher->UnlockQueueWakeup();
821
822                         return returnval;
823                 }
824                 return NULL;
825         }
826
827         unsigned long NewID()
828         {
829                 if (currid+1 == 0)
830                         currid++;
831
832                 return ++currid;
833         }
834
835         virtual Version GetVersion()
836         {
837                 return Version("MsSQL provider", VF_VENDOR | VF_SERVICEPROVIDER, API_VERSION);
838         }
839
840 };
841
842 void QueryThread::OnNotify()
843 {
844         Parent->SendQueue();
845 }
846
847 void QueryThread::Run()
848 {
849         this->LockQueue();
850         while (this->GetExitFlag() == false)
851         {
852                 SQLConn* conn = NULL;
853                 for (ConnMap::iterator i = connections.begin(); i != connections.end(); i++)
854                 {
855                         if (i->second->queue.totalsize())
856                         {
857                                 conn = i->second;
858                                 break;
859                         }
860                 }
861                 if (conn)
862                 {
863                         this->UnlockQueue();
864                         conn->DoLeadingQuery();
865                         this->NotifyParent();
866                         this->LockQueue();
867                         conn->queue.pop();
868                 }
869                 else
870                 {
871                         this->WaitForQueue();
872                 }
873         }
874         this->UnlockQueue();
875 }
876
877 MODULE_INIT(ModuleMsSQL)