• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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