Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(js-connectors): improve debug experience on conversion's panics #4175

Merged
merged 5 commits into from
Aug 31, 2023
Merged
Changes from 3 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
111 changes: 61 additions & 50 deletions query-engine/js-connectors/src/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,11 @@ pub struct Query {
/// This is used for most data types, except those that require connector-specific handling, e.g., `ColumnType::Boolean`.
/// In the future, after https://github.com/prisma/team-orm/issues/257, every connector-specific handling should be moved
/// out of Rust and into TypeScript.
fn js_value_to_quaint(json_value: serde_json::Value, column_type: ColumnType) -> QuaintValue<'static> {
fn js_value_to_quaint(
json_value: serde_json::Value,
column_type: ColumnType,
column_name: &str,
) -> QuaintValue<'static> {
// Note for the future: it may be worth revisiting how much bloat so many panics with different static
// strings add to the compiled artefact, and in case we should come up with a restricted set of panic
// messages, or even find a way of removing them altogether.
Expand All @@ -170,76 +174,79 @@ fn js_value_to_quaint(json_value: serde_json::Value, column_type: ColumnType) ->
QuaintValue::int32(n.as_i64().expect("number must be an i32") as i32)
}
serde_json::Value::Null => QuaintValue::Int32(None),
mismatch => panic!("Expected an i32 number, found {:?}", mismatch),
mismatch => panic!("Expected an i32 number in column {}, found {}", column_name, mismatch),
},
ColumnType::Int64 => match json_value {
serde_json::Value::String(s) => {
let n = s.parse::<i64>().expect("string-encoded number must be an i64");
QuaintValue::int64(n)
}
serde_json::Value::Null => QuaintValue::Int64(None),
mismatch => panic!("Expected a string, found {:?}", mismatch),
mismatch => panic!("Expected a string in column {}, found {}", column_name, mismatch),
},
ColumnType::Float => match json_value {
// n.as_f32() is not implemented, so we need to downcast from f64 instead.
// We assume that the JSON value is a valid f32 number, but we check for overflows anyway.
serde_json::Value::Number(n) => QuaintValue::float(f64_to_f32(n.as_f64().expect("number must be a f64"))),
serde_json::Value::Null => QuaintValue::Float(None),
mismatch => panic!("Expected a f32 number, found {:?}", mismatch),
mismatch => panic!("Expected a f32 number in column {}, found {}", column_name, mismatch),
},
ColumnType::Double => match json_value {
serde_json::Value::Number(n) => QuaintValue::double(n.as_f64().expect("number must be a f64")),
serde_json::Value::Null => QuaintValue::Double(None),
mismatch => panic!("Expected a f64 number, found {:?}", mismatch),
mismatch => panic!("Expected a f64 number in column {}, found {}", column_name, mismatch),
},
ColumnType::Numeric => match json_value {
serde_json::Value::String(s) => {
let decimal = BigDecimal::from_str(&s).expect("invalid numeric value");
QuaintValue::numeric(decimal)
}
serde_json::Value::Null => QuaintValue::Numeric(None),
mismatch => panic!("Expected a string-encoded number, found {:?}", mismatch),
mismatch => panic!(
"Expected a string-encoded number in column {}, found {}",
column_name, mismatch
),
},
ColumnType::Boolean => match json_value {
serde_json::Value::Bool(b) => QuaintValue::boolean(b),
serde_json::Value::Null => QuaintValue::Boolean(None),
mismatch => panic!("Expected a boolean, found {:?}", mismatch),
mismatch => panic!("Expected a boolean in column {}, found {}", column_name, mismatch),
},
ColumnType::Char => match json_value {
serde_json::Value::String(s) => QuaintValue::Char(s.chars().next()),
serde_json::Value::Null => QuaintValue::Char(None),
mismatch => panic!("Expected a string, found {:?}", mismatch),
mismatch => panic!("Expected a string in column {}, found {}", column_name, mismatch),
},
ColumnType::Text => match json_value {
serde_json::Value::String(s) => QuaintValue::text(s),
serde_json::Value::Null => QuaintValue::Text(None),
mismatch => panic!("Expected a string, found {:?}", mismatch),
mismatch => panic!("Expected a string in column {}, found {}", column_name, mismatch),
},
ColumnType::Date => match json_value {
serde_json::Value::String(s) => {
let date = NaiveDate::parse_from_str(&s, "%Y-%m-%d").expect("Expected a date string");
QuaintValue::date(date)
}
serde_json::Value::Null => QuaintValue::Date(None),
mismatch => panic!("Expected a string, found {:?}", mismatch),
mismatch => panic!("Expected a string in column {}, found {}", column_name, mismatch),
},
ColumnType::Time => match json_value {
serde_json::Value::String(s) => {
let time = NaiveTime::parse_from_str(&s, "%H:%M:%S").expect("Expected a time string");
QuaintValue::time(time)
}
serde_json::Value::Null => QuaintValue::Time(None),
mismatch => panic!("Expected a string, found {:?}", mismatch),
mismatch => panic!("Expected a string in column {}, found {}", column_name, mismatch),
},
ColumnType::DateTime => match json_value {
serde_json::Value::String(s) => {
let datetime = chrono::NaiveDateTime::parse_from_str(&s, "%Y-%m-%d %H:%M:%S")
.unwrap_or_else(|_| panic!("Expected a datetime string, found {:?}", &s));
.unwrap_or_else(|_| panic!("Expected a datetime string in column {}, found {}", column_name, &s));
let datetime: DateTime<Utc> = DateTime::from_utc(datetime, Utc);
QuaintValue::datetime(datetime)
}
serde_json::Value::Null => QuaintValue::DateTime(None),
mismatch => panic!("Expected a string, found {:?}", mismatch),
mismatch => panic!("Expected a string in column {}, found {}", column_name, mismatch),
},
ColumnType::Json => match json_value {
serde_json::Value::Null => QuaintValue::Json(None),
Expand All @@ -248,15 +255,15 @@ fn js_value_to_quaint(json_value: serde_json::Value, column_type: ColumnType) ->
ColumnType::Enum => match json_value {
serde_json::Value::String(s) => QuaintValue::enum_variant(s),
serde_json::Value::Null => QuaintValue::Enum(None),
mismatch => panic!("Expected a string, found {:?}", mismatch),
mismatch => panic!("Expected a string in column {}, found {}", column_name, mismatch),
},
ColumnType::Bytes => match json_value {
serde_json::Value::String(s) => QuaintValue::Bytes(Some(s.into_bytes().into())),
serde_json::Value::Null => QuaintValue::Bytes(None),
mismatch => panic!("Expected a string, found {:?}", mismatch),
mismatch => panic!("Expected a string in column {}, found {}", column_name, mismatch),
},
unimplemented => {
todo!("support column type: Column: {:?}", unimplemented)
todo!("support column type {:?} in column {}", unimplemented, column_name)
}
}
}
Expand All @@ -270,16 +277,20 @@ impl From<JSResultSet> for QuaintResultSet {
last_insert_id,
} = js_result_set;

let quaint_rows = rows
.into_iter()
.map(move |row| {
column_types
.iter()
.zip(row)
.map(|(column_type, value)| js_value_to_quaint(value, *column_type))
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
let mut quaint_rows = Vec::with_capacity(rows.len());

for row in rows {
let mut quaint_row = Vec::with_capacity(column_types.len());

for (i, row) in row.into_iter().enumerate() {
let column_type = column_types[i];
let column_name = column_names[i].as_str();

quaint_row.push(js_value_to_quaint(row, column_type, column_name));
}

quaint_rows.push(quaint_row);
}

let last_insert_id = last_insert_id.and_then(|id| id.parse::<u64>().ok());
let mut quaint_result_set = QuaintResultSet::new(column_names, quaint_rows);
Expand Down Expand Up @@ -404,7 +415,7 @@ mod proxy_test {
#[track_caller]
fn test_null(quaint_none: QuaintValue, column_type: ColumnType) {
let json_value = serde_json::Value::Null;
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");
assert_eq!(quaint_value, quaint_none);
}

Expand All @@ -418,19 +429,19 @@ mod proxy_test {
// 0
let n: i32 = 0;
let json_value = serde_json::Value::Number(serde_json::Number::from(n));
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");
assert_eq!(quaint_value, QuaintValue::Int32(Some(n)));

// max
let n: i32 = i32::MAX;
let json_value = serde_json::Value::Number(serde_json::Number::from(n));
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");
assert_eq!(quaint_value, QuaintValue::Int32(Some(n)));

// min
let n: i32 = i32::MIN;
let json_value = serde_json::Value::Number(serde_json::Number::from(n));
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");
assert_eq!(quaint_value, QuaintValue::Int32(Some(n)));
}

Expand All @@ -444,19 +455,19 @@ mod proxy_test {
// 0
let n: i64 = 0;
let json_value = serde_json::Value::String(n.to_string());
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");
assert_eq!(quaint_value, QuaintValue::Int64(Some(n)));

// max
let n: i64 = i64::MAX;
let json_value = serde_json::Value::String(n.to_string());
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");
assert_eq!(quaint_value, QuaintValue::Int64(Some(n)));

// min
let n: i64 = i64::MIN;
let json_value = serde_json::Value::String(n.to_string());
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");
assert_eq!(quaint_value, QuaintValue::Int64(Some(n)));
}

Expand All @@ -470,19 +481,19 @@ mod proxy_test {
// 0
let n: f32 = 0.0;
let json_value = serde_json::Value::Number(serde_json::Number::from_f64(n.into()).unwrap());
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");
assert_eq!(quaint_value, QuaintValue::Float(Some(n)));

// max
let n: f32 = f32::MAX;
let json_value = serde_json::Value::Number(serde_json::Number::from_f64(n.into()).unwrap());
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");
assert_eq!(quaint_value, QuaintValue::Float(Some(n)));

// min
let n: f32 = f32::MIN;
let json_value = serde_json::Value::Number(serde_json::Number::from_f64(n.into()).unwrap());
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");
assert_eq!(quaint_value, QuaintValue::Float(Some(n)));
}

Expand All @@ -496,19 +507,19 @@ mod proxy_test {
// 0
let n: f64 = 0.0;
let json_value = serde_json::Value::Number(serde_json::Number::from_f64(n).unwrap());
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");
assert_eq!(quaint_value, QuaintValue::Double(Some(n)));

// max
let n: f64 = f64::MAX;
let json_value = serde_json::Value::Number(serde_json::Number::from_f64(n).unwrap());
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");
assert_eq!(quaint_value, QuaintValue::Double(Some(n)));

// min
let n: f64 = f64::MIN;
let json_value = serde_json::Value::Number(serde_json::Number::from_f64(n).unwrap());
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");
assert_eq!(quaint_value, QuaintValue::Double(Some(n)));
}

Expand All @@ -523,14 +534,14 @@ mod proxy_test {
let decimal = BigDecimal::new(BigInt::parse_bytes(b"123499", 10).unwrap(), 2);

let json_value = serde_json::Value::String(n_as_string.into());
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");
assert_eq!(quaint_value, QuaintValue::Numeric(Some(decimal)));

let n_as_string = "1234.999999";
let decimal = BigDecimal::new(BigInt::parse_bytes(b"1234999999", 10).unwrap(), 6);

let json_value = serde_json::Value::String(n_as_string.into());
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");
assert_eq!(quaint_value, QuaintValue::Numeric(Some(decimal)));
}

Expand All @@ -544,13 +555,13 @@ mod proxy_test {
// true
let bool_val = true;
let json_value = serde_json::Value::Bool(bool_val);
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");
assert_eq!(quaint_value, QuaintValue::Boolean(Some(bool_val)));

// false
let bool_val = false;
let json_value = serde_json::Value::Bool(bool_val);
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");
assert_eq!(quaint_value, QuaintValue::Boolean(Some(bool_val)));
}

Expand All @@ -563,7 +574,7 @@ mod proxy_test {

let c = 'c';
let json_value = serde_json::Value::String(c.to_string());
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");
assert_eq!(quaint_value, QuaintValue::Char(Some(c)));
}

Expand All @@ -576,7 +587,7 @@ mod proxy_test {

let s = "some text";
let json_value = serde_json::Value::String(s.to_string());
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");
assert_eq!(quaint_value, QuaintValue::Text(Some(s.into())));
}

Expand All @@ -589,7 +600,7 @@ mod proxy_test {

let s = "2023-01-01";
let json_value = serde_json::Value::String(s.to_string());
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");

let date = NaiveDate::from_ymd_opt(2023, 1, 1).unwrap();
assert_eq!(quaint_value, QuaintValue::Date(Some(date)));
Expand All @@ -604,7 +615,7 @@ mod proxy_test {

let s = "23:59:59";
let json_value = serde_json::Value::String(s.to_string());
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");

let time: NaiveTime = NaiveTime::from_hms_opt(23, 59, 59).unwrap();
assert_eq!(quaint_value, QuaintValue::Time(Some(time)));
Expand All @@ -619,7 +630,7 @@ mod proxy_test {

let s = "2023-01-01 23:59:59";
let json_value = serde_json::Value::String(s.to_string());
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");

let datetime = NaiveDate::from_ymd_opt(2023, 1, 1)
.unwrap()
Expand All @@ -646,7 +657,7 @@ mod proxy_test {
]
});
let json_value = json.clone();
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");
assert_eq!(quaint_value, QuaintValue::Json(Some(json.clone())));
}

Expand All @@ -659,7 +670,7 @@ mod proxy_test {

let s = "some enum variant";
let json_value = serde_json::Value::String(s.to_string());
let quaint_value = js_value_to_quaint(json_value, column_type);
let quaint_value = js_value_to_quaint(json_value, column_type, "column_name");
assert_eq!(quaint_value, QuaintValue::Enum(Some(s.into())));
}
}
Loading