Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/changes/changelog.md
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
11 changes: 11 additions & 0 deletions doc/changes/changes_5.1.0.md
Original file line number Diff line number Diff line change
@@ -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.
66 changes: 66 additions & 0 deletions doc/changesets/96-render-timestamp-precision.md
Comment thread
redcatbear marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -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 `(<precision>)` 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`.
40 changes: 40 additions & 0 deletions spec/exasol/vscl/queryrenderer/ScalarFunctionAppender_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions src/exasol/vscl/queryrenderer/AbstractQueryAppender.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion src/exasol/vscl/types/type_definition.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ M.CharacterTypeDefinition = {}

---@class TimestampTypeDefinition
---@field type "TIMESTAMP"
---@field withLocalTimeZone boolean
---@field precision integer?
---@field withLocalTimeZone boolean?
M.TimestampTypeDefinition = {}

---@class GeometryTypeDefinition
Expand Down
6 changes: 3 additions & 3 deletions tools/install-luals.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions tools/run-type-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 $?"
Expand Down
Original file line number Diff line number Diff line change
@@ -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"
Expand Down