yq icon indicating copy to clipboard operation
yq copied to clipboard

Assignment for nested keys is broken for keys following referenced parent key

Open EmilyGraceSeville7cf opened this issue 1 year ago • 3 comments

Describe the bug = assignment is broken for nested keys following referenced one-level up property related to them.

Version of yq: 4.34.2 Operating system: Linux 22.04 Ubuntu Installed via: sudo apt install yq

Input Yaml Concise yaml document(s) (as simple as possible to show the bug, please keep it to 10 lines or less) data1.yml:

    {
        "first": {
            "type": "string"
        },
        "second": {
            "type": "string"
        },
        "referenced": {
            "type": "string"
        },
        "third": {
            "type": "string"
        },
        "fourth": {
            "type": "string"
        }
    }

Command The command you ran:

yq -P -o json '.[].type = .referenced'

Actual behavior

{
  "first": {
    "type": {
      "type": "string"
    }
  },
  "second": {
    "type": {
      "type": "string"
    }
  },
  "referenced": {
    "type": {
      "type": "string"
    }
  },
  "third": {
    "type": {
      "type": {
        "type": "string"
      }
    }
  },
  "fourth": {
    "type": {
      "type": {
        "type": "string"
      }
    }
  }
}

Expected behavior

{
  "first": {
    "type": {
      "type": "string"
    }
  },
  "second": {
    "type": {
      "type": "string"
    }
  },
  "referenced": {
    "type": {
      "type": "string"
    }
  },
  "third": {
    "type": {
      "type": "string"
    }
  },
  "fourth": {
    "type": {
      "type": "string"
    }
  }
}

type keys after referenced key are assigned incorrectly. I've compared output with jq and it works as expected.

EmilyGraceSeville7cf avatar Aug 01 '23 08:08 EmilyGraceSeville7cf

This is because yq works a little differently than jq - for better or worse.

You can get the right result by using a variable to force it to first make a copy of .referenced

yq '.referenced as $i | .[].type = $i' file.yaml

mikefarah avatar Oct 02 '23 04:10 mikefarah

Is it documented somewhere? Can u explain in details why it's the case? From the developer perspective.

EmilyGraceSeville7cf avatar Oct 02 '23 04:10 EmilyGraceSeville7cf

Not documented anywhere atm. Basically what yq is doing under the hood is looping through each LHS, evaluating the RHS (.referenced) and assigning the result.

The issue is, after .referenced is updated itself, it becomes {type: {type: string}} - from then on that's the value that's used when updating "third" and "fourth" (which is why they get an extra type.

By using the variable, we can evaluate the RHS first and cache it for use in the assignments.

It wouldn't be hard to change the way assignments work - but that could have all sorts of bad side effects for everyone already using yq and relying on the existing behavior.

mikefarah avatar Oct 02 '23 04:10 mikefarah