123123 "write_file",
124124 "config",
125125 "connect_to_table",
126+ "print_database_tables",
126127 "preview",
127128 "missing_vals_tbl",
128129 "get_action_metadata",
@@ -3919,6 +3920,47 @@ def _has_notes(self) -> bool:
39193920 return self.notes is not None and len(self.notes) > 0
39203921
39213922
3923+ def _handle_connection_errors(e: Exception, connection_string: str) -> None:
3924+ """
3925+ Shared error handling for database connection failures.
3926+
3927+ Raises appropriate ConnectionError with helpful messages based on the exception.
3928+ """
3929+
3930+ error_str = str(e).lower()
3931+ backend_install_map = {
3932+ "duckdb": "pip install 'ibis-framework[duckdb]'",
3933+ "postgresql": "pip install 'ibis-framework[postgres]'",
3934+ "postgres": "pip install 'ibis-framework[postgres]'",
3935+ "mysql": "pip install 'ibis-framework[mysql]'",
3936+ "sqlite": "pip install 'ibis-framework[sqlite]'",
3937+ "bigquery": "pip install 'ibis-framework[bigquery]'",
3938+ "snowflake": "pip install 'ibis-framework[snowflake]'",
3939+ }
3940+
3941+ # Check if this is a missing backend dependency
3942+ for backend, install_cmd in backend_install_map.items():
3943+ if backend in error_str and ("not found" in error_str or "no module" in error_str):
3944+ raise ConnectionError(
3945+ f"Missing {backend.upper()} backend for Ibis. Install it with:\n"
3946+ f" {install_cmd}\n\n"
3947+ f"Original error: {e}"
3948+ ) from e
3949+
3950+ # Generic connection error
3951+ raise ConnectionError( # pragma: no cover
3952+ f"Failed to connect using: {connection_string}\n"
3953+ f"Error: {e}\n\n"
3954+ f"Supported connection string formats:\n"
3955+ f"- DuckDB: 'duckdb:///path/to/file.ddb'\n"
3956+ f"- SQLite: 'sqlite:///path/to/file.db'\n"
3957+ f"- PostgreSQL: 'postgresql://user:pass@host:port/db'\n"
3958+ f"- MySQL: 'mysql://user:pass@host:port/db'\n"
3959+ f"- BigQuery: 'bigquery://project/dataset'\n"
3960+ f"- Snowflake: 'snowflake://user:pass@account/db/schema'"
3961+ ) from e
3962+
3963+
39223964def connect_to_table(connection_string: str) -> Any:
39233965 """
39243966 Connect to a database table using a connection string.
@@ -3998,7 +4040,11 @@ def connect_to_table(connection_string: str) -> Any:
39984040 pip install 'ibis-framework[duckdb]' # for DuckDB
39994041 pip install 'ibis-framework[postgres]' # for PostgreSQL
40004042 ```
4043+ See Also
4044+ --------
4045+ print_database_tables : List all available tables in a database for discovery
40014046 """
4047+
40024048 # Check if Ibis is available
40034049 if not _is_lib_present(lib_name="ibis"):
40044050 raise ImportError(
@@ -4012,14 +4058,10 @@ def connect_to_table(connection_string: str) -> Any:
40124058 if "::" not in connection_string:
40134059 # Try to connect to get available tables for helpful error message
40144060 try:
4015- # Extract the base connection string (without table name)
40164061 base_connection = connection_string
4017-
4018- # Connect to the database
40194062 conn = ibis.connect(base_connection)
40204063
4021- # Get list of available tables
4022- try:
4064+ try: # pragma: no cover
40234065 available_tables = conn.list_tables()
40244066 except Exception: # pragma: no cover
40254067 available_tables = []
@@ -4036,7 +4078,6 @@ def connect_to_table(connection_string: str) -> Any:
40364078 f" {connection_string}::TABLE_NAME\n\n"
40374079 f"Examples:\n"
40384080 )
4039- # Add examples with first few table names
40404081 for table in available_tables[:3]:
40414082 error_msg += f" {connection_string}::{table}\n"
40424083 else:
@@ -4051,43 +4092,8 @@ def connect_to_table(connection_string: str) -> Any:
40514092
40524093 except Exception as e:
40534094 if isinstance(e, ValueError):
4054- raise # Re-raise our custom ValueError
4055-
4056- # Check for backend-specific errors and provide installation guidance
4057- error_str = str(e).lower()
4058- backend_install_map = {
4059- "duckdb": "pip install 'ibis-framework[duckdb]'",
4060- "postgresql": "pip install 'ibis-framework[postgres]'",
4061- "postgres": "pip install 'ibis-framework[postgres]'",
4062- "mysql": "pip install 'ibis-framework[mysql]'",
4063- "sqlite": "pip install 'ibis-framework[sqlite]'",
4064- "bigquery": "pip install 'ibis-framework[bigquery]'",
4065- "snowflake": "pip install 'ibis-framework[snowflake]'",
4066- }
4067-
4068- # Check if this is a missing backend dependency
4069- for backend, install_cmd in backend_install_map.items(): # pragma: no cover
4070- if backend in error_str and ("not found" in error_str or "no module" in error_str):
4071- raise ConnectionError(
4072- f"Missing {backend.upper()} backend for Ibis. Install it with:\n"
4073- f" {install_cmd}\n\n"
4074- f"Original error: {e}\n\n"
4075- f"Supported connection string formats:\n"
4076- f"- DuckDB: 'duckdb:///path/to/file.ddb::table_name'\n"
4077- f"- SQLite: 'sqlite:///path/to/file.db::table_name'\n"
4078- f"- PostgreSQL: 'postgresql://user:pass@host:port/db::table_name'\n"
4079- f"- MySQL: 'mysql://user:pass@host:port/db::table_name'\n"
4080- f"- BigQuery: 'bigquery://project/dataset::table_name'\n"
4081- f"- Snowflake: 'snowflake://user:pass@account/db/schema::table_name'\n"
4082- f"\nNote: Use '::table_name' to specify the table within the database."
4083- ) from e
4084-
4085- # Generic connection error
4086- raise ConnectionError( # pragma: no cover
4087- f"Failed to connect to database using connection string: {connection_string}\n"
4088- f"Error: {e}\n\n"
4089- f"No table specified. Use the format: {connection_string}::TABLE_NAME"
4090- ) from e
4095+ raise
4096+ _handle_connection_errors(e, connection_string)
40914097
40924098 # Split connection string and table name
40934099 try:
@@ -4100,56 +4106,94 @@ def connect_to_table(connection_string: str) -> Any:
41004106 conn = ibis.connect(base_connection)
41014107 table = conn.table(table_name)
41024108 return table
4103-
41044109 except Exception as e:
4105- # Check for backend-specific errors and provide installation guidance
41064110 error_str = str(e).lower()
4107- backend_install_map = {
4108- "duckdb": "pip install 'ibis-framework[duckdb]'",
4109- "postgresql": "pip install 'ibis-framework[postgres]'",
4110- "postgres": "pip install 'ibis-framework[postgres]'",
4111- "mysql": "pip install 'ibis-framework[mysql]'",
4112- "sqlite": "pip install 'ibis-framework[sqlite]'",
4113- "bigquery": "pip install 'ibis-framework[bigquery]'",
4114- "snowflake": "pip install 'ibis-framework[snowflake]'",
4115- }
4116-
4117- # Check if this is a missing backend dependency
4118- for backend, install_cmd in backend_install_map.items():
4119- if backend in error_str and ("not found" in error_str or "no module" in error_str):
4120- raise ConnectionError(
4121- f"Missing {backend.upper()} backend for Ibis. Install it with:\n"
4122- f" {install_cmd}\n\n"
4123- f"Original error: {e}"
4124- ) from e
41254111
4126- # Check if table doesn't exist
4127- if "table" in error_str and ("not found" in error_str or "does not exist" in error_str):
4128- # Try to get available tables for helpful message
4112+ # Check if this is a "table not found" error
4113+ if "table" in error_str and (
4114+ "not found" in error_str or "does not exist" in error_str or "not exist" in error_str
4115+ ):
4116+ # Try to get available tables for a helpful error message
41294117 try: # pragma: no cover
41304118 available_tables = conn.list_tables()
41314119 if available_tables:
41324120 table_list = "\n".join(f" - {table}" for table in available_tables)
41334121 raise ValueError(
41344122 f"Table '{table_name}' not found in database.\n\n"
41354123 f"Available tables:\n{table_list}\n\n"
4136- f"Check the table name and try again with:\n"
4137- f" {base_connection}::CORRECT_TABLE_NAME"
4138- ) from e
4139- else:
4140- raise ValueError(
4141- f"Table '{table_name}' not found and no tables available in database."
4124+ f"Connection: {base_connection}"
41424125 ) from e
4126+ except ValueError:
4127+ # Re-raise the table-specific ValueError
4128+ raise
41434129 except Exception:
4144- raise ValueError(
4145- f"Table '{table_name}' not found in database. "
4146- f"Check the table name and connection string."
4147- ) from e
4130+ # If we can't list tables, just raise a simple error
4131+ pass
4132+
4133+ raise ValueError(
4134+ f"Table '{table_name}' not found in database.\n"
4135+ f"Connection: {base_connection}\n\n"
4136+ f"Original error: {e}"
4137+ ) from e
4138+
4139+ # For other errors, use the generic connection error handler
4140+ _handle_connection_errors(e, base_connection)
41484141
4149- # Generic connection error
4150- raise ConnectionError(
4151- f"Failed to connect to table '{table_name}' using: {base_connection}\nError: {e}"
4152- ) from e
4142+
4143+ def print_database_tables(connection_string: str) -> list[str]:
4144+ """
4145+ List all tables in a database from a connection string.
4146+
4147+ The `print_database_tables()` function connects to a database and returns a list of all
4148+ available tables. This is particularly useful for discovering what tables exist in a database
4149+ before connecting to a specific table with `connect_to_table(). The function automatically
4150+ filters out temporary Ibis tables (memtables) to show only user tables. It supports all database
4151+ backends available through Ibis, including DuckDB, SQLite, PostgreSQL, MySQL, BigQuery, and
4152+ Snowflake.
4153+
4154+ Parameters
4155+ ----------
4156+ connection_string
4157+ A database connection string WITHOUT the ::table_name suffix. Example:
4158+ `"duckdb:///path/to/database.ddb"`.
4159+
4160+ Returns
4161+ -------
4162+ list[str]
4163+ List of table names, excluding temporary Ibis tables.
4164+
4165+ See Also
4166+ --------
4167+ connect_to_table : Connect to a database table with full connection string documentation
4168+ """
4169+ # Check if connection string includes table specification (which is not allowed)
4170+ if "::" in connection_string:
4171+ raise ValueError(
4172+ "Connection string should not include table specification (::table_name).\n"
4173+ f"You've supplied: {connection_string}\n"
4174+ f"Expected format: 'duckdb:///path/to/database.ddb' (without ::table_name)"
4175+ )
4176+
4177+ # Check if Ibis is available
4178+ if not _is_lib_present(lib_name="ibis"):
4179+ raise ImportError(
4180+ "The Ibis library is not installed but is required for database connection strings.\n"
4181+ "Install it with: pip install 'ibis-framework[duckdb]' (or other backend as needed)"
4182+ )
4183+
4184+ import ibis
4185+
4186+ try:
4187+ # Connect to database
4188+ conn = ibis.connect(connection_string)
4189+ # Get all tables and filter out temporary Ibis tables
4190+ all_tables = conn.list_tables()
4191+ user_tables = [t for t in all_tables if "memtable" not in t]
4192+
4193+ return user_tables
4194+
4195+ except Exception as e:
4196+ _handle_connection_errors(e, connection_string)
41534197
41544198
41554199@dataclass
0 commit comments