Repository: duckdb/duckdb
File: tools/nodejs/src/statement.cpp (C++)

Code before:
	constructor.SuppressDestruct();

	exports.Set("Statement", t);
	return exports;
}

struct PrepareTask : public Task {
	PrepareTask(Statement &statement, Napi::Function callback) : Task(statement, callback) {
	}

	void DoWork() override {
		auto &statement = Get<Statement>();

		auto &connection = statement.connection_ref->connection;
		std::vector<std::unique_ptr<duckdb::SQLStatement>> statements;
		try {
			if (connection == nullptr) {
				throw duckdb::ConnectionException("Connection was never established or has been closed already");
			}
			statements = connection->ExtractStatements(statement.sql);
			if (statements.empty()) {
				throw duckdb::InvalidInputException("No statement to prepare!");
			}
		} catch (const duckdb::Exception &ex) {
			statement.statement = duckdb::make_unique<duckdb::PreparedStatement>(duckdb::PreservedError(ex));
			return;
		} catch (std::exception &ex) {
			statement.statement = duckdb::make_unique<duckdb::PreparedStatement>(duckdb::PreservedError(ex));
			return;
		}

		// if there are multiple statements, we directly execute the statements besides the last one
		// we only return the result of the last statement to the user, unless one of the previous statements fails
		for (idx_t i = 0; i + 1 < statements.size(); i++) {
			auto pending_query = connection->PendingQuery(std::move(statements[i]));
			try {
				auto res = pending_query->Execute();
				if (res->HasError()) {
					statement.statement = duckdb::make_unique<duckdb::PreparedStatement>(res->GetErrorObject());
					return;
				}
			} catch (const duckdb::Exception &ex) {
				statement.statement = duckdb::make_unique<duckdb::PreparedStatement>(duckdb::PreservedError(ex));
				return;
			} catch (std::exception &ex) {
				statement.statement = duckdb::make_unique<duckdb::PreparedStatement>(duckdb::PreservedError(ex));
				return;
			}
		}

		statement.statement = connection->Prepare(std::move(statements.back()));

Review comment (refactor):
Can we wrap the entire function into one big try/catch block? `PendingQuery` is not covered by the try/catch and there's a bunch of duplicate exception handling logic.

Code after:

	constructor = Napi::Persistent(t);
	constructor.SuppressDestruct();

	exports.Set("Statement", t);
	return exports;
}

static unique_ptr<duckdb::PreparedStatement> PrepareManyInternal(Statement &statement) {
	auto &connection = statement.connection_ref->connection;
	std::vector<unique_ptr<duckdb::SQLStatement>> statements;
	try {
		if (connection == nullptr) {
			throw duckdb::ConnectionException("Connection was never established or has been closed already");
		}

		// Prepare all statements
		statements = connection->ExtractStatements(statement.sql);
		if (statements.empty()) {
			throw duckdb::InvalidInputException("No statement to prepare!");
		}

		// if there are multiple statements, we directly execute the statements besides the last one
		// we only return the result of the last statement to the user, unless one of the previous statements fails
		for (idx_t i = 0; i + 1 < statements.size(); i++) {
			auto pending_query = connection->PendingQuery(std::move(statements[i]));
			auto res = pending_query->Execute();
			if (res->HasError()) {
				return duckdb::make_unique<duckdb::PreparedStatement>(res->GetErrorObject());
			}
		}

		return connection->Prepare(std::move(statements.back()));
	} catch (const duckdb::Exception &ex) {
		return duckdb::make_unique<duckdb::PreparedStatement>(duckdb::PreservedError(ex));
	} catch (std::exception &ex) {
		return duckdb::make_unique<duckdb::PreparedStatement>(duckdb::PreservedError(ex));
	}
}

struct PrepareTask : public Task {
	PrepareTask(Statement &statement, Napi::Function callback) : Task(statement, callback) {
	}

	void DoWork() override {
		auto &statement = Get<Statement>();
		statement.statement = PrepareManyInternal(statement);
	}

	void Callback() override {
		auto &statement = Get<Statement>();