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