Skip to content

Add regex support #43

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ Installation

'ENGINE': 'sql_server.pyodbc'

Regex Support
-------------

django-mssql-backend supports regex using a CLR .dll file. To install it, run ::

python manage.py install_regex_clr {database_name}

Configuration
-------------

Expand Down
68 changes: 68 additions & 0 deletions sql_server/pyodbc/creation.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import binascii
import os

from django.db.backends.base.creation import BaseDatabaseCreation


Expand Down Expand Up @@ -25,3 +28,68 @@ def sql_table_creation_suffix(self):
if collation:
suffix.append('COLLATE %s' % collation)
return ' '.join(suffix)

# The following code to add regex support in SQLServer is taken from django-mssql
# see https://bitbucket.org/Manfre/django-mssql
def enable_clr(self):
""" Enables clr for server if not already enabled
This function will not fail if current user doesn't have
permissions to enable clr, and clr is already enabled
"""
with self._nodb_connection.cursor() as cursor:
# check whether clr is enabled
cursor.execute('''
SELECT value FROM sys.configurations
WHERE name = 'clr enabled'
''')
res = None
try:
res = cursor.fetchone()
except Exception:
pass

if not res or not res[0]:
# if not enabled enable clr
cursor.execute("sp_configure 'clr enabled', 1")
cursor.execute("RECONFIGURE")

cursor.execute("sp_configure 'show advanced options', 1")
cursor.execute("RECONFIGURE")

cursor.execute("sp_configure 'clr strict security', 0")
cursor.execute("RECONFIGURE")

def install_regex_clr(self, database_name):
sql = '''
USE {database_name};
-- Drop and recreate the function if it already exists
IF OBJECT_ID('REGEXP_LIKE') IS NOT NULL
DROP FUNCTION [dbo].[REGEXP_LIKE]
IF EXISTS(select * from sys.assemblies where name like 'regex_clr')
DROP ASSEMBLY regex_clr
;
CREATE ASSEMBLY regex_clr
FROM 0x{assembly_hex}
WITH PERMISSION_SET = SAFE;
create function [dbo].[REGEXP_LIKE]
(
@input nvarchar(max),
@pattern nvarchar(max),
@caseSensitive int
)
RETURNS INT AS
EXTERNAL NAME regex_clr.UserDefinedFunctions.REGEXP_LIKE
'''.format(
database_name=self.connection.ops.quote_name(database_name),
assembly_hex=self.get_regex_clr_assembly_hex(),
).split(';')

self.enable_clr()

with self._nodb_connection.cursor() as cursor:
for s in sql:
cursor.execute(s)

def get_regex_clr_assembly_hex(self):
with open(os.path.join(os.path.dirname(__file__), 'regex_clr.dll'), 'rb') as f:
return binascii.hexlify(f.read()).decode('ascii')
2 changes: 1 addition & 1 deletion sql_server/pyodbc/features.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
supports_ignore_conflicts = False
supports_index_on_text_field = False
supports_paramstyle_pyformat = False
supports_regex_backreferencing = False
supports_regex_backreferencing = True
supports_sequence_reset = False
supports_subqueries_in_group_by = False
supports_tablespaces = True
Expand Down
Empty file.
Empty file.
25 changes: 25 additions & 0 deletions sql_server/pyodbc/management/commands/install_regex_clr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Add regex support in SQLServer
# Code taken from django-mssql (see https://bitbucket.org/Manfre/django-mssql)

from django.core.management.base import BaseCommand
from django.db import connection


class Command(BaseCommand):
help = "Installs the regex_clr.dll assembly with the database"

requires_model_validation = False

args = 'database_name'

def add_arguments(self, parser):
parser.add_argument('database_name')

def handle(self, *args, **options):
database_name = options['database_name']
if not database_name:
self.print_help('manage.py', 'install_regex_clr')
return

connection.creation.install_regex_clr(database_name)
print('Installed regex_clr to database %s' % database_name)
3 changes: 2 additions & 1 deletion sql_server/pyodbc/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,8 @@ def regex_lookup(self, lookup_type):
If the feature is not supported (or part of it is not supported), a
NotImplementedError exception can be raised.
"""
raise NotImplementedError('SQL Server has no built-in regular expression support.')
match_option = {'iregex': 0, 'regex': 1}[lookup_type]
return "dbo.REGEXP_LIKE(%%s, %%s, %s)=1" % (match_option,)

def limit_offset_sql(self, low_mark, high_mark):
"""Return LIMIT/OFFSET SQL clause."""
Expand Down
Binary file added sql_server/pyodbc/regex_clr.dll
Binary file not shown.
2 changes: 1 addition & 1 deletion test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ git fetch --depth=1 origin +refs/tags/*:refs/tags/*
git checkout $DJANGO_VERSION
pip install -r tests/requirements/py3.txt

python tests/runtests.py --settings=testapp.settings --noinput \
python tests/runtests.py --settings=testapp.settings --noinput --keepdb \
aggregation \
aggregation_regress \
annotations \
Expand Down
1 change: 1 addition & 0 deletions testapp/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
'django.contrib.contenttypes',
'django.contrib.staticfiles',
'django.contrib.auth',
'sql_server.pyodbc',
'testapp',
)

Expand Down
3 changes: 2 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ whitelist_externals =
/bin/bash

commands =
python manage.py test
python manage.py test --keepdb
python manage.py install_regex_clr test_default
bash test.sh

deps =
Expand Down