duckhaven-cli 0.3.0__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/CHANGELOG.md +11 -0
  2. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/PKG-INFO +9 -1
  3. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/README.md +8 -0
  4. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/_version.py +2 -2
  5. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/commands/query.py +1 -0
  6. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/execute.py +17 -4
  7. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/repl.py +1 -1
  8. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/.gitignore +0 -0
  9. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/LICENSE +0 -0
  10. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/pyproject.toml +0 -0
  11. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/__init__.py +0 -0
  12. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/commands/__init__.py +0 -0
  13. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/commands/admin.py +0 -0
  14. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/commands/api.py +0 -0
  15. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/commands/auth.py +0 -0
  16. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/commands/catalog.py +0 -0
  17. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/commands/dbt.py +0 -0
  18. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/commands/grant.py +0 -0
  19. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/commands/health.py +0 -0
  20. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/commands/profile.py +0 -0
  21. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/commands/saved.py +0 -0
  22. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/commands/session.py +0 -0
  23. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/config.py +0 -0
  24. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/context.py +0 -0
  25. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/errors.py +0 -0
  26. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/introspect.py +0 -0
  27. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/main.py +0 -0
  28. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/output/__init__.py +0 -0
  29. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/resolve.py +0 -0
  30. {duckhaven_cli-0.3.0 → duckhaven_cli-0.4.0}/src/dh/rest.py +0 -0
@@ -6,6 +6,17 @@ All notable changes to `duckhaven-cli` are documented here. The format follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.4.0] - 2026-10-15
10
+
11
+ ### Changed
12
+
13
+ - `dh sql` and the REPL ask the server to hold the submit for up to two seconds, so a quick
14
+ query finishes in one request instead of a submit and a poll. `--no-wait` still returns at
15
+ once. A server that predates the hold ignores it.
16
+ - Ready for DuckHaven `api_version` 3: `DECIMAL` and wide integers, which it sends as
17
+ strings, print as before in table and CSV output and stay exact strings with
18
+ `--format json`.
19
+
9
20
  ## [0.3.0] - 2026-10-13
10
21
 
11
22
  Brings the CLI up to date with the DuckHaven server's result cache, DuckLake catalogs,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: duckhaven-cli
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: dh — the command-line interface for DuckHaven
5
5
  Project-URL: Homepage, https://github.com/tamasmrtn/duckhaven-clients
6
6
  Project-URL: Source, https://github.com/tamasmrtn/duckhaven-clients
@@ -77,6 +77,14 @@ maintenance recommendations (`dh admin maintenance recommendations|apply`), and
77
77
  private editor tabs (`dh worksheet`). Against an older server these commands answer with the
78
78
  server's own not-found or validation error.
79
79
 
80
+ ## Exact numbers
81
+
82
+ A DuckHaven with `api_version` 3 or later sends `DECIMAL` values and 64-bit-or-wider integers
83
+ (`BIGINT`, `HUGEINT`, `count(*)`) as strings, because JSON numbers lose precision past about
84
+ 16 digits. `dh` prints them exactly as before in table and CSV output. With `--format json`
85
+ they stay strings (`"12.50"`, `"9007199254740993"`), exact. Scripts reading `dh` JSON should
86
+ convert those columns themselves rather than expect JSON numbers.
87
+
80
88
  ## Documentation
81
89
 
82
90
  - [Command-line quickstart](https://tamasmrtn.github.io/duckhaven/getting-started/cli-quickstart/) — install, sign in,
@@ -48,6 +48,14 @@ maintenance recommendations (`dh admin maintenance recommendations|apply`), and
48
48
  private editor tabs (`dh worksheet`). Against an older server these commands answer with the
49
49
  server's own not-found or validation error.
50
50
 
51
+ ## Exact numbers
52
+
53
+ A DuckHaven with `api_version` 3 or later sends `DECIMAL` values and 64-bit-or-wider integers
54
+ (`BIGINT`, `HUGEINT`, `count(*)`) as strings, because JSON numbers lose precision past about
55
+ 16 digits. `dh` prints them exactly as before in table and CSV output. With `--format json`
56
+ they stay strings (`"12.50"`, `"9007199254740993"`), exact. Scripts reading `dh` JSON should
57
+ convert those columns themselves rather than expect JSON numbers.
58
+
51
59
  ## Documentation
52
60
 
53
61
  - [Command-line quickstart](https://tamasmrtn.github.io/duckhaven/getting-started/cli-quickstart/) — install, sign in,
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.3.0'
22
- __version_tuple__ = version_tuple = (0, 3, 0)
21
+ __version__ = version = '0.4.0'
22
+ __version_tuple__ = version_tuple = (0, 4, 0)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -64,6 +64,7 @@ def _run(
64
64
  # Two timeouts would be a contract people get wrong; one cannot drift.
65
65
  timeout_s=budget,
66
66
  use_cache=False if no_cache else None,
67
+ hold=not no_wait,
67
68
  )
68
69
  if no_wait:
69
70
  cli.note(f"Submitted {submitted['id']}.")
@@ -1,7 +1,8 @@
1
1
  """One-shot query execution: submit, wait, fetch.
2
2
 
3
- `POST /workspaces/{workspace}/queries` answers **202** with a query that has not
4
- run yet, so every caller has to poll. This is that loop, done once and correctly.
3
+ `POST /workspaces/{workspace}/queries` holds the call briefly for the run to finish
4
+ and otherwise answers **202** with a query still running, which the caller polls.
5
+ This is that loop, done once and correctly.
5
6
 
6
7
  It is deliberately not the connector's code path. `duckhaven-sql-connector`
7
8
  speaks the *session* API (`connect()` opens one), and sessions are disabled by
@@ -34,6 +35,11 @@ _MAX_DELAY = 2.0
34
35
  #: above still paces the loop. Well under the client's socket timeout.
35
36
  _HOLD_S = 5.0
36
37
 
38
+ #: How long the submit itself asks the server to hold for a finish. Shorter than
39
+ #: `_HOLD_S`: until the submit answers, the query's id is unknown, so a Ctrl-C in
40
+ #: this window cannot cancel the query the way one during polling does.
41
+ _SUBMIT_HOLD_S = 2.0
42
+
37
43
  #: The server caps `limit` here; asking for more is rejected, not truncated.
38
44
  _MAX_PAGE = 1000
39
45
 
@@ -62,13 +68,20 @@ def submit(
62
68
  timeout_s: float | None = None,
63
69
  saved_query_id: str | None = None,
64
70
  use_cache: bool | None = None,
71
+ hold: bool = True,
65
72
  ) -> dict[str, Any]:
66
73
  """Hand the SQL to the server. Returns the accepted query.
67
74
 
68
- Usually not run yet; a read the result cache can answer comes back already
69
- `done`. `use_cache=False` always executes it.
75
+ Already `done` when it finished within the hold or was answered from the result
76
+ cache; otherwise still running, for `wait` to follow. `use_cache=False` always
77
+ executes it. `hold=False` answers at once, for a caller that will not wait.
70
78
  """
71
79
  body: dict[str, Any] = {"sql": sql}
80
+ if hold:
81
+ # A query that finishes inside the hold comes back done on this call, with
82
+ # nothing to poll. A server too old to know the field ignores it and
83
+ # answers 202 at once, as before.
84
+ body["wait_timeout_s"] = _SUBMIT_HOLD_S
72
85
  if agent:
73
86
  body["agent_id"] = agent
74
87
  if catalog:
@@ -144,7 +144,7 @@ def _run_one_shot(cli, settings, sql: str, agent: str | None) -> None:
144
144
  agent=chosen,
145
145
  catalog=settings.get("catalog"),
146
146
  )
147
- finished = execute.wait(client, submitted["id"], timeout=1200)
147
+ finished = execute.wait(client, submitted["id"], timeout=1200, known=submitted)
148
148
  execute.raise_for_status(finished)
149
149
  page = execute.fetch_rows(client, finished["id"])
150
150
  cli.emit({"columns": page["columns"], "rows": page["rows"], "total": page["total"]})
File without changes
File without changes