Skip to content

Commit b72336b

Browse files
committed
sql: Add pymssql backend
1 parent c494da4 commit b72336b

File tree

3 files changed

+124
-0
lines changed

3 files changed

+124
-0
lines changed

Orange/data/sql/backend/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,8 @@
44
from .postgres import Psycopg2Backend
55
except ImportError:
66
pass
7+
8+
try:
9+
from .mssql import PymssqlBackend
10+
except ImportError:
11+
pass

Orange/data/sql/backend/mssql.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
from contextlib import contextmanager
2+
3+
import pymssql
4+
5+
from Orange.data import StringVariable, TimeVariable, ContinuousVariable, DiscreteVariable
6+
from Orange.data.sql.backend import Backend
7+
from Orange.data.sql.backend.base import ToSql, BackendError
8+
9+
10+
class PymssqlBackend(Backend):
11+
display_name = "SQL Server"
12+
13+
def __init__(self, connection_params):
14+
connection_params["server"] = connection_params.pop("host", None)
15+
16+
for key in list(connection_params):
17+
if connection_params[key] is None:
18+
del connection_params[key]
19+
20+
super().__init__(connection_params)
21+
try:
22+
self.connection = pymssql.connect(**connection_params)
23+
except pymssql.Error as ex:
24+
raise BackendError(str(ex)) from ex
25+
26+
def list_tables_query(self, schema=None):
27+
return """
28+
SELECT [TABLE_SCHEMA], [TABLE_NAME]
29+
FROM information_schema.tables
30+
WHERE TABLE_TYPE='BASE TABLE'
31+
ORDER BY [TABLE_NAME]
32+
"""
33+
34+
def quote_identifier(self, name):
35+
return "[{}]".format(name)
36+
37+
def unquote_identifier(self, quoted_name):
38+
return quoted_name[1:-1]
39+
40+
def create_sql_query(self, table_name, fields, filters=(),
41+
group_by=None, order_by=None, offset=None, limit=None,
42+
use_time_sample=None):
43+
sql = ["SELECT"]
44+
if limit and not offset:
45+
sql.extend(["TOP", str(limit)])
46+
sql.append(', '.join(fields))
47+
sql.extend(["FROM", table_name])
48+
if use_time_sample:
49+
sql.append("TABLESAMPLE system_time(%i)" % use_time_sample)
50+
if filters:
51+
sql.extend(["WHERE", " AND ".join(filters)])
52+
if group_by:
53+
sql.extend(["GROUP BY", ", ".join(group_by)])
54+
55+
if offset and not order_by:
56+
order_by = fields[0].split("AS")[1:]
57+
58+
if order_by:
59+
sql.extend(["ORDER BY", ",".join(order_by)])
60+
if offset:
61+
sql.extend(["OFFSET", str(offset), "ROWS"])
62+
if limit:
63+
sql.extend(["FETCH FIRST", str(limit), "ROWS ONLY"])
64+
65+
return " ".join(sql)
66+
67+
@contextmanager
68+
def execute_sql_query(self, query, params=()):
69+
print(query)
70+
try:
71+
with self.connection.cursor() as cur:
72+
cur.execute(query, *params)
73+
yield cur
74+
finally:
75+
self.connection.commit()
76+
77+
def create_variable(self, field_name, field_metadata, type_hints, inspect_table=None):
78+
if field_name in type_hints:
79+
var = type_hints[field_name]
80+
else:
81+
var = self._guess_variable(field_name, field_metadata,
82+
inspect_table)
83+
84+
field_name_q = self.quote_identifier(field_name)
85+
if var.is_continuous:
86+
if isinstance(var, TimeVariable):
87+
var.to_sql = ToSql("DATEDIFF(s, '1970-01-01 00:00:00', {})".format(field_name_q))
88+
else:
89+
var.to_sql = ToSql(field_name_q)
90+
else: # discrete or string
91+
var.to_sql = ToSql(field_name_q)
92+
return var
93+
94+
def _guess_variable(self, field_name, field_metadata, inspect_table):
95+
from pymssql import STRING, NUMBER, DATETIME, DECIMAL
96+
97+
type_code, *rest = field_metadata
98+
99+
if type_code in (NUMBER, DECIMAL):
100+
return ContinuousVariable(field_name)
101+
102+
if type_code == DATETIME:
103+
tv = TimeVariable(field_name)
104+
tv.have_date = True
105+
tv.have_time = True
106+
return tv
107+
108+
if type_code == STRING:
109+
if inspect_table:
110+
values = [] #self._get_distinct_values(field_name, inspect_table)
111+
if values:
112+
return DiscreteVariable(field_name, values)
113+
114+
return StringVariable(field_name)
115+
116+
def count_approx(self, query):
117+
# TODO: Figure out how to do count estimates on mssql
118+
raise NotImplementedError

requirements-sql.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
psycopg2
2+
pymssql

0 commit comments

Comments
 (0)