Skip to content
New issue

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

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

Already on GitHub? Sign in 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).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants