Skip to content

now() in INSERT/UPSERT VALUES stores literal string "now()" instead of evaluating — TIMESTAMP columns end up as epoch 0 #33

Description

@hollanf

Summary

now() in the VALUES (...) list of INSERT / UPSERT is not evaluated. It is stored as the literal string "now()", which then surfaces as 1970-01-01T00:00:00.000000Z (epoch 0) when the target column is TIMESTAMP in a TYPE DOCUMENT STRICT collection. No error is raised.

Environment

  • NodeDB v0.0.2, nodedb-0.0.2-linux-arm64 release binary
  • HTTP endpoint http://localhost:6480/query

Reproduction

curl -s -X POST http://localhost:6480/query -H "Content-Type: application/json" \
  -d '{"sql":"CREATE COLLECTION upsert_now_test TYPE DOCUMENT STRICT (id STRING PRIMARY KEY, t TIMESTAMP)"}'

curl -s -X POST http://localhost:6480/query -H "Content-Type: application/json" \
  -d "{\"sql\":\"UPSERT INTO upsert_now_test (id, t) VALUES ('t1', now())\"}"

curl -s -X POST http://localhost:6480/query -H "Content-Type: application/json" \
  -d "{\"sql\":\"INSERT INTO upsert_now_test (id, t) VALUES ('t2', now())\"}"

curl -s -X POST http://localhost:6480/query -H "Content-Type: application/json" \
  -d '{"sql":"SELECT id, t FROM upsert_now_test"}'

Expected

t is the wall-clock time at DML evaluation.

Actual

[
  {"data": {"id": "t1", "t": "1970-01-01T00:00:00.000000Z"}},
  {"data": {"id": "t2", "t": "1970-01-01T00:00:00.000000Z"}}
]

Both the UPSERT and INSERT paths hit this. The statement is tagged as success (tag: "UPSERT" / tag: "INSERT"); no error raised.

Source pointers

  • nodedb-sql/src/planner/dml.rs:474-475 — expr_to_sql_value() falls through unknown function calls to Ok(SqlValue::String(format!("{expr}"))). The inline comment reads: "Other functions like now() — store as string for runtime eval." The runtime eval path doesn't exist on the DML write side.
  • nodedb/src/control/planner/sql_plan_convert/dml.rs:37-45 — the DOCUMENT STRICT write path does not invoke any scalar-function evaluator on the incoming values before msgpack encoding.
  • nodedb/src/control/planner/.../defaults.rs:21 — evaluate_default_expr() does handle NOW() correctly, but is only called for schema-defined DEFAULT values, not for user-supplied VALUES expressions.

Workaround

Compute ISO timestamp application-side and pass as a string literal:

UPSERT INTO foo (id, t) VALUES ('x', '2026-04-16T00:40:12.209Z');

Test coverage gap

No tests in the repo cover INSERT ... VALUES (col, now()) asserting a non-zero timestamp — only DEFAULT now() schema paths are exercised.

Activity

  1. farhan-syah commented on Apr 15, 2026

    @farhan-syah
    Member

    Fix in #35. Root cause was three independent code paths (planner SELECT projection, planner DML VALUES, and the pgwire UPSERT fast-path tokenizer) all failing to dispatch zero-arg scalar functions to the existing nodedb_query::functions::eval_function evaluator. Bare SELECT now() was also returning null for the same reason.

    Fix is a single shared const_fold::fold_constant() helper in nodedb-sql/src/planner/ that all three paths now route through, so future drift is impossible. Postgres semantics: now() snapshots once per statement.

    Verified across DOCUMENT STRICT, schemaless, KV, COLUMNAR, and bare SELECT — all return real timestamps now.

  2. added a commit that references this issue on Apr 15, 2026
  3. added
    type:bugA defect — broken, incorrect, or lost data
    sev:1-criticalData loss, corruption, security, or crash; no workaround
    engine:documentDocument engine (schemaless + strict)
    area:sqlParser, planner, SQL semantics
    on Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:sqlParser, planner, SQL semanticsengine:documentDocument engine (schemaless + strict)sev:1-criticalData loss, corruption, security, or crash; no workaroundtype:bugA defect — broken, incorrect, or lost data

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions