use jni::{
objects::{AutoLocal, JMethodID, JObject, JValueGen},
signature::{Primitive, ReturnType},
sys::jvalue,
JNIEnv,
};
use crate::{errors::Error, util, Connection};
use super::ResultSetMetaData;
pub struct ResultSet<'local> {
inner: AutoLocal<'local, JObject<'local>>,
get_meta_data: JMethodID,
get_row: JMethodID,
next: JMethodID,
was_null: JMethodID,
get_string: (JMethodID, JMethodID),
get_short: (JMethodID, JMethodID),
get_int: (JMethodID, JMethodID),
get_long: (JMethodID, JMethodID),
get_float: (JMethodID, JMethodID),
get_double: (JMethodID, JMethodID),
get_boolean: (JMethodID, JMethodID),
get_date: (JMethodID, JMethodID),
env: JNIEnv<'local>,
conn: &'local Connection<'local>,
}
impl<'local> ResultSet<'local> {
pub fn from_ref(
conn: &'local Connection<'local>,
statement: JObject<'local>,
) -> Result<Self, Error> {
let mut env = unsafe { conn.env() };
let statement = AutoLocal::new(statement, &env);
let class = AutoLocal::new(env.find_class("java/sql/ResultSet")?, &env);
let get_meta_data =
env.get_method_id(&class, "getMetaData", "()Ljava/sql/ResultSetMetaData;")?;
let get_row = env.get_method_id(&class, "getRow", "()I")?;
let next = env.get_method_id(&class, "next", "()Z")?;
let was_null = env.get_method_id(&class, "wasNull", "()Z")?;
let get_string = env.get_method_id(&class, "getString", "(I)Ljava/lang/String;")?;
let get_string_by_label = env.get_method_id(
&class,
"getString",
"(Ljava/lang/String;)Ljava/lang/String;",
)?;
let get_short = env.get_method_id(&class, "getShort", "(I)S")?;
let get_short_by_label = env.get_method_id(&class, "getShort", "(Ljava/lang/String;)S")?;
let get_int = env.get_method_id(&class, "getInt", "(I)I")?;
let get_int_by_label = env.get_method_id(&class, "getInt", "(Ljava/lang/String;)I")?;
let get_long = env.get_method_id(&class, "getLong", "(I)J")?;
let get_long_by_label = env.get_method_id(&class, "getLong", "(Ljava/lang/String;)J")?;
let get_float = env.get_method_id(&class, "getFloat", "(I)F")?;
let get_float_by_label = env.get_method_id(&class, "getFloat", "(Ljava/lang/String;)F")?;
let get_double = env.get_method_id(&class, "getDouble", "(I)D")?;
let get_double_by_label =
env.get_method_id(&class, "getDouble", "(Ljava/lang/String;)D")?;
let get_boolean = env.get_method_id(&class, "getBoolean", "(I)Z")?;
let get_boolean_by_label =
env.get_method_id(&class, "getBoolean", "(Ljava/lang/String;)Z")?;
let get_date = env.get_method_id(&class, "getDate", "(I)Ljava/sql/Date;")?;
let get_date_by_label =
env.get_method_id(&class, "getDate", "(Ljava/lang/String;)Ljava/sql/Date;")?;
Ok(ResultSet {
inner: statement,
get_meta_data,
get_row,
next,
was_null,
get_string: (get_string, get_string_by_label),
get_short: (get_short, get_short_by_label),
get_int: (get_int, get_int_by_label),
get_long: (get_long, get_long_by_label),
get_float: (get_float, get_float_by_label),
get_double: (get_double, get_double_by_label),
get_boolean: (get_boolean, get_boolean_by_label),
get_date: (get_date, get_date_by_label),
env,
conn,
})
}
pub fn get_meta_data(&self) -> Result<ResultSetMetaData<'local>, Error> {
let mut env = unsafe { self.conn.env() };
let result = unsafe {
env.call_method_unchecked(&self.inner, self.get_meta_data, ReturnType::Object, &[])?
};
if let JValueGen::Object(result) = result {
return Ok(ResultSetMetaData::from_ref(self.conn, result)?);
}
return Err(Error::ImpossibleError);
}
pub fn get_row(&self) -> Result<i32, Error> {
let mut env = unsafe { self.conn.env() };
return util::call::get_int(&mut env, &self.inner, &self.get_row);
}
pub fn next(&self) -> Result<bool, Error> {
let mut env = unsafe { self.conn.env() };
return util::call::get_bool(&mut env, &self.inner, &self.next);
}
fn was_null_inner<'a>(&self, env: &'a mut JNIEnv<'local>) -> Result<bool, Error> {
let value = util::call::get_bool(env, &self.inner, &self.was_null)?;
Ok(value)
}
pub fn was_null<'a>(&self) -> Result<bool, Error> {
let mut env: JNIEnv<'local> = unsafe { self.conn.env() };
let value = util::call::get_bool(&mut env, &self.inner, &self.was_null)?;
Ok(value)
}
pub fn get_string(&self, index: i32) -> Result<Option<String>, Error> {
let method = &self.get_string.0;
self.use_index(method, index, ReturnType::Object, |env, value| {
return util::cast::value_cast_string(env, value).map_err(Error::from);
})
}
pub fn get_string_by_label(&self, label: &str) -> Result<Option<String>, Error> {
let method = &self.get_string.1;
self.use_label(method, label, ReturnType::Object, |env, value| {
return util::cast::value_cast_string(env, value).map_err(Error::from);
})
}
pub fn get_short(&self, index: i32) -> Result<Option<i16>, Error> {
let method = &self.get_short.0;
let r_type = ReturnType::Primitive(Primitive::Short);
self.use_index(method, index, r_type, |_: &mut JNIEnv<'_>, value| {
return util::cast::value_cast_i16(value).map_err(Error::from);
})
}
pub fn get_short_by_label(&self, label: &str) -> Result<Option<i16>, Error> {
let method = &self.get_short.1;
let r_type = ReturnType::Primitive(Primitive::Short);
self.use_label(method, label, r_type, |_: &mut JNIEnv<'_>, value| {
return util::cast::value_cast_i16(value).map_err(Error::from);
})
}
pub fn get_int(&self, index: i32) -> Result<Option<i32>, Error> {
let method = &self.get_int.0;
let r_type = ReturnType::Primitive(Primitive::Int);
self.use_index(method, index, r_type, |_: &mut JNIEnv<'_>, value| {
return util::cast::value_cast_i32(value).map_err(Error::from);
})
}
pub fn get_int_by_label(&self, label: &str) -> Result<Option<i32>, Error> {
let method = &self.get_int.1;
let r_type = ReturnType::Primitive(Primitive::Int);
self.use_label(method, label, r_type, |_: &mut JNIEnv<'_>, value| {
return util::cast::value_cast_i32(value).map_err(Error::from);
})
}
pub fn get_long(&self, index: i32) -> Result<Option<i64>, Error> {
let method = &self.get_long.0;
let r_type = ReturnType::Primitive(Primitive::Long);
self.use_index(method, index, r_type, |_: &mut JNIEnv<'_>, value| {
return util::cast::value_cast_i64(value).map_err(Error::from);
})
}
pub fn get_long_by_label(&self, label: &str) -> Result<Option<i64>, Error> {
let method = &self.get_long.1;
let r_type = ReturnType::Primitive(Primitive::Long);
self.use_label(method, label, r_type, |_: &mut JNIEnv<'_>, value| {
return util::cast::value_cast_i64(value).map_err(Error::from);
})
}
pub fn get_float(&self, index: i32) -> Result<Option<f32>, Error> {
let method = &self.get_float.0;
let r_type = ReturnType::Primitive(Primitive::Float);
self.use_index(method, index, r_type, |_: &mut JNIEnv<'_>, value| {
return util::cast::value_cast_f32(value).map_err(Error::from);
})
}
pub fn get_float_by_label(&self, label: &str) -> Result<Option<f32>, Error> {
let method = &self.get_float.1;
let r_type = ReturnType::Primitive(Primitive::Float);
self.use_label(method, label, r_type, |_: &mut JNIEnv<'_>, value| {
return util::cast::value_cast_f32(value).map_err(Error::from);
})
}
pub fn get_double(&self, index: i32) -> Result<Option<f64>, Error> {
let method = &self.get_double.0;
let r_type = ReturnType::Primitive(Primitive::Double);
self.use_index(method, index, r_type, |_: &mut JNIEnv<'_>, value| {
return util::cast::value_cast_f64(value).map_err(Error::from);
})
}
pub fn get_double_by_label(&self, label: &str) -> Result<Option<f64>, Error> {
let method = &self.get_double.1;
let r_type = ReturnType::Primitive(Primitive::Double);
self.use_label(method, label, r_type, |_: &mut JNIEnv<'_>, value| {
return util::cast::value_cast_f64(value).map_err(Error::from);
})
}
pub fn get_boolean(&self, index: i32) -> Result<Option<bool>, Error> {
let method = &self.get_boolean.0;
let r_type = ReturnType::Primitive(Primitive::Boolean);
self.use_index(method, index, r_type, |_: &mut JNIEnv<'_>, value| {
return util::cast::value_cast_bool(value).map_err(Error::from);
})
}
pub fn get_boolean_by_label(&self, label: &str) -> Result<Option<bool>, Error> {
let method = &self.get_boolean.1;
let r_type = ReturnType::Primitive(Primitive::Boolean);
self.use_label(method, label, r_type, |_: &mut JNIEnv<'_>, value| {
return util::cast::value_cast_bool(value).map_err(Error::from);
})
}
pub fn get_timestamp_millis(&self, index: i32) -> Result<Option<i64>, Error> {
let method = &self.get_date.0;
let r_type = ReturnType::Object;
self.use_index(method, index, r_type, |env: &mut JNIEnv<'_>, value| {
return util::cast::value_cast_timestamp_millis(env, value).map_err(Error::from);
})
}
pub fn get_timestamp_millis_by_label(&self, label: &str) -> Result<Option<i64>, Error> {
let method = &self.get_date.1;
let r_type = ReturnType::Object;
self.use_label(method, label, r_type, |env: &mut JNIEnv<'_>, value| {
return util::cast::value_cast_timestamp_millis(env, value).map_err(Error::from);
})
}
#[cfg(feature = "chrono")]
pub fn get_local_time(
&self,
index: i32,
) -> Result<Option<chrono::DateTime<chrono::Local>>, Error> {
use chrono::{DateTime, Local, TimeZone};
let timestamp = self.get_timestamp_millis(index)?;
if let Some(timestamp) = timestamp {
let datetime: DateTime<Local> = Local
.timestamp_millis_opt(timestamp)
.earliest()
.ok_or(Error::ImpossibleError)?;
Ok(Some(datetime))
} else {
Ok(None)
}
}
#[cfg(feature = "chrono")]
pub fn get_local_time_by_label(
&self,
label: &str,
) -> Result<Option<chrono::DateTime<chrono::Local>>, Error> {
use chrono::{DateTime, Local, TimeZone};
let timestamp = self.get_timestamp_millis_by_label(label)?;
if let Some(timestamp) = timestamp {
let datetime: DateTime<Local> = Local
.timestamp_millis_opt(timestamp)
.earliest()
.ok_or(Error::ImpossibleError)?;
Ok(Some(datetime))
} else {
Ok(None)
}
}
#[cfg(feature = "chrono")]
pub fn get_utc_time(&self, index: i32) -> Result<Option<chrono::DateTime<chrono::Utc>>, Error> {
use chrono::{DateTime, TimeZone, Utc};
let timestamp = self.get_timestamp_millis(index)?;
if let Some(timestamp) = timestamp {
let datetime: DateTime<Utc> = Utc
.timestamp_millis_opt(timestamp)
.earliest()
.ok_or(Error::ImpossibleError)?;
Ok(Some(datetime))
} else {
Ok(None)
}
}
#[cfg(feature = "chrono")]
pub fn get_utc_time_by_label(
&self,
label: &str,
) -> Result<Option<chrono::DateTime<chrono::Utc>>, Error> {
use chrono::{DateTime, TimeZone, Utc};
let timestamp = self.get_timestamp_millis_by_label(label)?;
if let Some(timestamp) = timestamp {
let datetime: DateTime<Utc> = Utc
.timestamp_millis_opt(timestamp)
.earliest()
.ok_or(Error::ImpossibleError)?;
Ok(Some(datetime))
} else {
Ok(None)
}
}
fn use_index<'a, T, F>(
&self,
method: &JMethodID,
index: i32,
r_type: ReturnType,
f: F,
) -> Result<Option<T>, Error>
where
F: Fn(&mut JNIEnv<'local>, JValueGen<JObject<'local>>) -> Result<T, Error>,
{
let mut env: JNIEnv<'local> = unsafe { self.conn.env() };
let value = unsafe {
env.call_method_unchecked(&self.inner, method, r_type, &[jvalue { i: index }])
.map_err(Error::from)
}?;
if self.was_null_inner(&mut env)? {
return Ok(None);
}
let v = f(&mut env, value)?;
return Ok(Some(v));
}
fn use_label<'a, T, F>(
&self,
method: &JMethodID,
label: &str,
r_type: ReturnType,
f: F,
) -> Result<Option<T>, Error>
where
F: Fn(&mut JNIEnv<'local>, JValueGen<JObject<'local>>) -> Result<T, Error>,
{
let mut env: JNIEnv<'local> = unsafe { self.conn.env() };
let label: JObject<'_> = env.new_string(label)?.into();
let value = unsafe {
env.call_method_unchecked(
&self.inner,
method,
r_type,
&[JValueGen::Object(&label).as_jni()],
)
.map_err(Error::from)
}?;
env.delete_local_ref(label)?;
let was_null = self.was_null_inner(&mut env)?;
if was_null {
return Ok(None);
}
let v = f(&mut env, value)?;
return Ok(Some(v));
}
}
impl<'a> Drop for ResultSet<'a> {
fn drop(&mut self) {
let _ = util::auto_close(&mut self.env, &self.inner);
}
}