Repository: duckdb/duckdb
File: src/main/capi/prepared-c.cpp (C++)

Code before:
	if (!wrapper->statement || !wrapper->statement->HasError()) {
		return nullptr;
	}
	return wrapper->statement->error.Message().c_str();
}

idx_t duckdb_nparams(duckdb_prepared_statement prepared_statement) {
	auto wrapper = reinterpret_cast<PreparedStatementWrapper *>(prepared_statement);
	if (!wrapper || !wrapper->statement || wrapper->statement->HasError()) {
		return 0;
	}
	return wrapper->statement->named_param_map.size();
}

static duckdb::string duckdb_parameter_name_internal(duckdb_prepared_statement prepared_statement, idx_t index) {
	auto wrapper = (PreparedStatementWrapper *)prepared_statement;
	if (!wrapper || !wrapper->statement || wrapper->statement->HasError()) {
		return duckdb::string();
	}
	auto &named = wrapper->statement->named_param_map;
	if (index == 0 || index > named.size()) {
		return duckdb::string();
	}

	auto &cache = wrapper->param_index_to_name;
	if (cache.size() != named.size() + 1) {
		cache.assign(named.size() + 1, duckdb::string());
		for (auto &kv : named) {
			auto idx = kv.second;
			if (idx < cache.size() && cache[idx].empty()) {
				cache[idx] = kv.first;
			}
		}
	}
	// No parameter was found with this index when there is nothing in the cache (default value is `duckdb::string()`)
	return cache[index];
}

const char *duckdb_parameter_name(duckdb_prepared_statement prepared_statement, idx_t index) {
	auto identifier = duckdb_parameter_name_internal(prepared_statement, index);
	if (identifier == duckdb::string()) {
		return NULL;
	}
	return strdup(identifier.c_str());
}

duckdb_type duckdb_param_type(duckdb_prepared_statement prepared_statement, idx_t param_idx) {
	auto logical_type = duckdb_param_logical_type(prepared_statement, param_idx);
	if (!logical_type) {
		return DUCKDB_TYPE_INVALID;
	}

Review comment (performance):
Can we turn this into a function and then move that cache initialization function to where we create the prepared statement wrapper? So the places where we do `auto wrapper = new PreparedStatementWrapper();` followed by some try-catch to prepare the statements.

Code after:
		duckdb_prepare_param_index_to_name_map_internal(wrapper);
		return DuckDBSuccess;
	} catch (...) {
		delete wrapper;
		return DuckDBError;
	}
}

const char *duckdb_prepare_error(duckdb_prepared_statement prepared_statement) {
	auto wrapper = reinterpret_cast<PreparedStatementWrapper *>(prepared_statement);
	if (!wrapper) {
		return nullptr;
	}
	if (!wrapper->success) {
		return wrapper->error_data.Message().c_str();
	}
	if (!wrapper->statement || !wrapper->statement->HasError()) {
		return nullptr;
	}
	return wrapper->statement->error.Message().c_str();
}

idx_t duckdb_nparams(duckdb_prepared_statement prepared_statement) {
	auto wrapper = reinterpret_cast<PreparedStatementWrapper *>(prepared_statement);
	if (!wrapper || !wrapper->statement || wrapper->statement->HasError()) {
		return 0;
	}
	return wrapper->statement->named_param_map.size();
}

static duckdb::string duckdb_parameter_name_internal(duckdb_prepared_statement prepared_statement, idx_t index) {
	auto wrapper = (PreparedStatementWrapper *)prepared_statement;
	if (!wrapper || !wrapper->statement || wrapper->statement->HasError()) {
		return duckdb::string();
	}
	auto &cache = wrapper->param_index_to_name;
	auto it = cache.find(index);
	if (it == cache.end()) {
		return duckdb::string();
	}
	return it->second;
}

const char *duckdb_parameter_name(duckdb_prepared_statement prepared_statement, idx_t index) {
	auto identifier = duckdb_parameter_name_internal(prepared_statement, index);
	if (identifier == duckdb::string()) {
		return NULL;
	}
	return strdup(identifier.c_str());
}