1"""
2This is an effort to convert the pymssql low-level C module to Cython.
3"""
4#
5# _mssql.pyx
6#
7#   Copyright (C) 2003 Joon-cheol Park <jooncheol@gmail.com>
8#                 2008 Andrzej Kukula <akukula@gmail.com>
9#                 2009-2010 Damien Churchill <damoxc@gmail.com>
10#
11# This library is free software; you can redistribute it and/or
12# modify it under the terms of the GNU Lesser General Public
13# License as published by the Free Software Foundation; either
14# version 2.1 of the License, or (at your option) any later version.
15#
16# This library is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19# Lesser General Public License for more details.
20#
21# You should have received a copy of the GNU Lesser General Public
22# License along with this library; if not, write to the Free Software
23# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24# MA  02110-1301  USA
25#
26
27DEF PYMSSQL_DEBUG = 0
28DEF PYMSSQL_DEBUG_ERRORS = 0
29DEF PYMSSQL_CHARSETBUFSIZE = 100
30DEF MSSQLDB_MSGSIZE = 1024
31DEF PYMSSQL_MSGSIZE = (MSSQLDB_MSGSIZE * 8)
32DEF EXCOMM = 9
33
34# Provide constants missing in FreeTDS 0.82 so that we can build against it
35DEF DBVERSION_71 = 5
36DEF DBVERSION_72 = 6
37# Provide constant missing from FreeTDS 0.91 so that we can build against it
38DEF DBVERSION_73 = 7
39
40ROW_FORMAT_TUPLE = 1
41ROW_FORMAT_DICT = 2
42
43cdef int _ROW_FORMAT_TUPLE = ROW_FORMAT_TUPLE
44cdef int _ROW_FORMAT_DICT = ROW_FORMAT_DICT
45
46from cpython cimport PY_MAJOR_VERSION, PY_MINOR_VERSION
47
48from collections import Iterable
49import os
50import sys
51import socket
52import decimal
53import binascii
54import datetime
55import re
56import uuid
57
58from sqlfront cimport *
59
60from libc.stdio cimport fprintf, snprintf, stderr, FILE
61from libc.string cimport strlen, strncpy, memcpy
62
63from cpython cimport bool
64from cpython.mem cimport PyMem_Malloc, PyMem_Free
65from cpython.long cimport PY_LONG_LONG
66from cpython.ref cimport Py_INCREF
67from cpython.tuple cimport PyTuple_New, PyTuple_SetItem
68
69cdef extern from "version.h":
70    const char *PYMSSQL_VERSION
71
72cdef extern from "cpp_helpers.h":
73    cdef bint FREETDS_SUPPORTS_DBSETLDBNAME
74
75# Vars to store messages from the server in
76cdef int _mssql_last_msg_no = 0
77cdef int _mssql_last_msg_severity = 0
78cdef int _mssql_last_msg_state = 0
79cdef int _mssql_last_msg_line = 0
80cdef char *_mssql_last_msg_str = <char *>PyMem_Malloc(PYMSSQL_MSGSIZE)
81_mssql_last_msg_str[0] = <char>0
82cdef char *_mssql_last_msg_srv = <char *>PyMem_Malloc(PYMSSQL_MSGSIZE)
83_mssql_last_msg_srv[0] = <char>0
84cdef char *_mssql_last_msg_proc = <char *>PyMem_Malloc(PYMSSQL_MSGSIZE)
85_mssql_last_msg_proc[0] = <char>0
86IF PYMSSQL_DEBUG == 1:
87    cdef int _row_count = 0
88
89cdef bytes HOSTNAME = socket.gethostname().encode('utf-8')
90
91# List to store the connection objects in
92cdef list connection_object_list = list()
93
94# Store the 32bit int limit values
95cdef int MAX_INT = 2147483647
96cdef int MIN_INT = -2147483648
97
98# Store the module version
99__full_version__ = PYMSSQL_VERSION.decode('ascii')
100__version__ = '.'.join(__full_version__.split('.')[:3])
101VERSION = tuple(int(c) for c in __full_version__.split('.')[:3])
102
103#############################
104## DB-API type definitions ##
105#############################
106STRING = 1
107BINARY = 2
108NUMBER = 3
109DATETIME = 4
110DECIMAL = 5
111
112##################
113## DB-LIB types ##
114##################
115SQLBINARY = SYBBINARY
116SQLBIT = SYBBIT
117SQLBITN = 104
118SQLCHAR = SYBCHAR
119SQLDATETIME = SYBDATETIME
120SQLDATETIM4 = SYBDATETIME4
121SQLDATETIMN = SYBDATETIMN
122SQLDECIMAL = SYBDECIMAL
123SQLFLT4 = SYBREAL
124SQLFLT8 = SYBFLT8
125SQLFLTN = SYBFLTN
126SQLIMAGE = SYBIMAGE
127SQLINT1 = SYBINT1
128SQLINT2 = SYBINT2
129SQLINT4 = SYBINT4
130SQLINT8 = SYBINT8
131SQLINTN = SYBINTN
132SQLMONEY = SYBMONEY
133SQLMONEY4 = SYBMONEY4
134SQLMONEYN = SYBMONEYN
135SQLNUMERIC = SYBNUMERIC
136SQLREAL = SYBREAL
137SQLTEXT = SYBTEXT
138SQLVARBINARY = SYBVARBINARY
139SQLVARCHAR = SYBVARCHAR
140SQLUUID = 36
141
142SQLDATE = 40
143SQLTIME = 41
144SQLDATETIME2 = 42
145
146#######################
147## Exception classes ##
148#######################
149cdef extern from "pyerrors.h":
150    ctypedef class __builtin__.Exception [object PyBaseExceptionObject]:
151        pass
152
153cdef class MSSQLException(Exception):
154    """
155    Base exception class for the MSSQL driver.
156    """
157
158cdef class MSSQLDriverException(MSSQLException):
159    """
160    Inherits from the base class and raised when an error is caused within
161    the driver itself.
162    """
163
164cdef class MSSQLDatabaseException(MSSQLException):
165    """
166    Raised when an error occurs within the database.
167    """
168
169    cdef readonly int number
170    cdef readonly int severity
171    cdef readonly int state
172    cdef readonly int line
173    cdef readonly char *text
174    cdef readonly char *srvname
175    cdef readonly char *procname
176
177    property message:
178
179        def __get__(self):
180            if self.procname:
181                return 'SQL Server message %d, severity %d, state %d, ' \
182                    'procedure %s, line %d:\n%s' % (self.number,
183                    self.severity, self.state, self.procname,
184                    self.line, self.text)
185            else:
186                return 'SQL Server message %d, severity %d, state %d, ' \
187                    'line %d:\n%s' % (self.number, self.severity,
188                    self.state, self.line, self.text)
189
190# Module attributes for configuring _mssql
191login_timeout = 60
192
193min_error_severity = 6
194
195wait_callback = None
196
197def set_wait_callback(a_callable):
198    global wait_callback
199
200    wait_callback = a_callable
201
202# Buffer size for large numbers
203DEF NUMERIC_BUF_SZ = 45
204
205cdef bytes ensure_bytes(s, encoding='utf-8'):
206    try:
207        decoded = s.decode(encoding)
208        return decoded.encode(encoding)
209    except AttributeError:
210        return s.encode(encoding)
211
212cdef void log(char * message, ...):
213    if PYMSSQL_DEBUG == 1:
214        fprintf(stderr, "+++ %s\n", message)
215
216if PY_MAJOR_VERSION == '3':
217    string_types = str,
218else:
219    string_types = basestring,
220
221###################
222## Error Handler ##
223###################
224cdef int err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr,
225        char *dberrstr, char *oserrstr) with gil:
226    cdef char *mssql_lastmsgstr
227    cdef int *mssql_lastmsgno
228    cdef int *mssql_lastmsgseverity
229    cdef int *mssql_lastmsgstate
230    cdef int _min_error_severity = min_error_severity
231    cdef char mssql_message[PYMSSQL_MSGSIZE]
232
233    if severity < _min_error_severity:
234        return INT_CANCEL
235
236    if dberrstr == NULL:
237        dberrstr = ''
238    if oserrstr == NULL:
239        oserrstr = ''
240
241    IF PYMSSQL_DEBUG == 1 or PYMSSQL_DEBUG_ERRORS == 1:
242        fprintf(stderr, "\n*** err_handler(dbproc = %p, severity = %d,  " \
243            "dberr = %d, oserr = %d, dberrstr = '%s',  oserrstr = '%s'); " \
244            "DBDEAD(dbproc) = %d\n", <void *>dbproc, severity, dberr,
245            oserr, dberrstr, oserrstr, DBDEAD(dbproc));
246        fprintf(stderr, "*** previous max severity = %d\n\n",
247            _mssql_last_msg_severity);
248
249    mssql_lastmsgstr = _mssql_last_msg_str
250    mssql_lastmsgno = &_mssql_last_msg_no
251    mssql_lastmsgseverity = &_mssql_last_msg_severity
252    mssql_lastmsgstate = &_mssql_last_msg_state
253
254    for conn in connection_object_list:
255        if dbproc != (<MSSQLConnection>conn).dbproc:
256            continue
257        mssql_lastmsgstr = (<MSSQLConnection>conn).last_msg_str
258        mssql_lastmsgno = &(<MSSQLConnection>conn).last_msg_no
259        mssql_lastmsgseverity = &(<MSSQLConnection>conn).last_msg_severity
260        mssql_lastmsgstate = &(<MSSQLConnection>conn).last_msg_state
261        if DBDEAD(dbproc):
262            log("+++ err_handler: dbproc is dead; killing conn...\n")
263            conn.mark_disconnected()
264        break
265
266    if severity > mssql_lastmsgseverity[0]:
267        mssql_lastmsgseverity[0] = severity
268        mssql_lastmsgno[0] = dberr
269        mssql_lastmsgstate[0] = oserr
270
271    if oserr != DBNOERR and oserr != 0:
272        if severity == EXCOMM:
273            snprintf(
274                mssql_message, sizeof(mssql_message),
275                '%sDB-Lib error message %d, severity %d:\n%s\nNet-Lib error during %s (%d)\n',
276                mssql_lastmsgstr, dberr, severity, dberrstr, oserrstr, oserr)
277        else:
278            snprintf(
279                mssql_message, sizeof(mssql_message),
280                '%sDB-Lib error message %d, severity %d:\n%s\nOperating System error during %s (%d)\n',
281                mssql_lastmsgstr, dberr, severity, dberrstr, oserrstr, oserr)
282    else:
283        snprintf(
284            mssql_message, sizeof(mssql_message),
285            '%sDB-Lib error message %d, severity %d:\n%s\n',
286            mssql_lastmsgstr, dberr, severity, dberrstr)
287
288    strncpy(mssql_lastmsgstr, mssql_message, PYMSSQL_MSGSIZE)
289    mssql_lastmsgstr[ PYMSSQL_MSGSIZE - 1 ] = '\0'
290
291    return INT_CANCEL
292
293#####################
294## Message Handler ##
295#####################
296cdef int msg_handler(DBPROCESS *dbproc, DBINT msgno, int msgstate,
297        int severity, char *msgtext, char *srvname, char *procname,
298        LINE_T line) with gil:
299
300    cdef int *mssql_lastmsgno
301    cdef int *mssql_lastmsgseverity
302    cdef int *mssql_lastmsgstate
303    cdef int *mssql_lastmsgline
304    cdef char *mssql_lastmsgstr
305    cdef char *mssql_lastmsgsrv
306    cdef char *mssql_lastmsgproc
307    cdef int _min_error_severity = min_error_severity
308    cdef MSSQLConnection conn = None
309
310    IF PYMSSQL_DEBUG == 1:
311        fprintf(stderr, "\n+++ msg_handler(dbproc = %p, msgno = %d, " \
312            "msgstate = %d, severity = %d, msgtext = '%s', " \
313            "srvname = '%s', procname = '%s', line = %d)\n",
314            dbproc, msgno, msgstate, severity, msgtext, srvname,
315            procname, line);
316        fprintf(stderr, "+++ previous max severity = %d\n\n",
317            _mssql_last_msg_severity);
318
319    for cnx in connection_object_list:
320        if (<MSSQLConnection>cnx).dbproc != dbproc:
321            continue
322
323        conn = <MSSQLConnection>cnx
324        break
325
326    if conn is not None and conn.msghandler is not None:
327        conn.msghandler(msgstate, severity, srvname, procname, line, msgtext)
328
329    if severity < _min_error_severity:
330        return INT_CANCEL
331
332    if conn is not None:
333        mssql_lastmsgstr = conn.last_msg_str
334        mssql_lastmsgsrv = conn.last_msg_srv
335        mssql_lastmsgproc = conn.last_msg_proc
336        mssql_lastmsgno = &conn.last_msg_no
337        mssql_lastmsgseverity = &conn.last_msg_severity
338        mssql_lastmsgstate = &conn.last_msg_state
339        mssql_lastmsgline = &conn.last_msg_line
340    else:
341        mssql_lastmsgstr = _mssql_last_msg_str
342        mssql_lastmsgsrv = _mssql_last_msg_srv
343        mssql_lastmsgproc = _mssql_last_msg_proc
344        mssql_lastmsgno = &_mssql_last_msg_no
345        mssql_lastmsgseverity = &_mssql_last_msg_severity
346        mssql_lastmsgstate = &_mssql_last_msg_state
347        mssql_lastmsgline = &_mssql_last_msg_line
348
349    # Calculate the maximum severity of all messages in a row
350    # Fill the remaining fields as this is going to raise the exception
351    if severity > mssql_lastmsgseverity[0]:
352        mssql_lastmsgseverity[0] = severity
353        mssql_lastmsgno[0] = msgno
354        mssql_lastmsgstate[0] = msgstate
355        mssql_lastmsgline[0] = line
356        strncpy(mssql_lastmsgstr, msgtext, PYMSSQL_MSGSIZE)
357        strncpy(mssql_lastmsgsrv, srvname, PYMSSQL_MSGSIZE)
358        strncpy(mssql_lastmsgproc, procname, PYMSSQL_MSGSIZE)
359
360
361    return 0
362
363cdef int db_sqlexec(DBPROCESS *dbproc):
364    cdef RETCODE rtc
365
366    # The dbsqlsend function sends Transact-SQL statements, stored in the
367    # command buffer of the DBPROCESS, to SQL Server.
368    #
369    # It does not wait for a response. This gives us an opportunity to do other
370    # things while waiting for the server response.
371    #
372    # After dbsqlsend returns SUCCEED, dbsqlok must be called to verify the
373    # accuracy of the command batch. Then dbresults can be called to process
374    # the results.
375    with nogil:
376        rtc = dbsqlsend(dbproc)
377        if rtc != SUCCEED:
378            return rtc
379
380    # If we've reached here, dbsqlsend didn't fail so the query is in progress.
381
382    # Wait for results to come back and return the return code, optionally
383    # calling wait_callback first...
384    return db_sqlok(dbproc)
385
386cdef int db_sqlok(DBPROCESS *dbproc):
387    cdef RETCODE rtc
388
389    # If there is a wait callback, call it with the file descriptor we're
390    # waiting on.
391    # The wait_callback is a good place to do things like yield to another
392    # gevent greenlet -- e.g.: gevent.socket.wait_read(read_fileno)
393    if wait_callback:
394        read_fileno = dbiordesc(dbproc)
395        wait_callback(read_fileno)
396
397    # dbsqlok following dbsqlsend is the equivalent of dbsqlexec. This function
398    # must be called after dbsqlsend returns SUCCEED. When dbsqlok returns,
399    # then dbresults can be called to process the results.
400    with nogil:
401        rtc = dbsqlok(dbproc)
402
403    return rtc
404
405cdef void clr_err(MSSQLConnection conn):
406    if conn != None:
407        conn.last_msg_no = 0
408        conn.last_msg_severity = 0
409        conn.last_msg_state = 0
410        conn.last_msg_str[0] = 0
411    else:
412        _mssql_last_msg_no = 0
413        _mssql_last_msg_severity = 0
414        _mssql_last_msg_state = 0
415        _mssql_last_msg_str[0] = 0
416
417cdef RETCODE db_cancel(MSSQLConnection conn):
418    cdef RETCODE rtc
419
420    if conn == None:
421        return SUCCEED
422
423    if conn.dbproc == NULL:
424        return SUCCEED
425
426    with nogil:
427        rtc = dbcancel(conn.dbproc);
428
429    conn.clear_metadata()
430    return rtc
431
432##############################
433## MSSQL Row Iterator Class ##
434##############################
435cdef class MSSQLRowIterator:
436
437    def __init__(self, connection, int row_format):
438        self.conn = connection
439        self.row_format = row_format
440
441    def __iter__(self):
442        return self
443
444    def __next__(self):
445        assert_connected(self.conn)
446        clr_err(self.conn)
447        return self.conn.fetch_next_row(1, self.row_format)
448
449############################
450## MSSQL Connection Class ##
451############################
452cdef class MSSQLConnection:
453
454    property charset:
455        """
456        The current encoding in use.
457        """
458
459        def __get__(self):
460            if strlen(self._charset):
461                return self._charset.decode('ascii') if PY_MAJOR_VERSION == 3 else self._charset
462            return None
463
464    property connected:
465        """
466        True if the connection to a database is open.
467        """
468
469        def __get__(self):
470            return self._connected
471
472    property identity:
473        """
474        Returns identity value of the last inserted row. If the previous
475        operation did not involve inserting a row into a table with an
476        identity column, None is returned.
477
478        ** Usage **
479        >>> conn.execute_non_query("INSERT INTO table (name) VALUES ('John')")
480        >>> print 'Last inserted row has ID = %s' % conn.identity
481        Last inserted row has ID = 178
482        """
483
484        def __get__(self):
485            return self.execute_scalar('SELECT SCOPE_IDENTITY()')
486
487    property query_timeout:
488        """
489        A
490        """
491        def __get__(self):
492            return self._query_timeout
493
494        def __set__(self, value):
495            cdef int val = int(value)
496            cdef RETCODE rtc
497            if val < 0:
498                raise ValueError("The 'query_timeout' attribute must be >= 0.")
499
500            # XXX: Currently this will set it application wide :-(
501            rtc = dbsettime(val)
502            check_and_raise(rtc, self)
503
504            # if all is fine then set our attribute
505            self._query_timeout = val
506
507    property rows_affected:
508        """
509        Number of rows affected by last query. For SELECT statements this
510        value is only meaningful after reading all rows.
511        """
512
513        def __get__(self):
514            return self._rows_affected
515
516    property tds_version:
517        """
518        Returns what TDS version the connection is using.
519        """
520        def __get__(self):
521            cdef int version = dbtds(self.dbproc)
522            if version == 11:
523                return 7.3
524            elif version == 10:
525                return 7.2
526            elif version == 9:
527                return 7.1
528            elif version == 8:
529                return 7.0
530            elif version == 6:
531                return 5.0
532            elif version == 4:
533                return 4.2
534            return None
535
536    property tds_version_tuple:
537        """
538        Reports what TDS version the connection is using in tuple form which is
539        more easily handled (parse, compare) programmatically. If no TDS
540        version can be detected the value is None.
541        """
542        def __get__(self):
543            cdef int version = dbtds(self.dbproc)
544            if version == 11:
545                return (7, 3)
546            elif version == 10:
547                return (7, 2)
548            elif version == 9:
549                return (7, 1)
550            elif version == 8:
551                return (7, 0)
552            elif version == 6:
553                return (5, 0)
554            elif version == 4:
555                return (4, 2)
556            return None
557
558    def __cinit__(self):
559        log("_mssql.MSSQLConnection.__cinit__()")
560        self._connected = 0
561        self._charset = <char *>PyMem_Malloc(PYMSSQL_CHARSETBUFSIZE)
562        self._charset[0] = <char>0
563        self.last_msg_str = <char *>PyMem_Malloc(PYMSSQL_MSGSIZE)
564        self.last_msg_str[0] = <char>0
565        self.last_msg_srv = <char *>PyMem_Malloc(PYMSSQL_MSGSIZE)
566        self.last_msg_srv[0] = <char>0
567        self.last_msg_proc = <char *>PyMem_Malloc(PYMSSQL_MSGSIZE)
568        self.last_msg_proc[0] = <char>0
569        self.column_names = None
570        self.column_types = None
571
572    def __init__(self, server="localhost", user=None, password=None,
573            charset='UTF-8', database='', appname=None, port='1433', tds_version=None, conn_properties=None):
574        log("_mssql.MSSQLConnection.__init__()")
575
576        cdef LOGINREC *login
577        cdef RETCODE rtc
578        cdef char *_charset
579
580        # support MS methods of connecting locally
581        instance = ""
582        if "\\" in server:
583            server, instance = server.split("\\")
584
585        if server in (".", "(local)"):
586            server = "localhost"
587
588        server = server + "\\" + instance if instance else server
589
590        login = dblogin()
591        if login == NULL:
592            raise MSSQLDriverException("dblogin() failed")
593
594        appname = appname or "pymssql=%s" % __full_version__
595
596        # For Python 3, we need to convert unicode to byte strings
597        cdef bytes user_bytes
598        cdef char *user_cstr = NULL
599        if user is not None:
600            user_bytes = user.encode('utf-8')
601            user_cstr = user_bytes
602        cdef bytes password_bytes
603        cdef char *password_cstr = NULL
604        if password is not None:
605            password_bytes = password.encode('utf-8')
606            password_cstr = password_bytes
607        cdef bytes appname_bytes = appname.encode('utf-8')
608        cdef char *appname_cstr = appname_bytes
609
610        if user is not None:
611            DBSETLUSER(login, user_cstr)
612        if password is not None:
613            DBSETLPWD(login, password_cstr)
614        DBSETLAPP(login, appname_cstr)
615        if tds_version is not None:
616            DBSETLVERSION(login, _tds_ver_str_to_constant(tds_version))
617
618        # add the port to the server string if it doesn't have one already and
619        # if we are not using an instance
620        if ':' not in server and not instance:
621            server = '%s:%s' % (server, port)
622
623        # override the HOST to be the portion without the server, otherwise
624        # FreeTDS chokes when server still has the port definition.
625        # BUT, a patch on the mailing list fixes the need for this.  I am
626        # leaving it here just to remind us how to fix the problem if the bug
627        # doesn't get fixed for a while.  But if it does get fixed, this code
628        # can be deleted.
629        # patch: http://lists.ibiblio.org/pipermail/freetds/2011q2/026997.html
630        #if ':' in server:
631        #    os.environ['TDSHOST'] = server.split(':', 1)[0]
632        #else:
633        #    os.environ['TDSHOST'] = server
634
635        # Add ourselves to the global connection list
636        connection_object_list.append(self)
637
638        cdef bytes charset_bytes
639
640        # Set the character set name
641        if charset:
642            charset_bytes = charset.encode('utf-8')
643            _charset = charset_bytes
644            strncpy(self._charset, _charset, PYMSSQL_CHARSETBUFSIZE)
645            DBSETLCHARSET(login, self._charset)
646
647        # For Python 3, we need to convert unicode to byte strings
648        cdef bytes dbname_bytes
649        cdef char *dbname_cstr
650        # Put the DB name in the login LOGINREC because it helps with connections to Azure
651        if database:
652            if FREETDS_SUPPORTS_DBSETLDBNAME:
653                dbname_bytes = database.encode('ascii')
654                dbname_cstr = dbname_bytes
655                DBSETLDBNAME(login, dbname_cstr)
656            else:
657                log("_mssql.MSSQLConnection.__init__(): Warning: This version of FreeTDS doesn't support selecting the DB name when setting up the connection. This will keep connections to Azure from working.")
658
659        # Set the login timeout
660        # XXX: Currently this will set it application wide :-(
661        dbsetlogintime(login_timeout)
662
663        cdef bytes server_bytes = server.encode('utf-8')
664        cdef char *server_cstr = server_bytes
665
666        # Connect to the server
667        with nogil:
668            self.dbproc = dbopen(login, server_cstr)
669
670        # Frees the login record, can be called immediately after dbopen.
671        dbloginfree(login)
672
673        if self.dbproc == NULL:
674            log("_mssql.MSSQLConnection.__init__() -> dbopen() returned NULL")
675            connection_object_list.remove(self)
676            maybe_raise_MSSQLDatabaseException(None)
677            raise MSSQLDriverException("Connection to the database failed for an unknown reason.")
678
679        self._connected = 1
680
681        if conn_properties is None:
682            conn_properties = \
683                "SET ARITHABORT ON;"                \
684                "SET CONCAT_NULL_YIELDS_NULL ON;"   \
685                "SET ANSI_NULLS ON;"                \
686                "SET ANSI_NULL_DFLT_ON ON;"         \
687                "SET ANSI_PADDING ON;"              \
688                "SET ANSI_WARNINGS ON;"             \
689                "SET ANSI_NULL_DFLT_ON ON;"         \
690                "SET CURSOR_CLOSE_ON_COMMIT ON;"    \
691                "SET QUOTED_IDENTIFIER ON;"         \
692                "SET TEXTSIZE 2147483647;"  # http://msdn.microsoft.com/en-us/library/aa259190%28v=sql.80%29.aspx
693        elif isinstance(conn_properties, Iterable) and not isinstance(conn_properties, string_types):
694            conn_properties = ' '.join(conn_properties)
695        cdef bytes conn_props_bytes
696        cdef char *conn_props_cstr
697        if conn_properties:
698            log("_mssql.MSSQLConnection.__init__() -> dbcmd() setting connection values")
699            # Set connection properties, some reasonable values are used by
700            # default but they can be customized
701            conn_props_bytes = conn_properties.encode(charset)
702            conn_props_cstr = conn_props_bytes
703            dbcmd(self.dbproc, conn_props_bytes)
704
705            rtc = db_sqlexec(self.dbproc)
706            if (rtc == FAIL):
707                raise MSSQLDriverException("Could not set connection properties")
708
709        db_cancel(self)
710        clr_err(self)
711
712        if database:
713            self.select_db(database)
714
715    def __dealloc__(self):
716        log("_mssql.MSSQLConnection.__dealloc__()")
717        self.close()
718
719    def __enter__(self):
720        return self
721
722    def __exit__(self, exc_type, exc_value, traceback):
723        self.close()
724
725    def __iter__(self):
726        assert_connected(self)
727        clr_err(self)
728        return MSSQLRowIterator(self, ROW_FORMAT_DICT)
729
730    cpdef set_msghandler(self, object handler):
731        """
732        set_msghandler(handler) -- set the msghandler for the connection
733
734        This function allows setting a msghandler for the connection to
735        allow a client to gain access to the messages returned from the
736        server.
737        """
738        self.msghandler = handler
739
740    cpdef cancel(self):
741        """
742        cancel() -- cancel all pending results.
743
744        This function cancels all pending results from the last SQL operation.
745        It can be called more than once in a row. No exception is raised in
746        this case.
747        """
748        log("_mssql.MSSQLConnection.cancel()")
749        cdef RETCODE rtc
750
751        assert_connected(self)
752        clr_err(self)
753
754        rtc = db_cancel(self)
755        check_and_raise(rtc, self)
756
757    cdef void clear_metadata(self):
758        log("_mssql.MSSQLConnection.clear_metadata()")
759        self.column_names = None
760        self.column_types = None
761        self.num_columns = 0
762        self.last_dbresults = 0
763
764    def close(self):
765        """
766        close() -- close connection to an MS SQL Server.
767
768        This function tries to close the connection and free all memory used.
769        It can be called more than once in a row. No exception is raised in
770        this case.
771        """
772        log("_mssql.MSSQLConnection.close()")
773        if self == None:
774            return None
775
776        if not self._connected:
777            return None
778
779        clr_err(self)
780
781        with nogil:
782            dbclose(self.dbproc)
783
784        self.mark_disconnected()
785
786    def mark_disconnected(self):
787        log("_mssql.MSSQLConnection.mark_disconnected()")
788        self.dbproc = NULL
789        self._connected = 0
790        PyMem_Free(self.last_msg_proc)
791        PyMem_Free(self.last_msg_srv)
792        PyMem_Free(self.last_msg_str)
793        PyMem_Free(self._charset)
794        connection_object_list.remove(self)
795
796    cdef object convert_db_value(self, BYTE *data, int dbtype, int length):
797        log("_mssql.MSSQLConnection.convert_db_value()")
798        cdef char buf[NUMERIC_BUF_SZ] # buffer in which we store text rep of bug nums
799        cdef int converted_length
800        cdef long prevPrecision
801        cdef BYTE precision
802        cdef DBDATEREC di
803        cdef DBDATETIME dt
804        cdef DBCOL dbcol
805
806        IF PYMSSQL_DEBUG == 1:
807            sys.stderr.write("convert_db_value: dbtype = %d; length = %d\n" % (dbtype, length))
808
809        if dbtype == SQLBIT:
810            return bool(<int>(<DBBIT *>data)[0])
811
812        elif dbtype == SQLINT1:
813            return int(<int>(<DBTINYINT *>data)[0])
814
815        elif dbtype == SQLINT2:
816            return int(<int>(<DBSMALLINT *>data)[0])
817
818        elif dbtype == SQLINT4:
819            return int(<int>(<DBINT *>data)[0])
820
821        elif dbtype == SQLINT8:
822            return long(<PY_LONG_LONG>(<PY_LONG_LONG *>data)[0])
823
824        elif dbtype == SQLFLT4:
825            return float(<float>(<DBREAL *>data)[0])
826
827        elif dbtype == SQLFLT8:
828            return float(<double>(<DBFLT8 *>data)[0])
829
830        elif dbtype in (SQLMONEY, SQLMONEY4, SQLNUMERIC, SQLDECIMAL):
831            dbcol.SizeOfStruct = sizeof(dbcol)
832
833            if dbtype in (SQLMONEY, SQLMONEY4):
834                precision = 4
835            else:
836                precision = 0
837
838            converted_length = dbconvert(self.dbproc, dbtype, data, -1, SQLCHAR,
839                <BYTE *>buf, NUMERIC_BUF_SZ)
840
841            with decimal.localcontext() as ctx:
842                # Python 3 doesn't like decimal.localcontext() with prec == 0
843                ctx.prec = precision if precision > 0 else 1
844                return decimal.Decimal(_remove_locale(buf, converted_length).decode(self._charset))
845
846        elif dbtype in (SQLDATETIM4, SQLDATETIME2):
847            dbconvert(self.dbproc, dbtype, data, -1, SQLDATETIME,
848                <BYTE *>&dt, -1)
849            dbdatecrack(self.dbproc, &di, <DBDATETIME *><BYTE *>&dt)
850            return datetime.datetime(di.year, di.month, di.day,
851                di.hour, di.minute, di.second, di.millisecond * 1000)
852
853        elif dbtype == SQLDATE:
854            dbconvert(self.dbproc, dbtype, data, -1, SQLDATETIME,
855                <BYTE *>&dt, -1)
856            dbdatecrack(self.dbproc, &di, <DBDATETIME *><BYTE *>&dt)
857            return datetime.date(di.year, di.month, di.day)
858
859        elif dbtype == SQLTIME:
860            dbconvert(self.dbproc, dbtype, data, -1, SQLDATETIME,
861                <BYTE *>&dt, -1)
862            dbdatecrack(self.dbproc, &di, <DBDATETIME *><BYTE *>&dt)
863            return datetime.time(di.hour, di.minute, di.second, di.millisecond * 1000)
864
865        elif dbtype == SQLDATETIME:
866            dbdatecrack(self.dbproc, &di, <DBDATETIME *>data)
867            return datetime.datetime(di.year, di.month, di.day,
868                di.hour, di.minute, di.second, di.millisecond * 1000)
869
870        elif dbtype in (SQLVARCHAR, SQLCHAR, SQLTEXT):
871            if strlen(self._charset):
872                return (<char *>data)[:length].decode(self._charset)
873            else:
874                return (<char *>data)[:length]
875
876        elif dbtype == SQLUUID:
877            return uuid.UUID(bytes_le=(<char *>data)[:length])
878
879        else:
880            return (<char *>data)[:length]
881
882    cdef int convert_python_value(self, object value, BYTE **dbValue,
883            int *dbtype, int *length) except -1:
884        log("_mssql.MSSQLConnection.convert_python_value()")
885        cdef int *intValue
886        cdef double *dblValue
887        cdef float *fltValue
888        cdef PY_LONG_LONG *longValue
889        cdef char *strValue
890        cdef char *tmp
891        cdef BYTE *binValue
892        cdef DBTYPEINFO decimal_type_info
893
894        IF PYMSSQL_DEBUG == 1:
895            sys.stderr.write("convert_python_value: value = %r; dbtype = %d" % (value, dbtype[0]))
896
897        if value is None:
898            dbValue[0] = <BYTE *>NULL
899            return 0
900
901        if dbtype[0] in (SQLBIT, SQLBITN):
902            intValue = <int *>PyMem_Malloc(sizeof(int))
903            intValue[0] = <int>value
904            dbValue[0] = <BYTE *><DBBIT *>intValue
905            return 0
906
907        if dbtype[0] == SQLINTN:
908            dbtype[0] = SQLINT4
909
910        if dbtype[0] in (SQLINT1, SQLINT2, SQLINT4):
911            if value > MAX_INT:
912                raise MSSQLDriverException('value cannot be larger than %d' % MAX_INT)
913            elif value < MIN_INT:
914                raise MSSQLDriverException('value cannot be smaller than %d' % MIN_INT)
915            intValue = <int *>PyMem_Malloc(sizeof(int))
916            intValue[0] = <int>value
917            if dbtype[0] == SQLINT1:
918                dbValue[0] = <BYTE *><DBTINYINT *>intValue
919                return 0
920            if dbtype[0] == SQLINT2:
921                dbValue[0] = <BYTE *><DBSMALLINT *>intValue
922                return 0
923            if dbtype[0] == SQLINT4:
924                dbValue[0] = <BYTE *><DBINT *>intValue
925                return 0
926
927        if dbtype[0] == SQLINT8:
928            longValue = <PY_LONG_LONG *>PyMem_Malloc(sizeof(PY_LONG_LONG))
929            longValue[0] = <PY_LONG_LONG>value
930            dbValue[0] = <BYTE *>longValue
931            return 0
932
933        if dbtype[0] in (SQLFLT4, SQLREAL):
934            fltValue = <float *>PyMem_Malloc(sizeof(float))
935            fltValue[0] = <float>value
936            dbValue[0] = <BYTE *><DBREAL *>fltValue
937            return 0
938
939        if dbtype[0] == SQLFLT8:
940            dblValue = <double *>PyMem_Malloc(sizeof(double))
941            dblValue[0] = <double>value
942            dbValue[0] = <BYTE *><DBFLT8 *>dblValue
943            return 0
944
945        if dbtype[0] in (SQLDATETIM4, SQLDATETIME):
946            if type(value) not in (datetime.date, datetime.datetime):
947                raise TypeError('value can only be a date or datetime')
948
949            value = value.strftime('%Y-%m-%d %H:%M:%S.') + \
950                "%03d" % (value.microsecond // 1000)
951            value = value.encode(self.charset)
952            dbtype[0] = SQLCHAR
953
954        if dbtype[0] in (SQLNUMERIC, SQLDECIMAL):
955            # There seems to be no harm in setting precision higher than
956            # necessary
957            decimal_type_info.precision = 33
958
959            # Figure out `scale` - number of digits after decimal point
960            decimal_type_info.scale = abs(value.as_tuple().exponent)
961
962            # Need this to prevent Cython error:
963            # "Obtaining 'BYTE *' from temporary Python value"
964            # bytes_value = bytes(str(value), encoding="ascii")
965            bytes_value = unicode(value).encode("ascii")
966
967            decValue = <DBDECIMAL *>PyMem_Malloc(sizeof(DBDECIMAL))
968            length[0] = dbconvert_ps(
969                self.dbproc,
970                SQLCHAR,
971                bytes_value,
972                -1,
973                dbtype[0],
974                <BYTE *>decValue,
975                sizeof(DBDECIMAL),
976                &decimal_type_info,
977            )
978            dbValue[0] = <BYTE *>decValue
979
980            IF PYMSSQL_DEBUG == 1:
981                fprintf(stderr, "convert_python_value: Converted value to DBDECIMAL with length = %d\n", length[0])
982                for i in range(0, 35):
983                    fprintf(stderr, "convert_python_value: dbValue[0][%d] = %d\n", i, dbValue[0][i])
984
985            return 0
986
987        if dbtype[0] in (SQLMONEY, SQLMONEY4, SQLNUMERIC, SQLDECIMAL):
988            if type(value) in (int, long, bytes):
989                value = decimal.Decimal(value)
990
991            if type(value) not in (decimal.Decimal, float):
992                raise TypeError('value can only be a Decimal')
993
994            value = str(value)
995            dbtype[0] = SQLCHAR
996
997        if dbtype[0] in (SQLVARCHAR, SQLCHAR, SQLTEXT):
998            if not hasattr(value, 'startswith'):
999                raise TypeError('value must be a string type')
1000
1001            if strlen(self._charset) > 0 and type(value) is unicode:
1002                value = value.encode(self.charset)
1003
1004            strValue = <char *>PyMem_Malloc(len(value) + 1)
1005            tmp = value
1006            strncpy(strValue, tmp, len(value) + 1)
1007            strValue[ len(value) ] = '\0';
1008            dbValue[0] = <BYTE *>strValue
1009            return 0
1010
1011        if dbtype[0] in (SQLBINARY, SQLVARBINARY, SQLIMAGE):
1012            if PY_MAJOR_VERSION == 3 and type(value) not in (bytes, bytearray):
1013                raise TypeError('value can only be bytes or bytearray')
1014            elif PY_MAJOR_VERSION == 2 and type(value) not in (str, bytearray):
1015                raise TypeError('value can only be str or bytearray')
1016
1017            binValue = <BYTE *>PyMem_Malloc(len(value))
1018            memcpy(binValue, <char *>value, len(value))
1019            length[0] = len(value)
1020            dbValue[0] = <BYTE *>binValue
1021            return 0
1022
1023        if dbtype[0] == SQLUUID:
1024            binValue = <BYTE *>PyMem_Malloc(16)
1025            memcpy(binValue, <char *>value.bytes_le, 16)
1026            length[0] = 16
1027            dbValue[0] = <BYTE *>binValue
1028            return 0
1029
1030        # No conversion was possible so raise an error
1031        raise MSSQLDriverException('Unable to convert value')
1032
1033    cpdef execute_non_query(self, query_string, params=None):
1034        """
1035        execute_non_query(query_string, params=None)
1036
1037        This method sends a query to the MS SQL Server to which this object
1038        instance is connected. After completion, its results (if any) are
1039        discarded. An exception is raised on failure. If there are any pending
1040        results or rows prior to executing this command, they are silently
1041        discarded. This method accepts Python formatting. Please see
1042        execute_query() for more details.
1043
1044        This method is useful for INSERT, UPDATE, DELETE and for Data
1045        Definition Language commands, i.e. when you need to alter your database
1046        schema.
1047
1048        After calling this method, rows_affected property contains number of
1049        rows affected by the last SQL command.
1050        """
1051        log("_mssql.MSSQLConnection.execute_non_query() BEGIN")
1052        cdef RETCODE rtc
1053
1054        self.format_and_run_query(query_string, params)
1055
1056        with nogil:
1057            dbresults(self.dbproc)
1058            self._rows_affected = dbcount(self.dbproc)
1059
1060        rtc = db_cancel(self)
1061        check_and_raise(rtc, self)
1062        log("_mssql.MSSQLConnection.execute_non_query() END")
1063
1064    cpdef execute_query(self, query_string, params=None):
1065        """
1066        execute_query(query_string, params=None)
1067
1068        This method sends a query to the MS SQL Server to which this object
1069        instance is connected. An exception is raised on failure. If there
1070        are pending results or rows prior to executing this command, they
1071        are silently discarded. After calling this method you may iterate
1072        over the connection object to get rows returned by the query.
1073
1074        You can use Python formatting here and all values get properly
1075        quoted:
1076            conn.execute_query('SELECT * FROM empl WHERE id=%d', 13)
1077            conn.execute_query('SELECT * FROM empl WHERE id IN (%s)', ((5,6),))
1078            conn.execute_query('SELECT * FROM empl WHERE name=%s', 'John Doe')
1079            conn.execute_query('SELECT * FROM empl WHERE name LIKE %s', 'J%')
1080            conn.execute_query('SELECT * FROM empl WHERE name=%(name)s AND \
1081                city=%(city)s', { 'name': 'John Doe', 'city': 'Nowhere' } )
1082            conn.execute_query('SELECT * FROM cust WHERE salesrep=%s \
1083                AND id IN (%s)', ('John Doe', (1,2,3)))
1084            conn.execute_query('SELECT * FROM empl WHERE id IN (%s)',\
1085                (tuple(xrange(4)),))
1086            conn.execute_query('SELECT * FROM empl WHERE id IN (%s)',\
1087                (tuple([3,5,7,11]),))
1088
1089        This method is intented to be used on queries that return results,
1090        i.e. SELECT. After calling this method AND reading all rows from,
1091        result rows_affected property contains number of rows returned by
1092        last command (this is how MS SQL returns it).
1093        """
1094        log("_mssql.MSSQLConnection.execute_query() BEGIN")
1095        self.format_and_run_query(query_string, params)
1096        self.get_result()
1097        log("_mssql.MSSQLConnection.execute_query() END")
1098
1099    cpdef execute_row(self, query_string, params=None):
1100        """
1101        execute_row(query_string, params=None)
1102
1103        This method sends a query to the MS SQL Server to which this object
1104        instance is connected, then returns first row of data from result.
1105
1106        An exception is raised on failure. If there are pending results or
1107        rows prior to executing this command, they are silently discarded.
1108
1109        This method accepts Python formatting. Please see execute_query()
1110        for details.
1111
1112        This method is useful if you want just a single row and don't want
1113        or don't need to iterate, as in:
1114
1115        conn.execute_row('SELECT * FROM employees WHERE id=%d', 13)
1116
1117        This method works exactly the same as 'iter(conn).next()'. Remaining
1118        rows, if any, can still be iterated after calling this method.
1119        """
1120        log("_mssql.MSSQLConnection.execute_row()")
1121        self.format_and_run_query(query_string, params)
1122        return self.fetch_next_row(0, ROW_FORMAT_DICT)
1123
1124    cpdef execute_scalar(self, query_string, params=None):
1125        """
1126        execute_scalar(query_string, params=None)
1127
1128        This method sends a query to the MS SQL Server to which this object
1129        instance is connected, then returns first column of first row from
1130        result. An exception is raised on failure. If there are pending
1131
1132        results or rows prior to executing this command, they are silently
1133        discarded.
1134
1135        This method accepts Python formatting. Please see execute_query()
1136        for details.
1137
1138        This method is useful if you want just a single value, as in:
1139            conn.execute_scalar('SELECT COUNT(*) FROM employees')
1140
1141        This method works in the same way as 'iter(conn).next()[0]'.
1142        Remaining rows, if any, can still be iterated after calling this
1143        method.
1144        """
1145        cdef RETCODE rtc
1146        log("_mssql.MSSQLConnection.execute_scalar()")
1147
1148        self.format_and_run_query(query_string, params)
1149        self.get_result()
1150
1151        with nogil:
1152            rtc = dbnextrow(self.dbproc)
1153
1154        self._rows_affected = dbcount(self.dbproc)
1155
1156        if rtc == NO_MORE_ROWS:
1157            self.clear_metadata()
1158            self.last_dbresults = 0
1159            return None
1160
1161        return self.get_row(rtc, ROW_FORMAT_TUPLE)[0]
1162
1163    cdef fetch_next_row(self, int throw, int row_format):
1164        cdef RETCODE rtc
1165        log("_mssql.MSSQLConnection.fetch_next_row() BEGIN")
1166        try:
1167            self.get_result()
1168
1169            if self.last_dbresults == NO_MORE_RESULTS:
1170                log("_mssql.MSSQLConnection.fetch_next_row(): NO MORE RESULTS")
1171                self.clear_metadata()
1172                if throw:
1173                    raise StopIteration
1174                return None
1175
1176            with nogil:
1177                rtc = dbnextrow(self.dbproc)
1178
1179            check_cancel_and_raise(rtc, self)
1180
1181            if rtc == NO_MORE_ROWS:
1182                log("_mssql.MSSQLConnection.fetch_next_row(): NO MORE ROWS")
1183                self.clear_metadata()
1184                # 'rows_affected' is nonzero only after all records are read
1185                self._rows_affected = dbcount(self.dbproc)
1186                if throw:
1187                    raise StopIteration
1188                return None
1189
1190            return self.get_row(rtc, row_format)
1191        finally:
1192            log("_mssql.MSSQLConnection.fetch_next_row() END")
1193
1194    cdef format_and_run_query(self, query_string, params=None):
1195        """
1196        This is a helper function, which does most of the work needed by any
1197        execute_*() function. It returns NULL on error, None on success.
1198        """
1199        cdef RETCODE rtc
1200
1201        # For Python 3, we need to convert unicode to byte strings
1202        cdef bytes query_string_bytes
1203        cdef char *query_string_cstr
1204
1205        log("_mssql.MSSQLConnection.format_and_run_query() BEGIN")
1206
1207        try:
1208            # Cancel any pending results
1209            self.cancel()
1210
1211            if params:
1212                query_string = self.format_sql_command(query_string, params)
1213
1214            # For Python 3, we need to convert unicode to byte strings
1215            query_string_bytes = ensure_bytes(query_string, self.charset)
1216            query_string_cstr = query_string_bytes
1217
1218            log(query_string_cstr)
1219            if self.debug_queries:
1220                sys.stderr.write("#%s#\n" % query_string)
1221
1222            # Prepare the query buffer
1223            dbcmd(self.dbproc, query_string_cstr)
1224
1225            # Execute the query
1226            rtc = db_sqlexec(self.dbproc)
1227
1228            check_cancel_and_raise(rtc, self)
1229        finally:
1230            log("_mssql.MSSQLConnection.format_and_run_query() END")
1231
1232    cdef format_sql_command(self, format, params=None):
1233        log("_mssql.MSSQLConnection.format_sql_command()")
1234        return _substitute_params(format, params, self.charset)
1235
1236    def get_header(self):
1237        """
1238        get_header() -- get the Python DB-API compliant header information.
1239
1240        This method is infrastructure and doesn't need to be called by your
1241        code. It returns a list of 7-element tuples describing the current
1242        result header. Only name and DB-API compliant type is filled, rest
1243        of the data is None, as permitted by the specs.
1244        """
1245        cdef int col
1246        log("_mssql.MSSQLConnection.get_header() BEGIN")
1247        try:
1248            self.get_result()
1249
1250            if self.num_columns == 0:
1251                log("_mssql.MSSQLConnection.get_header(): num_columns == 0")
1252                return None
1253
1254            header_tuple = []
1255            for col in xrange(1, self.num_columns + 1):
1256                col_name = self.column_names[col - 1]
1257                col_type = self.column_types[col - 1]
1258                header_tuple.append((col_name, col_type, None, None, None, None, None))
1259            return tuple(header_tuple)
1260        finally:
1261            log("_mssql.MSSQLConnection.get_header() END")
1262
1263    def get_iterator(self, int row_format):
1264        """
1265        get_iterator(row_format) -- allows the format of the iterator to be specified
1266
1267        While the iter(conn) call will always return a dictionary, this
1268        method allows the return type of the row to be specified.
1269        """
1270        assert_connected(self)
1271        clr_err(self)
1272        return MSSQLRowIterator(self, row_format)
1273
1274    cdef get_result(self):
1275        cdef int coltype
1276        cdef char log_message[200]
1277
1278        log("_mssql.MSSQLConnection.get_result() BEGIN")
1279
1280        try:
1281            if self.last_dbresults:
1282                log("_mssql.MSSQLConnection.get_result(): last_dbresults == True, return None")
1283                return None
1284
1285            self.clear_metadata()
1286
1287            # Since python doesn't have a do/while loop do it this way
1288            while True:
1289                with nogil:
1290                    self.last_dbresults = dbresults(self.dbproc)
1291                self.num_columns = dbnumcols(self.dbproc)
1292                if self.last_dbresults != SUCCEED or self.num_columns > 0:
1293                    break
1294            check_cancel_and_raise(self.last_dbresults, self)
1295
1296            self._rows_affected = dbcount(self.dbproc)
1297
1298            if self.last_dbresults == NO_MORE_RESULTS:
1299                self.num_columns = 0
1300                log("_mssql.MSSQLConnection.get_result(): NO_MORE_RESULTS, return None")
1301                return None
1302
1303            self.num_columns = dbnumcols(self.dbproc)
1304
1305            snprintf(log_message, sizeof(log_message), "_mssql.MSSQLConnection.get_result(): num_columns = %d", self.num_columns)
1306            log_message[ sizeof(log_message) - 1 ] = '\0'
1307            log(log_message)
1308
1309            column_names = list()
1310            column_types = list()
1311
1312            for col in xrange(1, self.num_columns + 1):
1313                col_name = dbcolname(self.dbproc, col)
1314                if not col_name:
1315                    self.num_columns -= 1
1316                    return None
1317
1318                column_name = col_name.decode(self._charset)
1319                column_names.append(column_name)
1320                coltype = dbcoltype(self.dbproc, col)
1321                column_types.append(get_api_coltype(coltype))
1322
1323            self.column_names = tuple(column_names)
1324            self.column_types = tuple(column_types)
1325        finally:
1326            log("_mssql.MSSQLConnection.get_result() END")
1327
1328    cdef get_row(self, int row_info, int row_format):
1329        cdef DBPROCESS *dbproc = self.dbproc
1330        cdef int col
1331        cdef int col_type
1332        cdef int len
1333        cdef BYTE *data
1334        cdef tuple trecord
1335        cdef dict drecord
1336        log("_mssql.MSSQLConnection.get_row()")
1337
1338        if PYMSSQL_DEBUG == 1:
1339            global _row_count
1340            _row_count += 1
1341
1342        if row_format == _ROW_FORMAT_TUPLE:
1343            trecord = PyTuple_New(self.num_columns)
1344        elif row_format == _ROW_FORMAT_DICT:
1345            drecord = dict()
1346
1347        for col in xrange(1, self.num_columns + 1):
1348            with nogil:
1349                data = get_data(dbproc, row_info, col)
1350                col_type = get_type(dbproc, row_info, col)
1351                len = get_length(dbproc, row_info, col)
1352
1353            if data == NULL:
1354                value = None
1355            else:
1356                IF PYMSSQL_DEBUG == 1:
1357                    global _row_count
1358                    fprintf(stderr, 'Processing row %d, column %d,' \
1359                        'Got data=%x, coltype=%d, len=%d\n', _row_count, col,
1360                        data, col_type, len)
1361                value = self.convert_db_value(data, col_type, len)
1362
1363            if row_format == _ROW_FORMAT_TUPLE:
1364                Py_INCREF(value)
1365                PyTuple_SetItem(trecord, col - 1, value)
1366            elif row_format == _ROW_FORMAT_DICT:
1367                name = self.column_names[col - 1]
1368                drecord[col - 1] = value
1369                if name:
1370                    drecord[name] = value
1371
1372        if row_format == _ROW_FORMAT_TUPLE:
1373            return trecord
1374        elif row_format == _ROW_FORMAT_DICT:
1375            return drecord
1376
1377    def init_procedure(self, procname):
1378        """
1379        init_procedure(procname) -- creates and returns a MSSQLStoredProcedure
1380        object.
1381
1382        This methods initializes a stored procedure or function on the server
1383        and creates a MSSQLStoredProcedure object that allows parameters to
1384        be bound.
1385        """
1386        log("_mssql.MSSQLConnection.init_procedure()")
1387        return MSSQLStoredProcedure(procname.encode(self.charset), self)
1388
1389    def nextresult(self):
1390        """
1391        nextresult() -- move to the next result, skipping all pending rows.
1392
1393        This method fetches and discards any rows remaining from the current
1394        resultset, then it advances to the next (if any) resultset. Returns
1395        True if the next resultset is available, otherwise None.
1396        """
1397
1398        cdef RETCODE rtc
1399        log("_mssql.MSSQLConnection.nextresult()")
1400
1401        assert_connected(self)
1402        clr_err(self)
1403
1404        rtc = dbnextrow(self.dbproc)
1405        check_cancel_and_raise(rtc, self)
1406
1407        while rtc != NO_MORE_ROWS:
1408            rtc = dbnextrow(self.dbproc)
1409            check_cancel_and_raise(rtc, self)
1410
1411        self.last_dbresults = 0
1412        self.get_result()
1413
1414        if self.last_dbresults != NO_MORE_RESULTS:
1415            return 1
1416
1417    def select_db(self, dbname):
1418        """
1419        select_db(dbname) -- Select the current database.
1420
1421        This function selects the given database. An exception is raised on
1422        failure.
1423        """
1424        cdef RETCODE rtc
1425        log("_mssql.MSSQLConnection.select_db()")
1426
1427        # For Python 3, we need to convert unicode to byte strings
1428        cdef bytes dbname_bytes = dbname.encode('ascii')
1429        cdef char *dbname_cstr = dbname_bytes
1430
1431        dbuse(self.dbproc, dbname_cstr)
1432
1433##################################
1434## MSSQL Stored Procedure Class ##
1435##################################
1436cdef class MSSQLStoredProcedure:
1437
1438    property connection:
1439        """The underlying MSSQLConnection object."""
1440        def __get__(self):
1441            return self.conn
1442
1443    property name:
1444        """The name of the procedure that this object represents."""
1445        def __get__(self):
1446            return self.procname
1447
1448    property parameters:
1449        """The parameters that have been bound to this procedure."""
1450        def __get__(self):
1451            return self.params
1452
1453    def __init__(self, bytes name, MSSQLConnection connection):
1454        cdef RETCODE rtc
1455        log("_mssql.MSSQLStoredProcedure.__init__()")
1456
1457        # We firstly want to check if tdsver is >= 7 as anything less
1458        # doesn't support remote procedure calls.
1459        if connection.tds_version < 7:
1460            raise MSSQLDriverException("Stored Procedures aren't "
1461                "supported with a TDS version less than 7.")
1462
1463        self.conn = connection
1464        self.dbproc = connection.dbproc
1465        self.procname = name
1466        self.params = dict()
1467        self.output_indexes = list()
1468        self.param_count = 0
1469        self.had_positional = False
1470
1471        with nogil:
1472            rtc = dbrpcinit(self.dbproc, self.procname, 0)
1473
1474        check_cancel_and_raise(rtc, self.conn)
1475
1476    def __dealloc__(self):
1477        cdef _mssql_parameter_node *n
1478        cdef _mssql_parameter_node *p
1479        log("_mssql.MSSQLStoredProcedure.__dealloc__()")
1480
1481        n = self.params_list
1482        p = NULL
1483
1484        while n != NULL:
1485            PyMem_Free(n.value)
1486            p = n
1487            n = n.next
1488            PyMem_Free(p)
1489
1490    def bind(self, object value, int dbtype, str param_name=None,
1491            int output=False, int null=False, int max_length=-1):
1492        """
1493        bind(value, data_type, param_name = None, output = False,
1494            null = False, max_length = -1) -- bind a parameter
1495
1496        This method binds a parameter to the stored procedure.
1497        """
1498        cdef int length = -1
1499        cdef RETCODE rtc
1500        cdef BYTE status
1501        cdef BYTE *data
1502        cdef bytes param_name_bytes
1503        cdef char *param_name_cstr
1504        cdef _mssql_parameter_node *pn
1505        log("_mssql.MSSQLStoredProcedure.bind()")
1506
1507        # Set status according to output being True or False
1508        status = DBRPCRETURN if output else <BYTE>0
1509
1510        # Convert the PyObject to the db type
1511        self.conn.convert_python_value(value, &data, &dbtype, &length)
1512
1513        # We support nullable parameters by just not binding them
1514        if dbtype in (SQLINTN, SQLBITN) and data == NULL:
1515            return
1516
1517        # Store the converted parameter in our parameter list so we can
1518        # free() it later.
1519        if data != NULL:
1520            pn = <_mssql_parameter_node *>PyMem_Malloc(sizeof(_mssql_parameter_node))
1521            if pn == NULL:
1522                raise MSSQLDriverException('Out of memory')
1523            pn.next = self.params_list
1524            pn.value = data
1525            self.params_list = pn
1526
1527        # We may need to set the data length depending on the type being
1528        # passed to the server here.
1529        if dbtype in (SQLVARCHAR, SQLCHAR, SQLTEXT, SQLBINARY,
1530            SQLVARBINARY, SQLIMAGE):
1531            if null or data == NULL:
1532                length = 0
1533                if not output:
1534                    max_length = -1
1535            # only set the length for strings, binary may contain NULLs
1536            elif dbtype in (SQLVARCHAR, SQLCHAR, SQLTEXT):
1537                length = strlen(<char *>data)
1538        else:
1539            # Fixed length data type
1540            if null or (output and dbtype not in (SQLDECIMAL, SQLNUMERIC)):
1541                length = 0
1542            max_length = -1
1543
1544        # Add some monkey fixing for nullable bit types
1545        if dbtype == SQLBITN:
1546            if output:
1547                max_length = 1
1548                length = 0
1549            else:
1550                length = 1
1551
1552        if status != DBRPCRETURN:
1553            max_length = -1
1554
1555        if param_name:
1556            param_name_bytes = param_name.encode('ascii')
1557            param_name_cstr = param_name_bytes
1558            if self.had_positional:
1559                raise MSSQLDriverException('Cannot bind named parameter after positional')
1560        else:
1561            param_name_cstr = ''
1562            self.had_positional = True
1563
1564        IF PYMSSQL_DEBUG == 1:
1565            sys.stderr.write(
1566                "\n--- rpc_bind(name = '%s', status = %d, "
1567                "max_length = %d, data_type = %d, data_length = %d\n"
1568                % (param_name, status, max_length, dbtype, length)
1569            )
1570
1571        with nogil:
1572            rtc = dbrpcparam(self.dbproc, param_name_cstr, status, dbtype,
1573                max_length, length, data)
1574        check_cancel_and_raise(rtc, self.conn)
1575
1576        # Store the value in the parameters dictionary for returning
1577        # later, by name if that has been supplied.
1578        if param_name:
1579            self.params[param_name] = value
1580        self.params[self.param_count] = value
1581        if output:
1582            self.output_indexes.append(self.param_count)
1583        self.param_count += 1
1584
1585    def execute(self):
1586        cdef RETCODE rtc
1587        cdef int output_count, i, type, length
1588        cdef char *param_name_bytes
1589        cdef BYTE *data
1590        log("_mssql.MSSQLStoredProcedure.execute()")
1591
1592        # Cancel any pending results as this throws a server error
1593        # otherwise.
1594        db_cancel(self.conn)
1595
1596        # Send the RPC request
1597        with nogil:
1598            rtc = dbrpcsend(self.dbproc)
1599        check_cancel_and_raise(rtc, self.conn)
1600
1601        # Wait for results to come back and return the return code, optionally
1602        # calling wait_callback first...
1603        rtc = db_sqlok(self.dbproc)
1604
1605        check_cancel_and_raise(rtc, self.conn)
1606
1607        # Need to call this regardless of whether or not there are output
1608        # parameters in order for the return status to be correct.
1609        output_count = dbnumrets(self.dbproc)
1610
1611        # If there are any output parameters then we are going to want to
1612        # set the values in the parameters dictionary.
1613        if output_count:
1614            for i in xrange(1, output_count + 1):
1615                with nogil:
1616                    type = dbrettype(self.dbproc, i)
1617                    param_name_bytes = dbretname(self.dbproc, i)
1618                    length = dbretlen(self.dbproc, i)
1619                    data = dbretdata(self.dbproc, i)
1620
1621                value = self.conn.convert_db_value(data, type, length)
1622                if strlen(param_name_bytes):
1623                    param_name = param_name_bytes.decode('utf-8')
1624                    self.params[param_name] = value
1625                self.params[self.output_indexes[i-1]] = value
1626
1627        # Get the return value from the procedure ready for return.
1628        return dbretstatus(self.dbproc)
1629
1630cdef int check_and_raise(RETCODE rtc, MSSQLConnection conn) except 1:
1631    if rtc == FAIL:
1632        return maybe_raise_MSSQLDatabaseException(conn)
1633    elif get_last_msg_str(conn):
1634        return maybe_raise_MSSQLDatabaseException(conn)
1635
1636cdef int check_cancel_and_raise(RETCODE rtc, MSSQLConnection conn) except 1:
1637    if rtc == FAIL:
1638        db_cancel(conn)
1639        return maybe_raise_MSSQLDatabaseException(conn)
1640    elif get_last_msg_str(conn):
1641        return maybe_raise_MSSQLDatabaseException(conn)
1642
1643cdef char *get_last_msg_str(MSSQLConnection conn):
1644    return conn.last_msg_str if conn != None else _mssql_last_msg_str
1645
1646cdef char *get_last_msg_srv(MSSQLConnection conn):
1647    return conn.last_msg_srv if conn != None else _mssql_last_msg_srv
1648
1649cdef char *get_last_msg_proc(MSSQLConnection conn):
1650    return conn.last_msg_proc if conn != None else _mssql_last_msg_proc
1651
1652cdef int get_last_msg_no(MSSQLConnection conn):
1653    return conn.last_msg_no if conn != None else _mssql_last_msg_no
1654
1655cdef int get_last_msg_severity(MSSQLConnection conn):
1656    return conn.last_msg_severity if conn != None else _mssql_last_msg_severity
1657
1658cdef int get_last_msg_state(MSSQLConnection conn):
1659    return conn.last_msg_state if conn != None else _mssql_last_msg_state
1660
1661cdef int get_last_msg_line(MSSQLConnection conn):
1662    return conn.last_msg_line if conn != None else _mssql_last_msg_line
1663
1664cdef int maybe_raise_MSSQLDatabaseException(MSSQLConnection conn) except 1:
1665
1666    if get_last_msg_severity(conn) < min_error_severity:
1667        return 0
1668
1669    error_msg = get_last_msg_str(conn)
1670    if len(error_msg) == 0:
1671        error_msg = b"Unknown error"
1672
1673    ex = MSSQLDatabaseException((get_last_msg_no(conn), error_msg))
1674    (<MSSQLDatabaseException>ex).text = error_msg
1675    (<MSSQLDatabaseException>ex).srvname = get_last_msg_srv(conn)
1676    (<MSSQLDatabaseException>ex).procname = get_last_msg_proc(conn)
1677    (<MSSQLDatabaseException>ex).number = get_last_msg_no(conn)
1678    (<MSSQLDatabaseException>ex).severity = get_last_msg_severity(conn)
1679    (<MSSQLDatabaseException>ex).state = get_last_msg_state(conn)
1680    (<MSSQLDatabaseException>ex).line = get_last_msg_line(conn)
1681    db_cancel(conn)
1682    clr_err(conn)
1683    raise ex
1684
1685cdef void assert_connected(MSSQLConnection conn) except *:
1686    log("_mssql.assert_connected()")
1687    if not conn.connected:
1688        raise MSSQLDriverException("Not connected to any MS SQL server")
1689
1690cdef inline BYTE *get_data(DBPROCESS *dbproc, int row_info, int col) nogil:
1691    return dbdata(dbproc, col) if row_info == REG_ROW else \
1692        dbadata(dbproc, row_info, col)
1693
1694cdef inline int get_type(DBPROCESS *dbproc, int row_info, int col) nogil:
1695    return dbcoltype(dbproc, col) if row_info == REG_ROW else \
1696        dbalttype(dbproc, row_info, col)
1697
1698cdef inline int get_length(DBPROCESS *dbproc, int row_info, int col) nogil:
1699    return dbdatlen(dbproc, col) if row_info == REG_ROW else \
1700        dbadlen(dbproc, row_info, col)
1701
1702######################
1703## Helper Functions ##
1704######################
1705cdef int get_api_coltype(int coltype):
1706    if coltype in (SQLBIT, SQLINT1, SQLINT2, SQLINT4, SQLINT8, SQLINTN,
1707            SQLFLT4, SQLFLT8, SQLFLTN):
1708        return NUMBER
1709    elif coltype in (SQLMONEY, SQLMONEY4, SQLMONEYN, SQLNUMERIC,
1710            SQLDECIMAL):
1711        return DECIMAL
1712    elif coltype in (SQLDATETIME, SQLDATETIM4, SQLDATETIMN):
1713        return DATETIME
1714    elif coltype in (SQLVARCHAR, SQLCHAR, SQLTEXT):
1715        return STRING
1716    else:
1717        return BINARY
1718
1719cdef char *_remove_locale(char *s, size_t buflen):
1720    cdef char c
1721    cdef char *stripped = s
1722    cdef int i, x = 0, last_sep = -1
1723
1724    for i, c in enumerate(s[0:buflen]):
1725        if c in (',', '.'):
1726            last_sep = i
1727
1728    for i, c in enumerate(s[0:buflen]):
1729        if (c >= '0' and c <= '9') or c in ('+', '-'):
1730            stripped[x] = c
1731            x += 1
1732        elif i == last_sep:
1733            stripped[x] = c
1734            x += 1
1735    stripped[x] = 0
1736    return stripped
1737
1738def remove_locale(bytes value):
1739    cdef char *s = <char*>value
1740    cdef size_t l = strlen(s)
1741    return _remove_locale(s, l)
1742
1743cdef int _tds_ver_str_to_constant(verstr) except -1:
1744    """
1745        http://www.freetds.org/userguide/choosingtdsprotocol.htm
1746    """
1747    if verstr == u'4.2':
1748        return DBVERSION_42
1749    if verstr == u'7.0':
1750        return DBVERSION_70
1751    if verstr in (u'7.1', u'8.0'):
1752        return DBVERSION_71
1753    if verstr == u'7.2':
1754        return DBVERSION_72
1755    if verstr == u'7.3':
1756        return DBVERSION_73
1757    if verstr == u'8.0':
1758        return DBVERSION_71
1759    raise MSSQLException('unrecognized tds version: %s' % verstr)
1760
1761#######################
1762## Quoting Functions ##
1763#######################
1764cdef _quote_simple_value(value, charset='utf8'):
1765
1766    if value == None:
1767        return b'NULL'
1768
1769    if isinstance(value, bool):
1770        return '1' if value else '0'
1771
1772    if isinstance(value, float):
1773        return repr(value).encode(charset)
1774
1775    if isinstance(value, (int, long, decimal.Decimal)):
1776        return str(value).encode(charset)
1777
1778    if isinstance(value, uuid.UUID):
1779        return _quote_simple_value(str(value))
1780
1781    if isinstance(value, unicode):
1782        return ("N'" + value.replace("'", "''") + "'").encode(charset)
1783
1784    if isinstance(value, bytearray):
1785        return b'0x' + binascii.hexlify(bytes(value))
1786
1787    if isinstance(value, (str, bytes)):
1788        # see if it can be decoded as ascii if there are no null bytes
1789        if b'\0' not in value:
1790            try:
1791                value.decode('ascii')
1792                return b"'" + value.replace(b"'", b"''") + b"'"
1793            except UnicodeDecodeError:
1794                pass
1795
1796        # Python 3: handle bytes
1797        # @todo - Marc - hack hack hack
1798        if isinstance(value, bytes):
1799            return b'0x' + binascii.hexlify(value)
1800
1801        # will still be string type if there was a null byte in it or if the
1802        # decoding failed.  In this case, just send it as hex.
1803        if isinstance(value, str):
1804            return '0x' + value.encode('hex')
1805
1806    if isinstance(value, datetime.datetime):
1807        return "{ts '%04d-%02d-%02d %02d:%02d:%02d.%03d'}" % (
1808            value.year, value.month, value.day,
1809            value.hour, value.minute, value.second,
1810            value.microsecond / 1000)
1811
1812    if isinstance(value, datetime.date):
1813        return "{d '%04d-%02d-%02d'} " % (
1814        value.year, value.month, value.day)
1815
1816    return None
1817
1818cdef _quote_or_flatten(data, charset='utf8'):
1819    result = _quote_simple_value(data, charset)
1820
1821    if result is not None:
1822        return result
1823
1824    if not issubclass(type(data), (list, tuple)):
1825        raise ValueError('expected a simple type, a tuple or a list')
1826
1827    quoted = []
1828    for value in data:
1829        value = _quote_simple_value(value, charset)
1830
1831        if value is None:
1832            raise ValueError('found an unsupported type')
1833
1834        quoted.append(value)
1835    return b'(' + b','.join(quoted) + b')'
1836
1837# This function is supposed to take a simple value, tuple or dictionary,
1838# normally passed in via the params argument in the execute_* methods. It
1839# then quotes and flattens the arguments and returns then.
1840cdef _quote_data(data, charset='utf8'):
1841    result = _quote_simple_value(data)
1842
1843    if result is not None:
1844        return result
1845
1846    if issubclass(type(data), dict):
1847        result = {}
1848        for k, v in data.iteritems():
1849            result[k] = _quote_or_flatten(v, charset)
1850        return result
1851
1852    if issubclass(type(data), tuple):
1853        result = []
1854        for v in data:
1855            result.append(_quote_or_flatten(v, charset))
1856        return tuple(result)
1857
1858    raise ValueError('expected a simple type, a tuple or a dictionary.')
1859
1860_re_pos_param = re.compile(br'(%([sd]))')
1861_re_name_param = re.compile(br'(%\(([^\)]+)\)(?:[sd]))')
1862cdef _substitute_params(toformat, params, charset):
1863    if params is None:
1864        return toformat
1865
1866    if not issubclass(type(params),
1867            (bool, int, long, float, unicode, str, bytes, bytearray, dict, tuple,
1868             datetime.datetime, datetime.date, dict, decimal.Decimal, uuid.UUID)):
1869        raise ValueError("'params' arg (%r) can be only a tuple or a dictionary." % type(params))
1870
1871    if charset:
1872        quoted = _quote_data(params, charset)
1873    else:
1874        quoted = _quote_data(params)
1875
1876    # positional string substitution now requires a tuple
1877    if hasattr(quoted, 'startswith'):
1878        quoted = (quoted,)
1879
1880    if isinstance(toformat, unicode):
1881        toformat = toformat.encode(charset)
1882
1883    if isinstance(params, dict):
1884        """ assume name based substitutions """
1885        offset = 0
1886        for match in _re_name_param.finditer(toformat):
1887            param_key = match.group(2).decode(charset)
1888
1889            if not param_key in params:
1890                raise ValueError('params dictionary did not contain value for placeholder: %s' % param_key)
1891
1892            # calculate string positions so we can keep track of the offset to
1893            # be used in future substitutions on this string. This is
1894            # necessary b/c the match start() and end() are based on the
1895            # original string, but we modify the original string each time we
1896            # loop, so we need to make an adjustment for the difference between
1897            # the length of the placeholder and the length of the value being
1898            # substituted
1899            param_val = quoted[param_key]
1900            param_val_len = len(param_val)
1901            placeholder_len = len(match.group(1))
1902            offset_adjust = param_val_len - placeholder_len
1903
1904            # do the string substitution
1905            match_start = match.start(1) + offset
1906            match_end = match.end(1) + offset
1907            toformat = toformat[:match_start] + ensure_bytes(param_val) + toformat[match_end:]
1908
1909            # adjust the offset for the next usage
1910            offset += offset_adjust
1911    else:
1912        """ assume position based substitutions """
1913        offset = 0
1914        for count, match in enumerate(_re_pos_param.finditer(toformat)):
1915            # calculate string positions so we can keep track of the offset to
1916            # be used in future substitutions on this string. This is
1917            # necessary b/c the match start() and end() are based on the
1918            # original string, but we modify the original string each time we
1919            # loop, so we need to make an adjustment for the difference between
1920            # the length of the placeholder and the length of the value being
1921            # substituted
1922            try:
1923                param_val = quoted[count]
1924            except IndexError:
1925                raise ValueError('more placeholders in sql than params available')
1926            param_val_len = len(param_val)
1927            placeholder_len = 2
1928            offset_adjust = param_val_len - placeholder_len
1929
1930            # do the string substitution
1931            match_start = match.start(1) + offset
1932            match_end = match.end(1) + offset
1933            toformat = toformat[:match_start] + ensure_bytes(param_val) + toformat[match_end:]
1934            #print(param_val, param_val_len, offset_adjust, match_start, match_end)
1935            # adjust the offset for the next usage
1936            offset += offset_adjust
1937    return toformat
1938
1939# We'll add these methods to the module to allow for unit testing of the
1940# underlying C methods.
1941def quote_simple_value(value):
1942    return _quote_simple_value(value)
1943
1944def quote_or_flatten(data):
1945    return _quote_or_flatten(data)
1946
1947def quote_data(data):
1948    return _quote_data(data)
1949
1950def substitute_params(toformat, params, charset='utf8'):
1951    return _substitute_params(toformat, params, charset)
1952
1953###########################
1954## Compatibility Aliases ##
1955###########################
1956def connect(*args, **kwargs):
1957    return MSSQLConnection(*args, **kwargs)
1958
1959MssqlDatabaseException = MSSQLDatabaseException
1960MssqlDriverException = MSSQLDriverException
1961MssqlConnection = MSSQLConnection
1962
1963###########################
1964## Test Helper Functions ##
1965###########################
1966
1967def test_err_handler(connection, int severity, int dberr, int oserr, dberrstr, oserrstr):
1968    """
1969    Expose err_handler function and its side effects to facilitate testing.
1970    """
1971    cdef DBPROCESS *dbproc = NULL
1972    cdef char *dberrstrc = NULL
1973    cdef char *oserrstrc = NULL
1974    if dberrstr:
1975        dberrstr_byte_string = dberrstr.encode('UTF-8')
1976        dberrstrc = dberrstr_byte_string
1977    if oserrstr:
1978        oserrstr_byte_string = oserrstr.encode('UTF-8')
1979        oserrstrc = oserrstr_byte_string
1980    if connection:
1981        dbproc = (<MSSQLConnection>connection).dbproc
1982    results = (
1983        err_handler(dbproc, severity, dberr, oserr, dberrstrc, oserrstrc),
1984        get_last_msg_str(connection),
1985        get_last_msg_no(connection),
1986        get_last_msg_severity(connection),
1987        get_last_msg_state(connection)
1988    )
1989    clr_err(connection)
1990    return results
1991
1992
1993#####################
1994## Max Connections ##
1995#####################
1996def get_max_connections():
1997    """
1998    Get maximum simultaneous connections db-lib will open to the server.
1999    """
2000    return dbgetmaxprocs()
2001
2002def set_max_connections(int limit):
2003    """
2004    Set maximum simultaneous connections db-lib will open to the server.
2005
2006    :param limit: the connection limit
2007    :type limit: int
2008    """
2009    dbsetmaxprocs(limit)
2010
2011cdef void init_mssql():
2012    if dbinit() == FAIL:
2013        raise MSSQLDriverException("dbinit() failed")
2014
2015    dberrhandle(err_handler)
2016    dbmsghandle(msg_handler)
2017
2018init_mssql()
2019