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