diff --git a/scripts/sqllogictest/result.py b/scripts/sqllogictest/result.py index f7715f1c8ab2..b1f4a26f76c2 100644 --- a/scripts/sqllogictest/result.py +++ b/scripts/sqllogictest/result.py @@ -444,6 +444,12 @@ def matches_regex(input: str, actual_str: str) -> bool: else: should_match = False regex_str = input.replace(":", "") + # The exact match will never be the same, allow leading and trailing messages + if regex_str[:2] != '.*': + regex_str = ".*" + regex_str + if regex_str[-2:] != '.*': + regex_str = regex_str + '.*' + re_options = re.DOTALL re_pattern = re.compile(regex_str, re_options) regex_matches = bool(re_pattern.fullmatch(actual_str)) diff --git a/src/main/client_context.cpp b/src/main/client_context.cpp index a6377e667c19..de3bc162c374 100644 --- a/src/main/client_context.cpp +++ b/src/main/client_context.cpp @@ -731,10 +731,10 @@ unique_ptr ClientContext::PendingStatementInternal(ClientCon if (prepared->properties.parameter_count > 0 && parameter_count == 0) { string error_message = StringUtil::Format("Expected %lld parameters, but none were supplied", prepared->properties.parameter_count); - return ErrorResult(ErrorData(error_message), query); + return ErrorResult(InvalidInputException(error_message), query); } if (!prepared->properties.bound_all_parameters) { - return ErrorResult(ErrorData("Not all parameters were bound"), query); + return ErrorResult(InvalidInputException("Not all parameters were bound"), query); } // execute the prepared statement CheckIfPreparedStatementIsExecutable(*prepared); diff --git a/test/fuzzer/pedro/in_clause_optimization_error.test b/test/fuzzer/pedro/in_clause_optimization_error.test index b8a63c20a450..abc5b5b31c63 100644 --- a/test/fuzzer/pedro/in_clause_optimization_error.test +++ b/test/fuzzer/pedro/in_clause_optimization_error.test @@ -14,7 +14,7 @@ INSERT INTO t0 VALUES (42) statement error SELECT 0 FROM t0 WHERE ? IN (1); ---- -:Invalid Error.*Expected 1 parameters, but none were supplied.* +:Invalid Input Error.*Expected 1 parameters, but none were supplied.* statement ok PREPARE v1 AS SELECT 0 FROM t0 WHERE ? IN (1); diff --git a/test/sql/types/map/map_empty.test b/test/sql/types/map/map_empty.test index 7a415b88695d..958fa836c38a 100644 --- a/test/sql/types/map/map_empty.test +++ b/test/sql/types/map/map_empty.test @@ -5,4 +5,4 @@ statement error SELECT DISTINCT MAP { * : ? IN ( SELECT TRUE ) } ; ---- -Invalid Error: Expected 1 parameters, but none were supplied +Invalid Input Error: Expected 1 parameters, but none were supplied diff --git a/tools/pythonpkg/src/common/exceptions.cpp b/tools/pythonpkg/src/common/exceptions.cpp index 338a8cd6797f..05ee73d788cc 100644 --- a/tools/pythonpkg/src/common/exceptions.cpp +++ b/tools/pythonpkg/src/common/exceptions.cpp @@ -66,6 +66,12 @@ class PySequenceException : public DatabaseError { } }; +class PyDependencyException : public DatabaseError { +public: + explicit PyDependencyException(const string &err) : DatabaseError(err) { + } +}; + //===--------------------------------------------------------------------===// // Data Error //===--------------------------------------------------------------------===// @@ -265,6 +271,8 @@ void PyThrowException(ErrorData &error, PyObject *http_exception) { throw PyPermissionException(error.Message()); case ExceptionType::SEQUENCE: throw PySequenceException(error.Message()); + case ExceptionType::DEPENDENCY: + throw PyDependencyException(error.Message()); case ExceptionType::OUT_OF_RANGE: throw PyOutOfRangeException(error.Message()); case ExceptionType::CONVERSION: @@ -317,6 +325,7 @@ void RegisterExceptions(const py::module &m) { py::register_exception(m, "InterruptException", db_error); py::register_exception(m, "PermissionException", db_error); py::register_exception(m, "SequenceException", db_error); + py::register_exception(m, "DependencyException", db_error); // DataError auto data_error = py::register_exception(m, "DataError", db_error).ptr(); diff --git a/tools/pythonpkg/src/pyconnection.cpp b/tools/pythonpkg/src/pyconnection.cpp index 65015aeb6bc1..d5e514bc4f1f 100644 --- a/tools/pythonpkg/src/pyconnection.cpp +++ b/tools/pythonpkg/src/pyconnection.cpp @@ -526,6 +526,9 @@ case_insensitive_map_t TransformPreparedParameters(PreparedS case_insensitive_map_t named_values; if (py::is_list_like(params)) { if (prep.n_param != py::len(params)) { + if (py::len(params) == 0) { + throw InvalidInputException("Expected %d parameters, but none were supplied", prep.n_param); + } throw InvalidInputException("Prepared statement needs %d parameters, %d given", prep.n_param, py::len(params)); } @@ -572,6 +575,9 @@ unique_ptr DuckDBPyConnection::ExecuteInternal(PreparedStatement &p unique_lock lock(py_connection_lock); auto pending_query = prep.PendingQuery(named_values); + if (pending_query->HasError()) { + pending_query->ThrowError(); + } res = CompletePendingQuery(*pending_query); if (res->HasError()) { @@ -1181,6 +1187,9 @@ void DuckDBPyConnection::ExecuteImmediately(vector> sta "separate 'execute' calls if you want to use prepared parameters"); } auto pending_query = connection.PendingQuery(std::move(stmt), false); + if (pending_query->HasError()) { + pending_query->ThrowError(); + } auto res = CompletePendingQuery(*pending_query); if (res->HasError()) { diff --git a/tools/pythonpkg/tests/fast/api/test_duckdb_connection.py b/tools/pythonpkg/tests/fast/api/test_duckdb_connection.py index daf6c0fbd063..2b79ac6c620a 100644 --- a/tools/pythonpkg/tests/fast/api/test_duckdb_connection.py +++ b/tools/pythonpkg/tests/fast/api/test_duckdb_connection.py @@ -151,7 +151,7 @@ def test_pystatement(self): assert duckdb.query(statements[1]).fetchall() == [(21,)] assert duckdb.execute(statements[1]).fetchall() == [(21,)] - with pytest.raises(duckdb.InvalidInputException, match='Prepared statement needs 1 parameters, 0 given'): + with pytest.raises(duckdb.InvalidInputException, match='Expected 1 parameters, but none were supplied'): duckdb.execute(statements[0]) assert duckdb.execute(statements[0], {'1': 42}).fetchall() == [(42,)] diff --git a/tools/pythonpkg/tests/fast/test_runtime_error.py b/tools/pythonpkg/tests/fast/test_runtime_error.py index 98c5a332bda5..2ee26d3c1d3a 100644 --- a/tools/pythonpkg/tests/fast/test_runtime_error.py +++ b/tools/pythonpkg/tests/fast/test_runtime_error.py @@ -112,7 +112,7 @@ def test_conn_broken_statement_error(self, pandas): ) conn.execute("create view x as select * from df_in") del df_in - with pytest.raises(duckdb.InvalidInputException): + with pytest.raises(duckdb.CatalogException, match='Table with name df_in does not exist'): conn.execute("select 1; select * from x; select 3;") def test_conn_prepared_statement_error(self):