Skip to content

Commit

Permalink
Merge pull request duckdb#12888 from Tishj/pysqllogictest_fixes2
Browse files Browse the repository at this point in the history
[Python-Dev] Add `DependencyException`, throw earlier if `PendingQuery` fails
  • Loading branch information
Mytherin authored Jul 9, 2024
2 parents 9c3e499 + a70732e commit fd883da
Show file tree
Hide file tree
Showing 8 changed files with 30 additions and 6 deletions.
6 changes: 6 additions & 0 deletions scripts/sqllogictest/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,12 @@ def matches_regex(input: str, actual_str: str) -> bool:
else:
should_match = False
regex_str = input.replace("<!REGEX>:", "")
# 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))
Expand Down
4 changes: 2 additions & 2 deletions src/main/client_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -731,10 +731,10 @@ unique_ptr<PendingQueryResult> 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<PendingQueryResult>(ErrorData(error_message), query);
return ErrorResult<PendingQueryResult>(InvalidInputException(error_message), query);
}
if (!prepared->properties.bound_all_parameters) {
return ErrorResult<PendingQueryResult>(ErrorData("Not all parameters were bound"), query);
return ErrorResult<PendingQueryResult>(InvalidInputException("Not all parameters were bound"), query);
}
// execute the prepared statement
CheckIfPreparedStatementIsExecutable(*prepared);
Expand Down
2 changes: 1 addition & 1 deletion test/fuzzer/pedro/in_clause_optimization_error.test
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ INSERT INTO t0 VALUES (42)
statement error
SELECT 0 FROM t0 WHERE ? IN (1);
----
<REGEX>:Invalid Error.*Expected 1 parameters, but none were supplied.*
<REGEX>:Invalid Input Error.*Expected 1 parameters, but none were supplied.*

statement ok
PREPARE v1 AS SELECT 0 FROM t0 WHERE ? IN (1);
Expand Down
2 changes: 1 addition & 1 deletion test/sql/types/map/map_empty.test
Original file line number Diff line number Diff line change
Expand Up @@ -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
9 changes: 9 additions & 0 deletions tools/pythonpkg/src/common/exceptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ class PySequenceException : public DatabaseError {
}
};

class PyDependencyException : public DatabaseError {
public:
explicit PyDependencyException(const string &err) : DatabaseError(err) {
}
};

//===--------------------------------------------------------------------===//
// Data Error
//===--------------------------------------------------------------------===//
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -317,6 +325,7 @@ void RegisterExceptions(const py::module &m) {
py::register_exception<PyInterruptException>(m, "InterruptException", db_error);
py::register_exception<PyPermissionException>(m, "PermissionException", db_error);
py::register_exception<PySequenceException>(m, "SequenceException", db_error);
py::register_exception<PyDependencyException>(m, "DependencyException", db_error);

// DataError
auto data_error = py::register_exception<DataError>(m, "DataError", db_error).ptr();
Expand Down
9 changes: 9 additions & 0 deletions tools/pythonpkg/src/pyconnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,9 @@ case_insensitive_map_t<BoundParameterData> TransformPreparedParameters(PreparedS
case_insensitive_map_t<BoundParameterData> 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));
}
Expand Down Expand Up @@ -572,6 +575,9 @@ unique_ptr<QueryResult> DuckDBPyConnection::ExecuteInternal(PreparedStatement &p
unique_lock<std::mutex> 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()) {
Expand Down Expand Up @@ -1181,6 +1187,9 @@ void DuckDBPyConnection::ExecuteImmediately(vector<unique_ptr<SQLStatement>> 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()) {
Expand Down
2 changes: 1 addition & 1 deletion tools/pythonpkg/tests/fast/api/test_duckdb_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,)]

Expand Down
2 changes: 1 addition & 1 deletion tools/pythonpkg/tests/fast/test_runtime_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down

0 comments on commit fd883da

Please sign in to comment.