In 12.1, records would only be returned if there was dataI believe this is where the confusion begins. "There was data" is ambiguous, as I will explain.
The observed behavior (which was indeed the same in 12.1 and later versions, as Chris demonstrated already) was correctly documented since 12.1, but the documentation was clarified even further in later versions. For 19 we can read this (emphasis added):
JSON_table_on_error_clauseUse this clause to specify the value returned by the function when errors occur:
NULL ON ERROR .....
- If no match is found when the
row path expression is evaluated,
no rows are returned. - Sets the default error behavior for all
column expressions to NULL ON ERROR.
In your example, the row path expression is '$.data[ * ].audits[ * ]' and this is found in all rows. So far, "there is data". Now, if you are paying very close attention, you will observe that in your second row the nested object with key "audits" is found, but its value is not an array, it is the special value
null. So it seems that there should still be no match. In this case "there is data" only because Oracle uses implicit array wrapping and unwrapping.
On the other hand, the column-level path expressions can't be matched in your second row. That error is handled by returning NULL instead.
You are thinking about "there is data" globally, combining the row-level expression, given as argument to JSON_TABLE, and the column path expressions given in the COLUMNS clause. But that is not how JSON_TABLE works.
For a quick illustration, compare the two examples below, which show exactly how the two different rules are applied. Running this on 12.2.0.1 but I am sure the behavior has been unchanged from 12.1 to 23 or whatever the current version is. The behavior you think you remember from 12.1 is what you see in the second example; but notice that in that example, the row path expression can't be matched. In the first example only the column path expression can't be matched. The row path expression can, which is why we do get a row (with NULL in the column because there is no match for the column path expression).
set null NULL
column val format a10
select val
from json_table('{values:null}', '$' columns (val path '$.data'));
VAL
----------
NULL
select val
from json_table('{values:null}', '$.data' columns (val path '$'));
no rows selected