Skip to content
Closed
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ class UnivocityGenerator(
private val writerSettings = options.asWriterSettings
writerSettings.setHeaders(schema.fieldNames: _*)
private val gen = new CsvWriter(writer, writerSettings)
private var printHeader = options.headerFlag

// A `ValueConverter` is responsible for converting a value of an `InternalRow` to `String`.
// When the value is null, this converter should not be called.
Expand Down Expand Up @@ -72,15 +71,15 @@ class UnivocityGenerator(
values
}

def writeHeaders(): Unit = {
gen.writeHeaders()
}

/**
* Writes a single InternalRow to CSV using Univocity.
*/
def write(row: InternalRow): Unit = {
if (printHeader) {
gen.writeHeaders()
}
gen.writeRow(convertRow(row): _*)
printHeader = false
}

def writeToString(row: InternalRow): String = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,15 +171,21 @@ private[csv] class CsvOutputWriter(

private var univocityGenerator: Option[UnivocityGenerator] = None

override def write(row: InternalRow): Unit = {
val gen = univocityGenerator.getOrElse {
val charset = Charset.forName(params.charset)
val os = CodecStreams.createOutputStreamWriter(context, new Path(path), charset)
val newGen = new UnivocityGenerator(dataSchema, os, params)
univocityGenerator = Some(newGen)
newGen
}
private def getOrCreateGen(): UnivocityGenerator = univocityGenerator.getOrElse {
Comment thread
koertkuipers marked this conversation as resolved.
Outdated
val charset = Charset.forName(params.charset)
val os = CodecStreams.createOutputStreamWriter(context, new Path(path), charset)
val newGen = new UnivocityGenerator(dataSchema, os, params)
univocityGenerator = Some(newGen)
newGen
}

if (params.headerFlag) {
Comment thread
koertkuipers marked this conversation as resolved.
Outdated
val gen = getOrCreateGen()
gen.writeHeaders()
}

override def write(row: InternalRow): Unit = {
val gen = getOrCreateGen()
gen.write(row)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1987,6 +1987,21 @@ class CSVSuite extends QueryTest with SharedSQLContext with SQLTestUtils with Te
assert(errMsg2.contains("'lineSep' can contain only 1 character"))
}

test("SPARK-26208: write and read empty data to csv file with headers") {
withTempPath { path =>
val df1 = spark.range(10).repartition(2).filter(_ < 0).map(_.toString).toDF
// we have 2 partitions but they are both empty and will be filtered out upon writing
// thanks to SPARK-23271 one new empty partition will be inserted
df1.write.format("csv").option("header", true).save(path.getAbsolutePath)
val df2 = spark.read.format("csv").option("header", true).option("inferSchema", false)
.load(path.getAbsolutePath)
assert(df1.rdd.getNumPartitions == 2)
assert(df2.rdd.getNumPartitions == 1)
Comment thread
koertkuipers marked this conversation as resolved.
Outdated
assert(df1.schema === df2.schema)
checkAnswer(df1, df2)
}
}

test("do not produce empty files for empty partitions") {
withTempPath { dir =>
val path = dir.getCanonicalPath
Expand Down