Skip to content

UPDATE with string literal generates broken painless script (silent null overwrite) #92

@fupelaqu

Description

@fupelaqu

Summary

UPDATE <table> SET <col> = '<string-literal>' is translated into an
ingest-pipeline painless script that reads the literal as a field
reference on the doc
rather than emitting a string literal. The
resulting script always assigns null to the target column, and
ignore_failure = true swallows the underlying error so the update
appears to succeed.

Repro

CREATE TABLE IF NOT EXISTS customers (
  id INT,
  name VARCHAR,
  country KEYWORD,
  PRIMARY KEY (id)
);

INSERT INTO customers (id, name, country) VALUES (1, 'Alice', 'France');

UPDATE customers SET country = 'USA' WHERE id = 1;
-- ✅ Updated 1 documents in customers.  (looks successful)

SELECT id, name, country FROM customers WHERE id = 1;
-- name = 'Alice', country = NULL   ← BUG: expected country = 'USA'

Evidence

The pipeline emitted by the SQL → ingest-pipeline translation for the
UPDATE above (from a CI log against ES 7.17.29 via the REST client):

SCRIPT(
  description = "country ANY SCRIPT AS (USA)",
  lang = "painless",
  source = "def param1 = ctx.USA; ctx.country = param1",
  ignore_failure = true
)

The source should be the equivalent of ctx.country = 'USA'. Instead
it reads ctx.USA — i.e. it dereferences a field named USA on the
document, which doesn't exist. param1 becomes null, then
ctx.country = null overwrites the existing value.

Impact

  • Silent data corruption. The UPDATE reports success
    (✅ Updated 1 documents in customers.) but the target column is
    set to null. There is no error surfaced to the caller because
    ignore_failure = true masks the painless undefined-field error.
  • Test data setup pitfall. Found while writing test setup that
    relied on UPDATE ... SET country = 'USA' WHERE id = 1 to pin a
    customer's country. The follow-up query found 0 USA rows because the
    field had been nulled, not updated.
  • Production risk. Any user code issuing string-literal UPDATEs is
    silently nulling the targeted column. Numeric and boolean literals
    may be unaffected (untested).

Expected behaviour

UPDATE <table> SET <col> = '<string-literal>' WHERE <pred> should
produce a painless script that assigns the literal as a string value
to ctx.<col> — equivalent to ctx.<col> = '<literal>'.

Workaround

Use DELETE FROM <table> WHERE <pred> followed by INSERT INTO ...
with the full row. INSERT correctly serialises string literals.

Notes for the fix

  • Verify whether the same bug affects other literal types (numeric,
    boolean, date, null).
  • Consider removing or narrowing ignore_failure = true on
    user-issued UPDATE pipelines so this class of script error stops
    being silent.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions