Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

|=.? returns empty #2140

Closed
vintnes opened this issue Jun 11, 2020 · 11 comments · Fixed by #2750
Closed

|=.? returns empty #2140

vintnes opened this issue Jun 11, 2020 · 11 comments · Fixed by #2750
Labels

Comments

@vintnes
Copy link

vintnes commented Jun 11, 2020

Thank you for this life-changing language. I regret that my first correspondence is a complaint.

Try-catch returns empty following filter assignment.

Input

 jq -nc '{foo: "bar"} | .foo |= .?'  

Expected output

 {"foo":"bar"}

Actual output

{}

Furthermore, // is unable to catch the result:

$ jq -nc '{foo: "bar"} | .foo |= ( .? // "baz" )'
{}

Environment (please complete the following information):

  • Debian Stretch, jq 1.5
  • jqplay, jq 1.6

This is an extremely minimized example; I'm actually trying to process incoming strings which may or may not contain valid JSON, as in .value |= (fromjson? // .). I catch some undocumented error formatting when I try this:

$ jq -nc '{value: "[]"} | .value |= try fromjson catch .'
{"value":{"__jq":0}}

Thanks again for your efforts.

@wtlangford
Copy link
Contributor

That's a known bug that I think is fixed on master. If you're able, can you try building master from source?

@vintnes
Copy link
Author

vintnes commented Jun 11, 2020

@wtlangford Would you happen to have a link to the existing issue? I will close this as duplicate.

@wtlangford
Copy link
Contributor

Sure, it's issue #2011

@vintnes vintnes closed this as completed Jun 11, 2020
@itchyny
Copy link
Contributor

itchyny commented Jun 12, 2020

This is a different issue and reproducible with the master version.

 % jq --version
jq-master-a17dd32
 % jq -nc '{foo: "bar"} | .foo |= .?'
{}
 % jq -nc '{foo: "bar"} | .foo |= ( .? // "baz" )'
{}
 % jq -nc '{value: "[]"} | .value |= try fromjson catch .' # yes this works
{"value":[]}

@vintnes
Copy link
Author

vintnes commented Jun 12, 2020

Tentatively reopening on the basis that ? // has documented behavior different from that of try catch, specifically that catch . should return an error string, while // . should return the most recently piped input.

Also thought I should include my 1.5 workaround in case some poor soul with a conservative distro finds this thread because of the fromjson references:

.value as $string | .value = ( $string | fromjson? // . )

@vintnes vintnes reopened this Jun 12, 2020
@itchyny
Copy link
Contributor

itchyny commented Jun 12, 2020

I think FORK_OPT (and DESTRUCTURE_ALT) should only react to the errors inside their scopes while FORK can be backtracked from anywhere (even from after emitting a value). Can we pop the fork stack when it gets out of the try catch scope or distinguish backtracks and errors completely by creating another forkopt stack?

@leonid-s-usov
Copy link
Contributor

Alright, now we're talking! @itchyny please see this branch https://github.com/leonid-s-usov/jq/tree/bt_signalling
It has a complete revision of the backtrack signaling and I actually have pushed some changes to DESTRUCTURE_ALT based on the new signalling here wtlangford#2

I'm finishing some scope of work at my day job and then have the plan to get back on track with the changes mentioned. It's a bit ambitious, cause it involves the dlopen initiative of @nicowilliams , but we'll pull it out this summer, I believe

@itchyny
Copy link
Contributor

itchyny commented Jun 12, 2020

Okay, thanks for explanation. I finally understand what #1859 (comment) means and it fixes various issues around this subject.

@itchyny
Copy link
Contributor

itchyny commented Jun 12, 2020

Regarding DESTRUCTURE_ALT, I noticed that the the subsequent expressions is not clear in the document if the subsequent expression returns an error, the alternative operator will attempt to try the next binding.

 % jq -n '[0] as [$x] ?// $x | $x[0]' # ok
0
 % jq -n '[0] as [$x] ?// $x | $x | .[0]' # still the subsequent expression?
0
 % jq -n '([0] as [$x] ?// $x | $x) | .[0]' # is this ok? or should be an error?
0
 % jq -n '([0] as [$x] ?// $x | $x) | type | error' # should be number?
jq: error (at <unknown>): array
 % jq -n '([2] as $x ?// [$x] | $x) * ([3] as $x ?// [$x] | $x)' # useful? or should be an error?
6

@Kopfbremse
Copy link

After encountering this error, I asked about it on Stackoverflow. One reply contained a link to this issue
Update operator yields empty object in JQ

@vintnes
Copy link
Author

vintnes commented Nov 13, 2021

~ jq --version
jq-1.6-145-ga9f97e9-dirty

~ jq -nc '{foo: "bar"} | .foo |= .?'
null

@itchyny itchyny added the bug label Jun 3, 2023
nicowilliams added a commit to nicowilliams/jq that referenced this issue Jul 24, 2023
Close jqlang#1885, jqlang#2140, jqlang#2011, jqlang#2220, jqlang#2485, 2073

Rename the FORK_OPT opcode to TRY_BEGIN, add a TRY_END opcode, and wrap
errors when raising through a TRY_END so that they will not be caught by
the matching TRY_BEGIN.

Now a `try exp catch handler` expression generates code like:

    TRY_BEGIN handler
    <exp>
    TRY_END
    JUMP past_handler
    handler: <handler>
    past_handler:
    ...

On backtrack through TRY_BEGIN it just backtracks.

If anything past the whole thing raises when <exp> produced a value,
then the TRY_END will catch the error, wrap it in another, and
backtrack.  The TRY_BEGIN will see a wrapped error and then it will
unwrap and re-raise the error.

If <exp> raises, then TRY_BEGIN will catch the error and jump to the
handler, but the TRY_BEGIN will not stack_save() in that case, so on
raise/backtrack the TRY_BEGIN will not execute again (nor will the
TRY_END).
emanuele6 pushed a commit that referenced this issue Jul 24, 2023
Close #1885, #2140, #2011, #2220, #2485, #2073

Rename the FORK_OPT opcode to TRY_BEGIN, add a TRY_END opcode, and wrap
errors when raising through a TRY_END so that they will not be caught by
the matching TRY_BEGIN.

Now a `try exp catch handler` expression generates code like:

    TRY_BEGIN handler
    <exp>
    TRY_END
    JUMP past_handler
    handler: <handler>
    past_handler:
    ...

On backtrack through TRY_BEGIN it just backtracks.

If anything past the whole thing raises when <exp> produced a value,
then the TRY_END will catch the error, wrap it in another, and
backtrack.  The TRY_BEGIN will see a wrapped error and then it will
unwrap and re-raise the error.

If <exp> raises, then TRY_BEGIN will catch the error and jump to the
handler, but the TRY_BEGIN will not stack_save() in that case, so on
raise/backtrack the TRY_BEGIN will not execute again (nor will the
TRY_END).
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants