Skip to content

Commit 90cd416

Browse files
authored
feat(ibis): bump ibis to 10.0.0 (#1060)
1 parent 4409f95 commit 90cd416

17 files changed

+487
-299
lines changed

ibis-server/Dockerfile

+3
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ RUN apt-get update \
4949
# Install msodbcsql 18 driver for mssql
5050
RUN ACCEPT_EULA=Y apt-get -y install unixodbc-dev msodbcsql18
5151

52+
# Install libmysqlclient-dev for mysql
53+
RUN apt-get install -y default-libmysqlclient-dev
54+
5255
# libpq-dev is required for psycopg2
5356
RUN apt-get -y install libpq-dev \
5457
&& rm -rf /var/lib/apt/lists/*

ibis-server/app/model/__init__.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,11 @@ class MySqlConnectionInfo(BaseModel):
112112
database: SecretStr
113113
user: SecretStr
114114
password: SecretStr | None = None
115-
ssl_mode: SecretStr | None = Field(alias="sslMode", default=None)
115+
ssl_mode: SecretStr | None = Field(
116+
alias="sslMode",
117+
default="ENABLED",
118+
description="Use ssl connection or not. The default value is `ENABLED` because MySQL uses `caching_sha2_password` by default and the driver MySQLdb support caching_sha2_password with ssl only.",
119+
)
116120
ssl_ca: SecretStr | None = Field(alias="sslCA", default=None)
117121
kwargs: dict[str, str] | None = Field(
118122
description="Additional keyword arguments to pass to PyMySQL", default=None

ibis-server/app/model/data_source.py

+1-10
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,7 @@ def get_mssql_connection(cls, info: MSSqlConnectionInfo) -> BaseBackend:
140140
port=info.port.get_secret_value(),
141141
database=info.database.get_secret_value(),
142142
user=info.user.get_secret_value(),
143-
password=(
144-
info.password
145-
and cls._escape_special_characters_for_odbc(
146-
info.password.get_secret_value()
147-
)
148-
),
143+
password=info.password.get_secret_value(),
149144
driver=info.driver,
150145
TDS_Version=info.tds_version,
151146
**info.kwargs if info.kwargs else dict(),
@@ -197,10 +192,6 @@ def get_trino_connection(info: TrinoConnectionInfo) -> BaseBackend:
197192
password=(info.password and info.password.get_secret_value()),
198193
)
199194

200-
@staticmethod
201-
def _escape_special_characters_for_odbc(value: str) -> str:
202-
return "{" + value.replace("}", "}}") + "}"
203-
204195
@staticmethod
205196
def _create_ssl_context(info: ConnectionInfo) -> Optional[ssl.SSLContext]:
206197
ssl_mode = (

ibis-server/docs/development.md

+20
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,23 @@ If you encounter this error, you can add the `TrustServerCertificate` parameter
9292
}
9393
}
9494
```
95+
96+
### No driver for MySQL Server
97+
98+
If you want run tests related to MySQL Server or connect to MySQL through Wren Engine, you need to install the MySQL client libraries (e.g. `libmysqlclient`) by yourself.
99+
- For linux, you can install `libmysqlclient-dev`. By the way, there are some different names for different linux versions. You should take care about it.
100+
- For Mac, you can install `mysql-connector-c`
101+
- For Windows, you can dowanload [the libraries](https://dev.mysql.com/downloads/c-api)
102+
103+
104+
### Connect MySQL without SSL
105+
106+
By default, SSL mode is enabled and uses `caching_sha2_password` authentication, which only supports SSL connections. If you need to disable SSL, you must set SSLMode to DISABLED in your connection configuration to use `mysql_native_password` instead.
107+
108+
```json
109+
{
110+
"connectionInfo": {
111+
"ssl_mode": "DISABLED"
112+
}
113+
}
114+
```

ibis-server/poetry.lock

+394-262
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ibis-server/pyproject.toml

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ packages = [{ include = "app" }]
1010
python = ">=3.11,<3.12"
1111
fastapi = { version = "0.115.8", extras = ["standard"] }
1212
pydantic = "2.10.6"
13-
ibis-framework = { version = "9.5.0", extras = [
13+
ibis-framework = { version = "10.0.0", extras = [
1414
"bigquery",
1515
"clickhouse",
1616
"mssql",
@@ -26,13 +26,14 @@ orjson = "3.10.15"
2626
pandas = "2.2.3"
2727
sqlglot = { extras = [
2828
"rs",
29-
], version = ">=23.4,<25.21" } # the version should follow the ibis-framework
29+
], version = ">=23.4,<26.5" } # the version should follow the ibis-framework
3030
loguru = "0.7.3"
3131
asgi-correlation-id = "4.3.4"
3232
gql = { extras = ["aiohttp"], version = "3.5.0" }
3333
anyio = "4.8.0"
3434
duckdb = "1.1.3"
3535
opendal = ">=0.45"
36+
mysqlclient = { version = ">=2.2.4,<3", optional = true }
3637

3738
[tool.poetry.group.dev.dependencies]
3839
pytest = "8.3.4"

ibis-server/tests/routers/v2/connector/test_bigquery.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ async def test_query(client, manifest_str):
9191
370,
9292
"O",
9393
"172799.49",
94-
"1996-01-02",
94+
"1996-01-02 00:00:00.000000",
9595
"1_370",
9696
"2024-01-01 23:59:59.000000",
9797
"2024-01-01 23:59:59.000000 UTC",

ibis-server/tests/routers/v2/connector/test_canner.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ async def test_query(client, manifest_str):
9595
370,
9696
"O",
9797
"172799.49",
98-
"1996-01-02",
98+
"1996-01-02 00:00:00.000000",
9999
"1_370",
100100
"2024-01-01 23:59:59.000000",
101101
"2024-01-01 23:59:59.000000 UTC",

ibis-server/tests/routers/v2/connector/test_clickhouse.py

+10-8
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ async def test_query(client, manifest_str, clickhouse: ClickHouseContainer):
180180
370,
181181
"O",
182182
"172799.49",
183-
"1996-01-02",
183+
"1996-01-02 00:00:00.000000",
184184
"1_370",
185185
"2024-01-01 23:59:59.000000",
186186
"2024-01-01 23:59:59.000000 UTC",
@@ -519,18 +519,20 @@ async def test_metadata_list_tables(client, clickhouse: ClickHouseContainer):
519519
)
520520
assert response.status_code == 200
521521

522-
result = response.json()[1]
523-
assert result["name"] == "test.orders"
524-
assert result["primaryKey"] is not None
525-
assert result["description"] == "This is a table comment"
526-
assert result["properties"] == {
522+
results = response.json()
523+
orders = next((x for x in results if x["name"] == "test.orders"), None)
524+
assert orders is not None
525+
assert orders["name"] == "test.orders"
526+
assert orders["primaryKey"] is not None
527+
assert orders["description"] == "This is a table comment"
528+
assert orders["properties"] == {
527529
"catalog": None,
528530
"schema": "test",
529531
"table": "orders",
530532
"path": None,
531533
}
532-
assert len(result["columns"]) == 9
533-
assert result["columns"][8] == {
534+
assert len(orders["columns"]) == 9
535+
assert orders["columns"][8] == {
534536
"name": "o_comment",
535537
"nestedColumns": None,
536538
"type": "VARCHAR",

ibis-server/tests/routers/v2/connector/test_mssql.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import base64
2+
import urllib
23

34
import orjson
45
import pandas as pd
@@ -125,7 +126,7 @@ async def test_query(client, manifest_str, mssql: SqlServerContainer):
125126
370,
126127
"O",
127128
"172799.49",
128-
"1996-01-02",
129+
"1996-01-02 00:00:00.000000",
129130
"1_370",
130131
"2024-01-01 23:59:59.000000",
131132
"2024-01-01 23:59:59.000000 UTC",
@@ -427,6 +428,15 @@ async def test_password_with_special_characters(client):
427428
assert response.status_code == 200
428429
assert "Microsoft SQL Server 2019" in response.text
429430

431+
connection_url = _to_connection_url(mssql)
432+
response = await client.post(
433+
url=f"{base_url}/metadata/version",
434+
json={"connectionInfo": {"connectionUrl": connection_url}},
435+
)
436+
437+
assert response.status_code == 200
438+
assert "Microsoft SQL Server 2019" in response.text
439+
430440

431441
def _to_connection_info(mssql: SqlServerContainer):
432442
return {
@@ -441,4 +451,4 @@ def _to_connection_info(mssql: SqlServerContainer):
441451

442452
def _to_connection_url(mssql: SqlServerContainer):
443453
info = _to_connection_info(mssql)
444-
return f"mssql://{info['user']}:{info['password']}@{info['host']}:{info['port']}/{info['database']}?driver=ODBC+Driver+18+for+SQL+Server&TrustServerCertificate=YES"
454+
return f"mssql://{info['user']}:{urllib.parse.quote_plus(info['password'])}@{info['host']}:{info['port']}/{info['database']}?driver=ODBC+Driver+18+for+SQL+Server&TrustServerCertificate=YES"

ibis-server/tests/routers/v2/connector/test_mysql.py

+28-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
import orjson
44
import pandas as pd
5+
import pymysql
56
import pytest
67
import sqlalchemy
7-
from pymysql import OperationalError
8+
from MySQLdb import OperationalError
89
from sqlalchemy import text
910
from testcontainers.mysql import MySqlContainer
1011

@@ -116,6 +117,26 @@ def mysql(request) -> MySqlContainer:
116117
@pytest.fixture(scope="module")
117118
def mysql_ssl_off(request) -> MySqlContainer:
118119
mysql = MySqlContainer(image="mysql:8.0.40").with_command("--ssl=0").start()
120+
# We disable SSL for this container to test SSLMode.ENABLED.
121+
# However, Mysql use caching_sha2_password as default authentication plugin which requires the connection to use SSL.
122+
# MysqlDB used by ibis supports caching_sha2_password only with SSL. So, we need to change the authentication plugin to mysql_native_password.
123+
# Before changing the authentication plugin, we need to connect to the database using caching_sha2_password. That's why we use pymysql to connect to the database.
124+
# pymsql supports caching_sha2_password without SSL.
125+
conn = pymysql.connect(
126+
host="127.0.0.1",
127+
user="root",
128+
passwd="test",
129+
port=int(mysql.get_exposed_port(mysql.port)),
130+
)
131+
132+
cur = conn.cursor()
133+
cur.execute(
134+
"ALTER USER 'test'@'%' IDENTIFIED WITH mysql_native_password BY 'test';"
135+
)
136+
cur.execute("FLUSH PRIVILEGES;")
137+
conn.commit()
138+
conn.close()
139+
119140
request.addfinalizer(mysql.stop)
120141
return mysql
121142

@@ -139,7 +160,7 @@ async def test_query(client, manifest_str, mysql: MySqlContainer):
139160
370,
140161
"O",
141162
"172799.49",
142-
"1996-01-02",
163+
"1996-01-02 00:00:00.000000",
143164
"1_370",
144165
"2024-01-01 23:59:59.000000",
145166
"2024-01-01 23:59:59.000000",
@@ -417,7 +438,11 @@ async def test_metadata_db_version(client, mysql: MySqlContainer):
417438
@pytest.mark.parametrize(
418439
"ssl_mode, expected_exception, expected_error",
419440
[
420-
(SSLMode.ENABLED, OperationalError, "Bad handshake"),
441+
(
442+
SSLMode.ENABLED,
443+
OperationalError,
444+
"SSL connection error: SSL is required but the server doesn't support it",
445+
),
421446
(
422447
SSLMode.VERIFY_CA,
423448
ValueError,

ibis-server/tests/routers/v2/connector/test_postgres.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import orjson
55
import pandas as pd
6-
import psycopg2
6+
import psycopg
77
import pytest
88
import sqlalchemy
99
from sqlalchemy import text
@@ -143,7 +143,7 @@ async def test_query(client, manifest_str, postgres: PostgresContainer):
143143
370,
144144
"O",
145145
"172799.49",
146-
"1996-01-02",
146+
"1996-01-02 00:00:00.000000",
147147
"1_370",
148148
"2024-01-01 23:59:59.000000",
149149
"2024-01-01 23:59:59.000000 UTC",
@@ -296,8 +296,8 @@ async def test_dry_run_with_connection_url_and_password_with_bracket_should_not_
296296
).geturl()
297297

298298
with pytest.raises(
299-
psycopg2.OperationalError,
300-
match='FATAL: password authentication failed for user "test"',
299+
psycopg.OperationalError,
300+
match=r'.*FATAL: password authentication failed for user "test".*',
301301
):
302302
await client.post(
303303
url=f"{base_url}/query",

ibis-server/tests/routers/v2/connector/test_snowflake.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ async def test_query(client, manifest_str):
9090
36901,
9191
"O",
9292
"173665.47",
93-
"1996-01-02",
93+
"1996-01-02 00:00:00.000000",
9494
"1_36901",
9595
"2024-01-01 23:59:59.000000",
9696
"2024-01-01 23:59:59.000000 UTC",

ibis-server/tests/routers/v2/connector/test_trino.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ async def test_query(client, manifest_str, trino: TrinoContainer):
112112
370,
113113
"O",
114114
"172799.49",
115-
"1996-01-02",
115+
"1996-01-02 00:00:00.000000",
116116
"1_370",
117117
"2024-01-01 23:59:59.000000",
118118
"2024-01-01 23:59:59.000000",

ibis-server/tests/routers/v3/connector/bigquery/test_functions.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ async def test_scalar_function(client, manifest_str: str, connection_info):
9494
result = response.json()
9595
assert result == {
9696
"columns": ["col"],
97-
"data": [["2024-01-02"]],
97+
"data": [["2024-01-02 00:00:00.000000"]],
9898
"dtypes": {"col": "object"},
9999
}
100100

ibis-server/tests/routers/v3/connector/bigquery/test_query.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ async def test_query(client, manifest_str, connection_info):
8484
"2024-07-16 03:00:00.000000 UTC", # utc-4
8585
"36485_1202",
8686
1202,
87-
"1992-06-06",
87+
"1992-06-06 00:00:00.000000",
8888
36485,
8989
"F",
9090
"356711.63",

ibis-server/tests/routers/v3/connector/postgres/test_query.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ async def test_query(client, manifest_str, connection_info):
118118
"172799.49",
119119
"1_370",
120120
370,
121-
"1996-01-02",
121+
"1996-01-02 00:00:00.000000",
122122
1,
123123
"O",
124124
]

0 commit comments

Comments
 (0)