|
24 | 24 | ConnectionUrl, |
25 | 25 | DatabricksServicePrincipalConnectionInfo, |
26 | 26 | DatabricksTokenConnectionInfo, |
| 27 | + DorisConnectionInfo, |
27 | 28 | GcsFileConnectionInfo, |
28 | 29 | LocalFileConnectionInfo, |
29 | 30 | MinioFileConnectionInfo, |
|
36 | 37 | QueryCannerDTO, |
37 | 38 | QueryClickHouseDTO, |
38 | 39 | QueryDatabricksDTO, |
| 40 | + QueryDorisDTO, |
39 | 41 | QueryDTO, |
40 | 42 | QueryDuckDBDTO, |
41 | 43 | QueryGcsFileDTO, |
@@ -70,6 +72,7 @@ class DataSource(StrEnum): |
70 | 72 | clickhouse = auto() |
71 | 73 | mssql = auto() |
72 | 74 | mysql = auto() |
| 75 | + doris = auto() |
73 | 76 | oracle = auto() |
74 | 77 | postgres = auto() |
75 | 78 | redshift = auto() |
@@ -169,6 +172,8 @@ def _build_connection_info(self, data: dict) -> ConnectionInfo: |
169 | 172 | return MSSqlConnectionInfo.model_validate(data) |
170 | 173 | case DataSource.mysql: |
171 | 174 | return MySqlConnectionInfo.model_validate(data) |
| 175 | + case DataSource.doris: |
| 176 | + return DorisConnectionInfo.model_validate(data) |
172 | 177 | case DataSource.oracle: |
173 | 178 | return OracleConnectionInfo.model_validate(data) |
174 | 179 | case DataSource.postgres: |
@@ -240,6 +245,7 @@ class DataSourceExtension(Enum): |
240 | 245 | clickhouse = QueryClickHouseDTO |
241 | 246 | mssql = QueryMSSqlDTO |
242 | 247 | mysql = QueryMySqlDTO |
| 248 | + doris = QueryDorisDTO |
243 | 249 | oracle = QueryOracleDTO |
244 | 250 | postgres = QueryPostgresDTO |
245 | 251 | redshift = QueryRedshiftDTO |
@@ -402,6 +408,42 @@ def get_mysql_connection(cls, info: MySqlConnectionInfo) -> BaseBackend: |
402 | 408 | **kwargs, |
403 | 409 | ) |
404 | 410 |
|
| 411 | + @classmethod |
| 412 | + def get_doris_connection(cls, info: DorisConnectionInfo) -> BaseBackend: |
| 413 | + kwargs = {} |
| 414 | + |
| 415 | + # utf8mb4 is the actual charset used by Doris (MySQL-compatible) |
| 416 | + kwargs.setdefault("charset", "utf8mb4") |
| 417 | + |
| 418 | + if info.kwargs: |
| 419 | + kwargs.update(info.kwargs) |
| 420 | + # Doris is MySQL-protocol compatible, reuse ibis.mysql.connect() |
| 421 | + connection = ibis.mysql.connect( |
| 422 | + host=info.host.get_secret_value(), |
| 423 | + port=int(info.port.get_secret_value()), |
| 424 | + database=info.database.get_secret_value(), |
| 425 | + user=info.user.get_secret_value(), |
| 426 | + password=info.password.get_secret_value() if info.password else "", |
| 427 | + **kwargs, |
| 428 | + ) |
| 429 | + # Doris does not properly reflect the SERVER_STATUS_AUTOCOMMIT flag |
| 430 | + # in its MySQL-protocol handshake/OK packets. As a result, the |
| 431 | + # underlying mysqlclient driver's get_autocommit() always returns |
| 432 | + # False — even after explicitly calling autocommit(True). |
| 433 | + # |
| 434 | + # ibis's raw_sql() checks get_autocommit() and, when it returns |
| 435 | + # False, wraps every query in BEGIN/ROLLBACK. Doris (an OLAP engine) |
| 436 | + # does not support transactional SELECT inside BEGIN and will reject |
| 437 | + # with: "This is in a transaction, only insert, update, delete, |
| 438 | + # commit, rollback is acceptable." |
| 439 | + # |
| 440 | + # Fix: override get_autocommit on THIS connection instance only so |
| 441 | + # that ibis skips the BEGIN/ROLLBACK wrapping. This is a per-object |
| 442 | + # attribute override — it does NOT affect the MySQLdb class, other |
| 443 | + # MySQL connections, or any other data-source driver. |
| 444 | + connection.con.get_autocommit = lambda: True |
| 445 | + return connection |
| 446 | + |
405 | 447 | @staticmethod |
406 | 448 | def get_postgres_connection(info: PostgresConnectionInfo) -> BaseBackend: |
407 | 449 | return ibis.postgres.connect( |
|
0 commit comments