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