Yaoxuan Wu created FLINK-41290:
----------------------------------

             Summary: PyFlink streaming: Python UDTF in a lateral join loses 
the row kind of the input (NULL-padded LEFT JOIN rows; every row in thread mode)
                 Key: FLINK-41290
                 URL: https://issues.apache.org/jira/browse/FLINK-41290
             Project: Flink
          Issue Type: Bug
          Components: API / Python
    Affects Versions: 2.2.1, 2.3.0, 2.0.1, 2.4.0
            Reporter: Yaoxuan Wu


{code:python}
from pyflink.table import EnvironmentSettings, TableEnvironment, DataTypes as D
from pyflink.table.udf import udtf
t_env = TableEnvironment.create(EnvironmentSettings.in_streaming_mode())
t_env.get_config().set('parallelism.default', '1')
t_env.create_temporary_view('t', t_env.from_elements([(1, 10), (1, 20), (2, 
30)],
    D.ROW([D.FIELD('k', D.INT()), D.FIELD('v', D.INT())])))

@udtf(result_types=[D.INT()])
def explode(a):
    for x in a or []:
        yield x,
t_env.create_temporary_function('explode', explode)

agg = "(SELECT k, CASE WHEN COUNT(*) > 1 THEN ARRAY[k] END AS a, COUNT(*) AS c 
FROM t GROUP BY k)"
t_env.execute_sql(f"SELECT k, c, x FROM {agg} LEFT JOIN LATERAL 
TABLE(explode(a)) AS u(x) ON TRUE").print()
# expected (as with LEFT JOIN UNNEST(a)): +I[1, 1, NULL], -U[1, 1, NULL], +U[1, 
2, 1], +I[2, 1, NULL]
# actual:   +I[1, 1, NULL], +I[1, 1, NULL], +U[1, 2, 1], +U[2, 1, NULL]
{code}
The NULL-padded rows of the LEFT lateral join get a stale row kind: the {{-U}} 
is emitted as {{+I}} (the final result keeps (1, 1, NULL) twice) and the {{+I}} 
of key 2 as {{+U}}. With {{python.execution-mode = thread}} the inner lateral 
join ({{..., LATERAL TABLE(explode(a))}} over {{SELECT k, ARRAY[k] AS a, 
COUNT(*) AS c FROM t GROUP BY k}}) is wrong too: every output row is {{+I}}, so 
the retracted (1, 1, 1) stays in the result.

Cause: {{PythonTableFunctionOperator.emitResult}} calls 
{{reuseJoinedRow.setRowKind(input.getRowKind())}} only on the branch that emits 
a UDTF result; the LEFT-join padding branch ({{joinType == FlinkJoinType.LEFT 
&& !hasJoined}}) emits {{reuseJoinedRow.replace(input, <NULL row>)}} without 
it, and {{JoinedRowData.replace}} does not copy the row kind, so the padded row 
carries whatever kind the reused row had last. 
{{EmbeddedPythonTableFunctionOperator.processElement}} (thread mode) never sets 
the row kind on either branch. The native correlate copies the input row kind 
to every output row.

Reproduces on 2.0.1, 2.2.1, 2.3.0 and master.




--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to