Skip to content

Commit 5902ddb

Browse files
committed
Add regex support
1 parent 39c46d7 commit 5902ddb

File tree

10 files changed

+100
-4
lines changed

10 files changed

+100
-4
lines changed

sql_server/pyodbc/creation.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import binascii
2+
import os
3+
14
from django.db.backends.base.creation import BaseDatabaseCreation
25

36

@@ -25,3 +28,68 @@ def sql_table_creation_suffix(self):
2528
if collation:
2629
suffix.append('COLLATE %s' % collation)
2730
return ' '.join(suffix)
31+
32+
# The following code to add regex support in SQLServer is taken from django-mssql
33+
# see https://bitbucket.org/Manfre/django-mssql
34+
def enable_clr(self):
35+
""" Enables clr for server if not already enabled
36+
This function will not fail if current user doesn't have
37+
permissions to enable clr, and clr is already enabled
38+
"""
39+
with self._nodb_connection.cursor() as cursor:
40+
# check whether clr is enabled
41+
cursor.execute('''
42+
SELECT value FROM sys.configurations
43+
WHERE name = 'clr enabled'
44+
''')
45+
res = None
46+
try:
47+
res = cursor.fetchone()
48+
except Exception:
49+
pass
50+
51+
if not res or not res[0]:
52+
# if not enabled enable clr
53+
cursor.execute("sp_configure 'clr enabled', 1")
54+
cursor.execute("RECONFIGURE")
55+
56+
cursor.execute("sp_configure 'show advanced options', 1")
57+
cursor.execute("RECONFIGURE")
58+
59+
cursor.execute("sp_configure 'clr strict security', 0")
60+
cursor.execute("RECONFIGURE")
61+
62+
def install_regex_clr(self, database_name):
63+
sql = '''
64+
USE {database_name};
65+
-- Drop and recreate the function if it already exists
66+
IF OBJECT_ID('REGEXP_LIKE') IS NOT NULL
67+
DROP FUNCTION [dbo].[REGEXP_LIKE]
68+
IF EXISTS(select * from sys.assemblies where name like 'regex_clr')
69+
DROP ASSEMBLY regex_clr
70+
;
71+
CREATE ASSEMBLY regex_clr
72+
FROM 0x{assembly_hex}
73+
WITH PERMISSION_SET = SAFE;
74+
create function [dbo].[REGEXP_LIKE]
75+
(
76+
@input nvarchar(max),
77+
@pattern nvarchar(max),
78+
@caseSensitive int
79+
)
80+
RETURNS INT AS
81+
EXTERNAL NAME regex_clr.UserDefinedFunctions.REGEXP_LIKE
82+
'''.format(
83+
database_name=self.connection.ops.quote_name(database_name),
84+
assembly_hex=self.get_regex_clr_assembly_hex(),
85+
).split(';')
86+
87+
self.enable_clr()
88+
89+
with self._nodb_connection.cursor() as cursor:
90+
for s in sql:
91+
cursor.execute(s)
92+
93+
def get_regex_clr_assembly_hex(self):
94+
with open(os.path.join(os.path.dirname(__file__), 'regex_clr.dll'), 'rb') as f:
95+
return binascii.hexlify(f.read()).decode('ascii')

sql_server/pyodbc/features.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
2525
supports_ignore_conflicts = False
2626
supports_index_on_text_field = False
2727
supports_paramstyle_pyformat = False
28-
supports_regex_backreferencing = False
28+
supports_regex_backreferencing = True
2929
supports_sequence_reset = False
3030
supports_subqueries_in_group_by = False
3131
supports_tablespaces = True

sql_server/pyodbc/management/__init__.py

Whitespace-only changes.

sql_server/pyodbc/management/commands/__init__.py

Whitespace-only changes.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Add regex support in SQLServer
2+
# Code taken from django-mssql (see https://bitbucket.org/Manfre/django-mssql)
3+
4+
from django.core.management.base import BaseCommand
5+
from django.db import connection
6+
7+
8+
class Command(BaseCommand):
9+
help = "Installs the regex_clr.dll assembly with the database"
10+
11+
requires_model_validation = False
12+
13+
args = 'database_name'
14+
15+
def add_arguments(self, parser):
16+
parser.add_argument('database_name')
17+
18+
def handle(self, *args, **options):
19+
database_name = options['database_name']
20+
if not database_name:
21+
self.print_help('manage.py', 'install_regex_clr')
22+
return
23+
24+
connection.creation.install_regex_clr(database_name)
25+
print('Installed regex_clr to database %s' % database_name)

sql_server/pyodbc/operations.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,8 @@ def regex_lookup(self, lookup_type):
273273
If the feature is not supported (or part of it is not supported), a
274274
NotImplementedError exception can be raised.
275275
"""
276-
raise NotImplementedError('SQL Server has no built-in regular expression support.')
276+
match_option = {'iregex': 0, 'regex': 1}[lookup_type]
277+
return "dbo.REGEXP_LIKE(%%s, %%s, %s)=1" % (match_option,)
277278

278279
def limit_offset_sql(self, low_mark, high_mark):
279280
"""Return LIMIT/OFFSET SQL clause."""

sql_server/pyodbc/regex_clr.dll

4.5 KB
Binary file not shown.

test.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ git fetch --depth=1 origin +refs/tags/*:refs/tags/*
1212
git checkout $DJANGO_VERSION
1313
pip install -r tests/requirements/py3.txt
1414

15-
python tests/runtests.py --settings=testapp.settings --noinput \
15+
python tests/runtests.py --settings=testapp.settings --noinput --keepdb \
1616
aggregation \
1717
aggregation_regress \
1818
annotations \

testapp/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
'django.contrib.contenttypes',
1010
'django.contrib.staticfiles',
1111
'django.contrib.auth',
12+
'sql_server.pyodbc',
1213
'testapp',
1314
)
1415

tox.ini

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ whitelist_externals =
1212
/bin/bash
1313

1414
commands =
15-
python manage.py test
15+
python manage.py test --keepdb
16+
python manage.py install_regex_clr test_default
1617
bash test.sh
1718

1819
deps =

0 commit comments

Comments
 (0)