Skip to content

Commit 7abfee3

Browse files
authored
Merge pull request #72 from ClickHouse/database_fix
Ensure database exists before configuring driver
2 parents ff3f2cc + 9e1b61f commit 7abfee3

File tree

7 files changed

+90
-62
lines changed

7 files changed

+90
-62
lines changed

Diff for: dbt/adapters/clickhouse/__version__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
version = '1.1.4'
1+
version = '1.1.5'

Diff for: dbt/adapters/clickhouse/connections.py

+66-53
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,6 @@ class ClickhouseCredentials(Credentials):
5151
sync_request_timeout: int = 5
5252
compress_block_size: int = 1048576
5353
compression: str = ''
54-
use_default_schema: bool = (
55-
False # This is used in tests to make sure we connect always to the default database.
56-
)
5754
custom_settings: Optional[Dict[str, Any]] = None
5855

5956
@property
@@ -89,7 +86,6 @@ def _connection_keys(self):
8986
'sync_request_timeout',
9087
'compress_block_size',
9188
'compression',
92-
'use_default_schema',
9389
'custom_settings',
9490
)
9591

@@ -132,63 +128,22 @@ def open(cls, connection):
132128
driver = 'http'
133129
elif clickhouse_driver and port in (9000, 9440):
134130
driver = 'native'
135-
else:
136-
driver = 'unspecified'
137-
custom_settings = credentials.custom_settings or {}
138-
connection.state = 'fail'
139-
db_err = None
131+
client = None
140132
if clickhouse_connect and driver == 'http':
141-
try:
142-
connection.handle = clickhouse_connect.get_client(
143-
host=credentials.host,
144-
port=credentials.port,
145-
database='default' if credentials.use_default_schema else credentials.schema,
146-
username=credentials.user,
147-
password=credentials.password,
148-
interface='https' if credentials.secure else 'http',
149-
compress=False
150-
if credentials.compression == ''
151-
else bool(credentials.compression),
152-
connect_timeout=credentials.connect_timeout,
153-
send_receive_timeout=credentials.send_receive_timeout,
154-
http_user_agent=f'cc-dbt-{dbt_version}',
155-
verify=credentials.verify,
156-
query_limit=0,
157-
session_id='dbt::' + str(uuid.uuid4()),
158-
**custom_settings,
159-
)
160-
except clickhouse_connect.driver.exceptions.DatabaseError as exp:
161-
db_err = exp
133+
client, db_err = _connect_http(credentials)
162134
elif clickhouse_driver and driver == 'native':
163-
try:
164-
client = clickhouse_driver.Client(
165-
host=credentials.host,
166-
port=credentials.port,
167-
database='default',
168-
user=credentials.user,
169-
password=credentials.password,
170-
client_name=f'dbt-{dbt_version}',
171-
secure=credentials.secure,
172-
verify=credentials.verify,
173-
connect_timeout=credentials.connect_timeout,
174-
send_receive_timeout=credentials.send_receive_timeout,
175-
sync_request_timeout=credentials.sync_request_timeout,
176-
compress_block_size=credentials.compress_block_size,
177-
compression=False if credentials.compression == '' else credentials.compression,
178-
**custom_settings,
179-
)
180-
connection.handle = ChNativeAdapter(client)
181-
except clickhouse_driver.errors as exp:
182-
db_err = exp
135+
client, db_err = _connect_native(credentials)
183136
else:
184-
raise dbt.exceptions.FailedToConnectException(
185-
f'Library for ClickHouse driver type {driver} not found'
186-
)
137+
db_err = f'Library for ClickHouse driver type {driver} not found'
187138
if db_err:
139+
connection.state = 'fail'
188140
logger.debug(
189141
'Got an error when attempting to open a clickhouse connection: \'{}\'', str(db_err)
190142
)
143+
if client:
144+
client.close()
191145
raise dbt.exceptions.FailedToConnectException(str(db_err))
146+
connection.handle = client
192147
connection.state = 'open'
193148
return connection
194149

@@ -286,3 +241,61 @@ def begin(self):
286241

287242
def commit(self):
288243
pass
244+
245+
246+
def _connect_http(credentials):
247+
try:
248+
client = clickhouse_connect.get_client(
249+
host=credentials.host,
250+
port=credentials.port,
251+
username=credentials.user,
252+
password=credentials.password,
253+
interface='https' if credentials.secure else 'http',
254+
compress=False if credentials.compression == '' else bool(credentials.compression),
255+
connect_timeout=credentials.connect_timeout,
256+
send_receive_timeout=credentials.send_receive_timeout,
257+
client_name=f'cc-dbt-{dbt_version}',
258+
verify=credentials.verify,
259+
query_limit=0,
260+
session_id='dbt::' + str(uuid.uuid4()),
261+
**(credentials.custom_settings or {}),
262+
)
263+
return client, _ensure_database(client, credentials.schema)
264+
except clickhouse_connect.driver.exceptions.DatabaseError as exp:
265+
return None, exp
266+
267+
268+
def _connect_native(credentials):
269+
try:
270+
client = clickhouse_driver.Client(
271+
host=credentials.host,
272+
port=credentials.port,
273+
user=credentials.user,
274+
password=credentials.password,
275+
client_name=f'dbt-{dbt_version}',
276+
secure=credentials.secure,
277+
verify=credentials.verify,
278+
connect_timeout=credentials.connect_timeout,
279+
send_receive_timeout=credentials.send_receive_timeout,
280+
sync_request_timeout=credentials.sync_request_timeout,
281+
compress_block_size=credentials.compress_block_size,
282+
compression=False if credentials.compression == '' else credentials.compression,
283+
**(credentials.custom_settings or {}),
284+
)
285+
client = ChNativeAdapter(client)
286+
return client, _ensure_database(client, credentials.schema)
287+
except clickhouse_driver.errors.Error as exp:
288+
return None, exp
289+
290+
291+
def _ensure_database(client, database):
292+
if database:
293+
check_db = f'EXISTS DATABASE {database}'
294+
db_exists = client.command(check_db)
295+
if not db_exists:
296+
client.command(f'CREATE DATABASE {database}')
297+
db_exists = client.command(check_db)
298+
if not db_exists:
299+
return f'Unable to create DBT database {database}'
300+
client.database = database
301+
return None

Diff for: dbt/adapters/clickhouse/impl.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import dbt.exceptions
99
from dbt.adapters.base import AdapterConfig, available
1010
from dbt.adapters.base.impl import catch_as_completed
11-
from dbt.adapters.base.relation import InformationSchema
11+
from dbt.adapters.base.relation import BaseRelation, InformationSchema
1212
from dbt.adapters.sql import SQLAdapter
1313
from dbt.clients.agate_helper import table_from_rows
1414
from dbt.contracts.graph.manifest import Manifest
@@ -82,6 +82,12 @@ def check_schema_exists(self, database, schema):
8282
exists = True if schema in [row[0] for row in results] else False
8383
return exists
8484

85+
def drop_schema(self, relation: BaseRelation) -> None:
86+
super().drop_schema(relation)
87+
conn = self.connections.get_if_exists()
88+
if conn:
89+
conn.handle.database = None
90+
8591
def list_relations_without_caching(
8692
self, schema_relation: ClickhouseRelation
8793
) -> List[ClickhouseRelation]:

Diff for: dbt/adapters/clickhouse/nativeadapter.py

+14-4
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,25 @@ class ChNativeAdapter:
55
def __init__(self, client: clickhouse_driver.Client):
66
self.client = client
77

8-
def query(self, sql):
9-
return NativeAdapterResult(self.client.execute(sql, with_column_types=True))
8+
def query(self, sql, **kwargs):
9+
return NativeAdapterResult(self.client.execute(sql, with_column_types=True, **kwargs))
1010

11-
def command(self, sql):
12-
self.client.execute(sql)
11+
def command(self, sql, **kwargs):
12+
result = self.client.execute(sql, **kwargs)
13+
if len(result) and len(result[0]):
14+
return result[0][0]
1315

1416
def close(self):
1517
self.client.disconnect()
1618

19+
@property
20+
def database(self):
21+
return self.client.connection.database
22+
23+
@database.setter
24+
def database(self, database):
25+
self.client.connection.database = database
26+
1727

1828
class NativeAdapterResult:
1929
def __init__(self, native_result):

Diff for: dev_requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
dbt-core==1.1.0
2-
clickhouse-connect>=0.1.4
2+
clickhouse-connect>=0.1.5
33
clickhouse-driver>=0.2.3
44
pytest==7.0.0
55
pytest-dotenv==0.5.2

Diff for: setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def _dbt_clickhouse_version():
5454
},
5555
install_requires=[
5656
f'dbt-core~={dbt_version}',
57-
'clickhouse-connect>=0.1.4',
57+
'clickhouse-connect>=0.1.5',
5858
'clickhouse-driver>=0.2.3',
5959
],
6060
python_requires=">=3.7",

Diff for: tests/conftest.py

-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ def dbt_profile_target():
5858
'port': int(os.environ.get('PORT_ENV_VAR_NAME', 8123)), # docker client port
5959
'secure': False,
6060
'driver': driver,
61-
'use_default_schema': True, # In tests we always use default schema (a.k.a default database)
6261
}
6362

6463

0 commit comments

Comments
 (0)