From 4bbdf6f1a4db7430373070bc93eb800ad281b2e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Manciot?= Date: Thu, 13 Aug 2026 07:17:12 +0200 Subject: [PATCH 1/2] chore(build): bump version to 0.20.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First of the six-repo train: THIS -> extensions 0.2.4 -> arrow 0.2.5 -> jdbc 0.2.5 -> repl 0.20.4 -> helm 0.3.5. Thirty-five changes since 0.20.3. Two themes dominate, and both are classes of SILENT wrong answers rather than crashes. Completeness — a query returned fewer rows/groups than the index holds, with HTTP 200 and no warning: #197 PIT paging sorts by `_doc`, not the injected `_shard_doc` literal (60.5 -> 8.1 ms/page on a 10M index), and the PIT gate moves 7.10 -> 7.12 #200 no total-hit accounting on the paging path #201 preferSearchAfter honored on every ES version #202 the PIT is closed exactly once per extraction #205 GROUP BY emits an explicit terms size — it used to return only ES's default top 10 groups #207 window PARTITION BY sized the same way — top 10 partitions #209 an un-LIMITed row query routes through scroll — it used to return ES's default 10 hits #224 an explicit LIMIT above index.max_result_window pages through scroll instead of failing where no LIMIT succeeded Parser correctness — the statement that ran was not the statement written: #213 the WHOLE statement must parse (`phrase`). `DELETE FROM orders WHEREE id = 1` used to run as `DELETE FROM orders` and empty the index; six more shapes measured in the commit. Plus a quote- and comment-aware multi-statement split, COPY INTO FILE_FORMAT = parsed as documented, and literals protected from the normalizer #216 the licence-activation flag is read, not returned as a constant #220 a function call closes exactly the parentheses it opened; SCRIPT AS keeps its own closing parenthesis #212 watcher/DML WHERE qualifiers resolve, and the FROM shapes DELETE and UPDATE cannot express are rejected #191 a JOIN in a watcher search input is rejected, not dropped #211 #204 #215 jest Pipeline/Template/Watcher routed through the guarded wrapper; value-discard casts and licence-path throws removed ALTER COLUMN ... SET|ADD FIELD parses and actually applies; a column is multi-field or script-defined, never both Result shape and throughput: #226 result rows no longer surface Elasticsearch hit metadata #228 every Elasticsearch response is parsed exactly once, on every client and every path #229 streamed rows are normalized in a single pass, with a zero-rebuild passthrough when nothing needs changing arrow#139 tryParseAsDateTime guarded by a cheap temporal-shape check RELEASE NOTES for 0.20.4 — five user-visible changes, none a regression: 1. Result rows no longer carry `_index`, `_score` or `_sort`. `_id` is opt-in via the new HOCON setting `elastic.include-document-id` (default false, env `ELASTIC_INCLUDE_DOCUMENT_ID`). 2. Strict whole-statement parsing. SQL that previously parsed as a PREFIX and silently discarded the rest is now a hard error ("end of input expected"). This includes bare `UNION`, which was never a synonym for `UNION ALL`, and `CREATE TABLE ... WITH (...)`, a clause the grammar does not have. 3. Generated GROUP BY / PARTITION BY JSON now carries `"size":65536`. Downstream tests pinning that JSON need their expectations updated. 4. An un-LIMITed row query now returns every row where it returned 10. The licensed `maxQueryResults` cap moved with the routing — it keys on `returnsRows`, so windowed and script-only queries are capped too rather than bypassing it. 5. The PIT gate is 7.12, not 7.10. On 7.11 `_shard_doc` fails loudly and `_doc` silently loses rows, so 7.11 now uses classic scroll. Verified: the build loads and all 20 module version keys report 0.20.4. Follow-up after publication (NOT in this PR): the user-facing version sweep across README/installers/documentation and the softclient4es.dev site, per the release-doc-sweep convention — versions are updated only once the artifacts are verified on JFrog. Co-Authored-By: Claude Opus 5 (1M context) --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index a6c9fc0e..ac14c27d 100644 --- a/build.sbt +++ b/build.sbt @@ -20,7 +20,7 @@ ThisBuild / organization := "app.softnetwork" name := "softclient4es" -ThisBuild / version := "0.20.4-SNAPSHOT" +ThisBuild / version := "0.20.4" ThisBuild / scalaVersion := scala213 From 1b1dafb6bd38bd26d138332bf9fddf2e827a77c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Manciot?= Date: Thu, 13 Aug 2026 07:34:39 +0200 Subject: [PATCH 2/2] feat(install): add install.cmd so a host that blocks .ps1 can still install MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Windows client default execution policy is Restricted, and plenty of managed hosts are set to AllSigned. On those machines a DOWNLOADED install.ps1 cannot run at all, and the usual advice — change the machine's execution policy — is exactly what the policy exists to prevent. install.cmd is a wrapper and nothing else: powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0install.ps1" %* exit /b %ERRORLEVEL% -ExecutionPolicy Bypass on the command line applies to THAT PROCESS only: it changes nothing on the machine and needs no elevation. Note the piped one-liner (`irm ... | iex`) was never affected — `iex` runs a string, not a script file — so this is specifically for the download-then-run path, and for double-click. Deliberately NOT a second implementation. Every option, default, fallback and message stays in install.ps1 and `%*` forwards the flags verbatim, so the two entry points cannot drift. The only logic here is a guard that names the problem when install.ps1 is not sitting next to it. CRLF, pinned by a new .gitattributes (`*.cmd -text`, `*.bat -text`). The documented install path is `curl -O` from raw.githubusercontent.com, which serves the stored blob byte for byte — no checkout-time eol conversion ever runs, so without `-text` git would normalise the CRLF out of the blob and ship an LF-only batch file. LF-only .cmd files are a known source of failures around labels and multi-line blocks. Docs: README gets the cmd.exe install line; documentation/client/repl.md gets a Windows (cmd.exe) section that says which path the execution policy actually blocks, the two-file download, the flags-are-identical contract, and the honest limit — `-ExecutionPolicy Bypass` is overridden when the policy comes from GROUP POLICY (MachinePolicy / UserPolicy scopes), which no wrapper can work around. Also documents running the generated uninstall.ps1 the same way, since the installer still writes it as a .ps1. Running the REPL never needed PowerShell: bin\softclient4es.bat is batch. Verified on macOS with pwsh 7 (no Windows host here, so cmd.exe executing the wrapper itself is NOT covered): the exact invocation form the wrapper uses, `-NoProfile -File install.ps1 `, renders -Help correctly, and `-EsVersion 5` forwards a value-taking flag and propagates exit code 1 — which is what `exit /b %ERRORLEVEL%` relays to the caller. Co-Authored-By: Claude Opus 5 (1M context) --- .gitattributes | 9 +++++++ README.md | 5 ++++ documentation/client/repl.md | 51 ++++++++++++++++++++++++++++++++++++ install.cmd | 41 +++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+) create mode 100644 .gitattributes create mode 100644 install.cmd diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..64a6ede6 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,9 @@ +# Windows shell entry points must reach the user with CRLF line endings. +# +# install.cmd is documented as a `curl -O` download from raw.githubusercontent.com, +# which serves the stored blob byte for byte — no checkout-time eol conversion ever +# runs. `-text` keeps git from normalising the CRLF out of the blob, so the file a +# user downloads is the file cmd.exe was designed to read. (LF-only batch files are +# a known source of failures around labels and multi-line blocks.) +*.cmd -text +*.bat -text diff --git a/README.md b/README.md index 13cb4f77..3f9f2711 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,11 @@ curl -fsSL https://raw.githubusercontent.com/SOFTNETWORK-APP/SoftClient4ES/main/ irm https://raw.githubusercontent.com/SOFTNETWORK-APP/SoftClient4ES/main/install.ps1 | iex ``` +**Windows (cmd.exe, when `.ps1` files are blocked):** download `install.cmd` and `install.ps1` side by side. It takes the same flags as `install.ps1` and only launches it with `-ExecutionPolicy Bypass`, for that one process. +```bat +install.cmd +``` + ### Connect and Query ```bash diff --git a/documentation/client/repl.md b/documentation/client/repl.md index b1ad04c5..0ee93738 100644 --- a/documentation/client/repl.md +++ b/documentation/client/repl.md @@ -88,6 +88,44 @@ Invoke-WebRequest -Uri https://raw.githubusercontent.com/SOFTNETWORK-APP/SoftCli .\install.ps1 ``` +#### Windows (cmd.exe — when `.ps1` scripts are blocked) + +The piped one-liner above is unaffected by the execution policy (`iex` runs a +string, not a script file), but `.\install.ps1` on a **downloaded** file is: the +Windows client default is `Restricted`, and the machine may also be set to +`AllSigned`. `install.cmd` is the way in — it is a wrapper that runs +`install.ps1` with `-ExecutionPolicy Bypass` **for that one process**, changing +nothing on the machine and needing no elevation. + +Download **both** files, keep them in the same directory, then: + +```bat +curl -O https://raw.githubusercontent.com/SOFTNETWORK-APP/SoftClient4ES/main/install.cmd +curl -O https://raw.githubusercontent.com/SOFTNETWORK-APP/SoftClient4ES/main/install.ps1 +install.cmd +``` + +`install.cmd` accepts exactly the flags `install.ps1` does and forwards them +verbatim, so every option, default and fallback documented below applies +unchanged — there is no second implementation to drift: + +```bat +install.cmd -ListVersions -EsVersion 8 +install.cmd -Target "C:\tools\softclient4es" -EsVersion 8 -Version 0.20.4 +install.cmd -EsVersion 9 -NoExtensions +install.cmd -Help +``` + +> **When even the wrapper cannot help:** `-ExecutionPolicy Bypass` is +> deliberately overridden when the policy is enforced through **Group Policy** +> (the `MachinePolicy` / `UserPolicy` scopes). Check with +> `Get-ExecutionPolicy -List`: if the winning entry is one of those two scopes, +> no wrapper can work around it — ask your administrator, or unpack an install +> by hand from the assembly jar (see [Directory Structure](#directory-structure)). +> Note that this affects **installing** only: the launcher the installer writes, +> `bin\softclient4es.bat`, is a plain batch file, so *running* the REPL never +> needs PowerShell. + ### List Available Versions Before installing, you can list all available versions for a specific Elasticsearch version: @@ -104,6 +142,12 @@ Before installing, you can list all available versions for a specific Elasticsea .\install.ps1 -ListVersions -EsVersion 8 ``` +Or, when `.ps1` scripts are blocked, the same flags through the wrapper: + +```bat +install.cmd -ListVersions -EsVersion 8 +``` + **Example output:** ``` @@ -325,6 +369,13 @@ $env:PATH += ";$env:USERPROFILE\softclient4es\bin" ~\softclient4es\uninstall.ps1 ``` +The installer writes the uninstaller as a `.ps1`, so a host that blocks `.ps1` +blocks it too. Run it the same way `install.cmd` runs the installer: + +```bat +powershell -NoProfile -ExecutionPolicy Bypass -File "%USERPROFILE%\softclient4es\uninstall.ps1" +``` + --- ## Connection diff --git a/install.cmd b/install.cmd new file mode 100644 index 00000000..2fc4aec1 --- /dev/null +++ b/install.cmd @@ -0,0 +1,41 @@ +@echo off +rem =========================================================================== +rem SoftClient4ES Installation Script - cmd.exe entry point +rem =========================================================================== +rem +rem Why this file exists: on a host whose PowerShell execution policy is +rem Restricted or AllSigned (the Windows client default is Restricted), +rem double-clicking or calling install.ps1 fails before it runs a single line. +rem This wrapper is the supported way in: -ExecutionPolicy Bypass applies to +rem THIS process only, changes nothing on the machine, and needs no elevation. +rem +rem It is a wrapper and nothing else - every option, default, fallback and +rem message lives in install.ps1, so the two entry points can never drift. +rem Pass the same flags you would pass to install.ps1: +rem +rem install.cmd +rem install.cmd -ListVersions -EsVersion 8 +rem install.cmd -Target "C:\tools\softclient4es" -EsVersion 8 -Version 1.0.0 +rem install.cmd -EsVersion 7 -Version 0.20.4 -NoExtensions +rem install.cmd -Help +rem +rem Caveat worth knowing: -ExecutionPolicy Bypass is overridden when the policy +rem is enforced through Group Policy (the MachinePolicy / UserPolicy scopes). +rem That is deliberate on Microsoft's side and no wrapper can work around it - +rem on such a host, ask your administrator, or run the REPL from the jar by +rem hand (see documentation/client/repl.md). +rem =========================================================================== + +setlocal + +set "PS1=%~dp0install.ps1" + +if not exist "%PS1%" ( + echo [ERROR] install.ps1 was not found next to install.cmd. 1>&2 + echo [ERROR] Expected: %PS1% 1>&2 + echo [ERROR] Download both files from the same release and keep them together. 1>&2 + exit /b 1 +) + +powershell -NoProfile -ExecutionPolicy Bypass -File "%PS1%" %* +exit /b %ERRORLEVEL%