1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include <mutex>
16 #include <openssl/sha.h>
17 #include <string>
18 #include <sys/time.h>
19 #include <thread>
20 #include <vector>
21
22 // using the "sqlite3sym.h" in OHOS
23 #ifndef USE_SQLITE_SYMBOLS
24 #include "sqlite3.h"
25 #else
26 #include "sqlite3sym.h"
27 #endif
28
29 namespace {
30 constexpr int E_OK = 0;
31 constexpr int E_ERROR = 1;
32 constexpr int BUSY_TIMEOUT = 2000; // 2s.
33 const int MAX_BLOB_READ_SIZE = 5 * 1024 * 1024; // 5M limit
34 const std::string DEVICE_TYPE = "device";
35 const std::string SYNC_TABLE_TYPE = "sync_table_type_";
36 class ValueHashCalc {
37 public:
ValueHashCalc()38 ValueHashCalc() {};
~ValueHashCalc()39 ~ValueHashCalc()
40 {
41 delete context_;
42 context_ = nullptr;
43 }
44
Initialize()45 int Initialize()
46 {
47 context_ = new (std::nothrow) SHA256_CTX;
48 if (context_ == nullptr) {
49 return -E_ERROR;
50 }
51
52 int errCode = SHA256_Init(context_);
53 if (errCode == 0) {
54 return -E_ERROR;
55 }
56 return E_OK;
57 }
58
Update(const std::vector<uint8_t> & value)59 int Update(const std::vector<uint8_t> &value)
60 {
61 if (context_ == nullptr) {
62 return -E_ERROR;
63 }
64 int errCode = SHA256_Update(context_, value.data(), value.size());
65 if (errCode == 0) {
66 return -E_ERROR;
67 }
68 return E_OK;
69 }
70
GetResult(std::vector<uint8_t> & value)71 int GetResult(std::vector<uint8_t> &value)
72 {
73 if (context_ == nullptr) {
74 return -E_ERROR;
75 }
76
77 value.resize(SHA256_DIGEST_LENGTH);
78 int errCode = SHA256_Final(value.data(), context_);
79 if (errCode == 0) {
80 return -E_ERROR;
81 }
82
83 return E_OK;
84 }
85
86 private:
87 SHA256_CTX *context_ = nullptr;
88 };
89
90 const uint64_t MULTIPLES_BETWEEN_SECONDS_AND_MICROSECONDS = 1000000;
91
92 using Timestamp = uint64_t;
93 using TimeOffset = int64_t;
94
95 class TimeHelper {
96 public:
97 constexpr static int64_t BASE_OFFSET = 10000LL * 365LL * 24LL * 3600LL * 1000LL * 1000LL * 10L; // 10000 year 100ns
98
99 constexpr static int64_t MAX_VALID_TIME = BASE_OFFSET * 2; // 20000 year 100ns
100
101 constexpr static uint64_t TO_100_NS = 10; // 1us to 100ns
102
103 constexpr static Timestamp INVALID_TIMESTAMP = 0;
104
105 // Get current system time
GetSysCurrentTime()106 static Timestamp GetSysCurrentTime()
107 {
108 uint64_t curTime = 0;
109 int errCode = GetCurrentSysTimeInMicrosecond(curTime);
110 if (errCode != E_OK) {
111 return INVALID_TIMESTAMP;
112 }
113
114 std::lock_guard<std::mutex> lock(systemTimeLock_);
115 // If GetSysCurrentTime in 1us, we need increase the currentIncCount_
116 if (curTime == lastSystemTimeUs_) {
117 // if the currentIncCount_ has been increased MAX_INC_COUNT, keep the currentIncCount_
118 if (currentIncCount_ < MAX_INC_COUNT) {
119 currentIncCount_++;
120 }
121 } else {
122 lastSystemTimeUs_ = curTime;
123 currentIncCount_ = 0;
124 }
125 return (curTime * TO_100_NS) + currentIncCount_; // Currently Timestamp is uint64_t
126 }
127
GetSysCurrentRawTime(uint64_t & curTime)128 static int GetSysCurrentRawTime(uint64_t &curTime)
129 {
130 int errCode = GetCurrentSysTimeInMicrosecond(curTime);
131 if (errCode != E_OK) {
132 return errCode;
133 }
134 curTime *= TO_100_NS;
135 return E_OK;
136 }
137
138 // Init the TimeHelper
Initialize(Timestamp maxTimestamp)139 static void Initialize(Timestamp maxTimestamp)
140 {
141 std::lock_guard<std::mutex> lock(lastLocalTimeLock_);
142 if (lastLocalTime_ < maxTimestamp) {
143 lastLocalTime_ = maxTimestamp;
144 }
145 }
146
GetTime(TimeOffset timeOffset)147 static Timestamp GetTime(TimeOffset timeOffset)
148 {
149 Timestamp currentSysTime = GetSysCurrentTime();
150 Timestamp currentLocalTime = currentSysTime + timeOffset;
151 std::lock_guard<std::mutex> lock(lastLocalTimeLock_);
152 if (currentLocalTime <= lastLocalTime_ || currentLocalTime > MAX_VALID_TIME) {
153 lastLocalTime_++;
154 currentLocalTime = lastLocalTime_;
155 } else {
156 lastLocalTime_ = currentLocalTime;
157 }
158 return currentLocalTime;
159 }
160
161 private:
GetCurrentSysTimeInMicrosecond(uint64_t & outTime)162 static int GetCurrentSysTimeInMicrosecond(uint64_t &outTime)
163 {
164 struct timeval rawTime;
165 int errCode = gettimeofday(&rawTime, nullptr);
166 if (errCode < 0) {
167 return -E_ERROR;
168 }
169 outTime = static_cast<uint64_t>(rawTime.tv_sec) * MULTIPLES_BETWEEN_SECONDS_AND_MICROSECONDS +
170 static_cast<uint64_t>(rawTime.tv_usec);
171 return E_OK;
172 }
173
174 static std::mutex systemTimeLock_;
175 static Timestamp lastSystemTimeUs_;
176 static Timestamp currentIncCount_;
177 static const uint64_t MAX_INC_COUNT = 9; // last bit from 0-9
178
179 static Timestamp lastLocalTime_;
180 static std::mutex lastLocalTimeLock_;
181 };
182
183 std::mutex TimeHelper::systemTimeLock_;
184 Timestamp TimeHelper::lastSystemTimeUs_ = 0;
185 Timestamp TimeHelper::currentIncCount_ = 0;
186 Timestamp TimeHelper::lastLocalTime_ = 0;
187 std::mutex TimeHelper::lastLocalTimeLock_;
188
189 struct TransactFunc {
190 void (*xFunc)(sqlite3_context*, int, sqlite3_value**) = nullptr;
191 void (*xStep)(sqlite3_context*, int, sqlite3_value**) = nullptr;
192 void (*xFinal)(sqlite3_context*) = nullptr;
193 void(*xDestroy)(void*) = nullptr;
194 };
195
RegisterFunction(sqlite3 * db,const std::string & funcName,int nArg,void * uData,TransactFunc & func)196 int RegisterFunction(sqlite3 *db, const std::string &funcName, int nArg, void *uData, TransactFunc &func)
197 {
198 if (db == nullptr) {
199 return -E_ERROR;
200 }
201 return sqlite3_create_function_v2(db, funcName.c_str(), nArg, SQLITE_UTF8 | SQLITE_DETERMINISTIC, uData,
202 func.xFunc, func.xStep, func.xFinal, func.xDestroy);
203 }
204
CalcValueHash(const std::vector<uint8_t> & value,std::vector<uint8_t> & hashValue)205 int CalcValueHash(const std::vector<uint8_t> &value, std::vector<uint8_t> &hashValue)
206 {
207 ValueHashCalc hashCalc;
208 int errCode = hashCalc.Initialize();
209 if (errCode != E_OK) {
210 return -E_ERROR;
211 }
212
213 errCode = hashCalc.Update(value);
214 if (errCode != E_OK) {
215 return -E_ERROR;
216 }
217
218 errCode = hashCalc.GetResult(hashValue);
219 if (errCode != E_OK) {
220 return -E_ERROR;
221 }
222
223 return E_OK;
224 }
225
CalcHashKey(sqlite3_context * ctx,int argc,sqlite3_value ** argv)226 void CalcHashKey(sqlite3_context *ctx, int argc, sqlite3_value **argv)
227 {
228 // 1 means that the function only needs one parameter, namely key
229 if (ctx == nullptr || argc != 1 || argv == nullptr) {
230 return;
231 }
232 auto keyBlob = static_cast<const uint8_t *>(sqlite3_value_blob(argv[0]));
233 if (keyBlob == nullptr) {
234 sqlite3_result_error(ctx, "Parameters is invalid.", -1);
235 return;
236 }
237 int blobLen = sqlite3_value_bytes(argv[0]);
238 std::vector<uint8_t> value(keyBlob, keyBlob + blobLen);
239 std::vector<uint8_t> hashValue;
240 int errCode = CalcValueHash(value, hashValue);
241 if (errCode != E_OK) {
242 sqlite3_result_error(ctx, "Get hash value error.", -1);
243 return;
244 }
245 sqlite3_result_blob(ctx, hashValue.data(), hashValue.size(), SQLITE_TRANSIENT);
246 return;
247 }
248
RegisterCalcHash(sqlite3 * db)249 int RegisterCalcHash(sqlite3 *db)
250 {
251 TransactFunc func;
252 func.xFunc = &CalcHashKey;
253 return RegisterFunction(db, "calc_hash", 1, nullptr, func);
254 }
255
GetSysTime(sqlite3_context * ctx,int argc,sqlite3_value ** argv)256 void GetSysTime(sqlite3_context *ctx, int argc, sqlite3_value **argv)
257 {
258 if (ctx == nullptr || argc != 1 || argv == nullptr) { // 1: function need one parameter
259 return;
260 }
261 int timeOffset = static_cast<int64_t>(sqlite3_value_int64(argv[0]));
262 sqlite3_result_int64(ctx, (sqlite3_int64)TimeHelper::GetTime(timeOffset));
263 }
264
GetRawSysTime(sqlite3_context * ctx,int argc,sqlite3_value ** argv)265 void GetRawSysTime(sqlite3_context *ctx, int argc, sqlite3_value **argv)
266 {
267 if (ctx == nullptr || argc != 0 || argv == nullptr) { // 0: function need zero parameter
268 return;
269 }
270
271 uint64_t curTime = 0;
272 int errCode = TimeHelper::GetSysCurrentRawTime(curTime);
273 if (errCode != E_OK) {
274 sqlite3_result_error(ctx, "get raw sys time failed.", errCode);
275 return;
276 }
277 sqlite3_result_int64(ctx, (sqlite3_int64)(curTime));
278 }
279
GetLastTime(sqlite3_context * ctx,int argc,sqlite3_value ** argv)280 void GetLastTime(sqlite3_context *ctx, int argc, sqlite3_value **argv)
281 {
282 if (ctx == nullptr || argc != 0 || argv == nullptr) { // 0: function need zero parameter
283 return;
284 }
285
286 sqlite3_result_int64(ctx, (sqlite3_int64)TimeHelper::GetTime(0));
287 }
288
RegisterGetSysTime(sqlite3 * db)289 int RegisterGetSysTime(sqlite3 *db)
290 {
291 TransactFunc func;
292 func.xFunc = &GetSysTime;
293 return RegisterFunction(db, "get_sys_time", 1, nullptr, func);
294 }
295
RegisterGetRawSysTime(sqlite3 * db)296 int RegisterGetRawSysTime(sqlite3 *db)
297 {
298 TransactFunc func;
299 func.xFunc = &GetRawSysTime;
300 return RegisterFunction(db, "get_raw_sys_time", 0, nullptr, func);
301 }
302
RegisterGetLastTime(sqlite3 * db)303 int RegisterGetLastTime(sqlite3 *db)
304 {
305 TransactFunc func;
306 func.xFunc = &GetLastTime;
307 return RegisterFunction(db, "get_last_time", 0, nullptr, func);
308 }
309
ResetStatement(sqlite3_stmt * & stmt)310 int ResetStatement(sqlite3_stmt *&stmt)
311 {
312 if (stmt == nullptr || sqlite3_finalize(stmt) != SQLITE_OK) {
313 return -E_ERROR;
314 }
315 stmt = nullptr;
316 return E_OK;
317 }
318
GetStatement(sqlite3 * db,const std::string & sql,sqlite3_stmt * & stmt)319 int GetStatement(sqlite3 *db, const std::string &sql, sqlite3_stmt *&stmt)
320 {
321 int errCode = sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, nullptr);
322 if (errCode != SQLITE_OK) {
323 (void)ResetStatement(stmt);
324 return -E_ERROR;
325 }
326 return E_OK;
327 }
328
ExecuteRawSQL(sqlite3 * db,const std::string & sql)329 int ExecuteRawSQL(sqlite3 *db, const std::string &sql)
330 {
331 if (db == nullptr) {
332 return -E_ERROR;
333 }
334 char *errMsg = nullptr;
335 int errCode = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &errMsg);
336 if (errCode != SQLITE_OK) {
337 errCode = -E_ERROR;
338 }
339
340 if (errMsg != nullptr) {
341 sqlite3_free(errMsg);
342 errMsg = nullptr;
343 }
344 return errCode;
345 }
346
StepWithRetry(sqlite3_stmt * stmt)347 int StepWithRetry(sqlite3_stmt *stmt)
348 {
349 if (stmt == nullptr) {
350 return -E_ERROR;
351 }
352 int errCode = sqlite3_step(stmt);
353 if (errCode != SQLITE_DONE && errCode != SQLITE_ROW) {
354 return -E_ERROR;
355 }
356 return errCode;
357 }
358
GetColumnTestValue(sqlite3_stmt * stmt,int index,std::string & value)359 int GetColumnTestValue(sqlite3_stmt *stmt, int index, std::string &value)
360 {
361 if (stmt == nullptr) {
362 return -E_ERROR;
363 }
364 const unsigned char *val = sqlite3_column_text(stmt, index);
365 value = (val != nullptr) ? std::string(reinterpret_cast<const char *>(val)) : std::string();
366 return E_OK;
367 }
368
GetCurrentMaxTimestamp(sqlite3 * db,Timestamp & maxTimestamp)369 int GetCurrentMaxTimestamp(sqlite3 *db, Timestamp &maxTimestamp)
370 {
371 if (db == nullptr) {
372 return -E_ERROR;
373 }
374 std::string checkTableSql = "SELECT name FROM sqlite_master WHERE type = 'table' AND " \
375 "name LIKE 'naturalbase_rdb_aux_%_log';";
376 sqlite3_stmt *checkTableStmt = nullptr;
377 int errCode = GetStatement(db, checkTableSql, checkTableStmt);
378 if (errCode != E_OK) {
379 return -E_ERROR;
380 }
381 while ((errCode = StepWithRetry(checkTableStmt)) != SQLITE_DONE) {
382 if (errCode != SQLITE_ROW) {
383 ResetStatement(checkTableStmt);
384 return -E_ERROR;
385 }
386 std::string logTablename;
387 GetColumnTestValue(checkTableStmt, 0, logTablename);
388 if (logTablename.empty()) {
389 continue;
390 }
391
392 std::string getMaxTimestampSql = "SELECT MAX(timestamp) FROM " + logTablename + ";";
393 sqlite3_stmt *getTimeStmt = nullptr;
394 errCode = GetStatement(db, getMaxTimestampSql, getTimeStmt);
395 if (errCode != E_OK) {
396 continue;
397 }
398 errCode = StepWithRetry(getTimeStmt);
399 if (errCode != SQLITE_ROW) {
400 ResetStatement(getTimeStmt);
401 continue;
402 }
403 auto tableMaxTimestamp = static_cast<Timestamp>(sqlite3_column_int64(getTimeStmt, 0));
404 maxTimestamp = (maxTimestamp > tableMaxTimestamp) ? maxTimestamp : tableMaxTimestamp;
405 ResetStatement(getTimeStmt);
406 }
407 ResetStatement(checkTableStmt);
408 return E_OK;
409 }
410
GetColumnBlobValue(sqlite3_stmt * stmt,int index,std::vector<uint8_t> & value)411 int GetColumnBlobValue(sqlite3_stmt *stmt, int index, std::vector<uint8_t> &value)
412 {
413 if (stmt == nullptr) {
414 return -E_ERROR;
415 }
416
417 int keySize = sqlite3_column_bytes(stmt, index);
418 if (keySize < 0) {
419 value.resize(0);
420 return E_OK;
421 }
422 auto keyRead = static_cast<const uint8_t *>(sqlite3_column_blob(stmt, index));
423 if (keySize == 0 || keyRead == nullptr) {
424 value.resize(0);
425 } else {
426 if (keySize > MAX_BLOB_READ_SIZE) {
427 keySize = MAX_BLOB_READ_SIZE + 1;
428 }
429 value.resize(keySize);
430 value.assign(keyRead, keyRead + keySize);
431 }
432 return E_OK;
433 }
434
GetTableSyncType(sqlite3 * db,const std::string & tableName,std::string & tableType)435 int GetTableSyncType(sqlite3 *db, const std::string &tableName, std::string &tableType)
436 {
437 const char *selectSql = "SELECT value FROM naturalbase_rdb_aux_metadata WHERE key=?;";
438 sqlite3_stmt *statement = nullptr;
439 int errCode = sqlite3_prepare_v2(db, selectSql, -1, &statement, nullptr);
440 if (errCode != SQLITE_OK) {
441 (void)sqlite3_finalize(statement);
442 return -E_ERROR;
443 }
444
445 std::string keyStr = SYNC_TABLE_TYPE + tableName;
446 std::vector<uint8_t> key(keyStr.begin(), keyStr.end());
447 if (sqlite3_bind_blob(statement, 1, static_cast<const void *>(key.data()), key.size(),
448 SQLITE_TRANSIENT) != SQLITE_OK) {
449 return -E_ERROR;
450 }
451
452 if (sqlite3_step(statement) == SQLITE_ROW) {
453 std::vector<uint8_t> value;
454 if (GetColumnBlobValue(statement, 0, value) == E_OK) {
455 tableType.assign(value.begin(), value.end());
456 (void)sqlite3_finalize(statement);
457 return E_OK;
458 } else {
459 (void)sqlite3_finalize(statement);
460 return -E_ERROR;
461 }
462 } else if (sqlite3_step(statement) != SQLITE_DONE) {
463 (void)sqlite3_finalize(statement);
464 return -E_ERROR;
465 }
466 (void)sqlite3_finalize(statement);
467 tableType = DEVICE_TYPE;
468 return E_OK;
469 }
470
HandleDropCloudSyncTable(sqlite3 * db,const std::string & tableName)471 void HandleDropCloudSyncTable(sqlite3 *db, const std::string &tableName)
472 {
473 std::string logTblName = "naturalbase_rdb_aux_" + tableName + "_log";
474 std::string sql = "UPDATE " + logTblName + " SET data_key=-1, flag=0x03, timestamp=get_raw_sys_time();";
475 (void)sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr);
476 std::string keyStr = SYNC_TABLE_TYPE + tableName;
477 std::vector<uint8_t> key(keyStr.begin(), keyStr.end());
478 sql = "delete from naturalbase_rdb_aux_metadata where key = ?;";
479 sqlite3_stmt *statement = nullptr;
480 int errCode = sqlite3_prepare_v2(db, sql.c_str(), -1, &statement, nullptr);
481 if (errCode != SQLITE_OK) {
482 (void)sqlite3_finalize(statement);
483 return;
484 }
485
486 if (sqlite3_bind_blob(statement, 1, static_cast<const void *>(key.data()), key.size(),
487 SQLITE_TRANSIENT) != SQLITE_OK) {
488 return;
489 }
490 (void)sqlite3_step(statement);
491 (void)sqlite3_finalize(statement);
492 }
493
ClearTheLogAfterDropTable(sqlite3 * db,const char * tableName,const char * schemaName)494 void ClearTheLogAfterDropTable(sqlite3 *db, const char *tableName, const char *schemaName)
495 {
496 if (db == nullptr || tableName == nullptr || schemaName == nullptr) {
497 return;
498 }
499 sqlite3_stmt *stmt = nullptr;
500 std::string tableStr = std::string(tableName);
501 std::string logTblName = "naturalbase_rdb_aux_" + tableStr + "_log";
502 Timestamp dropTimeStamp = TimeHelper::GetTime(0);
503 std::string sql = "SELECT count(*) FROM sqlite_master WHERE type='table' AND name='" + logTblName + "';";
504 if (sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
505 (void)sqlite3_finalize(stmt);
506 return;
507 }
508
509 bool isLogTblExists = false;
510 if (sqlite3_step(stmt) == SQLITE_ROW && static_cast<bool>(sqlite3_column_int(stmt, 0))) {
511 isLogTblExists = true;
512 }
513 (void)sqlite3_finalize(stmt);
514 stmt = nullptr;
515
516 if (isLogTblExists) {
517 std::string tableType = DEVICE_TYPE;
518 if (GetTableSyncType(db, tableStr, tableType) != E_OK) {
519 return;
520 }
521 if (tableType == DEVICE_TYPE) {
522 RegisterGetSysTime(db);
523 RegisterGetLastTime(db);
524 sql = "UPDATE " + logTblName + " SET flag=0x03, timestamp=get_sys_time(0) "
525 "WHERE flag&0x03=0x02 AND timestamp<" + std::to_string(dropTimeStamp);
526 (void)sqlite3_exec(db, sql.c_str(), nullptr, nullptr, nullptr);
527 } else {
528 HandleDropCloudSyncTable(db, tableStr);
529 }
530 }
531 }
532
PostHandle(sqlite3 * db)533 void PostHandle(sqlite3 *db)
534 {
535 Timestamp currentMaxTimestamp = 0;
536 (void)GetCurrentMaxTimestamp(db, currentMaxTimestamp);
537 TimeHelper::Initialize(currentMaxTimestamp);
538 RegisterCalcHash(db);
539 RegisterGetSysTime(db);
540 RegisterGetLastTime(db);
541 RegisterGetRawSysTime(db);
542 (void)sqlite3_set_droptable_handle(db, &ClearTheLogAfterDropTable);
543 (void)sqlite3_busy_timeout(db, BUSY_TIMEOUT);
544 std::string recursiveTrigger = "PRAGMA recursive_triggers = ON;";
545 (void)ExecuteRawSQL(db, recursiveTrigger);
546 }
547 }
548
sqlite3_open_relational(const char * filename,sqlite3 ** ppDb)549 SQLITE_API int sqlite3_open_relational(const char *filename, sqlite3 **ppDb)
550 {
551 int err = sqlite3_open(filename, ppDb);
552 if (err != SQLITE_OK) {
553 return err;
554 }
555 PostHandle(*ppDb);
556 return err;
557 }
558
sqlite3_open16_relational(const void * filename,sqlite3 ** ppDb)559 SQLITE_API int sqlite3_open16_relational(const void *filename, sqlite3 **ppDb)
560 {
561 int err = sqlite3_open16(filename, ppDb);
562 if (err != SQLITE_OK) {
563 return err;
564 }
565 PostHandle(*ppDb);
566 return err;
567 }
568
sqlite3_open_v2_relational(const char * filename,sqlite3 ** ppDb,int flags,const char * zVfs)569 SQLITE_API int sqlite3_open_v2_relational(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs)
570 {
571 int err = sqlite3_open_v2(filename, ppDb, flags, zVfs);
572 if (err != SQLITE_OK) {
573 return err;
574 }
575 PostHandle(*ppDb);
576 return err;
577 }
578
579 // hw export the symbols
580 #ifdef SQLITE_DISTRIBUTE_RELATIONAL
581 #if defined(__GNUC__)
582 # define EXPORT_SYMBOLS __attribute__ ((visibility ("default")))
583 #elif defined(_MSC_VER)
584 # define EXPORT_SYMBOLS __declspec(dllexport)
585 #else
586 # define EXPORT_SYMBOLS
587 #endif
588
589 struct sqlite3_api_routines_relational {
590 int (*open)(const char *, sqlite3 **);
591 int (*open16)(const void *, sqlite3 **);
592 int (*open_v2)(const char *, sqlite3 **, int, const char *);
593 };
594
595 typedef struct sqlite3_api_routines_relational sqlite3_api_routines_relational;
596 static const sqlite3_api_routines_relational sqlite3HwApis = {
597 #ifdef SQLITE_DISTRIBUTE_RELATIONAL
598 sqlite3_open_relational,
599 sqlite3_open16_relational,
600 sqlite3_open_v2_relational
601 #else
602 0,
603 0,
604 0
605 #endif
606 };
607
608 EXPORT_SYMBOLS const sqlite3_api_routines_relational *sqlite3_export_relational_symbols = &sqlite3HwApis;
609 #endif