@@ -56,7 +56,9 @@ def list_tables(database: str, like: str = None):
56
56
result = client .command (query )
57
57
58
58
# Get all table comments in one query
59
- table_comments_query = f"SELECT name, comment FROM system.tables WHERE database = { format_query_value (database )} "
59
+ table_comments_query = (
60
+ f"SELECT name, comment FROM system.tables WHERE database = { format_query_value (database )} "
61
+ )
60
62
table_comments_result = client .query (table_comments_query )
61
63
table_comments = {row [0 ]: row [1 ] for row in table_comments_result .result_rows }
62
64
@@ -82,14 +84,16 @@ def get_table_info(table):
82
84
for i , col_name in enumerate (column_names ):
83
85
column_dict [col_name ] = row [i ]
84
86
# Add comment from our pre-fetched comments
85
- if table in column_comments and column_dict [' name' ] in column_comments [table ]:
86
- column_dict [' comment' ] = column_comments [table ][column_dict [' name' ]]
87
+ if table in column_comments and column_dict [" name" ] in column_comments [table ]:
88
+ column_dict [" comment" ] = column_comments [table ][column_dict [" name" ]]
87
89
else :
88
- column_dict [' comment' ] = None
90
+ column_dict [" comment" ] = None
89
91
columns .append (column_dict )
90
92
91
93
# Get row count and column count from the table
92
- row_count_query = f"SELECT count() FROM { quote_identifier (database )} .{ quote_identifier (table )} "
94
+ row_count_query = (
95
+ f"SELECT count() FROM { quote_identifier (database )} .{ quote_identifier (table )} "
96
+ )
93
97
row_count_result = client .query (row_count_query )
94
98
row_count = row_count_result .result_rows [0 ][0 ] if row_count_result .result_rows else 0
95
99
column_count = len (columns )
@@ -125,7 +129,8 @@ def get_table_info(table):
125
129
def execute_query (query : str ):
126
130
client = create_clickhouse_client ()
127
131
try :
128
- res = client .query (query , settings = {"readonly" : 1 })
132
+ read_only = get_readonly_setting (client )
133
+ res = client .query (query , settings = {"readonly" : read_only })
129
134
column_names = res .column_names
130
135
rows = []
131
136
for row in res .result_rows :
@@ -161,7 +166,10 @@ def run_select_query(query: str):
161
166
logger .warning (f"Query timed out after { SELECT_QUERY_TIMEOUT_SECS } seconds: { query } " )
162
167
future .cancel ()
163
168
# Return a properly structured response for timeout errors
164
- return {"status" : "error" , "message" : f"Query timed out after { SELECT_QUERY_TIMEOUT_SECS } seconds" }
169
+ return {
170
+ "status" : "error" ,
171
+ "message" : f"Query timed out after { SELECT_QUERY_TIMEOUT_SECS } seconds" ,
172
+ }
165
173
except Exception as e :
166
174
logger .error (f"Unexpected error in run_select_query: { str (e )} " )
167
175
# Catch all other exceptions and return them in a structured format
@@ -188,3 +196,33 @@ def create_clickhouse_client():
188
196
except Exception as e :
189
197
logger .error (f"Failed to connect to ClickHouse: { str (e )} " )
190
198
raise
199
+
200
+
201
+ def get_readonly_setting (client ) -> str :
202
+ """Get the appropriate readonly setting value to use for queries.
203
+
204
+ This function handles potential conflicts between server and client readonly settings:
205
+ - readonly=0: No read-only restrictions
206
+ - readonly=1: Only read queries allowed, settings cannot be changed
207
+ - readonly=2: Only read queries allowed, settings can be changed (except readonly itself)
208
+
209
+ If server has readonly=2 and client tries to set readonly=1, it would cause:
210
+ "Setting readonly is unknown or readonly" error
211
+
212
+ This function preserves the server's readonly setting unless it's 0, in which case
213
+ we enforce readonly=1 to ensure queries are read-only.
214
+
215
+ Args:
216
+ client: ClickHouse client connection
217
+
218
+ Returns:
219
+ String value of readonly setting to use
220
+ """
221
+ read_only = client .server_settings .get ("readonly" )
222
+ if read_only :
223
+ if read_only == "0" :
224
+ return "1" # Force read-only mode if server has it disabled
225
+ else :
226
+ return read_only .value # Respect server's readonly setting (likely 2)
227
+ else :
228
+ return "1" # Default to basic read-only mode if setting isn't present
0 commit comments