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