хорошая новость: получилось достать box.prepare (хоть его и нет в module.h но экспортируется без проблем). Результат:
pubfntest_sql_prepare(){letmutsp=Space::builder("TEST1").field(Field::unsigned("id")).create().unwrap();sp.index_builder("primary").index_type(IndexType::Tree).part(1).create().unwrap();letsql="SELECT * FROM TEST1";letb=Vec::<u8>::with_capacity(1000);letresult=unsafe{ffi::tarantool::sql_prepare(sql.as_ptr()as*constc_char,sql.len()asu32,b.as_ptr()as*constc_char)};lete=tarantool::error::TarantoolErrorCode::try_last();assert_eq!(e,None);assert_eq!(result,0);}
плохая новость: prepare работает с такой inner штукой тарантула как port, я так понял это такая мегаабстракция над чем то что можно представить как lua таблицу (или вообще над чем то что экспортит свой интерфейс куда то). Вобщем полиморфизм такой. Разбор этого port (sqlной его реализации) пока что представляется делом довольно сложным и видимо очень зависимым от внутреннего устройства тарантула (вобщем большая вероятность локнуться на конкретную версию) тк надо лезть сильно в кишки.
При условии что получится адеватно работать с port_sql, с box.execute проблем уже не будет.
Путем различных хаков получилось выполнить сценарий:
создать prepared stmt
вызвать execute
получить результат в формате msgpack
Примерный код для ознакомления:
pubfntest_sql_prepare(){//подготавливаем тестовые данныеletmutsp=Space::builder("TEST1").field(Field::unsigned("id")).field(Field::string("value")).create().unwrap();sp.index_builder("primary").index_type(IndexType::Tree).part(1).create().unwrap();sp.insert(&(1,"one")).unwrap();sp.insert(&(2,"two")).unwrap();sp.insert(&(3,"three")).unwrap();sp.insert(&(4,"four")).unwrap();letsql="SELECT * FROM TEST1";letresult=unsafe{// создаем prepared statementletport=Port{vtab:std::mem::uninitialized(),_unused:[0;60]};letr_prep=ffi::tarantool::sql_prepare(sql.as_ptr()as*constc_char,sql.len()asu32,&portas*constPort);// получаем stmt_idletsql_port=&portas*constPortas*constPortSql;letstmt=(*sql_port).sql_stmt;letsql=CStr::from_ptr(ffi::tarantool::sql_stmt_query_str(stmt));letstmt_id=ffi::tarantool::sql_stmt_calculate_id(sql.as_ptr(),sql.to_str().unwrap().len());// получаем текущий файбер, тк для execute нам нужен region (fiber->gc)letfiber_ptr=ffi::tarantool::fiber_self();// грязный хакletregion=fiber_ptr.offset(48);// вызываем executeletport2=Port{vtab:std::mem::uninitialized(),_unused:[0;60]};letr_ex=ffi::tarantool::sql_execute_prepared(stmt_id,std::ptr::null::<c_void>(),0,&port2as*constPort,region);// создаем буфер letslab_c=ffi::tarantool::cord_slab_cache();letmutobuf=Obuf::new();ffi::tarantool::obuf_create(&mutobufas*mutObuf,slab_c,1024);// дергаем метод dump_msgpack для того чтобы буфер заполнился результатами запроса упакованными в msgpack((*port2.vtab).dump_msgpack)(&port2as*constPort,&obufas*constObuf);letiov_ptr=obuf.iov[0].iov_baseas*mutu8;letv=std::slice::from_raw_parts(iov_ptr,obuf.iov[0].iov_len);// в v у нас msgpack encoded результатыprintln!("result: {:?}",v);ffi::tarantool::port_destroy(&port2as*constPort);ffi::tarantool::port_destroy(&portas*constPort);(r_prep,r_ex)};lete=tarantool::error::TarantoolErrorCode::try_last();assert_eq!(e,None);assert_eq!(result.0,0);assert_eq!(result.1,0);}
У данного кода 2 проблемы:
после консультации с @gmoshkin выяснилось что почти все используемое CAPI не относится к экспорируемому, и его уже не возможно использовать в версиях 2.10+
ручной разбор структуры fiber - максимально не безопасен ввиду наличия в оригинальной структуре полей спрятанных за ifdef назначение которых понять сложновато
Предлагаемый пример работы с rust api (в случае если получится утрясти имеющиеся проблемы):
pubfnprepare_and_execute_some_sql_example(){letquery="SELECT * FROM t1 WHERE a = ? and b = ?";{letstatement=tarantool::sql::prepare(query);letresult1=statement.execute::<Vec<T1Row>>(&(1,2));letresult2=statement.execute::<Vec<T1Row>>(&(2,3));// box.unprepare(statement) unexplicitly called when drop statement}}