SEPURI-SAI-KRISHNA opened a new pull request, #13126:
URL: https://github.com/apache/seatunnel/pull/13126

   Yes, glad to contribute to Apache SeaTunnel.
   
   ### Purpose of this pull request
   
   Closes #13124.
   
   The Cassandra source read a NULL number or boolean as `0` or `false`, so 
jobs succeeded with values that were never in the table. Other NULL types 
failed the job. The sink failed on most NULL values and wrote a NULL `inet` as 
`127.0.0.1`. The issue has the full table per type.
   
   Three checks:
   
   - `TypeConvertUtil#buildSeaTunnelRow` checks `row.isNull(i)` before the type 
switch, so a NULL cell stays a NULL field.
   - `TypeConvertUtil#reconvertAndInject` calls `statement.setToNull(index)` 
for a null value, which is what the `text`, `time`, `date` and `map` setters 
already end up doing.
   - `CassandraSinkWriter` fails the write when a primary key column is NULL, 
naming the table and column.
   
   They belong in one PR. The sink only got by on numbers because the source 
never handed it a NULL, so fixing the source alone would turn a succeeding 
Cassandra to Cassandra job into a failing one.
   
   The key check is needed because of the second change. The server rejects a 
NULL key, and that rejection does not fail the job: `async_write = true` only 
logs it, and with `async_write = false` the job still finishes after losing the 
good rows in the batch. Without the check, a NULL `bigint` key would go from a 
loud NullPointerException to silent loss, which is what a NULL `text` key does 
on `dev` today. The writer reads the key columns from the driver's table 
metadata, using the keyspace and table the prepared statement reports, so 
`keyspace.table` names work. If that metadata is not available, the check does 
nothing and the behavior for a NULL key is the server rejection above.
   
   A NULL `list`, `set` or `map` now comes through as NULL rather than empty. 
Cassandra does not store an empty collection separately from NULL, and this 
keeps one rule for every type.
   
   A NULL value overwrites what the target row already has in that column, as 
it should for an upsert, and leaves a tombstone like any NULL written to 
Cassandra. The `text` setter already behaves this way.
   
   Not fixed here: a failed async write still does not fail the job in general. 
I will raise that separately too.
   
   Not in this PR: the sink still fails on a non-NULL `DECIMAL` or `VARINT` 
from other sources, because it casts them to `Double` and `String`. I will 
raise that separately.
   
   ### Does this PR introduce _any_ user-facing change?
   
   Yes, and it is in `incompatible-changes.md` (EN and ZH). Jobs that received 
`0`, `false` or an empty collection for a NULL cell now receive NULL, so a 
target column declared `NOT NULL` will now reject these rows. Jobs that failed 
on a NULL value now succeed, unless the NULL is in a primary key column, which 
now always fails the job. That includes `text`, `ascii`, `time` and `date` 
keys, where a job used to succeed while losing rows. Values that are not NULL, 
including a stored `0` or `false`, do not change.
   
   ### How was this patch tested?
   
   **Unit tests** (JDK 11 and 17): `TypeConvertUtilTest` covers NULL cells of 
every type through a real driver `DefaultRow`, null values of every type 
through `reconvertAndInject`, and a stored `0` and `false`. On `dev` exactly 
the two NULL tests fail. `CassandraSinkWriterTest` is new, since the writer had 
no test: a NULL key with async and sync writes and with a `keyspace.table` 
name, and a NULL in another column. It needed a package-private constructor 
that takes the session. Removing the key check fails exactly the three NULL key 
tests. `connector-cassandra` passes (33 run, 0 failures).
   
   **Real Cassandra 4.1.1 job on Zeta**, Cassandra to Cassandra, where source 
row `id = 1` has every other column NULL and row `id = 2` holds real zeros, 
`c_int = 7` and `false`. On `dev`:
   
   ```
   numeric columns: status=FINISHED
     id=1: c_bigint=0 c_boolean=false c_double=0.0 c_float=0.0 c_int=0 
c_smallint=0 c_tinyint=0
     id=2: c_bigint=0 c_boolean=false c_double=0.0 c_float=0.0 c_int=7 
c_smallint=0 c_tinyint=0
   all 21 column types: status=FAILED, java.lang.NullPointerException, 0 rows 
written
   ```
   
   With this PR:
   
   ```
   numeric columns: status=FINISHED
     id=1: all 7 columns NULL
     id=2: c_bigint=0 c_boolean=false c_double=0.0 c_float=0.0 c_int=7 
c_smallint=0 c_tinyint=0
   all 21 column types: status=FINISHED, 2 rows written
     id=1: all 21 columns NULL
     id=2: the same 7 values, the other 14 columns NULL
   ```
   
   **NULL keys on the same server, FakeSource to Cassandra**, one row with a 
NULL key and one good row:
   
   ```
                             dev                                  this PR
   bigint key, async         FAILED, NullPointerException         FAILED, 
Primary key column id of table key_big is NULL
   bigint key, sync          FAILED, NullPointerException         FAILED, same 
message
   text key, async           FINISHED, NULL key row lost          FAILED, 
Primary key column k of table key_text is NULL
   text key, sync            FINISHED, both rows lost             FAILED, same 
message
   ```
   
   With this PR a NULL clustering column and a `keyspace.table` name fail the 
same way, `async_write = false` writes the all-NULL row with every column NULL, 
a NULL overwrites an existing value, and an empty `text` and `blob` stay empty 
rather than becoming NULL.
   
   **`CassandraIT`** gains a source row with only NULL cells, and 
`testCassandra` now asserts that every column of that row is NULL in the sink. 
`testNullPrimaryKeyFailsTheJob` is new. I compiled it locally. It runs on Zeta, 
Flink and Spark in this branch's fork CI, not on my machine, and I have not 
checked the Flink translation myself.
   
   Also run: `spotless:check`.
   
   ### Check list
   
   * [x] If any new Jar binary package adding in your PR, please add License 
Notice according
     [New License 
Guide](https://github.com/apache/seatunnel/blob/dev/docs/en/developer/new-license.md)
 (no new jar)
   * [x] If necessary, please update the documentation to describe the new 
feature. https://github.com/apache/seatunnel/tree/dev/docs (no option changes; 
behavior change is in `incompatible-changes.md`)
   * [x] If necessary, please update `incompatible-changes.md` to describe the 
incompatibility caused by this PR.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to