allow converting from DateTime2 to Datetimen in 'tds73'
This pr tries to address the following problem:
When inserting NaiveDateTime into ms datetime columns with bulk_insert and tds73 is turned on by default, it would raise
Err` value: BulkInput("invalid data type, expecting Some(VarLenSized(VarLenContext { type: Datetimen, len: 8, collation: None })) but found DateTime2(Some(DateTime2 { date: Date(693604), time: Time { increments: 330110000000, scale: 7 } }))
cannot just disable tds73 feature as date/time might in the same table with datetime simultaneously and into_sql/to_sql is impl for date/time in without tds73 only.
normal inserting with execute does not get this issue as it calls DateTime2 to None when converting.
https://github.com/prisma/tiberius/blob/98943b21b5f7a5135d3ae4bd51acbf268bb99189/src/tds/codec/column_data.rs#L615-L619
Don't know whether it is a good solution. Or maybe any other impl can solve this issue is appreciated. Thank you so much~
I'm seeing a similar issue, but with a regular DATETIME column. If I were to make a feature request, I'd ask that the legacy DateTime conversion functionality be made available for manual use.
As a workaround, I've copied/modified the legacy PrimitiveDateTime handling from tds/time.rs (gated behind #[cfg(not(feature = "tds73"))]), spitting out a ColumnData::DateTime variant that can be fed into bulk insert's TokenRow.
It's a little extra work, but it prevents a mandatory conversion to DateTime2:
pub fn naive_dt_to_datetime1<'a>(dt: NaiveDateTime) -> ColumnData<'a> {
fn to_days(date: NaiveDate, start_year: i32) -> i64 {
(date - NaiveDate::from_ymd_opt(start_year, 1, 1).unwrap()).num_days()
}
fn to_sec_fragments(from: NaiveTime) -> i64 {
let nanos: i64 = (from - NaiveTime::from_hms_opt(0, 0, 0).unwrap())
.num_nanoseconds()
.unwrap();
nanos * 300 / (1e9 as i64)
}
let date = dt.date();
let time = dt.time();
let days = to_days(date, 1900) as i32;
let seconds_fragments = to_sec_fragments(time);
let dt = tiberius::time::DateTime::new(days, seconds_fragments as u32);
ColumnData::DateTime(Some(dt))
}
For some example usage:
/// Convert my domain object into a row usable by bulk insert
impl<'a> IntoRow<'a> for EventRow {
fn into_row(self) -> TokenRow<'a> {
let mut row = TokenRow::new();
row.push(naive_dt_to_datetime1(self.timestamp.naive_utc()));
row.push(self.event_id.into_sql());
row
}
}
/// Perform bulk insert
async fn do_inserts(db: &Client, events: &Vec<EventRow>) -> anyhow::Result<()>{
let mut bulk = db.bulk_insert("TABLE_NAME").await?;
for evt in events {
let row = evt.into_row();
bulk.send(row).await?;
}
_ = bulk.finalize().await?;
Ok(())
}