1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
|
==========================
Migrating from 1.x to 2.x
==========================
Because of the DB-API standard and because effort was made to make the
interface of pymssql 2.x similar to that of pymssql 1.x, there are only a few
differences and usually upgrading is pretty easy.
There are a few differences though...
``str`` vs. ``unicode``
=======================
Note that we are talking about Python 2, because pymssql 1.x doesn't work on
Python 3.
pymssql 1.x will return ``str`` instances::
>>> pymssql.__version__
'1.0.3'
>>> conn.as_dict = True
>>> cursor = conn.cursor()
>>> cursor.execute("SELECT 'hello' AS str FROM foo")
>>> cursor.fetchall()
[{0: 'hello', 'str': 'hello'}]
whereas pymssql 2.x will return ``unicode`` instances::
>>> pymssql.__version__
u'2.0.1.2'
>>> conn.as_dict = True
>>> cursor = conn.cursor()
>>> cursor.execute("SELECT 'hello' AS str FROM foo")
>>> cursor.fetchall()
[{u'str': u'hello'}]
If your application has code that deals with ``str`` and ``unicode``
differently, then you may run into issues.
You can always convert a ``unicode`` to a ``str`` by encoding::
>>> cursor.execute("SELECT 'hello' AS str FROM foo")
>>> s = cursor.fetchone()['str']
>>> s
u'hello'
>>> s.encode('utf-8')
'hello'
Handling of ``uniqueidentifier`` columns
========================================
SQL Server has a data type called `uniqueidentifier
<http://technet.microsoft.com/en-us/library/ms187942.aspx>`_.
In pymssql 1.x, ``uniqueidentifier`` columns are returned in results as
byte strings with 16 bytes; if you want a :class:`python:uuid.UUID` instance,
then you have to construct it yourself from the byte string::
>>> cursor.execute("SELECT * FROM foo")
>>> id_value = cursor.fetchone()['uniqueidentifier']
>>> id_value
'j!\xcf\x14D\xce\xe6B\xab\xe0\xd9\xbey\x0cMK'
>>> type(id_value)
<type 'str'>
>>> len(id_value)
16
>>> import uuid
>>> id_uuid = uuid.UUID(bytes_le=id_value)
>>> id_uuid
UUID('14cf216a-ce44-42e6-abe0-d9be790c4d4b')
In pymssql 2.x, ``uniqueidentifier`` columns are returned in results as
instances of :class:`python:uuid.UUID` and if you want the bytes, like in
pymssql 1.x, you have to use :attr:`python:uuid.UUID.bytes_le` to get them::
>>> cursor.execute("SELECT * FROM foo")
>>> id_value = cursor.fetchone()['uniqueidentifier']
>>> id_value
UUID('14cf216a-ce44-42e6-abe0-d9be790c4d4b')
>>> type(id_value)
<class 'uuid.UUID'>
>>> id_value.bytes_le
'j!\xcf\x14D\xce\xe6B\xab\xe0\xd9\xbey\x0cMK'
Arguments to ``pymssql.connect``
================================
The arguments are a little bit different. Some notable differences:
In pymssql 1.x, the parameter to specify the host is called ``host`` and it can contain a host and port -- e.g.:
::
conn = pymssql.connect(host='SQLHOST:1433') # specified TCP port at a host
There are some other syntaxes for the ``host`` parameter that allow using a
comma instead of a colon to delimit host and port, to specify Windows hosts, to
specify a specific SQL Server instance, etc.
::
conn = pymssql.connect(host=r'SQLHOST,5000') # specified TCP port at a host
conn = pymssql.connect(host=r'(local)\SQLEXPRESS') # named instance on local machine [Win]
In pymssql 2.x, the ``host`` parameter is supported (I am unsure if it has all
of the functionality of pymssql 1.x). There is also a parameter to specify the
host that is called ``server``. There is a separate parameter called ``port``.
::
conn = pymssql.connect(server='SQLHOST', port=1500)
Parameter substitution
======================
For parameter substitution, pymssql 2.x supports the ``format`` and
``pyformat`` `PEP 249 paramstyles
<http://www.python.org/dev/peps/pep-0249/#paramstyle>`_.
Note that for ``pyformat``, PEP 249 only shows the example of a string substitution -- e.g.::
%(name)s
It is not clear from PEP 249 whether other types should be supported, like::
%(name)d
%(name)f
However, in this `mailing list thread
<http://python.6.x6.nabble.com/Some-obscurity-with-paramstyle-td2163302.html>`_,
the general consensus is that the string format should be the only one
required.
Note that pymssql 2.x does not support ``%(name)d``, whereas pymssql 1.x did.
So you may have to change code that uses this notation::
>>> pymssql.__version__
u'2.0.1.2'
>>> pymssql.paramstyle
'pyformat'
>>> cursor.execute("select 'hello' where 1 = %(name)d", dict(name=1))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "pymssql.pyx", line 430, in pymssql.Cursor.execute (pymssql.c:5900)
if not self._source._conn.nextresult():
pymssql.ProgrammingError: (102, "Incorrect syntax near '('.
DB-Lib error message 20018, severity 15:\n
General SQL Server error: Check messages from the SQL Server\n")
to::
>>> cursor.execute("select 'hello' where '1' = %(name)s", dict(name='1'))
>>> cursor.fetchall()
[(u'hello',)]
or::
>>> cursor.execute("select 'hello' where 1 = %d", 1)
>>> cursor.fetchall()
[(u'hello',)]
Examples of this problem:
* `Google Group post: paramstyle changed? <https://groups.google.com/forum/?fromgroups=#!searchin/pymssql/param/pymssql/sSriPxHfZNk/VoOrl-84MQwJ>`_
* `GitHub issue #155: pymssql 2.x does not support "%(foo)d" parameter substitution style; pymssql 1.x did <https://github.com/pymssql/pymssql/issues/155>`_
|