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