From 65f6027149b6547ec9a44768af1ec321d0306911 Mon Sep 17 00:00:00 2001 From: redcatbear Date: Fri, 14 Aug 2026 14:10:34 +0200 Subject: [PATCH 1/2] #96: Render timestamp precision. --- doc/changes/changelog.md | 1 + doc/changes/changes_5.1.0.md | 11 ++++ .../96-render-timestamp-precision.md | 66 +++++++++++++++++++ .../ScalarFunctionAppender_spec.lua | 40 +++++++++++ .../queryrenderer/AbstractQueryAppender.lua | 6 ++ src/exasol/vscl/types/type_definition.lua | 3 +- ...virtual-schema-common-lua-5.1.0-1.rockspec | 2 +- 7 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 doc/changes/changes_5.1.0.md create mode 100644 doc/changesets/96-render-timestamp-precision.md rename virtual-schema-common-lua-5.0.1-1.rockspec => virtual-schema-common-lua-5.1.0-1.rockspec (99%) diff --git a/doc/changes/changelog.md b/doc/changes/changelog.md index ec58d2b..77df678 100644 --- a/doc/changes/changelog.md +++ b/doc/changes/changelog.md @@ -1,5 +1,6 @@ # Changes +* [5.1.0](changes_5.1.0.md) * [5.0.1](changes_5.0.1.md) * [5.0.0](changes_5.0.0.md) * [4.0.1](changes_4.0.1.md) diff --git a/doc/changes/changes_5.1.0.md b/doc/changes/changes_5.1.0.md new file mode 100644 index 0000000..9828733 --- /dev/null +++ b/doc/changes/changes_5.1.0.md @@ -0,0 +1,11 @@ +# virtual-schema-common-lua 5.1.0, released 2026-08-14 + +Code name: Render TIMESTAMP Precision + +## Summary + +This release preserves the optional fractional-second precision of `TIMESTAMP` type definitions when rendering SQL. + +## Features + +* #96: Render `TIMESTAMP(p)` and `TIMESTAMP(p) WITH LOCAL TIME ZONE` in `CAST` and `JSON_VALUE ... RETURNING` expressions. diff --git a/doc/changesets/96-render-timestamp-precision.md b/doc/changesets/96-render-timestamp-precision.md new file mode 100644 index 0000000..b6a66eb --- /dev/null +++ b/doc/changesets/96-render-timestamp-precision.md @@ -0,0 +1,66 @@ +# GH-96 Render TIMESTAMP precision + +## Goal + +Preserve an optional fractional-second precision from a `TIMESTAMP` type definition when VSCL renders SQL. This enables adapters to render `CAST` and `JSON_VALUE ... RETURNING` expressions accurately for both plain timestamps and timestamps with local time zone. + +## Scope + +In scope: + +* Render `TIMESTAMP(p)` when the type definition supplies a precision from `0` through `9`. +* Render `TIMESTAMP(p) WITH LOCAL TIME ZONE` when both precision and the local-time-zone modifier are supplied. +* Keep the existing output for timestamp definitions without precision: `TIMESTAMP` and `TIMESTAMP WITH LOCAL TIME ZONE`. +* Update the LuaLS timestamp type definition and add focused renderer regression tests. +* Release the bug fix and document it in the changelog. + +Out of scope: + +* Validate or normalize precision values received from the Virtual Schema API. +* Change timestamp literals, other SQL data types, or the public query-renderer architecture. +* Alter the existing requirement or QueryRenderer sequence diagram, whose stated behavior already covers rendering the AST to SQL. + +## Design References + +* [System Requirements](../system_requirements.md) — `req~render-sql-query~1` +* [Query Push-down Model](../model/diagrams/sequence/seq_push_down.plantuml) — existing `dsn -> req~render-sql-query~1` coverage +* [Developer Guide](../developer_guide/developer_guide.md) — test, static-analysis, type-checking, and diagram-build commands +* [CI Build](../../.github/workflows/ci-build.yml) — required project quality gates + +## Strategy + +Extend the shared timestamp branch of `AbstractQueryAppender:_append_data_type`. After the existing `TIMESTAMP` token, append the parenthesized precision only when `data_type.precision` is present; append `WITH LOCAL TIME ZONE` afterwards when requested. This preserves both grammar order and current no-precision output for every caller of `_append_data_type`, including `CAST` and `JSON_VALUE`. + +No traced requirement or design change is planned: GH-96 corrects incomplete handling within the already-traced SQL-rendering requirement. The repository does not contain the expected `doc/design/quality_requirements.md`; therefore this plan derives verification from the checked-in developer guide, trace script, and CI workflow. + +## Task List + +- [x] Create and checkout a new Git branch `bugfix/96-render-timestamp-precision`. + +### Requirements And Design + +- [x] Confirm that `req~render-sql-query~1` remains semantically accurate and keep its current revision and existing `dsn -> req~render-sql-query~1` forwarding unchanged. +- [x] No requirement or QueryRenderer sequence-diagram review was necessary; implementation remained within the existing behavior. + +### Implementation + +- [x] Add optional `precision: integer?` to `TimestampTypeDefinition` in `src/exasol/vscl/types/type_definition.lua`. +- [x] Update `AbstractQueryAppender:_append_timestamp` to emit `()` before the optional ` WITH LOCAL TIME ZONE` suffix, without changing output when precision is absent. + +### Verification + +- [x] Add `ScalarFunctionAppender_spec.lua` regression cases for `CAST` rendering plain and local-time-zone timestamp types with explicit boundary precisions (including `0` and `9`) and without precision. +- [x] Add `ScalarFunctionAppender_spec.lua` regression cases for `JSON_VALUE ... RETURNING` timestamp types with explicit precision, with and without local time zone, and without precision. +- [x] Run the focused query-renderer specs, then `tools/run_tests.sh --run=ci`, and review the coverage report for the changed appender. +- [x] Run `tools/run_luacheck.sh` and `tools/run-type-check.sh`. +- [x] Run `tools/shellcheck.sh`, `tools/build_diagrams.sh`, and `tools/build_docs.sh` as required by the CI build. +- [x] Keep the OpenFastTrace trace clean with `tools/trace_requirements.sh`. + +### Update User Documentation + +- [x] Verify that the user and developer guides require no behavioral documentation change; document timestamp precision only in the release notes unless that review finds an existing type-rendering reference to update. + +## Version and Changelog Update + +- [x] Raise the LuaRocks package version from `5.0.1-1` to `5.1.0-1` as a backward-compatible feature release. +- [x] Add `doc/changes/changes_5.1.0.md` and link it from `doc/changes/changelog.md`, noting timestamp-precision rendering for `CAST` and `JSON_VALUE`. diff --git a/spec/exasol/vscl/queryrenderer/ScalarFunctionAppender_spec.lua b/spec/exasol/vscl/queryrenderer/ScalarFunctionAppender_spec.lua index b5049ca..1e59452 100644 --- a/spec/exasol/vscl/queryrenderer/ScalarFunctionAppender_spec.lua +++ b/spec/exasol/vscl/queryrenderer/ScalarFunctionAppender_spec.lua @@ -383,6 +383,23 @@ describe("ScalarFunctionRenderer", function() it_asserts("CAST(347 AS VARCHAR(3))", run_complex_function("CAST", {dataType = {type = "VARCHAR", size = 3}}, 347), "number to VARCHAR") + it_asserts("CAST(347 AS TIMESTAMP(0))", + run_complex_function("CAST", {dataType = {type = "TIMESTAMP", precision = 0}}, 347), + "number to TIMESTAMP with precision") + + it_asserts("CAST(347 AS TIMESTAMP(9) WITH LOCAL TIME ZONE)", + run_complex_function("CAST", {dataType = { + type = "TIMESTAMP", precision = 9, withLocalTimeZone = true + }}, 347), + "number to TIMESTAMP with precision and local time zone") + + it_asserts("CAST(347 AS TIMESTAMP)", run_complex_function("CAST", {dataType = {type = "TIMESTAMP"}}, 347), + "number to TIMESTAMP without precision") + + it_asserts("CAST(347 AS TIMESTAMP WITH LOCAL TIME ZONE)", + run_complex_function("CAST", {dataType = {type = "TIMESTAMP", withLocalTimeZone = true}}, 347), + "number to TIMESTAMP with local time zone without precision") + it_asserts("CAST(INTERVAL '+1-02' YEAR TO MONTH AS VARCHAR(7))", run_complex_function("CAST", {dataType = {type = "VARCHAR", size = 7}}, { value = "+1-02", @@ -451,6 +468,29 @@ describe("ScalarFunctionRenderer", function() error_behavior = {type = "ERROR"}, data_type = {size = 100, type = "VARCHAR"}, expected = [[JSON_VALUE('{"a": 1}', '$.a' RETURNING VARCHAR(100) NULL ON EMPTY ERROR ON ERROR)]] + }, { + argument_1 = '{"a": 1}', + argument_2 = '$.a', + empty_behavior = {type = "NULL"}, + error_behavior = {type = "ERROR"}, + data_type = {type = "TIMESTAMP", precision = 9}, + expected = [[JSON_VALUE('{"a": 1}', '$.a' RETURNING TIMESTAMP(9) NULL ON EMPTY ERROR ON ERROR)]] + }, { + argument_1 = '{"a": 1}', + argument_2 = '$.a', + empty_behavior = {type = "NULL"}, + error_behavior = {type = "ERROR"}, + data_type = {type = "TIMESTAMP", precision = 0, withLocalTimeZone = true}, + expected = [[JSON_VALUE('{"a": 1}', '$.a' RETURNING TIMESTAMP(0) WITH LOCAL TIME ZONE ]] + .. "NULL ON EMPTY ERROR ON ERROR)" + }, { + argument_1 = '{"a": 1}', + argument_2 = '$.a', + empty_behavior = {type = "NULL"}, + error_behavior = {type = "ERROR"}, + data_type = {type = "TIMESTAMP", withLocalTimeZone = true}, + expected = [[JSON_VALUE('{"a": 1}', '$.a' RETURNING TIMESTAMP WITH LOCAL TIME ZONE ]] + .. "NULL ON EMPTY ERROR ON ERROR)" } } for _, parameter in ipairs(parameters) do diff --git a/src/exasol/vscl/queryrenderer/AbstractQueryAppender.lua b/src/exasol/vscl/queryrenderer/AbstractQueryAppender.lua index d9f40b1..e4ab12a 100644 --- a/src/exasol/vscl/queryrenderer/AbstractQueryAppender.lua +++ b/src/exasol/vscl/queryrenderer/AbstractQueryAppender.lua @@ -67,6 +67,12 @@ end ---@param data_type TimestampTypeDefinition function AbstractQueryAppender:_append_timestamp(data_type) + local precision = data_type.precision + if precision ~= nil then + self:_append("(") + self:_append(precision) + self:_append(")") + end if data_type.withLocalTimeZone then self:_append(" WITH LOCAL TIME ZONE") end diff --git a/src/exasol/vscl/types/type_definition.lua b/src/exasol/vscl/types/type_definition.lua index e71b06a..71b5a5f 100644 --- a/src/exasol/vscl/types/type_definition.lua +++ b/src/exasol/vscl/types/type_definition.lua @@ -15,7 +15,8 @@ M.CharacterTypeDefinition = {} ---@class TimestampTypeDefinition ---@field type "TIMESTAMP" ----@field withLocalTimeZone boolean +---@field precision integer? +---@field withLocalTimeZone boolean? M.TimestampTypeDefinition = {} ---@class GeometryTypeDefinition diff --git a/virtual-schema-common-lua-5.0.1-1.rockspec b/virtual-schema-common-lua-5.1.0-1.rockspec similarity index 99% rename from virtual-schema-common-lua-5.0.1-1.rockspec rename to virtual-schema-common-lua-5.1.0-1.rockspec index da61a83..5bcf39d 100644 --- a/virtual-schema-common-lua-5.0.1-1.rockspec +++ b/virtual-schema-common-lua-5.1.0-1.rockspec @@ -1,7 +1,7 @@ ---@diagnostic disable: lowercase-global rockspec_format = "3.0" -local tag = "5.0.1" +local tag = "5.1.0" package = "virtual-schema-common-lua" version = tag .. "-1" From 31c5c895722eef9cef022251f06cd99a9eff6951 Mon Sep 17 00:00:00 2001 From: redcatbear Date: Fri, 14 Aug 2026 14:10:53 +0200 Subject: [PATCH 2/2] #96: Update Lua language server. --- tools/install-luals.sh | 6 +++--- tools/run-type-check.sh | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/tools/install-luals.sh b/tools/install-luals.sh index 850953f..d76aac4 100755 --- a/tools/install-luals.sh +++ b/tools/install-luals.sh @@ -7,15 +7,15 @@ set -o pipefail base_dir="$( cd "$(dirname "$0")/.." >/dev/null 2>&1 ; pwd -P )" readonly base_dir -readonly language_server_version="3.10.5" +readonly language_server_version="3.19.1" # Check if os is mac or linux if [[ "$OSTYPE" == "darwin"* ]]; then architecture="darwin-x64" - language_server_version_sha256="a1986521f9a2e1998d37341ece89cabcb9a7d8c8d4a837123f424519366452a7" + language_server_version_sha256="eb373c159cbe556711d7cd316315de2dce969bfd54b31edb7eb9cab2937f2cca" elif [[ "$OSTYPE" == "linux-gnu"* ]]; then architecture="linux-x64" - language_server_version_sha256="7ed04e25d83d89217f8acd4e0ff657e4d5a66550322555721bf5195f223b7f96" + language_server_version_sha256="e9235d2d72ef55bc41cf8c99cda2ed64777682024b4bb81f5dea425060c5cbb8" else echo "Unsupported OS: $OSTYPE" exit 1 diff --git a/tools/run-type-check.sh b/tools/run-type-check.sh index 106e121..3fc7823 100755 --- a/tools/run-type-check.sh +++ b/tools/run-type-check.sh @@ -17,6 +17,11 @@ readonly type_check_result_json="$type_check_log_dir"/check.json "$base_dir/tools/install-luals.sh" +# LuaLS 3.19.1 does not create check.json when there are no diagnostics. +# Initialize the report so that the processing below can handle both cases. +mkdir -p "$type_check_log_dir" +printf '{}\n' > "$type_check_result_json" + echo "Running type check using $language_server_executable..." if ! "$language_server_executable" --check="$base_dir" --loglevel=trace --logpath="$type_check_log_dir" --checklevel="$type_check_level" ; then echo "Type check failed with return code $?"