diff --git a/CHANGELOG.md b/CHANGELOG.md index 4858888..6efa14b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,26 @@ Also check the changes in springql-core: ## [Unreleased] +## [v0.16.0+4] - 2022-07-12 + +### Fixed + +- Undefined behaviors on the following structures: ([#62](https://github.com/SpringQL/SpringQL-client-c/pull/62)) + - SpringConfig + - SpringPipeline + - SpringSinkRow + - SpringSourceRow + - SpringSourceRowBuilder + +### Changed + +- `spring_push()` takes `SpringSourceRow *` instead of `const SpringSourceRow *`. It de-allocates `SpringSourceRow *`. ([#62](https://github.com/SpringQL/SpringQL-client-c/pull/62)) +- `spring_source_row_add_column_blob()` returns new builder instead of modifying the existing one. ([#62](https://github.com/SpringQL/SpringQL-client-c/pull/62)) + +### Removed + +- `spring_source_row_close()` ([#62](https://github.com/SpringQL/SpringQL-client-c/pull/62)) + ## [v0.16.0+3] - 2022-07-11 ### Fixed @@ -124,8 +144,9 @@ Depends on springql-core v0.7.1. [Semantic Versioning]: https://semver.org/ -[Unreleased]: https://github.com/SpringQL/SpringQL-client-c/compare/v0.16.0+3...HEAD +[Unreleased]: https://github.com/SpringQL/SpringQL-client-c/compare/v0.16.0+4...HEAD [Released]: https://github.com/SpringQL/SpringQL-client-c/releases +[v0.16.0+4]: https://github.com/SpringQL/SpringQL-client-c/compare/v0.16.0+4...v0.16.0+4 [v0.16.0+3]: https://github.com/SpringQL/SpringQL-client-c/compare/v0.16.0+2...v0.16.0+3 [v0.16.0+2]: https://github.com/SpringQL/SpringQL-client-c/compare/v0.16.0...v0.16.0+2 [v0.16.0]: https://github.com/SpringQL/SpringQL-client-c/compare/v0.15.0+2...v0.16.0 diff --git a/springql.h b/springql.h index f35e43d..8db9e44 100644 --- a/springql.h +++ b/springql.h @@ -42,27 +42,27 @@ typedef enum SpringErrno { /** * Configuration. */ -typedef void *SpringConfig; +typedef struct SpringConfig SpringConfig; /** * Pipeline (dataflow definition) in SpringQL. */ -typedef void *SpringPipeline; +typedef struct SpringPipeline SpringPipeline; /** * Row object to pop from an in memory queue. */ -typedef void *SpringSinkRow; +typedef struct SpringSinkRow SpringSinkRow; /** * Row object to push into an in memory queue. */ -typedef void *SpringSourceRow; +typedef struct SpringSourceRow SpringSourceRow; /** * Builder of SpringSourceRow */ -typedef void *SpringSourceRowBuilder; +typedef struct SpringSourceRowBuilder SpringSourceRowBuilder; /** * Returns default configuration. @@ -70,7 +70,7 @@ typedef void *SpringSourceRowBuilder; * Returned value is not modifiable (it is just a void pointer). * If you would like to change the default configuration, use `spring_config_toml()` instead. */ -SpringConfig *spring_config_default(void); +struct SpringConfig *spring_config_default(void); /** * Configuration by TOML format string. @@ -89,7 +89,7 @@ SpringConfig *spring_config_default(void); * - `overwrite_config_toml` includes invalid key and/or value. * - `overwrite_config_toml` is not valid as TOML. */ -SpringConfig *spring_config_toml(const char *overwrite_config_toml); +struct SpringConfig *spring_config_toml(const char *overwrite_config_toml); /** * Frees heap occupied by a `SpringConfig`. @@ -99,7 +99,7 @@ SpringConfig *spring_config_toml(const char *overwrite_config_toml); * - `Ok`: on success. * - `CNull`: `config` is a NULL pointer. */ -enum SpringErrno spring_config_close(SpringConfig *config); +enum SpringErrno spring_config_close(struct SpringConfig *config); /** * Creates and open an in-process stream pipeline. @@ -113,7 +113,7 @@ enum SpringErrno spring_config_close(SpringConfig *config); * * No errors are expected currently. */ -SpringPipeline *spring_open(const SpringConfig *config); +struct SpringPipeline *spring_open(const struct SpringConfig *config); /** * Frees heap occupied by a `SpringPipeline`. @@ -123,7 +123,7 @@ SpringPipeline *spring_open(const SpringConfig *config); * - `Ok`: on success. * - `CNull`: `pipeline` is a NULL pointer. */ -enum SpringErrno spring_close(SpringPipeline *pipeline); +enum SpringErrno spring_close(struct SpringPipeline *pipeline); /** * Execute commands (DDL) to modify the pipeline. @@ -138,7 +138,7 @@ enum SpringErrno spring_close(SpringPipeline *pipeline); * - `InvalidOption`: * - `OPTIONS` in `CREATE` statement includes invalid key or value. */ -enum SpringErrno spring_command(const SpringPipeline *pipeline, const char *sql); +enum SpringErrno spring_command(const struct SpringPipeline *pipeline, const char *sql); /** * Pop a row from an in memory queue. This is a blocking function. @@ -156,7 +156,7 @@ enum SpringErrno spring_command(const SpringPipeline *pipeline, const char *sql) * * - `Unavailable`: queue named `queue` does not exist. */ -SpringSinkRow *spring_pop(const SpringPipeline *pipeline, const char *queue); +struct SpringSinkRow *spring_pop(const struct SpringPipeline *pipeline, const char *queue); /** * Pop a row from an in memory queue. This is a non-blocking function. @@ -170,21 +170,23 @@ SpringSinkRow *spring_pop(const SpringPipeline *pipeline, const char *queue); * * - `Unavailable`: queue named `queue` does not exist. */ -SpringSinkRow *spring_pop_non_blocking(const SpringPipeline *pipeline, - const char *queue, - bool *is_err); +struct SpringSinkRow *spring_pop_non_blocking(const struct SpringPipeline *pipeline, + const char *queue, + bool *is_err); /** * Push a row into an in memory queue. This is a non-blocking function. * + * `row` is freed internally. + * * # Returns * * - `Ok`: on success. * - `Unavailable`: queue named `queue` does not exist. */ -enum SpringErrno spring_push(const SpringPipeline *pipeline, +enum SpringErrno spring_push(const struct SpringPipeline *pipeline, const char *queue, - const SpringSourceRow *row); + struct SpringSourceRow *row); /** * Create a source row from JSON string @@ -198,7 +200,7 @@ enum SpringErrno spring_push(const SpringPipeline *pipeline, * * - `InvalidFormat`: JSON string is invalid. */ -SpringSourceRow *spring_source_row_from_json(const char *json); +struct SpringSourceRow *spring_source_row_from_json(const char *json); /** * Start creating a source row using a builder. @@ -207,10 +209,12 @@ SpringSourceRow *spring_source_row_from_json(const char *json); * * Pointer to the builder */ -SpringSourceRowBuilder *spring_source_row_builder(void); +struct SpringSourceRowBuilder *spring_source_row_builder(void); /** - * Add a BLOB column to the builder. + * Add a BLOB column to the builder and return the new one. + * + * `builder` is freed internally. * * # Parameters * @@ -221,13 +225,17 @@ SpringSourceRowBuilder *spring_source_row_builder(void); * * # Returns * - * - `Ok`: on success. + * - non-NULL: Successfully created a row. + * - NULL: Error occurred. + * + * # Errors + * * - `Sql`: `column_name` is already added to the builder. */ -enum SpringErrno spring_source_row_add_column_blob(SpringSourceRowBuilder *builder, - const char *column_name, - const void *v, - int v_len); +struct SpringSourceRowBuilder *spring_source_row_add_column_blob(struct SpringSourceRowBuilder *builder, + const char *column_name, + const void *v, + int v_len); /** * Finish creating a source row using a builder. @@ -238,17 +246,7 @@ enum SpringErrno spring_source_row_add_column_blob(SpringSourceRowBuilder *build * * SpringSourceRow */ -SpringSourceRow *spring_source_row_build(SpringSourceRowBuilder *builder); - -/** - * Frees heap occupied by a `SpringSourceRow`. - * - * # Returns - * - * - `Ok`: on success. - * - `CNull`: `pipeline` is a NULL pointer. - */ -enum SpringErrno spring_source_row_close(SpringSourceRow *row); +struct SpringSourceRow *spring_source_row_build(struct SpringSourceRowBuilder *builder); /** * Frees heap occupied by a `SpringSinkRow`. @@ -258,7 +256,7 @@ enum SpringErrno spring_source_row_close(SpringSourceRow *row); * - `Ok`: on success. * - `CNull`: `pipeline` is a NULL pointer. */ -enum SpringErrno spring_sink_row_close(SpringSinkRow *row); +enum SpringErrno spring_sink_row_close(struct SpringSinkRow *row); /** * Get a 2-byte integer column. @@ -277,7 +275,7 @@ enum SpringErrno spring_sink_row_close(SpringSinkRow *row); * - `i_col` is out of range. * - `CNull`: Column value is NULL. */ -enum SpringErrno spring_column_short(const SpringSinkRow *row, uint16_t i_col, short *out); +enum SpringErrno spring_column_short(const struct SpringSinkRow *row, uint16_t i_col, short *out); /** * Get a 4-byte integer column. @@ -296,7 +294,7 @@ enum SpringErrno spring_column_short(const SpringSinkRow *row, uint16_t i_col, s * - `i_col` is out of range. * - `CNull`: Column value is NULL. */ -enum SpringErrno spring_column_int(const SpringSinkRow *row, uint16_t i_col, int *out); +enum SpringErrno spring_column_int(const struct SpringSinkRow *row, uint16_t i_col, int *out); /** * Get an 8-byte integer column. @@ -315,7 +313,7 @@ enum SpringErrno spring_column_int(const SpringSinkRow *row, uint16_t i_col, int * - `i_col` is out of range. * - `CNull`: Column value is NULL. */ -enum SpringErrno spring_column_long(const SpringSinkRow *row, uint16_t i_col, long *out); +enum SpringErrno spring_column_long(const struct SpringSinkRow *row, uint16_t i_col, long *out); /** * Get a 4-byte unsigned integer column. @@ -334,7 +332,7 @@ enum SpringErrno spring_column_long(const SpringSinkRow *row, uint16_t i_col, lo * - `i_col` is out of range. * - `CNull`: Column value is NULL. */ -enum SpringErrno spring_column_unsigned_int(const SpringSinkRow *row, +enum SpringErrno spring_column_unsigned_int(const struct SpringSinkRow *row, uint16_t i_col, unsigned int *out); @@ -356,7 +354,7 @@ enum SpringErrno spring_column_unsigned_int(const SpringSinkRow *row, * - `i_col` is out of range. * - `CNull`: Column value is NULL. */ -int spring_column_text(const SpringSinkRow *row, uint16_t i_col, char *out, int out_len); +int spring_column_text(const struct SpringSinkRow *row, uint16_t i_col, char *out, int out_len); /** * Get a BLOB column. @@ -376,7 +374,7 @@ int spring_column_text(const SpringSinkRow *row, uint16_t i_col, char *out, int * - `i_col` is out of range. * - `CNull`: Column value is NULL. */ -int spring_column_blob(const SpringSinkRow *row, uint16_t i_col, void *out, int out_len); +int spring_column_blob(const struct SpringSinkRow *row, uint16_t i_col, void *out, int out_len); /** * Get a bool column. @@ -395,7 +393,7 @@ int spring_column_blob(const SpringSinkRow *row, uint16_t i_col, void *out, int * - `i_col` is out of range. * - `CNull`: Column value is NULL. */ -enum SpringErrno spring_column_bool(const SpringSinkRow *row, uint16_t i_col, bool *out); +enum SpringErrno spring_column_bool(const struct SpringSinkRow *row, uint16_t i_col, bool *out); /** * Get a 4-byte floating point column. @@ -414,7 +412,7 @@ enum SpringErrno spring_column_bool(const SpringSinkRow *row, uint16_t i_col, bo * - `i_col` is out of range. * - `CNull`: Column value is NULL. */ -enum SpringErrno spring_column_float(const SpringSinkRow *row, uint16_t i_col, float *out); +enum SpringErrno spring_column_float(const struct SpringSinkRow *row, uint16_t i_col, float *out); /** * Write the most recent error number into `errno_` and message into a caller-provided buffer as a UTF-8 diff --git a/src/lib.rs b/src/lib.rs index d708867..ee301c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,8 @@ #![allow(clippy::missing_safety_doc)] // C header file does not need `Safety` section pub(crate) mod c_mem; -mod spring_config; + +pub mod spring_config; pub mod spring_errno; pub mod spring_last_err; mod spring_pipeline; @@ -13,6 +14,9 @@ mod spring_sink_row; mod spring_source_row; mod spring_source_row_builder; +#[cfg(test)] +mod tests; + use std::{ ffi::{c_void, CStr}, os::raw::{c_char, c_float, c_int, c_long, c_short, c_uint}, @@ -30,7 +34,10 @@ use crate::{ spring_source_row::SpringSourceRow, spring_source_row_builder::SpringSourceRowBuilder, }; -use ::springql::{error::SpringError, SpringPipeline as Pipeline}; +use ::springql::{ + error::SpringError, SpringPipeline as Pipeline, SpringSourceRow as RuSpringSourceRow, + SpringSourceRowBuilder as RuSpringSourceRowBuilder, +}; /// Returns default configuration. /// @@ -38,8 +45,8 @@ use ::springql::{error::SpringError, SpringPipeline as Pipeline}; /// If you would like to change the default configuration, use `spring_config_toml()` instead. #[no_mangle] pub extern "C" fn spring_config_default() -> *mut SpringConfig { - let config = ::springql::SpringConfig::default(); - SpringConfig::new(config).into_ptr() + let config = SpringConfig::default(); + config.into_ptr() } /// Configuration by TOML format string. @@ -64,8 +71,8 @@ pub unsafe extern "C" fn spring_config_toml( let s = CStr::from_ptr(overwrite_config_toml); let s = s.to_str().expect("failed to parse TOML string into UTF-8"); - let config = springql::SpringConfig::new(s).expect("failed to parse TOML config"); - SpringConfig::new(config).into_ptr() + let config = SpringConfig::from_toml(s).expect("failed to parse TOML config"); + config.into_ptr() } /// Frees heap occupied by a `SpringConfig`. @@ -79,7 +86,7 @@ pub unsafe extern "C" fn spring_config_close(config: *mut SpringConfig) -> Sprin if config.is_null() { SpringErrno::CNull } else { - SpringConfig::drop(config); + let _ = Box::from_raw(config); SpringErrno::Ok } } @@ -96,13 +103,10 @@ pub unsafe extern "C" fn spring_config_close(config: *mut SpringConfig) -> Sprin /// No errors are expected currently. #[no_mangle] pub unsafe extern "C" fn spring_open(config: *const SpringConfig) -> *mut SpringPipeline { - let config = (*config).as_config(); - let pipeline = Pipeline::new(config); - match pipeline { - Ok(pipe) => { - let ptr = SpringPipeline::new(pipe); - ptr.into_ptr() - } + let config = &*config; + let res_ru_pipeline = with_catch(|| Pipeline::new(config.as_ref())); + match res_ru_pipeline { + Ok(ru_pipeline) => SpringPipeline::from(ru_pipeline).into_ptr(), Err(_err) => ptr::null_mut(), } } @@ -118,7 +122,7 @@ pub unsafe extern "C" fn spring_close(pipeline: *mut SpringPipeline) -> SpringEr if pipeline.is_null() { SpringErrno::CNull } else { - SpringPipeline::drop(pipeline); + let _ = Box::from_raw(pipeline); SpringErrno::Ok } } @@ -139,9 +143,9 @@ pub unsafe extern "C" fn spring_command( pipeline: *const SpringPipeline, sql: *const c_char, ) -> SpringErrno { - let pipe = (*pipeline).as_pipeline(); + let ru_pipeline = (*pipeline).as_ref(); let sql = CStr::from_ptr(sql).to_string_lossy().into_owned(); - let result = with_catch(|| pipe.command(sql)); + let result = with_catch(|| ru_pipeline.command(sql)); match result { Ok(_) => SpringErrno::Ok, @@ -168,12 +172,12 @@ pub unsafe extern "C" fn spring_pop( pipeline: *const SpringPipeline, queue: *const c_char, ) -> *mut SpringSinkRow { - let pipeline = (*pipeline).as_pipeline(); + let ru_pipeline = (*pipeline).as_ref(); let queue = CStr::from_ptr(queue).to_string_lossy().into_owned(); - let result = with_catch(|| pipeline.pop(&queue)); + let result = with_catch(|| ru_pipeline.pop(&queue)); match result { - Ok(row) => { - let row = SpringSinkRow::new(row); + Ok(ru_row) => { + let row = SpringSinkRow::from(ru_row); row.into_ptr() } Err(_) => ptr::null_mut(), @@ -196,14 +200,13 @@ pub unsafe extern "C" fn spring_pop_non_blocking( queue: *const c_char, is_err: *mut bool, ) -> *mut SpringSinkRow { - let pipeline = (*pipeline).as_pipeline(); + let ru_pipeline = (*pipeline).as_ref(); let queue = CStr::from_ptr(queue).to_string_lossy().into_owned(); - let result = with_catch(|| pipeline.pop_non_blocking(&queue)); + let result = with_catch(|| ru_pipeline.pop_non_blocking(&queue)); match result { Ok(Some(row)) => { - let ptr = SpringSinkRow::new(row); *is_err = false; - ptr.into_ptr() + SpringSinkRow::from(row).into_ptr() } Ok(None) => { *is_err = false; @@ -218,6 +221,8 @@ pub unsafe extern "C" fn spring_pop_non_blocking( /// Push a row into an in memory queue. This is a non-blocking function. /// +/// `row` is freed internally. +/// /// # Returns /// /// - `Ok`: on success. @@ -226,12 +231,14 @@ pub unsafe extern "C" fn spring_pop_non_blocking( pub unsafe extern "C" fn spring_push( pipeline: *const SpringPipeline, queue: *const c_char, - row: *const SpringSourceRow, + row: *mut SpringSourceRow, ) -> SpringErrno { - let pipeline = (*pipeline).as_pipeline(); + let ru_pipeline = (*pipeline).as_ref(); let queue = CStr::from_ptr(queue).to_string_lossy().into_owned(); - let source_row = (*row).to_row(); - let result = with_catch(|| pipeline.push(&queue, source_row)); + + let source_row = Box::from_raw(row); + let source_row = RuSpringSourceRow::from(*source_row); + let result = with_catch(|| ru_pipeline.push(&queue, source_row)); match result { Ok(()) => SpringErrno::Ok, Err(e) => e, @@ -251,9 +258,9 @@ pub unsafe extern "C" fn spring_push( #[no_mangle] pub unsafe extern "C" fn spring_source_row_from_json(json: *const c_char) -> *mut SpringSourceRow { let json = CStr::from_ptr(json).to_string_lossy().into_owned(); - let res_source_row = with_catch(|| ::springql::SpringSourceRow::from_json(&json)); - match res_source_row { - Ok(source_row) => SpringSourceRow::new(source_row).into_ptr(), + let res_ru_source_row = with_catch(|| ::springql::SpringSourceRow::from_json(&json)); + match res_ru_source_row { + Ok(ru_source_row) => SpringSourceRow::from(ru_source_row).into_ptr(), Err(_) => ptr::null_mut(), } } @@ -267,7 +274,9 @@ pub unsafe extern "C" fn spring_source_row_from_json(json: *const c_char) -> *mu pub unsafe extern "C" fn spring_source_row_builder() -> *mut SpringSourceRowBuilder { SpringSourceRowBuilder::default().into_ptr() } -/// Add a BLOB column to the builder. +/// Add a BLOB column to the builder and return the new one. +/// +/// `builder` is freed internally. /// /// # Parameters /// @@ -278,7 +287,11 @@ pub unsafe extern "C" fn spring_source_row_builder() -> *mut SpringSourceRowBuil /// /// # Returns /// -/// - `Ok`: on success. +/// - non-NULL: Successfully created a row. +/// - NULL: Error occurred. +/// +/// # Errors +/// /// - `Sql`: `column_name` is already added to the builder. #[no_mangle] pub unsafe extern "C" fn spring_source_row_add_column_blob( @@ -286,21 +299,19 @@ pub unsafe extern "C" fn spring_source_row_add_column_blob( column_name: *const c_char, v: *const c_void, v_len: c_int, -) -> SpringErrno { +) -> *mut SpringSourceRowBuilder { let column_name = CStr::from_ptr(column_name).to_string_lossy().into_owned(); let v = v as *const u8; let v = slice::from_raw_parts(v, v_len as usize); let v = v.to_vec(); - let rust_builder = (*builder).to_row_builder(); - let res_rust_builder = with_catch(|| rust_builder.add_column(column_name, v)); - match res_rust_builder { - Ok(rust_builder) => { - *builder = SpringSourceRowBuilder::new(rust_builder); - SpringErrno::Ok - } - Err(e) => e, + let builder = Box::from_raw(builder); + let ru_builder = RuSpringSourceRowBuilder::from(*builder); + let res_ru_builder = with_catch(|| ru_builder.add_column(column_name, v)); + match res_ru_builder { + Ok(ru_builder) => SpringSourceRowBuilder::from(ru_builder).into_ptr(), + Err(_) => ptr::null_mut(), } } /// Finish creating a source row using a builder. @@ -314,26 +325,9 @@ pub unsafe extern "C" fn spring_source_row_add_column_blob( pub unsafe extern "C" fn spring_source_row_build( builder: *mut SpringSourceRowBuilder, ) -> *mut SpringSourceRow { - let rust_builder = (*builder).to_row_builder(); - let ret = SpringSourceRow::new(rust_builder.build()).into_ptr(); - SpringSourceRowBuilder::drop(builder); - ret -} - -/// Frees heap occupied by a `SpringSourceRow`. -/// -/// # Returns -/// -/// - `Ok`: on success. -/// - `CNull`: `pipeline` is a NULL pointer. -#[no_mangle] -pub extern "C" fn spring_source_row_close(row: *mut SpringSourceRow) -> SpringErrno { - if row.is_null() { - SpringErrno::CNull - } else { - SpringSourceRow::drop(row); - SpringErrno::Ok - } + let builder = Box::from_raw(builder); + let ru_builder = RuSpringSourceRowBuilder::from(*builder); + SpringSourceRow::from(ru_builder.build()).into_ptr() } /// Frees heap occupied by a `SpringSinkRow`. @@ -343,11 +337,11 @@ pub extern "C" fn spring_source_row_close(row: *mut SpringSourceRow) -> SpringEr /// - `Ok`: on success. /// - `CNull`: `pipeline` is a NULL pointer. #[no_mangle] -pub extern "C" fn spring_sink_row_close(row: *mut SpringSinkRow) -> SpringErrno { +pub unsafe extern "C" fn spring_sink_row_close(row: *mut SpringSinkRow) -> SpringErrno { if row.is_null() { SpringErrno::CNull } else { - SpringSinkRow::drop(row); + let _ = Box::from_raw(row); SpringErrno::Ok } } @@ -373,7 +367,7 @@ pub unsafe extern "C" fn spring_column_short( i_col: u16, out: *mut c_short, ) -> SpringErrno { - let row = (*row).as_row(); + let row = &*row; let i_col = i_col as usize; let result = with_catch(|| row.get_not_null_by_index(i_col as usize)); match result { @@ -406,7 +400,7 @@ pub unsafe extern "C" fn spring_column_int( i_col: u16, out: *mut c_int, ) -> SpringErrno { - let row = (*row).as_row(); + let row = &*row; let i_col = i_col as usize; let result = with_catch(|| row.get_not_null_by_index(i_col as usize)); match result { @@ -439,7 +433,7 @@ pub unsafe extern "C" fn spring_column_long( i_col: u16, out: *mut c_long, ) -> SpringErrno { - let row = (*row).as_row(); + let row = &*row; let i_col = i_col as usize; let result = with_catch(|| row.get_not_null_by_index(i_col as usize)); match result { @@ -472,7 +466,7 @@ pub unsafe extern "C" fn spring_column_unsigned_int( i_col: u16, out: *mut c_uint, ) -> SpringErrno { - let row = (*row).as_row(); + let row = &*row; let i_col = i_col as usize; let result = with_catch(|| row.get_not_null_by_index(i_col as usize)); match result { @@ -507,7 +501,7 @@ pub unsafe extern "C" fn spring_column_text( out: *mut c_char, out_len: c_int, ) -> c_int { - let row = (*row).as_row(); + let row = &*row; let i_col = i_col as usize; let result: Result = with_catch(|| row.get_not_null_by_index(i_col as usize)); @@ -543,7 +537,7 @@ pub unsafe extern "C" fn spring_column_blob( out: *mut c_void, out_len: c_int, ) -> c_int { - let row = (*row).as_row(); + let row = &*row; let i_col = i_col as usize; let result: Result, SpringErrno> = with_catch(|| row.get_not_null_by_index(i_col as usize)); @@ -577,7 +571,7 @@ pub unsafe extern "C" fn spring_column_bool( i_col: u16, out: *mut bool, ) -> SpringErrno { - let row = (*row).as_row(); + let row = &*row; let i_col = i_col as usize; let result = with_catch(|| row.get_not_null_by_index(i_col as usize)); match result { @@ -610,7 +604,7 @@ pub unsafe extern "C" fn spring_column_float( i_col: u16, out: *mut c_float, ) -> SpringErrno { - let row = (*row).as_row(); + let row = &*row; let i_col = i_col as usize; let result = with_catch(|| row.get_not_null_by_index(i_col as usize)); match result { diff --git a/src/spring_config.rs b/src/spring_config.rs index abd5337..1193712 100644 --- a/src/spring_config.rs +++ b/src/spring_config.rs @@ -1,28 +1,24 @@ // This file is part of https://github.com/SpringQL/SpringQL-client-c which is licensed under MIT OR Apache-2.0. See file LICENSE-MIT or LICENSE-APACHE for full license details. -use ::springql::SpringConfig as RawSpringConfig; -use std::{ffi::c_void, mem}; +use ::springql::{Result, SpringConfig as RuSpringConfig}; + /// Configuration. #[non_exhaustive] -#[repr(transparent)] -pub struct SpringConfig(*mut c_void); - -impl SpringConfig { - pub fn new(config: RawSpringConfig) -> Self { - SpringConfig(unsafe { mem::transmute(Box::new(config)) }) - } +#[derive(Clone, Eq, PartialEq, Debug, Default)] +pub struct SpringConfig(RuSpringConfig); - pub fn as_config(&self) -> &RawSpringConfig { - unsafe { &*(self.0 as *const RawSpringConfig) } +impl AsRef for SpringConfig { + fn as_ref(&self) -> &RuSpringConfig { + &self.0 } +} - pub fn drop(ptr: *mut SpringConfig) { - let outer = unsafe { Box::from_raw(ptr) }; - let inner = unsafe { Box::from_raw(outer.0) }; - drop(inner); - drop(outer); +impl SpringConfig { + pub(crate) fn from_toml(toml: &str) -> Result { + let config = RuSpringConfig::from_toml(toml)?; + Ok(Self(config)) } - pub fn into_ptr(self) -> *mut SpringConfig { + pub(crate) fn into_ptr(self) -> *mut SpringConfig { Box::into_raw(Box::new(self)) } } diff --git a/src/spring_errno.rs b/src/spring_errno.rs index 6bc0a6f..ac33ae0 100644 --- a/src/spring_errno.rs +++ b/src/spring_errno.rs @@ -7,6 +7,7 @@ use crate::spring_last_err::LastError; /// Errno (error number) to be returned erroneous functions. #[non_exhaustive] #[repr(C)] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] pub enum SpringErrno { Ok = 0, diff --git a/src/spring_pipeline.rs b/src/spring_pipeline.rs index b5a34f5..0b71302 100644 --- a/src/spring_pipeline.rs +++ b/src/spring_pipeline.rs @@ -1,29 +1,26 @@ // This file is part of https://github.com/SpringQL/SpringQL-client-c which is licensed under MIT OR Apache-2.0. See file LICENSE-MIT or LICENSE-APACHE for full license details. -use ::springql::SpringPipeline as Pipeline; -use std::{ffi::c_void, mem}; +use ::springql::SpringPipeline as RuSpringPipeline; + /// Pipeline (dataflow definition) in SpringQL. #[non_exhaustive] -#[repr(transparent)] -pub struct SpringPipeline(*mut c_void); - -impl SpringPipeline { - pub fn new(pipe: Pipeline) -> Self { - SpringPipeline(unsafe { mem::transmute(Box::new(pipe)) }) - } +#[derive(Debug)] +pub struct SpringPipeline(RuSpringPipeline); - pub fn as_pipeline(&self) -> &Pipeline { - unsafe { &*(self.0 as *const Pipeline) } +impl From for SpringPipeline { + fn from(pipeline: RuSpringPipeline) -> Self { + SpringPipeline(pipeline) } +} - pub fn drop(ptr: *mut SpringPipeline) { - let outer = unsafe { Box::from_raw(ptr) }; - let inner = unsafe { Box::from_raw(outer.0) }; - drop(inner); - drop(outer); +impl AsRef for SpringPipeline { + fn as_ref(&self) -> &RuSpringPipeline { + &self.0 } +} - pub fn into_ptr(self) -> *mut SpringPipeline { +impl SpringPipeline { + pub(crate) fn into_ptr(self) -> *mut SpringPipeline { Box::into_raw(Box::new(self)) } } diff --git a/src/spring_sink_row.rs b/src/spring_sink_row.rs index 1415254..b482f45 100644 --- a/src/spring_sink_row.rs +++ b/src/spring_sink_row.rs @@ -1,31 +1,27 @@ // This file is part of https://github.com/SpringQL/SpringQL-client-c which is licensed under MIT OR Apache-2.0. See file LICENSE-MIT or LICENSE-APACHE for full license details. -use ::springql::SpringSinkRow as SinkRow; - -use std::{ffi::c_void, mem}; +use springql::{Result, SpringSinkRow as RuSpringSinkRow, SpringValue}; /// Row object to pop from an in memory queue. #[non_exhaustive] -#[repr(transparent)] -pub struct SpringSinkRow(*mut c_void); - -impl SpringSinkRow { - pub fn new(sink_row: SinkRow) -> Self { - SpringSinkRow(unsafe { mem::transmute(Box::new(sink_row)) }) - } +#[derive(Debug)] +pub struct SpringSinkRow(RuSpringSinkRow); - pub fn as_row(&self) -> &SinkRow { - unsafe { &*(self.0 as *const SinkRow) } +impl From for SpringSinkRow { + fn from(sink_row: RuSpringSinkRow) -> Self { + SpringSinkRow(sink_row) } +} - pub fn drop(ptr: *mut SpringSinkRow) { - let outer = unsafe { Box::from_raw(ptr) }; - let inner = unsafe { Box::from_raw(outer.0) }; - drop(inner); - drop(outer); +impl SpringSinkRow { + pub(crate) fn get_not_null_by_index(&self, i_col: usize) -> Result + where + T: SpringValue, + { + self.0.get_not_null_by_index(i_col) } - pub fn into_ptr(self) -> *mut SpringSinkRow { + pub(crate) fn into_ptr(self) -> *mut Self { Box::into_raw(Box::new(self)) } } diff --git a/src/spring_source_row.rs b/src/spring_source_row.rs index f792a7c..4177593 100644 --- a/src/spring_source_row.rs +++ b/src/spring_source_row.rs @@ -1,30 +1,24 @@ // This file is part of https://github.com/SpringQL/SpringQL-client-c which is licensed under MIT OR Apache-2.0. See file LICENSE-MIT or LICENSE-APACHE for full license details. -use ::springql::SpringSourceRow as SourceRow; - -use std::{ffi::c_void, mem}; +use ::springql::SpringSourceRow as RuSpringSourceRow; /// Row object to push into an in memory queue. #[non_exhaustive] -#[repr(transparent)] -pub struct SpringSourceRow(*mut c_void); - -impl SpringSourceRow { - pub fn new(source_row: SourceRow) -> Self { - SpringSourceRow(unsafe { mem::transmute(Box::new(source_row)) }) - } +#[derive(Clone, Debug)] +pub struct SpringSourceRow(RuSpringSourceRow); - pub fn to_row(&self) -> SourceRow { - unsafe { &*(self.0 as *const SourceRow) }.clone() +impl From for SpringSourceRow { + fn from(source_row: RuSpringSourceRow) -> Self { + SpringSourceRow(source_row) } - - pub fn drop(ptr: *mut SpringSourceRow) { - let outer = unsafe { Box::from_raw(ptr) }; - let inner = unsafe { Box::from_raw(outer.0) }; - drop(inner); - drop(outer); +} +impl From for RuSpringSourceRow { + fn from(source_row: SpringSourceRow) -> Self { + source_row.0 } +} +impl SpringSourceRow { pub fn into_ptr(self) -> *mut SpringSourceRow { Box::into_raw(Box::new(self)) } diff --git a/src/spring_source_row_builder.rs b/src/spring_source_row_builder.rs index 7391f7c..e553ce0 100644 --- a/src/spring_source_row_builder.rs +++ b/src/spring_source_row_builder.rs @@ -1,37 +1,25 @@ // This file is part of https://github.com/SpringQL/SpringQL-client-c which is licensed under MIT OR Apache-2.0. See file LICENSE-MIT or LICENSE-APACHE for full license details. -use ::springql::SpringSourceRowBuilder as SourceRowBuilder; - -use std::{ffi::c_void, mem}; +use ::springql::SpringSourceRowBuilder as RuSpringSourceRowBuilder; /// Builder of SpringSourceRow #[non_exhaustive] -#[repr(transparent)] -pub struct SpringSourceRowBuilder(*mut c_void); - -impl SpringSourceRowBuilder { - pub fn new(underlying: SourceRowBuilder) -> Self { - SpringSourceRowBuilder(unsafe { mem::transmute(Box::new(underlying)) }) - } +#[derive(PartialEq, Debug, Default)] +pub struct SpringSourceRowBuilder(RuSpringSourceRowBuilder); - pub fn to_row_builder(&self) -> SourceRowBuilder { - unsafe { &*(self.0 as *const SourceRowBuilder) }.clone() +impl From for SpringSourceRowBuilder { + fn from(builder: RuSpringSourceRowBuilder) -> Self { + SpringSourceRowBuilder(builder) } - - pub fn drop(ptr: *mut SpringSourceRowBuilder) { - let outer = unsafe { Box::from_raw(ptr) }; - let inner = unsafe { Box::from_raw(outer.0) }; - drop(inner); - drop(outer); +} +impl From for RuSpringSourceRowBuilder { + fn from(builder: SpringSourceRowBuilder) -> Self { + builder.0 } +} +impl SpringSourceRowBuilder { pub fn into_ptr(self) -> *mut SpringSourceRowBuilder { Box::into_raw(Box::new(self)) } } - -impl Default for SpringSourceRowBuilder { - fn default() -> Self { - Self::new(SourceRowBuilder::default()) - } -} diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000..4328343 --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,6 @@ +// This file is part of https://github.com/SpringQL/SpringQL-client-c which is licensed under MIT OR Apache-2.0. See file LICENSE-MIT or LICENSE-APACHE for full license details. + +mod spring_config; +mod spring_open; +mod spring_sink_row; +mod spring_source_row_builder; diff --git a/src/tests/spring_config.rs b/src/tests/spring_config.rs new file mode 100644 index 0000000..1cb5dd0 --- /dev/null +++ b/src/tests/spring_config.rs @@ -0,0 +1,24 @@ +// This file is part of https://github.com/SpringQL/SpringQL-client-c which is licensed under MIT OR Apache-2.0. See file LICENSE-MIT or LICENSE-APACHE for full license details. + +use std::ffi::CString; + +use crate::*; + +#[test] +fn test_spring_config_default() { + unsafe { + let config = spring_config_default(); + assert!(!config.is_null()); + spring_config_close(config); + } +} + +#[test] +fn test_spring_config_toml() { + let toml = CString::new("").unwrap(); + unsafe { + let config = spring_config_toml(toml.as_ptr()); + assert!(!config.is_null()); + spring_config_close(config); + } +} diff --git a/src/tests/spring_open.rs b/src/tests/spring_open.rs new file mode 100644 index 0000000..2a2fbf0 --- /dev/null +++ b/src/tests/spring_open.rs @@ -0,0 +1,17 @@ +// This file is part of https://github.com/SpringQL/SpringQL-client-c which is licensed under MIT OR Apache-2.0. See file LICENSE-MIT or LICENSE-APACHE for full license details. + +use crate::*; + +#[test] +fn test_spring_open() { + unsafe { + let config = spring_config_default(); + assert!(!config.is_null()); + + let pipeline = spring_open(config); + assert!(!config.is_null()); + + spring_close(pipeline); + spring_config_close(config); + } +} diff --git a/src/tests/spring_sink_row.rs b/src/tests/spring_sink_row.rs new file mode 100644 index 0000000..f8d7760 --- /dev/null +++ b/src/tests/spring_sink_row.rs @@ -0,0 +1,75 @@ +// This file is part of https://github.com/SpringQL/SpringQL-client-c which is licensed under MIT OR Apache-2.0. See file LICENSE-MIT or LICENSE-APACHE for full license details. + +use std::ffi::CString; + +use crate::*; + +unsafe fn command(pipeline: *const SpringPipeline, sql: &str) { + let sql = CString::new(sql).unwrap(); + let errno = spring_command(pipeline, sql.as_ptr()); + assert_eq!(errno, SpringErrno::Ok); +} + +#[test] +fn test_spring_sink_row() { + unsafe { + let config = spring_config_default(); + assert!(!config.is_null()); + + let pipeline = spring_open(config); + assert!(!config.is_null()); + + command(pipeline, "CREATE SOURCE STREAM source_1 (b BLOB NOT NULL);"); + command(pipeline, "CREATE SINK STREAM sink_1 (b BLOB NOT NULL);"); + command( + pipeline, + " + CREATE PUMP pump_1 AS + INSERT INTO sink_1 (b) + SELECT STREAM source_1.b FROM source_1; + ", + ); + command( + pipeline, + " + CREATE SINK WRITER queue_sink FOR sink_1 + TYPE IN_MEMORY_QUEUE OPTIONS (NAME 'q_sink'); + ", + ); + command( + pipeline, + " + CREATE SOURCE READER queue_src FOR source_1 + TYPE IN_MEMORY_QUEUE OPTIONS (NAME 'q_src'); + ", + ); + + let source_row = { + let col = CString::new("b").unwrap(); + let val = vec![0x01u8, 0x02, 0x03]; + + let builder = spring_source_row_builder(); + let builder = spring_source_row_add_column_blob( + builder, + col.as_ptr(), + val.as_ptr().cast(), + val.len().try_into().unwrap(), + ); + assert!(!builder.is_null()); + + spring_source_row_build(builder) + }; + + let q_src = CString::new("q_src").unwrap(); + let errno = spring_push(pipeline, q_src.as_ptr(), source_row); + assert_eq!(errno, SpringErrno::Ok); + + let q_sink = CString::new("q_sink").unwrap(); + let sink_row = spring_pop(pipeline, q_sink.as_ptr()); + + spring_sink_row_close(sink_row); + + spring_close(pipeline); + spring_config_close(config); + } +} diff --git a/src/tests/spring_source_row_builder.rs b/src/tests/spring_source_row_builder.rs new file mode 100644 index 0000000..e4f922b --- /dev/null +++ b/src/tests/spring_source_row_builder.rs @@ -0,0 +1,24 @@ +// This file is part of https://github.com/SpringQL/SpringQL-client-c which is licensed under MIT OR Apache-2.0. See file LICENSE-MIT or LICENSE-APACHE for full license details. + +use std::ffi::CString; + +use crate::*; + +#[test] +fn test_spring_source_row_builder() { + unsafe { + let c1_col = CString::new("c1").unwrap(); + let c1_value = vec![0x01u8, 0x02, 0x03]; + + let builder = spring_source_row_builder(); + let builder = spring_source_row_add_column_blob( + builder, + c1_col.as_ptr(), + c1_value.as_ptr().cast(), + c1_value.len().try_into().unwrap(), + ); + assert!(!builder.is_null()); + + let _row = spring_source_row_build(builder); + } +}