Skip to content

Commit 8bb12e1

Browse files
authored
feat(mssql): support connecting with a url (#9894)
Closes #9856.
1 parent d112c15 commit 8bb12e1

File tree

3 files changed

+63
-3
lines changed

3 files changed

+63
-3
lines changed

ibis/backends/mssql/__init__.py

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from contextlib import closing
99
from operator import itemgetter
1010
from typing import TYPE_CHECKING, Any
11+
from urllib.parse import unquote_plus
1112

1213
import pyodbc
1314
import sqlglot as sg
@@ -21,12 +22,13 @@
2122
import ibis.expr.schema as sch
2223
import ibis.expr.types as ir
2324
from ibis import util
24-
from ibis.backends import CanCreateCatalog, CanCreateDatabase, CanCreateSchema, NoUrl
25+
from ibis.backends import CanCreateCatalog, CanCreateDatabase, CanCreateSchema
2526
from ibis.backends.sql import SQLBackend
2627
from ibis.backends.sql.compilers.base import STAR, C
2728

2829
if TYPE_CHECKING:
2930
from collections.abc import Iterable, Mapping
31+
from urllib.parse import ParseResult
3032

3133
import pandas as pd
3234
import polars as pl
@@ -73,7 +75,7 @@ def datetimeoffset_to_datetime(value):
7375
# Databases: sys.schemas
7476

7577

76-
class Backend(SQLBackend, CanCreateCatalog, CanCreateDatabase, CanCreateSchema, NoUrl):
78+
class Backend(SQLBackend, CanCreateCatalog, CanCreateDatabase, CanCreateSchema):
7779
name = "mssql"
7880
compiler = sc.mssql.compiler
7981
supports_create_or_replace = False
@@ -169,6 +171,40 @@ def _post_connect(self):
169171
with closing(self.con.cursor()) as cur:
170172
cur.execute("SET DATEFIRST 1")
171173

174+
def _from_url(self, url: ParseResult, **kwargs):
175+
database, *_ = url.path[1:].split("/", 1)
176+
kwargs.update(
177+
{
178+
"user": url.username,
179+
"password": unquote_plus(url.password or ""),
180+
"host": url.hostname,
181+
"database": database or "",
182+
"port": url.port or None,
183+
}
184+
)
185+
186+
self._convert_kwargs(kwargs)
187+
188+
if "host" in kwargs and not kwargs["host"]:
189+
del kwargs["host"]
190+
191+
if "user" in kwargs and not kwargs["user"]:
192+
del kwargs["user"]
193+
194+
if "password" in kwargs and kwargs["password"] is None:
195+
del kwargs["password"]
196+
197+
if "port" in kwargs and kwargs["port"] is None:
198+
del kwargs["port"]
199+
200+
if "database" in kwargs and not kwargs["database"]:
201+
del kwargs["database"]
202+
203+
if "driver" in kwargs and not kwargs["driver"]:
204+
del kwargs["driver"]
205+
206+
return self.connect(**kwargs)
207+
172208
def get_schema(
173209
self, name: str, *, catalog: str | None = None, database: str | None = None
174210
) -> sch.Schema:

ibis/backends/mssql/tests/test_client.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from __future__ import annotations
22

3+
from urllib.parse import urlencode
4+
35
import pytest
46
import sqlglot as sg
57
import sqlglot.expressions as sge
@@ -8,6 +10,14 @@
810
import ibis
911
import ibis.expr.datatypes as dt
1012
from ibis import udf
13+
from ibis.backends.mssql.tests.conftest import (
14+
IBIS_TEST_MSSQL_DB,
15+
MSSQL_HOST,
16+
MSSQL_PASS,
17+
MSSQL_PORT,
18+
MSSQL_PYODBC_DRIVER,
19+
MSSQL_USER,
20+
)
1121

1222
RAW_DB_TYPES = [
1323
# Exact numbers
@@ -204,3 +214,17 @@ def test_create_temp_table_from_obj(con):
204214
assert persisted_from_temp.to_pyarrow().equals(t2.to_pyarrow())
205215

206216
con.drop_table("fuhreal")
217+
218+
219+
def test_from_url():
220+
user = MSSQL_USER
221+
password = MSSQL_PASS
222+
host = MSSQL_HOST
223+
port = MSSQL_PORT
224+
database = IBIS_TEST_MSSQL_DB
225+
driver = MSSQL_PYODBC_DRIVER
226+
new_con = ibis.connect(
227+
f"mssql://{user}:{password}@{host}:{port}/{database}?{urlencode(dict(driver=driver))}"
228+
)
229+
result = new_con.sql("SELECT 1 AS [a]").to_pandas().a.iat[0]
230+
assert result == 1

ibis/backends/oracle/tests/test_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def test_list_tables_schema_warning_refactor(con):
7979
assert con.list_tables(database="SYS", like="EXU8OPT") == ["EXU8OPT"]
8080

8181

82-
def test_from_url(con):
82+
def test_from_url():
8383
new_con = ibis.connect("oracle://ibis:ibis@localhost:1521/IBIS_TESTING")
8484

8585
assert new_con.list_tables()

0 commit comments

Comments
 (0)