1use jni::objects::{JClass, JString};
10use jni::sys::{jint, jlong, jstring};
11use jni::EnvUnowned;
12
13use std::ffi::CString;
14
15#[no_mangle]
19pub extern "system" fn Java_sz_1orm_1java_SzOrmPool_poolNew<'local>(
20 mut unowned_env: EnvUnowned<'local>,
21 _class: JClass<'local>,
22 dsn: JString<'local>,
23 max_connections: jint,
24) -> jlong {
25 unowned_env
26 .with_env(|_env| -> jni::errors::Result<jlong> {
27 let dsn_str: String = dsn.to_string();
28 let c_dsn = CString::new(dsn_str).map_err(|_| jni::errors::Error::JavaException)?;
29 let config = sz_orm_cabi::PoolConfigC {
30 max_connections: max_connections.max(1) as u32,
31 min_connections: 1,
32 connect_timeout_ms: 5000,
33 idle_timeout_ms: 300000,
34 };
35 let handle = unsafe { sz_orm_cabi::sz_orm_pool_new(c_dsn.as_ptr(), &config) };
37 Ok(handle as jlong)
38 })
39 .resolve::<jni::errors::ThrowRuntimeExAndDefault>()
40}
41
42#[no_mangle]
48pub unsafe extern "system" fn Java_sz_1orm_1java_SzOrmPool_poolFree<'local>(
49 mut unowned_env: EnvUnowned<'local>,
50 _class: JClass<'local>,
51 handle: jlong,
52) {
53 if handle == 0 {
54 return;
55 }
56 let _ = unowned_env.with_env(|_env| -> jni::errors::Result<()> {
57 unsafe {
59 sz_orm_cabi::sz_orm_pool_free(handle as sz_orm_cabi::SzOrmPoolHandle);
60 }
61 Ok(())
62 });
63}
64
65#[no_mangle]
71pub unsafe extern "system" fn Java_sz_1orm_1java_SzOrmPool_ping<'local>(
72 mut unowned_env: EnvUnowned<'local>,
73 _class: JClass<'local>,
74 handle: jlong,
75) -> jint {
76 if handle == 0 {
77 return 0;
78 }
79 unowned_env
80 .with_env(|_env| -> jni::errors::Result<jint> {
81 let healthy =
83 unsafe { sz_orm_cabi::sz_orm_ping(handle as sz_orm_cabi::SzOrmPoolHandle) };
84 Ok(healthy)
85 })
86 .resolve::<jni::errors::ThrowRuntimeExAndDefault>()
87}
88
89#[no_mangle]
98pub unsafe extern "system" fn Java_sz_1orm_1java_SzOrmPool_query<'local>(
99 mut unowned_env: EnvUnowned<'local>,
100 _class: JClass<'local>,
101 handle: jlong,
102 sql: JString<'local>,
103) -> jstring {
104 if handle == 0 {
105 return std::ptr::null_mut();
106 }
107 unowned_env
108 .with_env(|env| -> jni::errors::Result<jstring> {
109 let sql_str: String = sql.to_string();
110 let c_sql = CString::new(sql_str).map_err(|_| jni::errors::Error::JavaException)?;
111 let result = unsafe {
113 sz_orm_cabi::sz_orm_query(handle as sz_orm_cabi::SzOrmPoolHandle, c_sql.as_ptr())
114 };
115 if result.is_null() {
116 return Ok(std::ptr::null_mut());
117 }
118 let result_box = unsafe { Box::from_raw(result) };
120 let json = if result_box.success != 0 && !result_box.json.is_null() {
121 unsafe { std::ffi::CStr::from_ptr(result_box.json) }
123 .to_str()
124 .map(|s| s.to_string())
125 .unwrap_or_default()
126 } else {
127 String::new()
128 };
129 unsafe {
131 sz_orm_cabi::sz_orm_query_result_free(Box::into_raw(result_box));
132 }
133 let jstr = env.new_string(&json)?;
134 Ok(jstr.into_raw())
135 })
136 .resolve::<jni::errors::ThrowRuntimeExAndDefault>()
137}
138
139#[no_mangle]
146pub unsafe extern "system" fn Java_sz_1orm_1java_SzOrmPool_execute<'local>(
147 mut unowned_env: EnvUnowned<'local>,
148 _class: JClass<'local>,
149 handle: jlong,
150 sql: JString<'local>,
151) -> jlong {
152 if handle == 0 {
153 return -1;
154 }
155 unowned_env
156 .with_env(|_env| -> jni::errors::Result<jlong> {
157 let sql_str: String = sql.to_string();
158 let c_sql = CString::new(sql_str).map_err(|_| jni::errors::Error::JavaException)?;
159 let result = unsafe {
161 sz_orm_cabi::sz_orm_execute(handle as sz_orm_cabi::SzOrmPoolHandle, c_sql.as_ptr())
162 };
163 if result.success == 0 {
164 Ok(-1)
165 } else {
166 Ok(result.rows_affected as jlong)
167 }
168 })
169 .resolve::<jni::errors::ThrowRuntimeExAndDefault>()
170}
171
172#[no_mangle]
174pub extern "system" fn Java_sz_1orm_1java_SzOrmPool_version<'local>(
175 mut unowned_env: EnvUnowned<'local>,
176 _class: JClass<'local>,
177) -> jint {
178 unowned_env
179 .with_env(|_env| -> jni::errors::Result<jint> { Ok(sz_orm_cabi::sz_orm_version() as jint) })
180 .resolve::<jni::errors::ThrowRuntimeExAndDefault>()
181}
182
183#[no_mangle]
189pub extern "system" fn Java_sz_1orm_1java_SzOrmPool_beginTransaction<'local>(
190 mut unowned_env: EnvUnowned<'local>,
191 _class: JClass<'local>,
192 pool_handle: jlong,
193) -> jlong {
194 if pool_handle == 0 {
195 return 0;
196 }
197 unowned_env
198 .with_env(|_env| -> jni::errors::Result<jlong> {
199 let tx = unsafe {
201 sz_orm_cabi::sz_orm_transaction_begin(pool_handle as sz_orm_cabi::SzOrmPoolHandle)
202 };
203 Ok(tx as jlong)
204 })
205 .resolve::<jni::errors::ThrowRuntimeExAndDefault>()
206}
207
208#[no_mangle]
210pub extern "system" fn Java_sz_1orm_1java_SzOrmPool_commitTransaction<'local>(
211 mut unowned_env: EnvUnowned<'local>,
212 _class: JClass<'local>,
213 tx_handle: jlong,
214) -> jint {
215 if tx_handle == 0 {
216 return 0;
217 }
218 unowned_env
219 .with_env(|_env| -> jni::errors::Result<jint> {
220 let r = unsafe {
222 sz_orm_cabi::sz_orm_transaction_commit(
223 tx_handle as sz_orm_cabi::SzOrmTransactionHandle,
224 )
225 };
226 Ok(r)
227 })
228 .resolve::<jni::errors::ThrowRuntimeExAndDefault>()
229}
230
231#[no_mangle]
233pub extern "system" fn Java_sz_1orm_1java_SzOrmPool_rollbackTransaction<'local>(
234 mut unowned_env: EnvUnowned<'local>,
235 _class: JClass<'local>,
236 tx_handle: jlong,
237) -> jint {
238 if tx_handle == 0 {
239 return 0;
240 }
241 unowned_env
242 .with_env(|_env| -> jni::errors::Result<jint> {
243 let r = unsafe {
245 sz_orm_cabi::sz_orm_transaction_rollback(
246 tx_handle as sz_orm_cabi::SzOrmTransactionHandle,
247 )
248 };
249 Ok(r)
250 })
251 .resolve::<jni::errors::ThrowRuntimeExAndDefault>()
252}
253
254#[no_mangle]
260pub unsafe extern "system" fn Java_sz_1orm_1java_SzOrmPool_freeTransaction<'local>(
261 mut unowned_env: EnvUnowned<'local>,
262 _class: JClass<'local>,
263 tx_handle: jlong,
264) {
265 if tx_handle == 0 {
266 return;
267 }
268 let _ = unowned_env.with_env(|_env| -> jni::errors::Result<()> {
269 unsafe {
271 sz_orm_cabi::sz_orm_transaction_free(tx_handle as sz_orm_cabi::SzOrmTransactionHandle);
272 }
273 Ok(())
274 });
275}
276
277#[no_mangle]
283pub extern "system" fn Java_sz_1orm_1java_SzOrmPool_modelInsert<'local>(
284 mut unowned_env: EnvUnowned<'local>,
285 _class: JClass<'local>,
286 pool_handle: jlong,
287 table: JString<'local>,
288 fields_json: JString<'local>,
289 values_json: JString<'local>,
290) -> jlong {
291 if pool_handle == 0 {
292 return -1;
293 }
294 unowned_env
295 .with_env(|_env| -> jni::errors::Result<jlong> {
296 let table_s: String = table.to_string();
297 let fields_s: String = fields_json.to_string();
298 let values_s: String = values_json.to_string();
299 let c_table = CString::new(table_s).map_err(|_| jni::errors::Error::JavaException)?;
300 let c_fields = CString::new(fields_s).map_err(|_| jni::errors::Error::JavaException)?;
301 let c_values = CString::new(values_s).map_err(|_| jni::errors::Error::JavaException)?;
302 let r = unsafe {
304 sz_orm_cabi::sz_orm_model_insert(
305 pool_handle as sz_orm_cabi::SzOrmPoolHandle,
306 c_table.as_ptr(),
307 c_fields.as_ptr(),
308 c_values.as_ptr(),
309 )
310 };
311 if r.success == 0 {
312 Ok(-1)
313 } else {
314 Ok(r.rows_affected as jlong)
315 }
316 })
317 .resolve::<jni::errors::ThrowRuntimeExAndDefault>()
318}
319
320#[no_mangle]
322pub extern "system" fn Java_sz_1orm_1java_SzOrmPool_modelUpdate<'local>(
323 mut unowned_env: EnvUnowned<'local>,
324 _class: JClass<'local>,
325 pool_handle: jlong,
326 table: JString<'local>,
327 set_json: JString<'local>,
328 where_clause: JString<'local>,
329 where_params_json: JString<'local>,
330) -> jlong {
331 if pool_handle == 0 {
332 return -1;
333 }
334 unowned_env
335 .with_env(|_env| -> jni::errors::Result<jlong> {
336 let table_s: String = table.to_string();
337 let set_s: String = set_json.to_string();
338 let where_s: String = where_clause.to_string();
339 let where_params_s: String = where_params_json.to_string();
340 let c_table = CString::new(table_s).map_err(|_| jni::errors::Error::JavaException)?;
341 let c_set = CString::new(set_s).map_err(|_| jni::errors::Error::JavaException)?;
342 let c_where = CString::new(where_s).map_err(|_| jni::errors::Error::JavaException)?;
343 let c_where_params =
344 CString::new(where_params_s).map_err(|_| jni::errors::Error::JavaException)?;
345 let r = unsafe {
347 sz_orm_cabi::sz_orm_model_update(
348 pool_handle as sz_orm_cabi::SzOrmPoolHandle,
349 c_table.as_ptr(),
350 c_set.as_ptr(),
351 c_where.as_ptr(),
352 c_where_params.as_ptr(),
353 )
354 };
355 if r.success == 0 {
356 Ok(-1)
357 } else {
358 Ok(r.rows_affected as jlong)
359 }
360 })
361 .resolve::<jni::errors::ThrowRuntimeExAndDefault>()
362}
363
364#[no_mangle]
366pub extern "system" fn Java_sz_1orm_1java_SzOrmPool_modelDelete<'local>(
367 mut unowned_env: EnvUnowned<'local>,
368 _class: JClass<'local>,
369 pool_handle: jlong,
370 table: JString<'local>,
371 where_clause: JString<'local>,
372 where_params_json: JString<'local>,
373) -> jlong {
374 if pool_handle == 0 {
375 return -1;
376 }
377 unowned_env
378 .with_env(|_env| -> jni::errors::Result<jlong> {
379 let table_s: String = table.to_string();
380 let where_s: String = where_clause.to_string();
381 let where_params_s: String = where_params_json.to_string();
382 let c_table = CString::new(table_s).map_err(|_| jni::errors::Error::JavaException)?;
383 let c_where = CString::new(where_s).map_err(|_| jni::errors::Error::JavaException)?;
384 let c_where_params =
385 CString::new(where_params_s).map_err(|_| jni::errors::Error::JavaException)?;
386 let r = unsafe {
388 sz_orm_cabi::sz_orm_model_delete(
389 pool_handle as sz_orm_cabi::SzOrmPoolHandle,
390 c_table.as_ptr(),
391 c_where.as_ptr(),
392 c_where_params.as_ptr(),
393 )
394 };
395 if r.success == 0 {
396 Ok(-1)
397 } else {
398 Ok(r.rows_affected as jlong)
399 }
400 })
401 .resolve::<jni::errors::ThrowRuntimeExAndDefault>()
402}
403
404#[no_mangle]
406pub extern "system" fn Java_sz_1orm_1java_SzOrmPool_modelFind<'local>(
407 mut unowned_env: EnvUnowned<'local>,
408 _class: JClass<'local>,
409 pool_handle: jlong,
410 table: JString<'local>,
411 where_clause: JString<'local>,
412 where_params_json: JString<'local>,
413) -> jstring {
414 if pool_handle == 0 {
415 return std::ptr::null_mut();
416 }
417 unowned_env
418 .with_env(|env| -> jni::errors::Result<jstring> {
419 let table_s: String = table.to_string();
420 let where_s: String = where_clause.to_string();
421 let where_params_s: String = where_params_json.to_string();
422 let c_table = CString::new(table_s).map_err(|_| jni::errors::Error::JavaException)?;
423 let c_where = CString::new(where_s).map_err(|_| jni::errors::Error::JavaException)?;
424 let c_where_params =
425 CString::new(where_params_s).map_err(|_| jni::errors::Error::JavaException)?;
426 let ptr = unsafe {
428 sz_orm_cabi::sz_orm_model_find(
429 pool_handle as sz_orm_cabi::SzOrmPoolHandle,
430 c_table.as_ptr(),
431 c_where.as_ptr(),
432 c_where_params.as_ptr(),
433 )
434 };
435 let json = if ptr.is_null() {
436 String::new()
437 } else {
438 let s = unsafe { std::ffi::CStr::from_ptr(ptr) }
440 .to_str()
441 .map(|s| s.to_string())
442 .unwrap_or_default();
443 unsafe { sz_orm_cabi::sz_orm_string_free(ptr) };
445 s
446 };
447 let jstr = env.new_string(&json)?;
448 Ok(jstr.into_raw())
449 })
450 .resolve::<jni::errors::ThrowRuntimeExAndDefault>()
451}
452
453#[no_mangle]
455pub extern "system" fn Java_sz_1orm_1java_SzOrmPool_modelInsertTx<'local>(
456 mut unowned_env: EnvUnowned<'local>,
457 _class: JClass<'local>,
458 tx_handle: jlong,
459 table: JString<'local>,
460 fields_json: JString<'local>,
461 values_json: JString<'local>,
462) -> jlong {
463 if tx_handle == 0 {
464 return -1;
465 }
466 unowned_env
467 .with_env(|_env| -> jni::errors::Result<jlong> {
468 let table_s: String = table.to_string();
469 let fields_s: String = fields_json.to_string();
470 let values_s: String = values_json.to_string();
471 let c_table = CString::new(table_s).map_err(|_| jni::errors::Error::JavaException)?;
472 let c_fields = CString::new(fields_s).map_err(|_| jni::errors::Error::JavaException)?;
473 let c_values = CString::new(values_s).map_err(|_| jni::errors::Error::JavaException)?;
474 let r = unsafe {
476 sz_orm_cabi::sz_orm_model_insert_tx(
477 tx_handle as sz_orm_cabi::SzOrmTransactionHandle,
478 c_table.as_ptr(),
479 c_fields.as_ptr(),
480 c_values.as_ptr(),
481 )
482 };
483 if r.success == 0 {
484 Ok(-1)
485 } else {
486 Ok(r.rows_affected as jlong)
487 }
488 })
489 .resolve::<jni::errors::ThrowRuntimeExAndDefault>()
490}
491
492#[no_mangle]
494pub extern "system" fn Java_sz_1orm_1java_SzOrmPool_modelUpdateTx<'local>(
495 mut unowned_env: EnvUnowned<'local>,
496 _class: JClass<'local>,
497 tx_handle: jlong,
498 table: JString<'local>,
499 set_json: JString<'local>,
500 where_clause: JString<'local>,
501 where_params_json: JString<'local>,
502) -> jlong {
503 if tx_handle == 0 {
504 return -1;
505 }
506 unowned_env
507 .with_env(|_env| -> jni::errors::Result<jlong> {
508 let table_s: String = table.to_string();
509 let set_s: String = set_json.to_string();
510 let where_s: String = where_clause.to_string();
511 let where_params_s: String = where_params_json.to_string();
512 let c_table = CString::new(table_s).map_err(|_| jni::errors::Error::JavaException)?;
513 let c_set = CString::new(set_s).map_err(|_| jni::errors::Error::JavaException)?;
514 let c_where = CString::new(where_s).map_err(|_| jni::errors::Error::JavaException)?;
515 let c_where_params =
516 CString::new(where_params_s).map_err(|_| jni::errors::Error::JavaException)?;
517 let r = unsafe {
519 sz_orm_cabi::sz_orm_model_update_tx(
520 tx_handle as sz_orm_cabi::SzOrmTransactionHandle,
521 c_table.as_ptr(),
522 c_set.as_ptr(),
523 c_where.as_ptr(),
524 c_where_params.as_ptr(),
525 )
526 };
527 if r.success == 0 {
528 Ok(-1)
529 } else {
530 Ok(r.rows_affected as jlong)
531 }
532 })
533 .resolve::<jni::errors::ThrowRuntimeExAndDefault>()
534}
535
536#[no_mangle]
538pub extern "system" fn Java_sz_1orm_1java_SzOrmPool_modelDeleteTx<'local>(
539 mut unowned_env: EnvUnowned<'local>,
540 _class: JClass<'local>,
541 tx_handle: jlong,
542 table: JString<'local>,
543 where_clause: JString<'local>,
544 where_params_json: JString<'local>,
545) -> jlong {
546 if tx_handle == 0 {
547 return -1;
548 }
549 unowned_env
550 .with_env(|_env| -> jni::errors::Result<jlong> {
551 let table_s: String = table.to_string();
552 let where_s: String = where_clause.to_string();
553 let where_params_s: String = where_params_json.to_string();
554 let c_table = CString::new(table_s).map_err(|_| jni::errors::Error::JavaException)?;
555 let c_where = CString::new(where_s).map_err(|_| jni::errors::Error::JavaException)?;
556 let c_where_params =
557 CString::new(where_params_s).map_err(|_| jni::errors::Error::JavaException)?;
558 let r = unsafe {
560 sz_orm_cabi::sz_orm_model_delete_tx(
561 tx_handle as sz_orm_cabi::SzOrmTransactionHandle,
562 c_table.as_ptr(),
563 c_where.as_ptr(),
564 c_where_params.as_ptr(),
565 )
566 };
567 if r.success == 0 {
568 Ok(-1)
569 } else {
570 Ok(r.rows_affected as jlong)
571 }
572 })
573 .resolve::<jni::errors::ThrowRuntimeExAndDefault>()
574}
575
576#[no_mangle]
578pub extern "system" fn Java_sz_1orm_1java_SzOrmPool_modelFindTx<'local>(
579 mut unowned_env: EnvUnowned<'local>,
580 _class: JClass<'local>,
581 tx_handle: jlong,
582 table: JString<'local>,
583 where_clause: JString<'local>,
584 where_params_json: JString<'local>,
585) -> jstring {
586 if tx_handle == 0 {
587 return std::ptr::null_mut();
588 }
589 unowned_env
590 .with_env(|env| -> jni::errors::Result<jstring> {
591 let table_s: String = table.to_string();
592 let where_s: String = where_clause.to_string();
593 let where_params_s: String = where_params_json.to_string();
594 let c_table = CString::new(table_s).map_err(|_| jni::errors::Error::JavaException)?;
595 let c_where = CString::new(where_s).map_err(|_| jni::errors::Error::JavaException)?;
596 let c_where_params =
597 CString::new(where_params_s).map_err(|_| jni::errors::Error::JavaException)?;
598 let ptr = unsafe {
600 sz_orm_cabi::sz_orm_model_find_tx(
601 tx_handle as sz_orm_cabi::SzOrmTransactionHandle,
602 c_table.as_ptr(),
603 c_where.as_ptr(),
604 c_where_params.as_ptr(),
605 )
606 };
607 let json = if ptr.is_null() {
608 String::new()
609 } else {
610 let s = unsafe { std::ffi::CStr::from_ptr(ptr) }
612 .to_str()
613 .map(|s| s.to_string())
614 .unwrap_or_default();
615 unsafe { sz_orm_cabi::sz_orm_string_free(ptr) };
617 s
618 };
619 let jstr = env.new_string(&json)?;
620 Ok(jstr.into_raw())
621 })
622 .resolve::<jni::errors::ThrowRuntimeExAndDefault>()
623}
624
625#[cfg(test)]
626mod tests {
627 use std::ffi::CString;
628 use sz_orm_cabi::{PoolConfigC, SzOrmPoolHandle};
629
630 fn test_pool() -> SzOrmPoolHandle {
631 let dsn = CString::new("sqlite::memory:").unwrap();
632 let config = PoolConfigC {
633 max_connections: 1,
634 min_connections: 1,
635 connect_timeout_ms: 5000,
636 idle_timeout_ms: 300000,
637 };
638 unsafe { sz_orm_cabi::sz_orm_pool_new(dsn.as_ptr(), &config) }
640 }
641
642 #[test]
643 fn test_java_binding_pool_create() {
644 let pool = test_pool();
645 assert!(!pool.is_null(), "pool should be created via cabi");
646 unsafe { sz_orm_cabi::sz_orm_pool_free(pool) };
648 }
649
650 #[test]
651 fn test_java_binding_ping() {
652 let pool = test_pool();
653 assert!(!pool.is_null());
654 let healthy = unsafe { sz_orm_cabi::sz_orm_ping(pool) };
656 assert_eq!(healthy, 1);
657 unsafe { sz_orm_cabi::sz_orm_pool_free(pool) };
659 }
660
661 #[test]
662 fn test_java_binding_version() {
663 let v = sz_orm_cabi::sz_orm_version();
664 assert!(v >= 1, "version should be >= 1, got {v}");
665 }
666
667 #[test]
668 fn test_java_binding_execute_and_query() {
669 let pool = test_pool();
670 assert!(!pool.is_null());
671
672 let create = CString::new("CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT)").unwrap();
673 let r = unsafe { sz_orm_cabi::sz_orm_execute(pool, create.as_ptr()) };
675 assert_eq!(r.success, 1);
676
677 let insert = CString::new("INSERT INTO t (v) VALUES ('x')").unwrap();
678 let r = unsafe { sz_orm_cabi::sz_orm_execute(pool, insert.as_ptr()) };
680 assert_eq!(r.success, 1);
681 assert_eq!(r.rows_affected, 1);
682
683 let q = CString::new("SELECT v FROM t").unwrap();
684 let result_ptr = unsafe { sz_orm_cabi::sz_orm_query(pool, q.as_ptr()) };
686 assert!(!result_ptr.is_null());
687 let result_box = unsafe { Box::from_raw(result_ptr) };
689 assert_eq!(result_box.success, 1);
690 unsafe { sz_orm_cabi::sz_orm_query_result_free(Box::into_raw(result_box)) };
692
693 unsafe { sz_orm_cabi::sz_orm_pool_free(pool) };
695 }
696
697 #[test]
698 fn test_java_binding_null_dsn_returns_null() {
699 let pool = unsafe { sz_orm_cabi::sz_orm_pool_new(std::ptr::null(), std::ptr::null()) };
701 assert!(pool.is_null(), "null dsn should return null pool");
702 }
703
704 #[test]
705 fn test_java_binding_pool_config_clamped() {
706 let dsn = CString::new("sqlite::memory:").unwrap();
707 let config = PoolConfigC {
708 max_connections: 0,
709 min_connections: 0,
710 connect_timeout_ms: 100,
711 idle_timeout_ms: 100,
712 };
713 let pool = unsafe { sz_orm_cabi::sz_orm_pool_new(dsn.as_ptr(), &config) };
715 if !pool.is_null() {
717 unsafe { sz_orm_cabi::sz_orm_pool_free(pool) };
719 }
720 }
721
722 fn model_test_pool() -> SzOrmPoolHandle {
725 let pool = test_pool();
726 assert!(!pool.is_null());
727 let create =
728 CString::new("CREATE TABLE jm_t (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)")
729 .unwrap();
730 let r = unsafe { sz_orm_cabi::sz_orm_execute(pool, create.as_ptr()) };
732 assert_eq!(r.success, 1, "CREATE should succeed");
733 pool
734 }
735
736 #[test]
737 fn test_java_model_insert_find_roundtrip() {
738 let pool = model_test_pool();
739 let table = CString::new("jm_t").unwrap();
740 let fields = CString::new(r#"["name","age"]"#).unwrap();
741 let values = CString::new(r#"["Alice",30]"#).unwrap();
742 let r = unsafe {
744 sz_orm_cabi::sz_orm_model_insert(pool, table.as_ptr(), fields.as_ptr(), values.as_ptr())
745 };
746 assert_eq!(
747 r.success, 1,
748 "java model_insert should succeed, code={}",
749 r.error_code
750 );
751 assert_eq!(r.rows_affected, 1);
752
753 let where_clause = CString::new("name = ?").unwrap();
754 let where_params = CString::new(r#"["Alice"]"#).unwrap();
755 let ptr = unsafe {
757 sz_orm_cabi::sz_orm_model_find(
758 pool,
759 table.as_ptr(),
760 where_clause.as_ptr(),
761 where_params.as_ptr(),
762 )
763 };
764 assert!(!ptr.is_null());
765 let json = unsafe { std::ffi::CStr::from_ptr(ptr) }
767 .to_string_lossy()
768 .into_owned();
769 unsafe { sz_orm_cabi::sz_orm_string_free(ptr) };
771 assert!(
772 json.contains("Alice"),
773 "java model_find should contain Alice: {json}"
774 );
775 unsafe { sz_orm_cabi::sz_orm_pool_free(pool) };
777 }
778
779 #[test]
780 fn test_java_model_update_delete_find() {
781 let pool = model_test_pool();
782 let table = CString::new("jm_t").unwrap();
783 let fields = CString::new(r#"["name","age"]"#).unwrap();
784 let values = CString::new(r#"["Bob",25]"#).unwrap();
785 unsafe {
787 sz_orm_cabi::sz_orm_model_insert(pool, table.as_ptr(), fields.as_ptr(), values.as_ptr())
788 };
789
790 let set_json = CString::new(r#"{"age":26}"#).unwrap();
791 let where_clause = CString::new("name = ?").unwrap();
792 let where_params = CString::new(r#"["Bob"]"#).unwrap();
793 let r = unsafe {
795 sz_orm_cabi::sz_orm_model_update(
796 pool,
797 table.as_ptr(),
798 set_json.as_ptr(),
799 where_clause.as_ptr(),
800 where_params.as_ptr(),
801 )
802 };
803 assert_eq!(
804 r.success, 1,
805 "java model_update should succeed, code={}",
806 r.error_code
807 );
808
809 let ptr = unsafe {
811 sz_orm_cabi::sz_orm_model_find(
812 pool,
813 table.as_ptr(),
814 where_clause.as_ptr(),
815 where_params.as_ptr(),
816 )
817 };
818 let json = unsafe { std::ffi::CStr::from_ptr(ptr) }
820 .to_string_lossy()
821 .into_owned();
822 unsafe { sz_orm_cabi::sz_orm_string_free(ptr) };
823 assert!(
824 json.contains("26"),
825 "java find after update should contain age 26: {json}"
826 );
827
828 let r = unsafe {
830 sz_orm_cabi::sz_orm_model_delete(
831 pool,
832 table.as_ptr(),
833 where_clause.as_ptr(),
834 where_params.as_ptr(),
835 )
836 };
837 assert_eq!(
838 r.success, 1,
839 "java model_delete should succeed, code={}",
840 r.error_code
841 );
842
843 let ptr = unsafe {
845 sz_orm_cabi::sz_orm_model_find(
846 pool,
847 table.as_ptr(),
848 where_clause.as_ptr(),
849 where_params.as_ptr(),
850 )
851 };
852 let json = unsafe { std::ffi::CStr::from_ptr(ptr) }
854 .to_string_lossy()
855 .into_owned();
856 unsafe { sz_orm_cabi::sz_orm_string_free(ptr) };
857 assert!(
858 json == "[]" || json == "null" || json.is_empty(),
859 "java find after delete should be empty: {json}"
860 );
861 unsafe { sz_orm_cabi::sz_orm_pool_free(pool) };
863 }
864
865 #[test]
866 fn test_java_model_insert_illegal_table() {
867 let pool = model_test_pool();
868 let table = CString::new("jm_t; DROP--").unwrap();
869 let fields = CString::new(r#"["name"]"#).unwrap();
870 let values = CString::new(r#"["X"]"#).unwrap();
871 let r = unsafe {
873 sz_orm_cabi::sz_orm_model_insert(pool, table.as_ptr(), fields.as_ptr(), values.as_ptr())
874 };
875 assert_eq!(r.success, 0, "illegal table should fail");
876 assert_eq!(
877 r.error_code,
878 sz_orm_cabi::SzOrmErrorCode::InvalidArgument.as_i32()
879 );
880 unsafe { sz_orm_cabi::sz_orm_pool_free(pool) };
882 }
883
884 #[test]
885 fn test_java_transaction_model_rollback() {
886 let pool = model_test_pool();
887 let tx = unsafe { sz_orm_cabi::sz_orm_transaction_begin(pool) };
889 assert!(!tx.is_null());
890
891 let table = CString::new("jm_t").unwrap();
892 let fields = CString::new(r#"["name","age"]"#).unwrap();
893 let values = CString::new(r#"["TxUser",99]"#).unwrap();
894 let r = unsafe {
896 sz_orm_cabi::sz_orm_model_insert_tx(
897 tx,
898 table.as_ptr(),
899 fields.as_ptr(),
900 values.as_ptr(),
901 )
902 };
903 assert_eq!(
904 r.success, 1,
905 "java model_insert_tx should succeed, code={}",
906 r.error_code
907 );
908
909 assert_eq!(unsafe { sz_orm_cabi::sz_orm_transaction_rollback(tx) }, 1);
911 unsafe { sz_orm_cabi::sz_orm_transaction_free(tx) };
913
914 let where_clause = CString::new("name = ?").unwrap();
915 let where_params = CString::new(r#"["TxUser"]"#).unwrap();
916 let ptr = unsafe {
918 sz_orm_cabi::sz_orm_model_find(
919 pool,
920 table.as_ptr(),
921 where_clause.as_ptr(),
922 where_params.as_ptr(),
923 )
924 };
925 let json = unsafe { std::ffi::CStr::from_ptr(ptr) }
927 .to_string_lossy()
928 .into_owned();
929 unsafe { sz_orm_cabi::sz_orm_string_free(ptr) };
930 assert!(
931 json == "[]" || json == "null" || json.is_empty(),
932 "java find after rollback should be empty: {json}"
933 );
934 unsafe { sz_orm_cabi::sz_orm_pool_free(pool) };
936 }
937
938 #[test]
939 fn test_java_transaction_model_commit() {
940 let pool = model_test_pool();
941 let tx = unsafe { sz_orm_cabi::sz_orm_transaction_begin(pool) };
943 assert!(!tx.is_null());
944
945 let table = CString::new("jm_t").unwrap();
946 let fields = CString::new(r#"["name","age"]"#).unwrap();
947 let values = CString::new(r#"["CommitUser",50]"#).unwrap();
948 unsafe {
950 sz_orm_cabi::sz_orm_model_insert_tx(
951 tx,
952 table.as_ptr(),
953 fields.as_ptr(),
954 values.as_ptr(),
955 )
956 };
957
958 assert_eq!(unsafe { sz_orm_cabi::sz_orm_transaction_commit(tx) }, 1);
960 unsafe { sz_orm_cabi::sz_orm_transaction_free(tx) };
962
963 let where_clause = CString::new("name = ?").unwrap();
964 let where_params = CString::new(r#"["CommitUser"]"#).unwrap();
965 let ptr = unsafe {
967 sz_orm_cabi::sz_orm_model_find(
968 pool,
969 table.as_ptr(),
970 where_clause.as_ptr(),
971 where_params.as_ptr(),
972 )
973 };
974 let json = unsafe { std::ffi::CStr::from_ptr(ptr) }
976 .to_string_lossy()
977 .into_owned();
978 unsafe { sz_orm_cabi::sz_orm_string_free(ptr) };
979 assert!(
980 json.contains("CommitUser"),
981 "java find after commit should contain CommitUser: {json}"
982 );
983 unsafe { sz_orm_cabi::sz_orm_pool_free(pool) };
985 }
986}