Skip to content

Commit afecfb5

Browse files
Michael de VilliersMichael de Villiers
Michael de Villiers
authored and
Michael de Villiers
committed
Initial commit
0 parents  commit afecfb5

File tree

5 files changed

+346
-0
lines changed

5 files changed

+346
-0
lines changed

.gitignore

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
6+
# C extensions
7+
*.so
8+
9+
# Distribution / packaging
10+
.Python
11+
env/
12+
build/
13+
develop-eggs/
14+
dist/
15+
downloads/
16+
eggs/
17+
.eggs/
18+
lib/
19+
lib64/
20+
parts/
21+
sdist/
22+
var/
23+
*.egg-info/
24+
.installed.cfg
25+
*.egg
26+
27+
# PyInstaller
28+
# Usually these files are written by a python script from a template
29+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
30+
*.manifest
31+
*.spec
32+
33+
# Installer logs
34+
pip-log.txt
35+
pip-delete-this-directory.txt
36+
37+
# Unit test / coverage reports
38+
htmlcov/
39+
.tox/
40+
.coverage
41+
.coverage.*
42+
.cache
43+
nosetests.xml
44+
coverage.xml
45+
*,cover
46+
.hypothesis/
47+
48+
# Translations
49+
*.mo
50+
*.pot
51+
52+
# Django stuff:
53+
*.log
54+
local_settings.py
55+
56+
# Flask stuff:
57+
instance/
58+
.webassets-cache
59+
60+
# Scrapy stuff:
61+
.scrapy
62+
63+
# Sphinx documentation
64+
docs/_build/
65+
66+
# PyBuilder
67+
target/
68+
69+
# IPython Notebook
70+
.ipynb_checkpoints
71+
72+
# pyenv
73+
.python-version
74+
75+
# celery beat schedule file
76+
celerybeat-schedule
77+
78+
# dotenv
79+
.env
80+
81+
# virtualenv
82+
.venv/
83+
venv/
84+
ENV/
85+
86+
# Spyder project settings
87+
.spyderproject
88+
89+
# Rope project settings
90+
.ropeproject

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2016 Michael de Villiers
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.rst

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
Peewee
2+
######
3+
A database driver to add Microsoft SQL Server and Azure support using
4+
`pymssql <http://pymssql.org>`_ and `FreeTDS <http://freetds.org>`_ in
5+
Peewee and should run on most Unix-like systems, Microsoft Windows and Mac OS X.
6+
7+
In it's current state you should be able to access data and possibly do certain
8+
updates. You will not be able to create tables or introspect existing databases,
9+
so you will have to write your models manually. Offsets will also not work,
10+
since the query compiler will need some significant rewrite to support TSQL
11+
dialect, however limit will work (TOP).
12+
13+
Installation
14+
============
15+
Install the latest stable release from Pypi:
16+
17+
pip install peewee-mssql
18+
19+
Or alternatively, select a release or development version from Github and run:
20+
21+
python setup.py install
22+
23+
Getting Started
24+
===============
25+
For help on installing and configuring `FreeTDS <http://freetds.org>`_ I
26+
recommend taking a look at the
27+
`guide <http://pymssql.org/en/latest/freetds.html>`_ in the
28+
`pymssql <http://www.pymssql.org>`_ documentation.
29+
30+
And then you should be able to instantiate a database as below and start
31+
building your models for accessing data:
32+
33+
.. code-block:: python
34+
35+
from peewee_mssql import MssqlDatabase
36+
37+
db = MssqlDatabase('MyDatabase', host='host.example.com', user=r'domain\username', password='password')
38+
39+
If you are using Microsoft SQL Server 2005 you will need to use the legacy
40+
datetime data types, simple pass `use_legacy_datetime=True` to the
41+
database driver when initializing.

peewee_mssql.py

+151
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
from peewee import Database, ImproperlyConfigured, OP, QueryCompiler, CompoundSelect, SQL, Clause, CommaClause
2+
from playhouse.db_url import register_database
3+
4+
try:
5+
import pymssql
6+
except ImportError:
7+
pymssql = None
8+
9+
try:
10+
from playhouse.pool import PooledDatabase
11+
except ImportError:
12+
PooledDatabase = None
13+
14+
__version__ = '0.1.0'
15+
16+
class MssqlQueryCompiler(QueryCompiler):
17+
# TODO: implement limit and offset properly, we can use:
18+
# SELECT *
19+
# FROM (SELECT ROW_NUMBER() OVER(ORDER BY id) RowNr, id FROM tbl) t
20+
# WHERE RowNr BETWEEN 10 AND 20
21+
22+
def generate_select(self, query, alias_map=None):
23+
model = query.model_class
24+
db = model._meta.database
25+
26+
alias_map = self.calculate_alias_map(query, alias_map)
27+
28+
if isinstance(query, CompoundSelect):
29+
clauses = [_StripParens(query)]
30+
else:
31+
if not query._distinct:
32+
clauses = [SQL('SELECT')]
33+
else:
34+
clauses = [SQL('SELECT DISTINCT')]
35+
if query._distinct not in (True, False):
36+
clauses += [SQL('ON'), EnclosedClause(*query._distinct)]
37+
38+
# basic support for query limit
39+
if query._limit is not None or (query._offset and db.limit_max):
40+
limit = query._limit if query._limit is not None else db.limit_max
41+
clauses.append(SQL('TOP %s' % limit))
42+
43+
select_clause = Clause(*query._select)
44+
select_clause.glue = ', '
45+
46+
clauses.extend((select_clause, SQL('FROM')))
47+
if query._from is None:
48+
clauses.append(model.as_entity().alias(alias_map[model]))
49+
else:
50+
clauses.append(CommaClause(*query._from))
51+
52+
if query._windows is not None:
53+
clauses.append(SQL('WINDOW'))
54+
clauses.append(CommaClause(*[
55+
Clause(
56+
SQL(window._alias),
57+
SQL('AS'),
58+
window.__sql__())
59+
for window in query._windows]))
60+
61+
join_clauses = self.generate_joins(query._joins, model, alias_map)
62+
if join_clauses:
63+
clauses.extend(join_clauses)
64+
65+
if query._where is not None:
66+
clauses.extend([SQL('WHERE'), query._where])
67+
68+
if query._group_by:
69+
clauses.extend([SQL('GROUP BY'), CommaClause(*query._group_by)])
70+
71+
if query._having:
72+
clauses.extend([SQL('HAVING'), query._having])
73+
74+
if query._order_by:
75+
clauses.extend([SQL('ORDER BY'), CommaClause(*query._order_by)])
76+
77+
# NO OFFSET SUPPORT
78+
79+
for_update, no_wait = query._for_update
80+
if for_update:
81+
stmt = 'FOR UPDATE NOWAIT' if no_wait else 'FOR UPDATE'
82+
clauses.append(SQL(stmt))
83+
84+
return self.build_query(clauses, alias_map)
85+
86+
class MssqlDatabase(Database):
87+
compiler_class = MssqlQueryCompiler
88+
commit_select = False
89+
interpolation = '%s'
90+
quote_char = '"'
91+
92+
field_overrides = {
93+
'bool': 'tinyint',
94+
'double': 'float(53)',
95+
'float': 'float',
96+
'int': 'int',
97+
'string': 'nvarchar',
98+
'fixed_char': 'nchar',
99+
'text': 'nvarchar(max)',
100+
'blob': 'varbinary',
101+
'uuid': 'nchar(40)',
102+
'primary_key': 'int identity',
103+
'datetime': 'datetime2',
104+
'date': 'date',
105+
'time': 'time',
106+
}
107+
108+
op_overrides = {
109+
OP.LIKE: 'LIKE BINARY',
110+
OP.ILIKE: 'LIKE',
111+
}
112+
113+
def _connect(self, database, **kwargs):
114+
if not pymssql:
115+
raise ImproperlyConfigured('pymssql must be installed')
116+
117+
if kwargs.pop('use_legacy_datetime', False):
118+
self.field_overrides['datetime'] = 'datetime'
119+
self.field_overrides['date'] = 'nvarchar(15)'
120+
self.field_overrides['time'] = 'nvarchar(10)'
121+
122+
return pymssql.connect(database=database, **kwargs)
123+
124+
def get_tables(self, schema=None):
125+
# should I not be using sys.tables?
126+
if schema:
127+
query = ('SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE '
128+
'TABLE_SCHEMA = %s AND TABLE_TYPE = %s ORDER BY TABLE_NAME')
129+
cursor = self.execute_sql(query, (schema, 'BASE TABLE',),
130+
require_commit=False)
131+
else:
132+
query = ('SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE '
133+
'TABLE_TYPE = %s ORDER BY TABLE_NAME')
134+
cursor = self.execute_sql(query, ('BASE TABLE',),
135+
require_commit=False)
136+
137+
return [row[0] for row in cursor.fetchall()]
138+
139+
def execute_sql(self, sql, params, *args, **kwargs):
140+
# convert params to tuple
141+
params = tuple(params)
142+
143+
return super(MssqlDatabase, self).execute_sql(sql, params, *args, **kwargs)
144+
145+
register_database(MssqlDatabase, 'mssql')
146+
147+
if PooledDatabase:
148+
class PooledMssqlDatabase(PooledDatabase, MssqlDatabase):
149+
pass # TODO: implement _is_closed()
150+
151+
register_database(PooledMssqlDatabase, 'mssql+pool')

setup.py

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from setuptools import setup
2+
3+
from peewee_mssql import __version__
4+
5+
def long_description():
6+
descr = open('README.rst', 'r').read()
7+
8+
try:
9+
descr += '\n\n' + open('docs/changelog.rst', 'r').read()
10+
except:
11+
pass
12+
13+
return descr
14+
15+
setup(
16+
name='peewee-mssql',
17+
version=__version__,
18+
url='https://github.com/cour4g3/peewee-mssql',
19+
license='MIT',
20+
author='Michael de Villiers',
21+
author_email='[email protected]',
22+
description='Database driver to add Microsoft SQL Server/Azure support to Peewee',
23+
long_description=long_description(),
24+
py_modules=['peewee_mssql'],
25+
platforms='any',
26+
install_requires=[
27+
'peewee',
28+
'pymssql',
29+
],
30+
classifiers=[
31+
'Development Status :: 3 - Alpha',
32+
'Intended Audience :: Developers',
33+
'License :: OSI Approved :: MIT License',
34+
'Operating System :: OS Independent',
35+
'Programming Language :: Python',
36+
'Topic :: Software Development :: Libraries :: Python Modules',
37+
'Programming Language :: Python :: 2.7',
38+
'Programming Language :: Python :: 2.6',
39+
'Programming Language :: Python :: 3.3',
40+
'Programming Language :: Python :: 3.4',
41+
'Programming Language :: Python :: 3.5',
42+
],
43+
)

0 commit comments

Comments
 (0)