Given a table design with a non-nullable uuid column AND a nullable uuid column, how does one insert using python 3.7.9 with Pyspark 2.4.3 dataframe and postgresql-42.2.18.jar driver?
table_df = spark.read.format('jdbc) \
.option('driver', 'org.postgresql.Driver') \
.option('dbtable', 'example_table') \
.load()
table_df.printSchema()
root
|-- id: string (nullable = false)
|-- created: timestamp (nullable = true)
|-- modified: timestamp (nullable = true)
|-- example_uuid: string (nullable = true)
from pyspark.sql.functions import when, lit, col
from pyspark.sql.types import NullType, StringType
def replace(column, value):
return when (column == value, lit(None).cast(NullType())).otherwise(column.cast(StringType()))
example_df = tasklog_df.withColumn("example_uuid", replace(col("example_uuid"), "NULL"))
example_df.write.mode('append').format('jbdc') \
.option('driver', 'org.postgresql.Driver')\
.option('stringtype', 'unspecified') \
.save()
This results in Pyspark trying to insert
INSERT INTO example_table
("id",
"created",
"modified",
"example_uuid")
VALUES
('b49a90aa-a415-4aeb-a7ed-bfc42e43f5c7',
'2020-03-29 02:00:11.06534-07',
'2020-03-29 02:00:11.065361-07',
NULL)
Which then results in the infamous
ERROR: column "example_uuid" is of type uuid but expression is of type character
Hint: You will need to rewrite or cast the expression.
I have cast the data. Pyspark isn't generating the correct INSERT statement or the postgres driver is treating the word NULL as a character instead of a keyword. I need to use the .option('stringtype', 'unspecified') in order to not have Pyspark complain about the id column being a uuid.
The lit(None).cast(NullType()) seems to be doing nothing.
There's no entry in pyspark.sql.types for a uuid type.
Without the option('stringtype', 'unspecified') then Pyspark throws the error:
Caused by: org.postgresql.util.PSQLException: ERROR: column "id" is of type uuid but expression is of type character varying
Hint: You will need to rewrite or cast the expression.
The only approach left appears to be to split the dataframe into two dataframes, one with example_uuid field that contains NULL and one where the example_uuid field is a uuid. Then remove the example_uuid field from the dataframe with the NULLs so that it doesn't throw an error when saving to the table. This seems like a lot of wasted effort when Pyspark should just support uuid types. Advice or suggestions?