Skip to main content

sz_orm_cpp/
lib.rs

1//! # SZ-ORM C++ Bindings
2//!
3//! Provides sz-orm-core's Pool/Query API to C++ via `extern "C"` + C ABI
4//! (SQLite backend, real and usable).
5//!
6//! C++ side wrapper see `cpp/szorm.h` (use `szorm::Pool` after `#include <szorm.h>`).
7
8use std::ffi::{c_char, CStr, CString};
9
10use sz_orm_cabi::{
11    PoolConfigC, PoolMetricsC, PoolStatsC, QueryJsonResult, QueryResultC, SzOrmPoolHandle,
12    SzOrmQueryBuilderHandle, SzOrmTransactionHandle,
13};
14
15/// Create connection pool (real creation, SQLite backend)
16///
17/// Returns handle, null indicates failure.
18///
19/// # Safety
20///
21/// SAFETY: `dsn` must be a valid NUL-terminated C string; `config` may be null.
22#[no_mangle]
23pub unsafe extern "C" fn sz_orm_cpp_pool_new(
24    dsn: *const c_char,
25    config: *const PoolConfigC,
26) -> SzOrmPoolHandle {
27    // SAFETY: 转发到 cabi
28    unsafe { sz_orm_cabi::sz_orm_pool_new(dsn, config) }
29}
30
31/// Free connection pool
32///
33/// # Safety
34///
35/// SAFETY: handle must be a valid handle returned by `sz_orm_cpp_pool_new`.
36#[no_mangle]
37pub unsafe extern "C" fn sz_orm_cpp_pool_free(handle: SzOrmPoolHandle) {
38    // SAFETY: 转发到 cabi
39    unsafe { sz_orm_cabi::sz_orm_pool_free(handle) }
40}
41
42/// Health check (real acquire + ping)
43///
44/// # Safety
45///
46/// SAFETY: handle must be a valid handle returned by `sz_orm_cpp_pool_new`.
47#[no_mangle]
48pub unsafe extern "C" fn sz_orm_cpp_ping(handle: SzOrmPoolHandle) -> i32 {
49    // SAFETY: 转发到 cabi
50    unsafe { sz_orm_cabi::sz_orm_ping(handle) }
51}
52
53/// Execute query, return JSON row array (caller frees with `sz_orm_cpp_string_free`)
54///
55/// # Safety
56///
57/// SAFETY: handle must be a valid handle returned by `sz_orm_cpp_pool_new`;
58/// `sql` must be a valid NUL-terminated C string.
59#[no_mangle]
60pub unsafe extern "C" fn sz_orm_cpp_query(
61    handle: SzOrmPoolHandle,
62    sql: *const c_char,
63) -> *mut c_char {
64    if handle.is_null() || sql.is_null() {
65        return std::ptr::null_mut();
66    }
67    // SAFETY: 调用方保证 handle 有效
68    let result = unsafe { sz_orm_cabi::sz_orm_query(handle, sql) };
69    if result.is_null() {
70        return std::ptr::null_mut();
71    }
72    // SAFETY: result 由 sz_orm_query 分配
73    let result_box = unsafe { Box::from_raw(result) };
74    let json = if result_box.success != 0 && !result_box.json.is_null() {
75        // SAFETY: json 是有效 NUL 结尾 C 字符串
76        unsafe { CStr::from_ptr(result_box.json) }
77            .to_str()
78            .map(|s| s.to_string())
79            .unwrap_or_default()
80    } else {
81        // SAFETY: result_box 配对释放
82        unsafe {
83            sz_orm_cabi::sz_orm_query_result_free(Box::into_raw(result_box));
84        }
85        return std::ptr::null_mut();
86    };
87    // SAFETY: result_box 配对释放
88    unsafe {
89        sz_orm_cabi::sz_orm_query_result_free(Box::into_raw(result_box));
90    }
91    match CString::new(json) {
92        Ok(c) => c.into_raw(),
93        Err(_) => std::ptr::null_mut(),
94    }
95}
96
97/// Free string returned by `sz_orm_cpp_query`
98///
99/// # Safety
100///
101/// SAFETY: `ptr` must be a pointer returned by `sz_orm_cpp_query` (or null).
102#[no_mangle]
103pub unsafe extern "C" fn sz_orm_cpp_string_free(ptr: *mut c_char) {
104    if ptr.is_null() {
105        return;
106    }
107    // SAFETY: ptr 由 CString::into_raw 分配,配对释放
108    unsafe {
109        drop(CString::from_raw(ptr));
110    }
111}
112
113/// Execute write statement, return heap-allocated `QueryResultC` (free with `sz_orm_cpp_result_free`)
114///
115/// # Safety
116///
117/// SAFETY: handle must be a valid handle returned by `sz_orm_cpp_pool_new`;
118/// `sql` must be a valid NUL-terminated C string.
119#[no_mangle]
120pub unsafe extern "C" fn sz_orm_cpp_execute(
121    handle: SzOrmPoolHandle,
122    sql: *const c_char,
123) -> *mut QueryResultC {
124    if handle.is_null() || sql.is_null() {
125        return std::ptr::null_mut();
126    }
127    // SAFETY: 转发到 cabi
128    let result = unsafe { sz_orm_cabi::sz_orm_execute(handle, sql) };
129    Box::into_raw(Box::new(result))
130}
131
132/// Free result returned by `sz_orm_cpp_execute`
133///
134/// # Safety
135///
136/// SAFETY: `ptr` must be a pointer returned by `sz_orm_cpp_execute` (or null).
137#[no_mangle]
138pub unsafe extern "C" fn sz_orm_cpp_result_free(ptr: *mut QueryResultC) {
139    if ptr.is_null() {
140        return;
141    }
142    // SAFETY: ptr 由 sz_orm_cpp_execute 分配,配对释放
143    unsafe {
144        drop(Box::from_raw(ptr));
145    }
146}
147
148/// Get version number
149#[no_mangle]
150pub extern "C" fn sz_orm_cpp_version() -> u32 {
151    sz_orm_cabi::sz_orm_version()
152}
153
154// ============================================================================
155// 事务 API 转发(REQ-BND-006)
156// ============================================================================
157
158/// Begin transaction, return transaction handle (null indicates failure)
159///
160/// # Safety
161///
162/// SAFETY: `handle` must be a valid handle returned by `sz_orm_cpp_pool_new`.
163#[no_mangle]
164pub unsafe extern "C" fn sz_orm_cpp_transaction_begin(
165    handle: SzOrmPoolHandle,
166) -> SzOrmTransactionHandle {
167    // SAFETY: 转发到 cabi
168    unsafe { sz_orm_cabi::sz_orm_transaction_begin(handle) }
169}
170
171/// Execute SQL in transaction, return heap-allocated `QueryResultC`
172///
173/// # Safety
174///
175/// SAFETY: `tx_handle` must be valid; `sql` must be a valid NUL-terminated C string.
176#[no_mangle]
177pub unsafe extern "C" fn sz_orm_cpp_transaction_execute(
178    tx_handle: SzOrmTransactionHandle,
179    sql: *const c_char,
180) -> *mut QueryResultC {
181    if tx_handle.is_null() || sql.is_null() {
182        return std::ptr::null_mut();
183    }
184    // SAFETY: 转发到 cabi
185    let result = unsafe { sz_orm_cabi::sz_orm_transaction_execute(tx_handle, sql) };
186    Box::into_raw(Box::new(result))
187}
188
189/// Commit transaction, return 1=success 0=failure
190///
191/// # Safety
192///
193/// SAFETY: `tx_handle` must be valid.
194#[no_mangle]
195pub unsafe extern "C" fn sz_orm_cpp_transaction_commit(tx_handle: SzOrmTransactionHandle) -> i32 {
196    // SAFETY: 转发到 cabi
197    unsafe { sz_orm_cabi::sz_orm_transaction_commit(tx_handle) }
198}
199
200/// Rollback transaction, return 1=success 0=failure
201///
202/// # Safety
203///
204/// SAFETY: `tx_handle` must be valid.
205#[no_mangle]
206pub unsafe extern "C" fn sz_orm_cpp_transaction_rollback(tx_handle: SzOrmTransactionHandle) -> i32 {
207    // SAFETY: 转发到 cabi
208    unsafe { sz_orm_cabi::sz_orm_transaction_rollback(tx_handle) }
209}
210
211/// Free transaction handle (auto rollback if still active)
212///
213/// # Safety
214///
215/// SAFETY: `tx_handle` must be valid.
216#[no_mangle]
217pub unsafe extern "C" fn sz_orm_cpp_transaction_free(tx_handle: SzOrmTransactionHandle) {
218    // SAFETY: 转发到 cabi
219    unsafe { sz_orm_cabi::sz_orm_transaction_free(tx_handle) }
220}
221
222// ============================================================================
223// 模型级 API 转发(REQ-BND-013)
224// ============================================================================
225
226/// Insert row on pool, return heap-allocated `QueryResultC`
227///
228/// # Safety
229///
230/// SAFETY: `handle` must be valid; all parameters must be valid NUL-terminated C strings.
231#[no_mangle]
232pub unsafe extern "C" fn sz_orm_cpp_model_insert(
233    handle: SzOrmPoolHandle,
234    table: *const c_char,
235    fields_json: *const c_char,
236    values_json: *const c_char,
237) -> *mut QueryResultC {
238    if handle.is_null() || table.is_null() || fields_json.is_null() || values_json.is_null() {
239        return std::ptr::null_mut();
240    }
241    // SAFETY: 转发到 cabi
242    let result =
243        unsafe { sz_orm_cabi::sz_orm_model_insert(handle, table, fields_json, values_json) };
244    Box::into_raw(Box::new(result))
245}
246
247/// Update row on pool, return heap-allocated `QueryResultC`
248///
249/// # Safety
250///
251/// SAFETY: `handle` must be valid; all parameters must be valid NUL-terminated C strings.
252#[no_mangle]
253pub unsafe extern "C" fn sz_orm_cpp_model_update(
254    handle: SzOrmPoolHandle,
255    table: *const c_char,
256    set_json: *const c_char,
257    where_clause: *const c_char,
258    where_params_json: *const c_char,
259) -> *mut QueryResultC {
260    if handle.is_null() || table.is_null() || set_json.is_null() {
261        return std::ptr::null_mut();
262    }
263    // SAFETY: 转发到 cabi
264    let result = unsafe {
265        sz_orm_cabi::sz_orm_model_update(handle, table, set_json, where_clause, where_params_json)
266    };
267    Box::into_raw(Box::new(result))
268}
269
270/// Delete row on pool, return heap-allocated `QueryResultC`
271///
272/// # Safety
273///
274/// SAFETY: `handle` must be valid; all parameters must be valid NUL-terminated C strings.
275#[no_mangle]
276pub unsafe extern "C" fn sz_orm_cpp_model_delete(
277    handle: SzOrmPoolHandle,
278    table: *const c_char,
279    where_clause: *const c_char,
280    where_params_json: *const c_char,
281) -> *mut QueryResultC {
282    if handle.is_null() || table.is_null() {
283        return std::ptr::null_mut();
284    }
285    // SAFETY: 转发到 cabi
286    let result =
287        unsafe { sz_orm_cabi::sz_orm_model_delete(handle, table, where_clause, where_params_json) };
288    Box::into_raw(Box::new(result))
289}
290
291/// Query row on pool, return JSON row array string (free with `sz_orm_cpp_string_free`)
292///
293/// # Safety
294///
295/// SAFETY: `handle` must be valid; all parameters must be valid NUL-terminated C strings.
296#[no_mangle]
297pub unsafe extern "C" fn sz_orm_cpp_model_find(
298    handle: SzOrmPoolHandle,
299    table: *const c_char,
300    where_clause: *const c_char,
301    where_params_json: *const c_char,
302) -> *mut c_char {
303    if handle.is_null() || table.is_null() {
304        return std::ptr::null_mut();
305    }
306    // SAFETY: 转发到 cabi
307    unsafe { sz_orm_cabi::sz_orm_model_find(handle, table, where_clause, where_params_json) }
308}
309
310/// Insert row in transaction, return heap-allocated `QueryResultC`
311///
312/// # Safety
313///
314/// SAFETY: `tx_handle` must be valid; all parameters must be valid NUL-terminated C strings.
315#[no_mangle]
316pub unsafe extern "C" fn sz_orm_cpp_model_insert_tx(
317    tx_handle: SzOrmTransactionHandle,
318    table: *const c_char,
319    fields_json: *const c_char,
320    values_json: *const c_char,
321) -> *mut QueryResultC {
322    if tx_handle.is_null() || table.is_null() || fields_json.is_null() || values_json.is_null() {
323        return std::ptr::null_mut();
324    }
325    // SAFETY: 转发到 cabi
326    let result =
327        unsafe { sz_orm_cabi::sz_orm_model_insert_tx(tx_handle, table, fields_json, values_json) };
328    Box::into_raw(Box::new(result))
329}
330
331/// Update row in transaction, return heap-allocated `QueryResultC`
332///
333/// # Safety
334///
335/// SAFETY: `tx_handle` must be valid; all parameters must be valid NUL-terminated C strings.
336#[no_mangle]
337pub unsafe extern "C" fn sz_orm_cpp_model_update_tx(
338    tx_handle: SzOrmTransactionHandle,
339    table: *const c_char,
340    set_json: *const c_char,
341    where_clause: *const c_char,
342    where_params_json: *const c_char,
343) -> *mut QueryResultC {
344    if tx_handle.is_null() || table.is_null() || set_json.is_null() {
345        return std::ptr::null_mut();
346    }
347    // SAFETY: 转发到 cabi
348    let result = unsafe {
349        sz_orm_cabi::sz_orm_model_update_tx(
350            tx_handle,
351            table,
352            set_json,
353            where_clause,
354            where_params_json,
355        )
356    };
357    Box::into_raw(Box::new(result))
358}
359
360/// Delete row in transaction, return heap-allocated `QueryResultC`
361///
362/// # Safety
363///
364/// SAFETY: `tx_handle` must be valid; all parameters must be valid NUL-terminated C strings.
365#[no_mangle]
366pub unsafe extern "C" fn sz_orm_cpp_model_delete_tx(
367    tx_handle: SzOrmTransactionHandle,
368    table: *const c_char,
369    where_clause: *const c_char,
370    where_params_json: *const c_char,
371) -> *mut QueryResultC {
372    if tx_handle.is_null() || table.is_null() {
373        return std::ptr::null_mut();
374    }
375    // SAFETY: 转发到 cabi
376    let result = unsafe {
377        sz_orm_cabi::sz_orm_model_delete_tx(tx_handle, table, where_clause, where_params_json)
378    };
379    Box::into_raw(Box::new(result))
380}
381
382/// Query row in transaction, return JSON row array string (free with `sz_orm_cpp_string_free`)
383///
384/// # Safety
385///
386/// SAFETY: `tx_handle` must be valid; all parameters must be valid NUL-terminated C strings.
387#[no_mangle]
388pub unsafe extern "C" fn sz_orm_cpp_model_find_tx(
389    tx_handle: SzOrmTransactionHandle,
390    table: *const c_char,
391    where_clause: *const c_char,
392    where_params_json: *const c_char,
393) -> *mut c_char {
394    if tx_handle.is_null() || table.is_null() {
395        return std::ptr::null_mut();
396    }
397    // SAFETY: 转发到 cabi
398    unsafe { sz_orm_cabi::sz_orm_model_find_tx(tx_handle, table, where_clause, where_params_json) }
399}
400
401// ============================================================================
402// QueryBuilder API 转发(v6.9.0 REQ-BND-QB)
403// ============================================================================
404
405/// Create QueryBuilder, return handle (null indicates failure)
406///
407/// # Safety
408///
409/// SAFETY: 返回的句柄必须通过 `sz_orm_cpp_qb_free` 释放。
410#[no_mangle]
411pub unsafe extern "C" fn sz_orm_cpp_qb_new(db_type: u32) -> SzOrmQueryBuilderHandle {
412    // SAFETY: 转发到 cabi
413    unsafe { sz_orm_cabi::sz_orm_qb_new(db_type) }
414}
415
416/// Set table name on QueryBuilder
417///
418/// # Safety
419///
420/// SAFETY: `qb` 必须有效;`table` 必须是有效的 NUL 终止 C 字符串。
421#[no_mangle]
422pub unsafe extern "C" fn sz_orm_cpp_qb_table(
423    qb: SzOrmQueryBuilderHandle,
424    table: *const c_char,
425) -> i32 {
426    // SAFETY: 转发到 cabi
427    unsafe { sz_orm_cabi::sz_orm_qb_table(qb, table) }
428}
429
430/// Add WHERE eq condition
431///
432/// # Safety
433///
434/// SAFETY: `qb` 必须有效;`field` 和 `value_json` 必须是有效的 NUL 终止 C 字符串。
435#[no_mangle]
436pub unsafe extern "C" fn sz_orm_cpp_qb_where_eq(
437    qb: SzOrmQueryBuilderHandle,
438    field: *const c_char,
439    value_json: *const c_char,
440) -> i32 {
441    // SAFETY: 转发到 cabi
442    unsafe { sz_orm_cabi::sz_orm_qb_where_eq(qb, field, value_json) }
443}
444
445/// Add ORDER BY clause
446///
447/// # Safety
448///
449/// SAFETY: `qb` 必须有效;`field` 必须是有效的 NUL 终止 C 字符串。
450#[no_mangle]
451pub unsafe extern "C" fn sz_orm_cpp_qb_order_by(
452    qb: SzOrmQueryBuilderHandle,
453    field: *const c_char,
454    desc: i32,
455) -> i32 {
456    // SAFETY: 转发到 cabi
457    unsafe { sz_orm_cabi::sz_orm_qb_order_by(qb, field, desc) }
458}
459
460/// Set LIMIT
461///
462/// # Safety
463///
464/// SAFETY: `qb` 必须有效。
465#[no_mangle]
466pub unsafe extern "C" fn sz_orm_cpp_qb_limit(qb: SzOrmQueryBuilderHandle, limit: u64) -> i32 {
467    // SAFETY: 转发到 cabi
468    unsafe { sz_orm_cabi::sz_orm_qb_limit(qb, limit) }
469}
470
471/// Build SQL + params JSON, return C string (free with `sz_orm_cpp_qb_result_free`)
472///
473/// # Safety
474///
475/// SAFETY: `qb` 必须有效。
476#[no_mangle]
477pub unsafe extern "C" fn sz_orm_cpp_qb_build(qb: SzOrmQueryBuilderHandle) -> *mut c_char {
478    // SAFETY: 转发到 cabi
479    unsafe { sz_orm_cabi::sz_orm_qb_build(qb) }
480}
481
482/// Free QueryBuilder handle
483///
484/// # Safety
485///
486/// SAFETY: `qb` 必须是 `sz_orm_cpp_qb_new` 返回的句柄(或 null)。
487#[no_mangle]
488pub unsafe extern "C" fn sz_orm_cpp_qb_free(qb: SzOrmQueryBuilderHandle) {
489    // SAFETY: 转发到 cabi
490    unsafe { sz_orm_cabi::sz_orm_qb_free(qb) }
491}
492
493/// Free build result string
494///
495/// # Safety
496///
497/// SAFETY: `result` 必须是 `sz_orm_cpp_qb_build` 返回的指针(或 null)。
498#[no_mangle]
499pub unsafe extern "C" fn sz_orm_cpp_qb_result_free(result: *mut c_char) {
500    // SAFETY: 转发到 cabi
501    unsafe { sz_orm_cabi::sz_orm_qb_result_free(result) }
502}
503
504// ============================================================================
505// ActiveModel API 转发(v6.9.0 REQ-BND-AM)
506// ============================================================================
507
508/// ActiveModel create (insert), return heap-allocated `QueryResultC`
509///
510/// # Safety
511///
512/// SAFETY: `handle` 必须有效;所有字符串参数必须是有效的 NUL 终止 C 字符串。
513#[no_mangle]
514pub unsafe extern "C" fn sz_orm_cpp_active_model_create(
515    handle: SzOrmPoolHandle,
516    table: *const c_char,
517    fields_json: *const c_char,
518    values_json: *const c_char,
519) -> *mut QueryResultC {
520    if handle.is_null() || table.is_null() || fields_json.is_null() || values_json.is_null() {
521        return std::ptr::null_mut();
522    }
523    // SAFETY: 转发到 cabi
524    let result =
525        unsafe { sz_orm_cabi::sz_orm_model_insert(handle, table, fields_json, values_json) };
526    Box::into_raw(Box::new(result))
527}
528
529/// ActiveModel set (update), return heap-allocated `QueryResultC`
530///
531/// # Safety
532///
533/// SAFETY: `handle` 必须有效;所有字符串参数必须是有效的 NUL 终止 C 字符串。
534#[no_mangle]
535pub unsafe extern "C" fn sz_orm_cpp_active_model_set(
536    handle: SzOrmPoolHandle,
537    table: *const c_char,
538    set_json: *const c_char,
539    where_clause: *const c_char,
540    where_params_json: *const c_char,
541) -> *mut QueryResultC {
542    if handle.is_null() || table.is_null() || set_json.is_null() {
543        return std::ptr::null_mut();
544    }
545    // SAFETY: 转发到 cabi
546    let result = unsafe {
547        sz_orm_cabi::sz_orm_model_update(handle, table, set_json, where_clause, where_params_json)
548    };
549    Box::into_raw(Box::new(result))
550}
551
552/// ActiveModel get (find), return JSON string (free with `sz_orm_cpp_string_free`)
553///
554/// # Safety
555///
556/// SAFETY: `handle` 必须有效;所有字符串参数必须是有效的 NUL 终止 C 字符串。
557#[no_mangle]
558pub unsafe extern "C" fn sz_orm_cpp_active_model_get(
559    handle: SzOrmPoolHandle,
560    table: *const c_char,
561    where_clause: *const c_char,
562    where_params_json: *const c_char,
563) -> *mut c_char {
564    if handle.is_null() || table.is_null() {
565        return std::ptr::null_mut();
566    }
567    // SAFETY: 转发到 cabi
568    unsafe { sz_orm_cabi::sz_orm_model_find(handle, table, where_clause, where_params_json) }
569}
570
571/// ActiveModel save (insert), return heap-allocated `QueryResultC`
572///
573/// # Safety
574///
575/// SAFETY: `handle` 必须有效;所有字符串参数必须是有效的 NUL 终止 C 字符串。
576#[no_mangle]
577pub unsafe extern "C" fn sz_orm_cpp_active_model_save(
578    handle: SzOrmPoolHandle,
579    table: *const c_char,
580    fields_json: *const c_char,
581    values_json: *const c_char,
582) -> *mut QueryResultC {
583    if handle.is_null() || table.is_null() || fields_json.is_null() || values_json.is_null() {
584        return std::ptr::null_mut();
585    }
586    // SAFETY: 转发到 cabi
587    let result =
588        unsafe { sz_orm_cabi::sz_orm_model_insert(handle, table, fields_json, values_json) };
589    Box::into_raw(Box::new(result))
590}
591
592/// ActiveModel delete, return heap-allocated `QueryResultC`
593///
594/// # Safety
595///
596/// SAFETY: `handle` 必须有效;所有字符串参数必须是有效的 NUL 终止 C 字符串。
597#[no_mangle]
598pub unsafe extern "C" fn sz_orm_cpp_active_model_delete(
599    handle: SzOrmPoolHandle,
600    table: *const c_char,
601    where_clause: *const c_char,
602    where_params_json: *const c_char,
603) -> *mut QueryResultC {
604    if handle.is_null() || table.is_null() {
605        return std::ptr::null_mut();
606    }
607    // SAFETY: 转发到 cabi
608    let result =
609        unsafe { sz_orm_cabi::sz_orm_model_delete(handle, table, where_clause, where_params_json) };
610    Box::into_raw(Box::new(result))
611}
612
613/// Get pool statistics
614///
615/// # Safety
616///
617/// SAFETY: handle must be a valid handle returned by `sz_orm_cpp_pool_new`.
618#[no_mangle]
619pub unsafe extern "C" fn sz_orm_cpp_pool_stats(handle: SzOrmPoolHandle) -> PoolStatsC {
620    // SAFETY: 转发到 cabi,handle 有效性由调用方保证
621    unsafe { sz_orm_cabi::sz_orm_pool_stats(handle) }
622}
623
624/// Get pool metrics
625///
626/// # Safety
627///
628/// SAFETY: handle must be a valid handle returned by `sz_orm_cpp_pool_new`.
629#[no_mangle]
630pub unsafe extern "C" fn sz_orm_cpp_pool_metrics(handle: SzOrmPoolHandle) -> PoolMetricsC {
631    // SAFETY: 转发到 cabi,handle 有效性由调用方保证
632    unsafe { sz_orm_cabi::sz_orm_pool_metrics(handle) }
633}
634
635/// Query single row as JSON
636///
637/// # Safety
638///
639/// SAFETY: handle must be valid; sql must be a valid NUL-terminated C string.
640#[no_mangle]
641pub unsafe extern "C" fn sz_orm_cpp_query_one(
642    handle: SzOrmPoolHandle,
643    sql: *const c_char,
644) -> *mut QueryJsonResult {
645    // SAFETY: 转发到 cabi,参数有效性由调用方保证
646    unsafe { sz_orm_cabi::sz_orm_query_one(handle, sql) }
647}
648
649/// Execute batch SQL
650///
651/// # Safety
652///
653/// SAFETY: handle must be valid; sql must be a valid NUL-terminated C string.
654#[no_mangle]
655pub unsafe extern "C" fn sz_orm_cpp_execute_batch(
656    handle: SzOrmPoolHandle,
657    sql: *const c_char,
658) -> QueryResultC {
659    // SAFETY: 转发到 cabi,参数有效性由调用方保证
660    unsafe { sz_orm_cabi::sz_orm_execute_batch(handle, sql) }
661}
662
663/// Free query result
664///
665/// # Safety
666///
667/// SAFETY: result must be a valid pointer returned by `sz_orm_cpp_query_one`.
668#[no_mangle]
669pub unsafe extern "C" fn sz_orm_cpp_query_result_free(result: *mut QueryJsonResult) {
670    // SAFETY: 转发到 cabi,result 有效性由调用方保证
671    unsafe { sz_orm_cabi::sz_orm_query_result_free(result) }
672}
673
674/// Get error description string
675///
676/// # Safety
677///
678/// SAFETY: Caller must free the returned string with `sz_orm_cpp_string_free`.
679#[no_mangle]
680pub unsafe extern "C" fn sz_orm_cpp_error_description(code: i32) -> *mut c_char {
681    // SAFETY: 转发到 cabi
682    unsafe { sz_orm_cabi::sz_orm_error_description(code) }
683}
684
685/// Check if table exists
686///
687/// # Safety
688///
689/// SAFETY: handle must be valid; table must be a valid NUL-terminated C string.
690#[no_mangle]
691pub unsafe extern "C" fn sz_orm_cpp_table_exists(
692    handle: SzOrmPoolHandle,
693    table: *const c_char,
694) -> i32 {
695    // SAFETY: 转发到 cabi,参数有效性由调用方保证
696    unsafe { sz_orm_cabi::sz_orm_table_exists(handle, table) }
697}
698
699/// Count rows in table
700///
701/// # Safety
702///
703/// SAFETY: handle must be valid; table must be a valid NUL-terminated C string.
704#[no_mangle]
705pub unsafe extern "C" fn sz_orm_cpp_count(handle: SzOrmPoolHandle, table: *const c_char) -> i64 {
706    // SAFETY: 转发到 cabi,参数有效性由调用方保证
707    unsafe { sz_orm_cabi::sz_orm_count(handle, table) }
708}
709
710#[cfg(test)]
711mod tests {
712    use super::*;
713
714    fn test_pool() -> SzOrmPoolHandle {
715        let dsn = CString::new("sqlite::memory:").unwrap();
716        // SAFETY: dsn 有效
717        unsafe { sz_orm_cpp_pool_new(dsn.as_ptr(), std::ptr::null()) }
718    }
719
720    #[test]
721    fn test_cpp_pool_new_creates_real_pool() {
722        let pool = test_pool();
723        assert!(!pool.is_null(), "pool_new should create a real pool");
724        // SAFETY: pool 有效
725        unsafe { sz_orm_cpp_pool_free(pool) };
726    }
727
728    #[test]
729    fn test_cpp_ping_healthy() {
730        let pool = test_pool();
731        assert!(!pool.is_null());
732        // SAFETY: pool 有效
733        let healthy = unsafe { sz_orm_cpp_ping(pool) };
734        assert_eq!(healthy, 1, "fresh pool should be healthy");
735        // SAFETY: pool 有效
736        unsafe { sz_orm_cpp_pool_free(pool) };
737    }
738
739    #[test]
740    fn test_cpp_execute_and_query_roundtrip() {
741        let pool = test_pool();
742        assert!(!pool.is_null());
743
744        let create =
745            CString::new("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)").unwrap();
746        // SAFETY: pool/sql 有效
747        let rp = unsafe { sz_orm_cpp_execute(pool, create.as_ptr()) };
748        assert!(!rp.is_null(), "CREATE should succeed");
749        // SAFETY: rp 由 sz_orm_cpp_execute 分配
750        let r = unsafe { Box::from_raw(rp) };
751        assert_eq!(
752            r.success, 1,
753            "CREATE should succeed, got code {}",
754            r.error_code
755        );
756        // SAFETY: r 配对释放
757        unsafe { sz_orm_cpp_result_free(Box::into_raw(r)) };
758
759        let insert = CString::new("INSERT INTO users (name) VALUES ('Alice'), ('Bob')").unwrap();
760        // SAFETY: pool/sql 有效
761        let rp = unsafe { sz_orm_cpp_execute(pool, insert.as_ptr()) };
762        assert!(!rp.is_null(), "INSERT should succeed");
763        // SAFETY: rp 由 sz_orm_cpp_execute 分配
764        let r = unsafe { Box::from_raw(rp) };
765        assert_eq!(r.success, 1);
766        assert_eq!(r.rows_affected, 2, "two rows inserted");
767        // SAFETY: r 配对释放
768        unsafe { sz_orm_cpp_result_free(Box::into_raw(r)) };
769
770        let q = CString::new("SELECT id, name FROM users ORDER BY id").unwrap();
771        // SAFETY: pool/sql 有效
772        let json_ptr = unsafe { sz_orm_cpp_query(pool, q.as_ptr()) };
773        assert!(!json_ptr.is_null(), "query should return JSON");
774        // SAFETY: json_ptr 有效
775        let json = unsafe { CStr::from_ptr(json_ptr) }
776            .to_string_lossy()
777            .into_owned();
778        assert!(
779            json.contains("Alice"),
780            "query rows should contain Alice: {json}"
781        );
782        assert!(
783            json.contains("Bob"),
784            "query rows should contain Bob: {json}"
785        );
786        // SAFETY: json_ptr 由 sz_orm_cpp_query 分配,配对释放
787        unsafe { sz_orm_cpp_string_free(json_ptr) };
788
789        // SAFETY: pool 有效
790        unsafe { sz_orm_cpp_pool_free(pool) };
791    }
792
793    #[test]
794    fn test_cpp_query_invalid_sql_returns_null() {
795        let pool = test_pool();
796        assert!(!pool.is_null());
797        let q = CString::new("SELECT * FROM nonexistent").unwrap();
798        // SAFETY: pool/sql 有效
799        let json_ptr = unsafe { sz_orm_cpp_query(pool, q.as_ptr()) };
800        assert!(json_ptr.is_null(), "invalid SQL should return null");
801        // SAFETY: pool 有效
802        unsafe { sz_orm_cpp_pool_free(pool) };
803    }
804
805    #[test]
806    fn test_cpp_null_args() {
807        // SAFETY: null 参数合法调用
808        let pool = unsafe { sz_orm_cpp_pool_new(std::ptr::null(), std::ptr::null()) };
809        assert!(pool.is_null());
810        // SAFETY: null 参数合法调用
811        let r = unsafe { sz_orm_cpp_execute(std::ptr::null_mut(), std::ptr::null()) };
812        assert!(r.is_null());
813        // SAFETY: null 参数合法调用
814        let q = unsafe { sz_orm_cpp_query(std::ptr::null_mut(), std::ptr::null()) };
815        assert!(q.is_null());
816    }
817
818    #[test]
819    fn test_cpp_version() {
820        let v = sz_orm_cpp_version();
821        assert!(v >= 1, "version should be >= 1, got {v}");
822    }
823
824    #[test]
825    fn test_cpp_free_null_safe() {
826        // SAFETY: null 参数合法调用
827        unsafe { sz_orm_cpp_string_free(std::ptr::null_mut()) };
828        // SAFETY: null 参数合法调用
829        unsafe { sz_orm_cpp_result_free(std::ptr::null_mut()) };
830    }
831
832    #[test]
833    fn test_cpp_ping_after_free_returns_zero() {
834        // 使用 null handle 测试 ping(而非释放后的 UAF)
835        // SAFETY: null handle 合法调用
836        let healthy = unsafe { sz_orm_cpp_ping(std::ptr::null_mut()) };
837        assert_eq!(healthy, 0, "null pool should not be healthy");
838    }
839
840    #[test]
841    fn test_cpp_multiple_pools_independent() {
842        let dsn1 = CString::new("sqlite::memory:").unwrap();
843        // SAFETY: dsn1 有效
844        let pool1 = unsafe { sz_orm_cpp_pool_new(dsn1.as_ptr(), std::ptr::null()) };
845        let dsn2 = CString::new("sqlite::memory:").unwrap();
846        // SAFETY: dsn2 有效
847        let pool2 = unsafe { sz_orm_cpp_pool_new(dsn2.as_ptr(), std::ptr::null()) };
848        assert!(!pool1.is_null());
849        assert!(!pool2.is_null());
850        assert_ne!(pool1, pool2, "pools should have distinct handles");
851        // SAFETY: pools 有效
852        unsafe {
853            sz_orm_cpp_pool_free(pool1);
854            sz_orm_cpp_pool_free(pool2);
855        }
856    }
857
858    #[test]
859    fn test_cpp_execute_empty_sql() {
860        let pool = test_pool();
861        assert!(!pool.is_null());
862        let create = CString::new("CREATE TABLE cpp_e (id INTEGER PRIMARY KEY)").unwrap();
863        // SAFETY: pool/sql 有效
864        let r = unsafe { sz_orm_cpp_execute(pool, create.as_ptr()) };
865        // SAFETY: r 配对释放
866        unsafe { sz_orm_cpp_result_free(r) };
867        // SAFETY: pool 有效
868        unsafe { sz_orm_cpp_pool_free(pool) };
869    }
870
871    #[test]
872    fn test_cpp_query_empty_table() {
873        let pool = test_pool();
874        assert!(!pool.is_null());
875        let create = CString::new("CREATE TABLE empty_t (id INTEGER PRIMARY KEY)").unwrap();
876        // SAFETY: pool/sql 有效
877        let r = unsafe { sz_orm_cpp_execute(pool, create.as_ptr()) };
878        // SAFETY: r 配对释放
879        unsafe { sz_orm_cpp_result_free(r) };
880        let q = CString::new("SELECT id FROM empty_t").unwrap();
881        // SAFETY: pool/sql 有效
882        let json_ptr = unsafe { sz_orm_cpp_query(pool, q.as_ptr()) };
883        assert!(
884            !json_ptr.is_null(),
885            "empty table query should still return JSON"
886        );
887        // SAFETY: json_ptr 有效
888        let json = unsafe { CStr::from_ptr(json_ptr) }
889            .to_string_lossy()
890            .into_owned();
891        assert!(
892            json.contains("[]") || json.is_empty() || json.contains("id"),
893            "empty result should be empty array: {json}"
894        );
895        // SAFETY: json_ptr 配对释放
896        unsafe { sz_orm_cpp_string_free(json_ptr) };
897        // SAFETY: pool 有效
898        unsafe { sz_orm_cpp_pool_free(pool) };
899    }
900
901    // ===== 新增测试:事务 + 模型级 API 转发(REQ-BND-006/013/014)=====
902
903    fn model_test_pool() -> SzOrmPoolHandle {
904        let pool = test_pool();
905        assert!(!pool.is_null());
906        let create =
907            CString::new("CREATE TABLE cm_t (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)")
908                .unwrap();
909        // SAFETY: pool/sql 有效
910        let r = unsafe { sz_orm_cpp_execute(pool, create.as_ptr()) };
911        // SAFETY: r 配对释放
912        unsafe { sz_orm_cpp_result_free(r) };
913        pool
914    }
915
916    unsafe fn free_cpp_result(ptr: *mut QueryResultC) -> QueryResultC {
917        if ptr.is_null() {
918            return QueryResultC {
919                success: 0,
920                error_code: sz_orm_cabi::SzOrmErrorCode::InvalidArgument.as_i32(),
921                rows_affected: 0,
922                last_insert_id: 0,
923            };
924        }
925        // SAFETY: ptr 由 sz_orm_cpp_model_* 分配
926        let r = unsafe { Box::from_raw(ptr) };
927        *r
928    }
929
930    unsafe fn free_cpp_str(ptr: *mut c_char) -> String {
931        if ptr.is_null() {
932            return String::new();
933        }
934        // SAFETY: ptr 有效
935        let s = unsafe { CStr::from_ptr(ptr) }
936            .to_string_lossy()
937            .into_owned();
938        // SAFETY: ptr 配对释放
939        unsafe { sz_orm_cpp_string_free(ptr) };
940        s
941    }
942
943    #[test]
944    fn test_cpp_model_insert_find_roundtrip() {
945        let pool = model_test_pool();
946        let table = CString::new("cm_t").unwrap();
947        let fields = CString::new(r#"["name","age"]"#).unwrap();
948        let values = CString::new(r#"["Alice",30]"#).unwrap();
949        // SAFETY: pool/table/fields/values 有效
950        let r = unsafe {
951            sz_orm_cpp_model_insert(pool, table.as_ptr(), fields.as_ptr(), values.as_ptr())
952        };
953        // SAFETY: r 有效
954        let r = unsafe { free_cpp_result(r) };
955        assert_eq!(
956            r.success, 1,
957            "cpp model_insert should succeed, code={}",
958            r.error_code
959        );
960        assert_eq!(r.rows_affected, 1);
961
962        let where_clause = CString::new("name = ?").unwrap();
963        let where_params = CString::new(r#"["Alice"]"#).unwrap();
964        // SAFETY: pool/table/where 有效
965        let ptr = unsafe {
966            sz_orm_cpp_model_find(
967                pool,
968                table.as_ptr(),
969                where_clause.as_ptr(),
970                where_params.as_ptr(),
971            )
972        };
973        assert!(!ptr.is_null());
974        // SAFETY: ptr 有效
975        let json = unsafe { free_cpp_str(ptr) };
976        assert!(
977            json.contains("Alice"),
978            "cpp model_find should contain Alice: {json}"
979        );
980        // SAFETY: pool 有效
981        unsafe { sz_orm_cpp_pool_free(pool) };
982    }
983
984    #[test]
985    fn test_cpp_model_update_delete_find() {
986        let pool = model_test_pool();
987        let table = CString::new("cm_t").unwrap();
988        let fields = CString::new(r#"["name","age"]"#).unwrap();
989        let values = CString::new(r#"["Bob",25]"#).unwrap();
990        // SAFETY: pool/table/fields/values 有效
991        let r = unsafe {
992            sz_orm_cpp_model_insert(pool, table.as_ptr(), fields.as_ptr(), values.as_ptr())
993        };
994        // SAFETY: r 有效
995        unsafe { free_cpp_result(r) };
996
997        let set_json = CString::new(r#"{"age":26}"#).unwrap();
998        let where_clause = CString::new("name = ?").unwrap();
999        let where_params = CString::new(r#"["Bob"]"#).unwrap();
1000        // SAFETY: pool/table/set/where 有效
1001        let r = unsafe {
1002            sz_orm_cpp_model_update(
1003                pool,
1004                table.as_ptr(),
1005                set_json.as_ptr(),
1006                where_clause.as_ptr(),
1007                where_params.as_ptr(),
1008            )
1009        };
1010        // SAFETY: r 有效
1011        let r = unsafe { free_cpp_result(r) };
1012        assert_eq!(
1013            r.success, 1,
1014            "cpp model_update should succeed, code={}",
1015            r.error_code
1016        );
1017
1018        // SAFETY: pool/table/where 有效
1019        let ptr = unsafe {
1020            sz_orm_cpp_model_find(
1021                pool,
1022                table.as_ptr(),
1023                where_clause.as_ptr(),
1024                where_params.as_ptr(),
1025            )
1026        };
1027        // SAFETY: ptr 有效
1028        let json = unsafe { free_cpp_str(ptr) };
1029        assert!(
1030            json.contains("26"),
1031            "cpp find after update should contain age 26: {json}"
1032        );
1033
1034        // SAFETY: pool/table/where 有效
1035        let r = unsafe {
1036            sz_orm_cpp_model_delete(
1037                pool,
1038                table.as_ptr(),
1039                where_clause.as_ptr(),
1040                where_params.as_ptr(),
1041            )
1042        };
1043        // SAFETY: r 有效
1044        let r = unsafe { free_cpp_result(r) };
1045        assert_eq!(
1046            r.success, 1,
1047            "cpp model_delete should succeed, code={}",
1048            r.error_code
1049        );
1050
1051        // SAFETY: pool/table/where 有效
1052        let ptr = unsafe {
1053            sz_orm_cpp_model_find(
1054                pool,
1055                table.as_ptr(),
1056                where_clause.as_ptr(),
1057                where_params.as_ptr(),
1058            )
1059        };
1060        // SAFETY: ptr 有效
1061        let json = unsafe { free_cpp_str(ptr) };
1062        assert!(
1063            json == "[]" || json == "null" || json.is_empty(),
1064            "cpp find after delete should be empty: {json}"
1065        );
1066        // SAFETY: pool 有效
1067        unsafe { sz_orm_cpp_pool_free(pool) };
1068    }
1069
1070    #[test]
1071    fn test_cpp_model_insert_illegal_table() {
1072        let pool = model_test_pool();
1073        let table = CString::new("cm_t; DROP--").unwrap();
1074        let fields = CString::new(r#"["name"]"#).unwrap();
1075        let values = CString::new(r#"["X"]"#).unwrap();
1076        // SAFETY: pool/table/fields/values 有效(table 含注入向量)
1077        let r = unsafe {
1078            sz_orm_cpp_model_insert(pool, table.as_ptr(), fields.as_ptr(), values.as_ptr())
1079        };
1080        // SAFETY: r 有效
1081        let r = unsafe { free_cpp_result(r) };
1082        assert_eq!(r.success, 0, "illegal table should fail");
1083        assert_eq!(
1084            r.error_code,
1085            sz_orm_cabi::SzOrmErrorCode::InvalidArgument.as_i32()
1086        );
1087        // SAFETY: pool 有效
1088        unsafe { sz_orm_cpp_pool_free(pool) };
1089    }
1090
1091    #[test]
1092    fn test_cpp_transaction_model_rollback() {
1093        let pool = model_test_pool();
1094        // SAFETY: pool 有效
1095        let tx = unsafe { sz_orm_cpp_transaction_begin(pool) };
1096        assert!(!tx.is_null());
1097
1098        let table = CString::new("cm_t").unwrap();
1099        let fields = CString::new(r#"["name","age"]"#).unwrap();
1100        let values = CString::new(r#"["TxUser",99]"#).unwrap();
1101        // SAFETY: tx/table/fields/values 有效
1102        let r = unsafe {
1103            sz_orm_cpp_model_insert_tx(tx, table.as_ptr(), fields.as_ptr(), values.as_ptr())
1104        };
1105        // SAFETY: r 有效
1106        let r = unsafe { free_cpp_result(r) };
1107        assert_eq!(
1108            r.success, 1,
1109            "cpp model_insert_tx should succeed, code={}",
1110            r.error_code
1111        );
1112
1113        // SAFETY: tx 有效
1114        assert_eq!(unsafe { sz_orm_cpp_transaction_rollback(tx) }, 1);
1115        // SAFETY: tx 有效
1116        unsafe { sz_orm_cpp_transaction_free(tx) };
1117
1118        let where_clause = CString::new("name = ?").unwrap();
1119        let where_params = CString::new(r#"["TxUser"]"#).unwrap();
1120        // SAFETY: pool/table/where 有效
1121        let ptr = unsafe {
1122            sz_orm_cpp_model_find(
1123                pool,
1124                table.as_ptr(),
1125                where_clause.as_ptr(),
1126                where_params.as_ptr(),
1127            )
1128        };
1129        // SAFETY: ptr 有效
1130        let json = unsafe { free_cpp_str(ptr) };
1131        assert!(
1132            json == "[]" || json == "null" || json.is_empty(),
1133            "cpp find after rollback should be empty: {json}"
1134        );
1135        // SAFETY: pool 有效
1136        unsafe { sz_orm_cpp_pool_free(pool) };
1137    }
1138
1139    #[test]
1140    fn test_cpp_transaction_model_commit() {
1141        let pool = model_test_pool();
1142        // SAFETY: pool 有效
1143        let tx = unsafe { sz_orm_cpp_transaction_begin(pool) };
1144        assert!(!tx.is_null());
1145
1146        let table = CString::new("cm_t").unwrap();
1147        let fields = CString::new(r#"["name","age"]"#).unwrap();
1148        let values = CString::new(r#"["CommitUser",50]"#).unwrap();
1149        // SAFETY: tx/table/fields/values 有效
1150        let r = unsafe {
1151            sz_orm_cpp_model_insert_tx(tx, table.as_ptr(), fields.as_ptr(), values.as_ptr())
1152        };
1153        // SAFETY: r 有效
1154        unsafe { free_cpp_result(r) };
1155
1156        // SAFETY: tx 有效
1157        assert_eq!(unsafe { sz_orm_cpp_transaction_commit(tx) }, 1);
1158        // SAFETY: tx 有效
1159        unsafe { sz_orm_cpp_transaction_free(tx) };
1160
1161        let where_clause = CString::new("name = ?").unwrap();
1162        let where_params = CString::new(r#"["CommitUser"]"#).unwrap();
1163        // SAFETY: pool/table/where 有效
1164        let ptr = unsafe {
1165            sz_orm_cpp_model_find(
1166                pool,
1167                table.as_ptr(),
1168                where_clause.as_ptr(),
1169                where_params.as_ptr(),
1170            )
1171        };
1172        // SAFETY: ptr 有效
1173        let json = unsafe { free_cpp_str(ptr) };
1174        assert!(
1175            json.contains("CommitUser"),
1176            "cpp find after commit should contain CommitUser: {json}"
1177        );
1178        // SAFETY: pool 有效
1179        unsafe { sz_orm_cpp_pool_free(pool) };
1180    }
1181
1182    // ===== v6.9.0 新增测试:QueryBuilder + ActiveModel 转发 =====
1183
1184    #[test]
1185    fn test_cpp_qb_new_and_free() {
1186        // SAFETY: db_type=0 (MySQL) 合法
1187        let qb = unsafe { sz_orm_cpp_qb_new(0) };
1188        assert!(!qb.is_null(), "qb_new should return non-null handle");
1189        // SAFETY: qb 有效
1190        unsafe { sz_orm_cpp_qb_free(qb) };
1191    }
1192
1193    #[test]
1194    fn test_cpp_qb_full_chain() {
1195        // SAFETY: db_type=0 (MySQL) 合法
1196        let qb = unsafe { sz_orm_cpp_qb_new(0) };
1197        assert!(!qb.is_null());
1198
1199        let table = CString::new("users").unwrap();
1200        // SAFETY: qb 有效
1201        assert_eq!(
1202            unsafe { sz_orm_cpp_qb_table(qb, table.as_ptr()) },
1203            sz_orm_cabi::SzOrmErrorCode::Ok.as_i32()
1204        );
1205
1206        let field = CString::new("status").unwrap();
1207        let value = CString::new(r#""active""#).unwrap();
1208        // SAFETY: qb 有效
1209        assert_eq!(
1210            unsafe { sz_orm_cpp_qb_where_eq(qb, field.as_ptr(), value.as_ptr()) },
1211            sz_orm_cabi::SzOrmErrorCode::Ok.as_i32()
1212        );
1213
1214        let order_field = CString::new("id").unwrap();
1215        // SAFETY: qb 有效
1216        assert_eq!(
1217            unsafe { sz_orm_cpp_qb_order_by(qb, order_field.as_ptr(), 1) },
1218            sz_orm_cabi::SzOrmErrorCode::Ok.as_i32()
1219        );
1220
1221        // SAFETY: qb 有效
1222        assert_eq!(
1223            unsafe { sz_orm_cpp_qb_limit(qb, 10) },
1224            sz_orm_cabi::SzOrmErrorCode::Ok.as_i32()
1225        );
1226
1227        // SAFETY: qb 有效
1228        let result_ptr = unsafe { sz_orm_cpp_qb_build(qb) };
1229        assert!(!result_ptr.is_null(), "qb_build should return JSON");
1230        // SAFETY: result_ptr 有效
1231        let json = unsafe { CStr::from_ptr(result_ptr) }
1232            .to_string_lossy()
1233            .into_owned();
1234        // SAFETY: result_ptr 配对释放
1235        unsafe { sz_orm_cpp_qb_result_free(result_ptr) };
1236
1237        assert!(json.contains("users"), "SQL should contain table: {json}");
1238        assert!(
1239            json.contains("status"),
1240            "SQL should contain where field: {json}"
1241        );
1242
1243        // SAFETY: qb 有效
1244        unsafe { sz_orm_cpp_qb_free(qb) };
1245    }
1246
1247    #[test]
1248    fn test_cpp_qb_invalid_db_type() {
1249        // SAFETY: 无效 db_type 返回 null
1250        let qb = unsafe { sz_orm_cpp_qb_new(999) };
1251        assert!(qb.is_null(), "invalid db_type should return null");
1252    }
1253
1254    #[test]
1255    fn test_cpp_qb_free_null_is_noop() {
1256        // SAFETY: null 指针 free 是安全空操作
1257        unsafe { sz_orm_cpp_qb_free(std::ptr::null_mut()) };
1258    }
1259
1260    #[test]
1261    fn test_cpp_active_model_create_get_delete() {
1262        let pool = model_test_pool();
1263
1264        let table = CString::new("cm_t").unwrap();
1265        let fields = CString::new(r#"["name","age"]"#).unwrap();
1266        let values = CString::new(r#"["AMUser",42]"#).unwrap();
1267        // SAFETY: pool/table/fields/values 有效
1268        let r = unsafe {
1269            sz_orm_cpp_active_model_create(pool, table.as_ptr(), fields.as_ptr(), values.as_ptr())
1270        };
1271        // SAFETY: r 有效
1272        let r = unsafe { free_cpp_result(r) };
1273        assert_eq!(r.success, 1, "active_model create should succeed");
1274
1275        let where_clause = CString::new("name = ?").unwrap();
1276        let where_params = CString::new(r#"["AMUser"]"#).unwrap();
1277        // SAFETY: pool/table/where 有效
1278        let ptr = unsafe {
1279            sz_orm_cpp_active_model_get(
1280                pool,
1281                table.as_ptr(),
1282                where_clause.as_ptr(),
1283                where_params.as_ptr(),
1284            )
1285        };
1286        // SAFETY: ptr 有效
1287        let json = unsafe { free_cpp_str(ptr) };
1288        assert!(json.contains("AMUser"), "get should find AMUser: {json}");
1289
1290        // SAFETY: pool/table/where 有效
1291        let r = unsafe {
1292            sz_orm_cpp_active_model_delete(
1293                pool,
1294                table.as_ptr(),
1295                where_clause.as_ptr(),
1296                where_params.as_ptr(),
1297            )
1298        };
1299        // SAFETY: r 有效
1300        let r = unsafe { free_cpp_result(r) };
1301        assert_eq!(r.success, 1, "active_model delete should succeed");
1302
1303        // SAFETY: pool 有效
1304        unsafe { sz_orm_cpp_pool_free(pool) };
1305    }
1306
1307    #[test]
1308    fn test_cpp_active_model_set_update() {
1309        let pool = model_test_pool();
1310
1311        let table = CString::new("cm_t").unwrap();
1312        let fields = CString::new(r#"["name","age"]"#).unwrap();
1313        let values = CString::new(r#"["SetUser",20]"#).unwrap();
1314        // SAFETY: pool/table/fields/values 有效
1315        let r = unsafe {
1316            sz_orm_cpp_active_model_save(pool, table.as_ptr(), fields.as_ptr(), values.as_ptr())
1317        };
1318        // SAFETY: r 有效
1319        unsafe { free_cpp_result(r) };
1320
1321        let set_json = CString::new(r#"{"age":21}"#).unwrap();
1322        let where_clause = CString::new("name = ?").unwrap();
1323        let where_params = CString::new(r#"["SetUser"]"#).unwrap();
1324        // SAFETY: pool/table/set/where 有效
1325        let r = unsafe {
1326            sz_orm_cpp_active_model_set(
1327                pool,
1328                table.as_ptr(),
1329                set_json.as_ptr(),
1330                where_clause.as_ptr(),
1331                where_params.as_ptr(),
1332            )
1333        };
1334        // SAFETY: r 有效
1335        let r = unsafe { free_cpp_result(r) };
1336        assert_eq!(r.success, 1, "active_model set should succeed");
1337
1338        // SAFETY: pool/table/where 有效
1339        let ptr = unsafe {
1340            sz_orm_cpp_active_model_get(
1341                pool,
1342                table.as_ptr(),
1343                where_clause.as_ptr(),
1344                where_params.as_ptr(),
1345            )
1346        };
1347        // SAFETY: ptr 有效
1348        let json = unsafe { free_cpp_str(ptr) };
1349        assert!(
1350            json.contains("21"),
1351            "get after set should contain age 21: {json}"
1352        );
1353
1354        // SAFETY: pool 有效
1355        unsafe { sz_orm_cpp_pool_free(pool) };
1356    }
1357}