Skip to content

Commit fd99a00

Browse files
authored
Prevent invalid Insert statements with empty batches (#528)
1 parent 0ae65e4 commit fd99a00

1 file changed

Lines changed: 65 additions & 0 deletions

File tree

core/src/sql/arrow_sql_gen/statement.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,6 +1080,20 @@ impl<'a> InsertBuilder<'a> {
10801080
query_builder: T,
10811081
on_conflict: Option<OnConflict>,
10821082
) -> Result<String> {
1083+
if self.record_batches.is_empty() {
1084+
return Result::Err(Error::FailedToCreateInsertStatement {
1085+
source: "no record batches have been provided".into(),
1086+
});
1087+
}
1088+
1089+
let num_rows: usize = self.record_batches.iter().map(|b| b.num_rows()).sum();
1090+
// return an error to avoid generating invalid SQL (i.e., INSERT without VALUES)
1091+
if num_rows == 0 {
1092+
return Result::Err(Error::FailedToCreateInsertStatement {
1093+
source: "no rows have been provided".into(),
1094+
});
1095+
}
1096+
10831097
let columns: Vec<Alias> = (self.record_batches[0])
10841098
.schema()
10851099
.fields()
@@ -1533,6 +1547,57 @@ mod tests {
15331547
);
15341548
}
15351549

1550+
#[test]
1551+
fn test_table_insertion_empty_batches() {
1552+
// no batches are provided
1553+
let result =
1554+
InsertBuilder::new(&TableReference::from("users"), &vec![]).build_postgres(None);
1555+
assert_eq!(
1556+
result.unwrap_err().to_string(),
1557+
"Failed to build insert statement: no record batches have been provided"
1558+
);
1559+
1560+
// batches are provided but are empty (would result in invalid SQL)
1561+
let schema = Schema::new(vec![
1562+
Field::new("id", DataType::Int32, false),
1563+
Field::new("name", DataType::Utf8, false),
1564+
]);
1565+
let empty_batch = RecordBatch::new_empty(Arc::new(schema.clone()));
1566+
let result = InsertBuilder::new(
1567+
&TableReference::from("users"),
1568+
&vec![empty_batch.clone(), empty_batch.clone()],
1569+
)
1570+
.build_postgres(None);
1571+
assert_eq!(
1572+
result.unwrap_err().to_string(),
1573+
"Failed to build insert statement: no rows have been provided"
1574+
);
1575+
1576+
// if only some batches are empty, the SQL is valid
1577+
let filled_batch = RecordBatch::try_new(
1578+
Arc::new(schema.clone()),
1579+
vec![
1580+
Arc::new(array::Int32Array::from(vec![1, 2, 3])),
1581+
Arc::new(array::StringArray::from(vec!["a", "b", "c"])),
1582+
],
1583+
)
1584+
.expect("Unable to build record batch");
1585+
let sql = InsertBuilder::new(
1586+
&TableReference::from("users"),
1587+
&vec![
1588+
empty_batch.clone(),
1589+
filled_batch.clone(),
1590+
empty_batch.clone(),
1591+
],
1592+
)
1593+
.build_postgres(None)
1594+
.unwrap();
1595+
assert_eq!(
1596+
sql,
1597+
"INSERT INTO \"users\" (\"id\", \"name\") VALUES (1, 'a'), (2, 'b'), (3, 'c')"
1598+
);
1599+
}
1600+
15361601
#[test]
15371602
fn test_table_insertion_with_schema() {
15381603
let schema1 = Schema::new(vec![

0 commit comments

Comments
 (0)