Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: duckdb_api and custom_user_agent options #9603

Merged
merged 6 commits into from Nov 9, 2023

Conversation

elefeint
Copy link
Contributor

@elefeint elefeint commented Nov 8, 2023

Introduces two new settings: duckdb_api representing the DuckDB surface (C API, Python, JDBC etc.) and custom_user_agent that is settable by the user. Additionally, a table function pragma_user_agent() and PRAGMA user_agent can be used to query the overall user-agent value for the current session.

Example usage from CLI:

D PRAGMA user_agent;
┌───────────────────────────────────┐
│            user_agent             │
│              varchar              │
├───────────────────────────────────┤
│ duckdb/v0.9.2-dev254(linux_amd64) │
└───────────────────────────────────┘
D select * from pragma_user_agent();
┌───────────────────────────────────┐
│            user_agent             │
│              varchar              │
├───────────────────────────────────┤
│ duckdb/v0.9.2-dev254(linux_amd64) │
└───────────────────────────────────┘

Examples of overall user-agent strings from different APIs, assuming that the end user passed CUSTOM_STRING as the value of custom_user_agent:

  • C API: duckdb/v0.9.2-dev256(linux_amd64) capi CUSTOM_STRING
  • Python: duckdb/v0.9.2-dev256(linux_amd64) python CUSTOM_STRING
  • JDBC: test_custom_user_agent duckdb/v0.9.2-dev256(linux_amd64) jdbc CUSTOM_STRING
  • ODBC: duckdb/v0.9.2-dev253(linux_amd64) odbc CUSTOM_STRING

cc: @bleskes

Copy link
Member

@Mause Mause left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't seem to be using these new values anywhere yet, is that intentional?

@@ -310,13 +312,22 @@ static const char *const JDBC_STREAM_RESULTS = "jdbc_stream_results";
jobject _duckdb_jdbc_startup(JNIEnv *env, jclass, jbyteArray database_j, jboolean read_only, jobject props) {
auto database = byte_array_to_string(env, database_j);
DBConfig config;
config.options.duckdb_api = "jni";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not call this Java? You also seem to be overriding it in the Java layer, which is a bit confusing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I'll rename to "java". This override is here just in case someone uses Java without JDBC -- making the distinction between "java" and "jdbc" would make this obscure case easier to troubleshoot.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if that would be possible? The Java API is the the JDBC API

public static void test_user_agent() throws Exception {
try (Connection conn = DriverManager.getConnection("jdbc:duckdb:")) {
try (PreparedStatement stmt1 =
conn.prepareStatement("SELECT value FROM duckdb_settings() WHERE name = 'custom_user_agent'");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: use the current_setting scalar function

@carlopi
Copy link
Contributor

carlopi commented Nov 8, 2023

Can be potentially added later, but maybe it's worth raising together with this: could it make sense to have a possibility to set this via a C++ define?

Idea would be that then also CLI could get relevant tags by doing something like:
GEN=ninja USER_AGENT=some_string make will build a duckdb that by default will have some_string as default custom_user_agent (with the possibility to be overridden at configuration).

This would allow also to guarantee the invariant that a custom_user_agent is always non-empty (either as the default value for USER_AGENT, say cli_unknown, or provided at build time, or overridden at configuration time).

Idea (if this makes sense) is being able to distinguish whether something has been built (and likely distributed) via our github releases, via homebrew or via other means.
That can be done by having Release CI for CLI add a USER_AGENT=cli_official_release or homebrew builds have a USER_AGENT=cli_homebrew.

That could be useful for example to give instructions to users on how to update duckdb itself (like if duckdb is distributed via homebrew, first the version can be checked vs latest release, then brew reinstall duckdb could be suggested as a likely option), or to be able to point to relevant documentation. Another use of this could be in bug reports, to be able to get more informations in an easy way (SELECT current_setting('custom_user_agent'); could even be part of the template).

Copy link
Collaborator

@Mytherin Mytherin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! LGTM - some minor comments:

@maiadegraaf could you perhaps look at the ODBC portion of the PR?

src/main/capi/config-c.cpp Outdated Show resolved Hide resolved
tools/jdbc/src/jni/duckdb_java.cpp Outdated Show resolved Hide resolved
}

if (db_config->options.duckdb_api.empty()) {
db_config->options.duckdb_api = "capi";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of doing the check here perhaps it's cleaner/easier to set duckdb_api to "capi" in duckdb_create_config?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some logic for setting capi is still needed here because duckdb_open_ext() is often called with nullptr passed in for config.

Copy link
Contributor

@maiadegraaf maiadegraaf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ODBC stuff looks good to me. Just a heads up, I'm currently reworking the connection string so the changes in tools/odbc/driver.cpp will most likely be altered shortly.

The tests look good too, can they be moved to tests/connect.cpp instead, since they test the connection string rather than SQLSetEnvAttr or SQLGetEnvAttr.

tools/odbc/test/tests/set_attr.cpp Outdated Show resolved Hide resolved
@elefeint
Copy link
Contributor Author

elefeint commented Nov 8, 2023

@carlopi I love the idea of knowing how duckdb got built; it would be very helpful for troubleshooting. Maybe a separate compile-time constant DUCKDB_DISTRIBUTION, similar to DUCKDB_VERSION, that could then become a component returned from PRAGMA user_agent? It would be best in a followup since those would be CMake changes.

@github-actions github-actions bot marked this pull request as draft November 8, 2023 18:33
@elefeint elefeint marked this pull request as ready for review November 8, 2023 19:27
@elefeint
Copy link
Contributor Author

elefeint commented Nov 9, 2023

@Mause I was thinking the typical usage would be invocation through the user-facing PRAGMA user_agent, for example for troubleshooting.

@Mause
Copy link
Member

Mause commented Nov 9, 2023

@Mause I was thinking the typical usage would be invocation through the user-facing PRAGMA user_agent, for example for troubleshooting.

Why not use it as the User-Agent header for extension downloads? Would give an idea of extension usage per language & when the extension version is detached from the DuckDB version, idea's of usage there too

@Mytherin Mytherin merged commit 34e40ae into duckdb:main Nov 9, 2023
46 of 47 checks passed
@Mytherin
Copy link
Collaborator

Mytherin commented Nov 9, 2023

Thanks!

Why not use it as the User-Agent header for extension downloads? Would give an idea of extension usage per language & when the extension version is detached from the DuckDB version, idea's of usage there too

That's a good idea, but let's add that in a later PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants