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.
Summary
now()in theVALUES (...)list ofINSERT/UPSERTis not evaluated. It is stored as the literal string"now()", which then surfaces as1970-01-01T00:00:00.000000Z(epoch 0) when the target column isTIMESTAMPin aTYPE DOCUMENT STRICTcollection. No error is raised.Environment
nodedb-0.0.2-linux-arm64release binaryhttp://localhost:6480/queryReproduction
Expected
tis 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
UPSERTandINSERTpaths 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 toOk(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 handleNOW()correctly, but is only called for schema-definedDEFAULTvalues, not for user-suppliedVALUESexpressions.Workaround
Compute ISO timestamp application-side and pass as a string literal:
Test coverage gap
No tests in the repo cover
INSERT ... VALUES (col, now())asserting a non-zero timestamp — onlyDEFAULT now()schema paths are exercised.