feat add SQLite, compile OK

This commit is contained in:
Liu_Weichao
2023-10-25 16:07:23 +08:00
parent 0705bdaf1a
commit dfa6b664ac
30 changed files with 219137 additions and 3 deletions
@@ -0,0 +1,467 @@
static int _rtthread_io_read(sqlite3_file *file_id, void *pbuf, int cnt, sqlite3_int64 offset)
{
RTTHREAD_SQLITE_FILE_T *file = (RTTHREAD_SQLITE_FILE_T*)file_id;
sqlite3_int64 new_offset;
int r_cnt;
assert(file_id);
assert(offset >= 0);
assert(cnt > 0);
new_offset = lseek(file->fd, offset, SEEK_SET);
if (new_offset != offset)
{
return SQLITE_IOERR_READ;
}
do {
r_cnt = read(file->fd, pbuf, cnt);
if (r_cnt == cnt)
{
break;
}
if (r_cnt < 0)
{
if (errno != EINTR)
{
return SQLITE_IOERR_READ;
}
r_cnt = 1;
continue;
}
else if (r_cnt > 0)
{
cnt -= r_cnt;
pbuf = (void*)(r_cnt + (char*)pbuf);
}
} while (r_cnt > 0);
if (r_cnt != cnt)
{
memset(&((char*)pbuf)[r_cnt], 0, cnt - r_cnt);
return SQLITE_IOERR_SHORT_READ;
}
return SQLITE_OK;
}
static int _rtthread_io_write(sqlite3_file* file_id, const void *pbuf, int cnt, sqlite3_int64 offset)
{
RTTHREAD_SQLITE_FILE_T *file = (RTTHREAD_SQLITE_FILE_T*)file_id;
sqlite3_int64 new_offset;
int w_cnt;
assert(file_id);
assert(cnt > 0);
new_offset = lseek(file->fd, offset, SEEK_SET);
if (new_offset != offset)
{
return SQLITE_IOERR_WRITE;
}
do {
w_cnt = write(file->fd, pbuf, cnt);
if (w_cnt == cnt)
{
break;
}
if (w_cnt < 0)
{
if (errno != EINTR)
{
return SQLITE_IOERR_WRITE;
}
w_cnt = 1;
continue;
}
else if (w_cnt > 0)
{
cnt -= w_cnt;
pbuf = (void*)(w_cnt + (char*)pbuf);
}
} while (w_cnt > 0);
if (w_cnt != cnt)
{
return SQLITE_FULL;
}
return SQLITE_OK;
}
static int _rtthread_io_truncate(sqlite3_file* file_id, sqlite3_int64 size)
{
return SQLITE_IOERR_TRUNCATE;
}
static int _rtthread_io_sync(sqlite3_file* file_id, int flags)
{
RTTHREAD_SQLITE_FILE_T *file = (RTTHREAD_SQLITE_FILE_T*)file_id;
assert((flags & 0x0F) == SQLITE_SYNC_NORMAL
|| (flags & 0x0F) == SQLITE_SYNC_FULL);
fsync(file->fd);
return SQLITE_OK;
}
static int _rtthread_io_file_size(sqlite3_file* file_id, sqlite3_int64 *psize)
{
int rc;
struct stat buf;
RTTHREAD_SQLITE_FILE_T *file = (RTTHREAD_SQLITE_FILE_T*)file_id;
assert(file_id);
rc = fstat(file->fd, &buf);
if (rc != 0)
{
return SQLITE_IOERR_FSTAT;
}
*psize = buf.st_size;
/* When opening a zero-size database, the findInodeInfo() procedure
** writes a single byte into that file in order to work around a bug
** in the OS-X msdos filesystem. In order to avoid problems with upper
** layers, we need to report this file size as zero even though it is
** really 1. Ticket #3260.
*/
if (*psize == 1) *psize = 0;
return SQLITE_OK;
}
/*
** This routine checks if there is a RESERVED lock held on the specified
** file by this or any other process. If such a lock is held, set *pResOut
** to a non-zero value otherwise *pResOut is set to zero. The return value
** is set to SQLITE_OK unless an I/O error occurs during lock checking.
*/
static int _rtthread_io_check_reserved_lock(sqlite3_file *file_id, int *pResOut)
{
RTTHREAD_SQLITE_FILE_T *file = (RTTHREAD_SQLITE_FILE_T*)file_id;
rt_sem_t psem = &file->sem;
int reserved = 0;
/* Check if a thread in this process holds such a lock */
if (file->eFileLock > SHARED_LOCK)
{
reserved = 1;
}
/* Otherwise see if some other process holds it. */
if (!reserved)
{
if (rt_sem_trytake(psem) != RT_EOK)
{
/* someone else has the lock when we are in NO_LOCK */
reserved = (file->eFileLock < SHARED_LOCK);
}
else
{
/* we could have it if we want it */
rt_sem_release(psem);
}
}
*pResOut = reserved;
return SQLITE_OK;
}
/*
** Lock the file with the lock specified by parameter eFileLock - one
** of the following:
**
** (1) SHARED_LOCK
** (2) RESERVED_LOCK
** (3) PENDING_LOCK
** (4) EXCLUSIVE_LOCK
**
** Sometimes when requesting one lock state, additional lock states
** are inserted in between. The locking might fail on one of the later
** transitions leaving the lock state different from what it started but
** still short of its goal. The following chart shows the allowed
** transitions and the inserted intermediate states:
**
** UNLOCKED -> SHARED
** SHARED -> RESERVED
** SHARED -> (PENDING) -> EXCLUSIVE
** RESERVED -> (PENDING) -> EXCLUSIVE
** PENDING -> EXCLUSIVE
**
** Semaphore locks only really support EXCLUSIVE locks. We track intermediate
** lock states in the sqlite3_file structure, but all locks SHARED or
** above are really EXCLUSIVE locks and exclude all other processes from
** access the file.
**
** This routine will only increase a lock. Use the sqlite3OsUnlock()
** routine to lower a locking level.
*/
static int _rtthread_io_lock(sqlite3_file *file_id, int eFileLock)
{
RTTHREAD_SQLITE_FILE_T *file = (RTTHREAD_SQLITE_FILE_T*)file_id;
rt_sem_t psem = &file->sem;
int rc = SQLITE_OK;
/* if we already have a lock, it is exclusive.
** Just adjust level and punt on outta here. */
if (file->eFileLock > NO_LOCK)
{
file->eFileLock = eFileLock;
rc = SQLITE_OK;
goto sem_end_lock;
}
/* lock semaphore now but bail out when already locked. */
if (rt_sem_trytake(psem) != RT_EOK)
{
rc = SQLITE_BUSY;
goto sem_end_lock;
}
/* got it, set the type and return ok */
file->eFileLock = eFileLock;
sem_end_lock:
return rc;
}
/*
** Lower the locking level on file descriptor pFile to eFileLock. eFileLock
** must be either NO_LOCK or SHARED_LOCK.
**
** If the locking level of the file descriptor is already at or below
** the requested locking level, this routine is a no-op.
*/
static int _rtthread_io_unlock(sqlite3_file *file_id, int eFileLock)
{
RTTHREAD_SQLITE_FILE_T *file = (RTTHREAD_SQLITE_FILE_T*)file_id;
rt_sem_t psem = &file->sem;
assert(eFileLock <= SHARED_LOCK);
/* no-op if possible */
if (file->eFileLock == eFileLock)
{
return SQLITE_OK;
}
/* shared can just be set because we always have an exclusive */
if (eFileLock == SHARED_LOCK)
{
file->eFileLock = SHARED_LOCK;
return SQLITE_OK;
}
/* no, really unlock. */
rt_sem_release(psem);
file->eFileLock = NO_LOCK;
return SQLITE_OK;
}
static int _rtthread_io_close(sqlite3_file *file_id)
{
int rc = 0;
RTTHREAD_SQLITE_FILE_T *file = (RTTHREAD_SQLITE_FILE_T*)file_id;
if (file->fd >= 0)
{
_rtthread_io_unlock(file_id, NO_LOCK);
rt_sem_detach(&file->sem);
rc = close(file->fd);
file->fd = -1;
}
return rc;
}
static int _rtthread_fcntl_size_hint(sqlite3_file *file_id, i64 nByte)
{
RTTHREAD_SQLITE_FILE_T *file = (RTTHREAD_SQLITE_FILE_T*)file_id;
if (file->szChunk > 0)
{
i64 nSize; /* Required file size */
struct stat buf; /* Used to hold return values of fstat() */
if (fstat(file->fd, &buf))
{
return SQLITE_IOERR_FSTAT;
}
nSize = ((nByte + file->szChunk - 1) / file->szChunk) * file->szChunk;
if (nSize > (i64)buf.st_size)
{
/* If the OS does not have posix_fallocate(), fake it. Write a
** single byte to the last byte in each block that falls entirely
** within the extended region. Then, if required, a single byte
** at offset (nSize-1), to set the size of the file correctly.
** This is a similar technique to that used by glibc on systems
** that do not have a real fallocate() call.
*/
int nBlk = 512; /* File-system block size */
int nWrite = 0; /* Number of bytes written by seekAndWrite */
i64 iWrite; /* Next offset to write to */
iWrite = (buf.st_size / nBlk) * nBlk + nBlk - 1;
assert(iWrite >= buf.st_size);
assert(((iWrite + 1) % nBlk) == 0);
for (/*no-op*/; iWrite < nSize + nBlk - 1; iWrite += nBlk)
{
if (iWrite >= nSize)
{
iWrite = nSize - 1;
}
nWrite = _rtthread_io_write(file_id, "", 1, iWrite);
if (nWrite != 1)
{
return SQLITE_IOERR_WRITE;
}
}
}
}
return SQLITE_OK;
}
/*
** Information and control of an open file handle.
*/
static int _rtthread_io_file_ctrl(sqlite3_file *file_id, int op, void *pArg)
{
RTTHREAD_SQLITE_FILE_T *file = (RTTHREAD_SQLITE_FILE_T*)file_id;
switch( op )
{
case SQLITE_FCNTL_LOCKSTATE: {
*(int*)pArg = file->eFileLock;
return SQLITE_OK;
}
case SQLITE_LAST_ERRNO: {
*(int*)pArg = 0;
return SQLITE_OK;
}
case SQLITE_FCNTL_CHUNK_SIZE: {
file->szChunk = *(int *)pArg;
return SQLITE_OK;
}
case SQLITE_FCNTL_SIZE_HINT: {
int rc;
rc = _rtthread_fcntl_size_hint(file_id, *(i64 *)pArg);
return rc;
}
case SQLITE_FCNTL_PERSIST_WAL: {
return SQLITE_OK;
}
case SQLITE_FCNTL_POWERSAFE_OVERWRITE: {
return SQLITE_OK;
}
case SQLITE_FCNTL_VFSNAME: {
*(char**)pArg = sqlite3_mprintf("%s", file->pvfs->zName);
return SQLITE_OK;
}
case SQLITE_FCNTL_TEMPFILENAME: {
char *zTFile = sqlite3_malloc(file->pvfs->mxPathname );
if( zTFile )
{
_rtthread_get_temp_name(file->pvfs->mxPathname, zTFile);
*(char**)pArg = zTFile;
}
return SQLITE_OK;
}
}
return SQLITE_NOTFOUND;
}
static int _rtthread_io_sector_size(sqlite3_file *file_id)
{
return SQLITE_DEFAULT_SECTOR_SIZE;
}
static int _rtthread_io_device_characteristics(sqlite3_file *file_id)
{
return 0;
}
/*
** If possible, return a pointer to a mapping of file fd starting at offset
** iOff. The mapping must be valid for at least nAmt bytes.
**
** If such a pointer can be obtained, store it in *pp and return SQLITE_OK.
** Or, if one cannot but no error occurs, set *pp to 0 and return SQLITE_OK.
** Finally, if an error does occur, return an SQLite error code. The final
** value of *pp is undefined in this case.
**
** If this function does return a pointer, the caller must eventually
** release the reference by calling unixUnfetch().
*/
static int _rtthread_io_fetch(sqlite3_file *file_id, i64 iOff, int nAmt, void **pp)
{
*pp = 0;
return SQLITE_OK;
}
/*
** If the third argument is non-NULL, then this function releases a
** reference obtained by an earlier call to unixFetch(). The second
** argument passed to this function must be the same as the corresponding
** argument that was passed to the unixFetch() invocation.
**
** Or, if the third argument is NULL, then this function is being called
** to inform the VFS layer that, according to POSIX, any existing mapping
** may now be invalid and should be unmapped.
*/
static int _rtthread_io_unfetch(sqlite3_file *fd, i64 iOff, void *p)
{
return SQLITE_OK;
}
static const sqlite3_io_methods _rtthread_io_method = {
3,
_rtthread_io_close,
_rtthread_io_read,
_rtthread_io_write,
_rtthread_io_truncate,
_rtthread_io_sync,
_rtthread_io_file_size,
_rtthread_io_lock,
_rtthread_io_unlock,
_rtthread_io_check_reserved_lock,
_rtthread_io_file_ctrl,
_rtthread_io_sector_size,
_rtthread_io_device_characteristics,
0,
0,
0,
0,
_rtthread_io_fetch,
_rtthread_io_unfetch
};
@@ -0,0 +1,228 @@
#if defined(SQLITE_MUTEX_RTTHREAD)
/*
* rt-thread mutex
*/
struct sqlite3_mutex {
struct rt_mutex mutex; /* Mutex controlling the lock */
int id; /* Mutex type */
};
SQLITE_PRIVATE void sqlite3MemoryBarrier(void)
{
}
/*
** Initialize and deinitialize the mutex subsystem.
The argument to sqlite3_mutex_alloc() must one of these integer constants:
SQLITE_MUTEX_FAST
SQLITE_MUTEX_RECURSIVE
SQLITE_MUTEX_STATIC_MASTER
SQLITE_MUTEX_STATIC_MEM
SQLITE_MUTEX_STATIC_OPEN
SQLITE_MUTEX_STATIC_PRNG
SQLITE_MUTEX_STATIC_LRU
SQLITE_MUTEX_STATIC_PMEM
SQLITE_MUTEX_STATIC_APP1
SQLITE_MUTEX_STATIC_APP2
SQLITE_MUTEX_STATIC_APP3
SQLITE_MUTEX_STATIC_VFS1
SQLITE_MUTEX_STATIC_VFS2
SQLITE_MUTEX_STATIC_VFS3
The first two constants (SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE)
cause sqlite3_mutex_alloc() to create a new mutex. The new mutex is recursive
when SQLITE_MUTEX_RECURSIVE is used but not necessarily so when SQLITE_MUTEX_FAST
is used. The mutex implementation does not need to make a distinction between
SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does not want to.
SQLite will only request a recursive mutex in cases where it really needs one.
If a faster non-recursive mutex implementation is available on the host platform,
the mutex subsystem might return such a mutex in response to SQLITE_MUTEX_FAST.
The other allowed parameters to sqlite3_mutex_alloc()
(anything other than SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) each return
a pointer to a static preexisting mutex. Nine static mutexes are used by the
current version of SQLite. Future versions of SQLite may add additional static
mutexes. Static mutexes are for internal use by SQLite only. Applications that
use SQLite mutexes should use only the dynamic mutexes returned by SQLITE_MUTEX_FAST
or SQLITE_MUTEX_RECURSIVE.
Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST or SQLITE_MUTEX_RECURSIVE)
is used then sqlite3_mutex_alloc() returns a different mutex on every call.
For the static mutex types, the same mutex is returned on every call that has the same type number.
*/
static sqlite3_mutex _static_mutex[12];
static int _rtthread_mtx_init(void)
{
int i;
rt_err_t err;
for (i = 0; i < sizeof(_static_mutex) / sizeof(_static_mutex[0]); i++)
{
err = rt_mutex_init(&_static_mutex[i].mutex, "sqlmtx", RT_IPC_FLAG_PRIO);
if (err != RT_EOK)
{
return SQLITE_ERROR;
}
}
return SQLITE_OK;
}
static int _rtthread_mtx_end(void)
{
int i;
rt_err_t err;
for (i = 0; i < sizeof(_static_mutex) / sizeof(_static_mutex[0]); i++)
{
err = rt_mutex_detach(&_static_mutex[i].mutex);
_static_mutex[i].mutex.owner = 0;
_static_mutex[i].mutex.hold = 0;
if (err != RT_EOK)
{
return SQLITE_ERROR;
}
}
return SQLITE_OK;
}
static sqlite3_mutex * _rtthread_mtx_alloc(int id)
{
sqlite3_mutex *p = NULL;
switch (id)
{
case SQLITE_MUTEX_FAST:
case SQLITE_MUTEX_RECURSIVE:
p = sqlite3Malloc(sizeof(sqlite3_mutex));
if (p != NULL)
{
rt_mutex_init(&p->mutex, "sqlmtx", RT_IPC_FLAG_PRIO);
p->id = id;
}
break;
default:
assert(id - 2 >= 0);
assert(id - 2 < ArraySize(_static_mutex) );
p = &_static_mutex[id - 2];
p->id = id;
break;
}
return p;
}
static void _rtthread_mtx_free(sqlite3_mutex * p)
{
assert(p != 0);
rt_mutex_detach(&p->mutex);
switch (p->id)
{
case SQLITE_MUTEX_FAST:
case SQLITE_MUTEX_RECURSIVE:
sqlite3_free(p);
break;
default:
break;
}
}
static void _rtthread_mtx_enter(sqlite3_mutex *p)
{
assert(p != 0);
rt_mutex_take(&p->mutex, RT_WAITING_FOREVER);
}
static int _rtthread_mtx_try(sqlite3_mutex *p)
{
assert(p != 0);
if (rt_mutex_take(&p->mutex, RT_WAITING_NO) != RT_EOK)
{
return SQLITE_BUSY;
}
return SQLITE_OK;
}
static void _rtthread_mtx_leave(sqlite3_mutex *p)
{
assert(p != 0);
rt_mutex_release(&p->mutex);
}
#ifdef SQLITE_DEBUG
/*
If the argument to sqlite3_mutex_held() is a NULL pointer then the routine
should return 1. This seems counter-intuitive since clearly the mutex cannot
be held if it does not exist. But the reason the mutex does not exist is
because the build is not using mutexes. And we do not want the assert()
containing the call to sqlite3_mutex_held() to fail, so a non-zero return
is the appropriate thing to do. The sqlite3_mutex_notheld() interface should
also return 1 when given a NULL pointer.
*/
static int _rtthread_mtx_held(sqlite3_mutex *p)
{
if (p != 0)
{
if ((rt_thread_self() == p->mutex.owner) && (p->mutex.hold > 0))
{
return 1;
}
return 0;
}
return 1;
}
static int _rtthread_mtx_noheld(sqlite3_mutex *p)
{
if (_rtthread_mtx_held(p))
{
return 0;
}
return 1;
}
#endif /* SQLITE_DEBUG */
SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void)
{
static const sqlite3_mutex_methods sMutex = {
_rtthread_mtx_init,
_rtthread_mtx_end,
_rtthread_mtx_alloc,
_rtthread_mtx_free,
_rtthread_mtx_enter,
_rtthread_mtx_try,
_rtthread_mtx_leave,
#ifdef SQLITE_DEBUG
_rtthread_mtx_held,
_rtthread_mtx_noheld
#else
0,
0
#endif
};
return &sMutex;
}
#endif /* SQLITE_MUTEX_RTTHREAD */
@@ -0,0 +1,654 @@
#ifdef SQLITE_OS_RTTHREAD
#ifndef SQLITE_OMIT_LOAD_EXTENSION
#error "rt-thread not support load extension, compile with SQLITE_OMIT_LOAD_EXTENSION."
#endif
#define RTTHREAD_MAX_PATHNAME 256
#include <dfs_posix.h>
/*
** Define various macros that are missing from some systems.
*/
#ifndef O_LARGEFILE
# define O_LARGEFILE 0
#endif
#ifdef SQLITE_DISABLE_LFS
# undef O_LARGEFILE
# define O_LARGEFILE 0
#endif
#ifndef O_NOFOLLOW
# define O_NOFOLLOW 0
#endif
#ifndef O_BINARY
# define O_BINARY 0
#endif
#ifndef RT_USING_NEWLIB
#ifndef EINTR
#define EINTR 4 /* Interrupted system call */
#endif
#ifndef ENOLCK
#define ENOLCK 46 /* No record locks available */
#endif
#ifndef EACCES
#define EACCES 13 /* Permission denied */
#endif
#ifndef EPERM
#define EPERM 1 /* Operation not permitted */
#endif
#ifndef ETIMEDOUT
#define ETIMEDOUT 145 /* Connection timed out */
#endif
#ifndef ENOTCONN
#define ENOTCONN 134 /* Transport endpoint is not connected */
#endif
#if defined(__GNUC__) || defined(__ADSPBLACKFIN__)
int _gettimeofday(struct timeval *tp, void *ignore) __attribute__((weak));
int _gettimeofday(struct timeval *tp, void *ignore)
#elif defined(__CC_ARM)
__weak int _gettimeofday(struct timeval *tp, void *ignore)
#elif defined(__IAR_SYSTEMS_ICC__)
#if __VER__ > 540
__weak
#endif
int _gettimeofday(struct timeval *tp, void *ignore)
#else
int _gettimeofday(struct timeval *tp, void *ignore)
#endif
{
return 0;
}
#endif /* RT_USING_NEWLIB */
static int _Access(const char *pathname, int mode)
{
int fd;
fd = open(pathname, O_RDONLY, mode);
if (fd >= 0)
{
close(fd);
return 0;
}
return -1;
}
#define _RTTHREAD_LOG_ERROR(a,b,c) _rtthread_log_error_at_line(a,b,c,__LINE__)
static int _rtthread_log_error_at_line(
int errcode, /* SQLite error code */
const char *zFunc, /* Name of OS function that failed */
const char *zPath, /* File path associated with error */
int iLine /* Source line number where error occurred */
)
{
char *zErr; /* Message from strerror() or equivalent */
int iErrno = errno; /* Saved syscall error number */
/* If this is not a threadsafe build (SQLITE_THREADSAFE==0), then use
** the strerror() function to obtain the human-readable error message
** equivalent to errno. Otherwise, use strerror_r().
*/
#if SQLITE_THREADSAFE && defined(HAVE_STRERROR_R)
char aErr[80];
memset(aErr, 0, sizeof(aErr));
zErr = aErr;
/* If STRERROR_R_CHAR_P (set by autoconf scripts) or __USE_GNU is defined,
** assume that the system provides the GNU version of strerror_r() that
** returns a pointer to a buffer containing the error message. That pointer
** may point to aErr[], or it may point to some static storage somewhere.
** Otherwise, assume that the system provides the POSIX version of
** strerror_r(), which always writes an error message into aErr[].
**
** If the code incorrectly assumes that it is the POSIX version that is
** available, the error message will often be an empty string. Not a
** huge problem. Incorrectly concluding that the GNU version is available
** could lead to a segfault though.
*/
#if defined(STRERROR_R_CHAR_P) || defined(__USE_GNU)
zErr =
#endif
strerror_r(iErrno, aErr, sizeof(aErr)-1);
#elif SQLITE_THREADSAFE
/* This is a threadsafe build, but strerror_r() is not available. */
zErr = "";
#else
/* Non-threadsafe build, use strerror(). */
zErr = strerror(iErrno);
#endif
if( zPath==0 )
zPath = "";
sqlite3_log(errcode, "os_rtthread.c:%d: (%d) %s(%s) - %s",
iLine, iErrno, zFunc, zPath, zErr);
return errcode;
}
typedef struct
{
sqlite3_io_methods const *pMethod;
sqlite3_vfs *pvfs;
int fd;
int eFileLock;
int szChunk;
struct rt_semaphore sem;
} RTTHREAD_SQLITE_FILE_T;
static const char* _rtthread_temp_file_dir(void)
{
const char *azDirs[] = {
0,
"/sql",
"/sql/tmp"
"/tmp",
0 /* List terminator */
};
unsigned int i;
struct stat buf;
const char *zDir = 0;
azDirs[0] = sqlite3_temp_directory;
for (i = 0; i < sizeof(azDirs) / sizeof(azDirs[0]); zDir = azDirs[i++])
{
if( zDir == 0 ) continue;
if( stat(zDir, &buf) ) continue;
if( !S_ISDIR(buf.st_mode) ) continue;
break;
}
return zDir;
}
/*
** Create a temporary file name in zBuf. zBuf must be allocated
** by the calling process and must be big enough to hold at least
** pVfs->mxPathname bytes.
*/
static int _rtthread_get_temp_name(int nBuf, char *zBuf)
{
const unsigned char zChars[] = "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789";
unsigned int i, j;
const char *zDir;
zDir = _rtthread_temp_file_dir();
if (zDir == 0)
{
zDir = ".";
}
/* Check that the output buffer is large enough for the temporary file
** name. If it is not, return SQLITE_ERROR.
*/
if ((strlen(zDir) + strlen(SQLITE_TEMP_FILE_PREFIX) + 18) >= (size_t)nBuf)
{
return SQLITE_ERROR;
}
do {
sqlite3_snprintf(nBuf-18, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX, zDir);
j = (int)strlen(zBuf);
sqlite3_randomness(15, &zBuf[j]);
for (i = 0; i < 15; i++, j++)
{
zBuf[j] = (char)zChars[((unsigned char)zBuf[j]) % (sizeof(zChars) - 1)];
}
zBuf[j] = 0;
zBuf[j + 1] = 0;
} while (_Access(zBuf, 0) == 0);
return SQLITE_OK;
}
#include "rtthread_io_methods.c"
/*
** Invoke open(). Do so multiple times, until it either succeeds or
** fails for some reason other than EINTR.
**
** If the file creation mode "m" is 0 then set it to the default for
** SQLite. The default is SQLITE_DEFAULT_FILE_PERMISSIONS (normally
** 0644) as modified by the system umask. If m is not 0, then
** make the file creation mode be exactly m ignoring the umask.
**
** The m parameter will be non-zero only when creating -wal, -journal,
** and -shm files. We want those files to have *exactly* the same
** permissions as their original database, unadulterated by the umask.
** In that way, if a database file is -rw-rw-rw or -rw-rw-r-, and a
** transaction crashes and leaves behind hot journals, then any
** process that is able to write to the database will also be able to
** recover the hot journals.
*/
static int _rtthread_fs_open(const char *file_path, int f, mode_t m)
{
int fd = -1;
while (fd < 0)
{
#if defined(O_CLOEXEC)
fd = open(file_path, f | O_CLOEXEC, m);
#else
fd = open(file_path, f, m);
#endif
if (fd < 0)
{
if (errno == EINTR)
continue;
break;
}
}
return fd;
}
static int _rtthread_vfs_open(sqlite3_vfs *pvfs, const char *file_path, sqlite3_file *file_id, int flags, int *pOutFlags)
{
RTTHREAD_SQLITE_FILE_T *p;
int fd;
int eType = flags & 0xFFFFFF00; /* Type of file to open */
int rc = SQLITE_OK; /* Function Return Code */
int openFlags = 0;
mode_t openMode = 0;
int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE);
int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE);
int isCreate = (flags & SQLITE_OPEN_CREATE);
int isReadonly = (flags & SQLITE_OPEN_READONLY);
int isReadWrite = (flags & SQLITE_OPEN_READWRITE);
/* If argument zPath is a NULL pointer, this function is required to open
** a temporary file. Use this buffer to store the file name in.
*/
char zTmpname[RTTHREAD_MAX_PATHNAME + 2];
p = (RTTHREAD_SQLITE_FILE_T*)file_id;
/* Check the following statements are true:
**
** (a) Exactly one of the READWRITE and READONLY flags must be set, and
** (b) if CREATE is set, then READWRITE must also be set, and
** (c) if EXCLUSIVE is set, then CREATE must also be set.
** (d) if DELETEONCLOSE is set, then CREATE must also be set.
*/
assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly));
assert(isCreate==0 || isReadWrite);
assert(isExclusive==0 || isCreate);
assert(isDelete==0 || isCreate);
/* The main DB, main journal, WAL file and master journal are never
** automatically deleted. Nor are they ever temporary files. */
assert( (!isDelete && file_path) || eType!=SQLITE_OPEN_MAIN_DB );
assert( (!isDelete && file_path) || eType!=SQLITE_OPEN_MAIN_JOURNAL );
assert( (!isDelete && file_path) || eType!=SQLITE_OPEN_MASTER_JOURNAL );
assert( (!isDelete && file_path) || eType!=SQLITE_OPEN_WAL );
/* Assert that the upper layer has set one of the "file-type" flags. */
assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
|| eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL
|| eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL
|| eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
);
/* Database filenames are double-zero terminated if they are not
** URIs with parameters. Hence, they can always be passed into
** sqlite3_uri_parameter(). */
assert((eType != SQLITE_OPEN_MAIN_DB) || (flags & SQLITE_OPEN_URI) || file_path[strlen(file_path) + 1] == 0);
memset(p, 0, sizeof(RTTHREAD_SQLITE_FILE_T));
if (!file_path)
{
rc = _rtthread_get_temp_name(RTTHREAD_MAX_PATHNAME + 2, zTmpname);
if (rc != SQLITE_OK )
{
return rc;
}
file_path = zTmpname;
/* Generated temporary filenames are always double-zero terminated
** for use by sqlite3_uri_parameter(). */
assert(file_path[strlen(file_path) + 1] == 0);
}
/* Determine the value of the flags parameter passed to POSIX function
** open(). These must be calculated even if open() is not called, as
** they may be stored as part of the file handle and used by the
** 'conch file' locking functions later on. */
if (isReadonly) openFlags |= O_RDONLY;
if (isReadWrite) openFlags |= O_RDWR;
if (isCreate) openFlags |= O_CREAT;
if (isExclusive) openFlags |= (O_EXCL | O_NOFOLLOW);
openFlags |= (O_LARGEFILE | O_BINARY);
fd = _rtthread_fs_open(file_path, openFlags, openMode);
if (fd < 0 && (errno != -EISDIR) && isReadWrite && !isExclusive)
{
/* Failed to open the file for read/write access. Try read-only. */
flags &= ~(SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
openFlags &= ~(O_RDWR | O_CREAT);
flags |= SQLITE_OPEN_READONLY;
openFlags |= O_RDONLY;
isReadonly = 1;
fd = _rtthread_fs_open(file_path, openFlags, openMode);
}
if (fd < 0)
{
rc = _RTTHREAD_LOG_ERROR(SQLITE_CANTOPEN_BKPT, "open", file_path);
return rc;
}
if (pOutFlags)
{
*pOutFlags = flags;
}
if (isDelete)
{
unlink(file_path);
}
p->fd = fd;
p->pMethod = &_rtthread_io_method;
p->eFileLock = NO_LOCK;
p->szChunk = 0;
p->pvfs = pvfs;
rt_sem_init(&p->sem, "vfssem", 1, RT_IPC_FLAG_PRIO);
return rc;
}
int _rtthread_vfs_delete(sqlite3_vfs* pvfs, const char *file_path, int syncDir)
{
int rc = SQLITE_OK;
if (unlink(file_path) == (-1))
{
if (errno == -ENOENT)
{
rc = SQLITE_IOERR_DELETE_NOENT;
}
else
{
rc = _RTTHREAD_LOG_ERROR(SQLITE_IOERR_DELETE, "unlink", file_path);
}
return rc;
}
// sync dir: open dir -> fsync -> close
if ((syncDir & 1) != 0)
{
int ii;
int fd = -1;
char zDirname[RTTHREAD_MAX_PATHNAME + 1];
sqlite3_snprintf(RTTHREAD_MAX_PATHNAME, zDirname, "%s", file_path);
for (ii=(int)strlen(zDirname); ii > 1 && zDirname[ii] != '/'; ii--);
if (ii > 0)
{
zDirname[ii] = '\0';
fd = _rtthread_fs_open(zDirname, O_RDONLY | O_BINARY, 0);
}
if (fd >= 0)
{
if (fsync(fd))
{
rc = _RTTHREAD_LOG_ERROR(SQLITE_IOERR_DIR_FSYNC, "fsync", file_path);
}
close(fd);
}
rc = SQLITE_OK;
}
return rc;
}
static int _rtthread_vfs_access(sqlite3_vfs* pvfs, const char *file_path, int flags, int *pResOut)
{
int amode = 0;
#ifndef F_OK
# define F_OK 0
#endif
#ifndef R_OK
# define R_OK 4
#endif
#ifndef W_OK
# define W_OK 2
#endif
switch (flags)
{
case SQLITE_ACCESS_EXISTS:
amode = F_OK;
break;
case SQLITE_ACCESS_READWRITE:
amode = W_OK | R_OK;
break;
case SQLITE_ACCESS_READ:
amode = R_OK;
break;
default:
_RTTHREAD_LOG_ERROR(flags, "access", file_path);
return -1;
}
*pResOut = (_Access(file_path, amode) == 0);
if (flags == SQLITE_ACCESS_EXISTS && *pResOut)
{
struct stat buf;
if (0 == stat(file_path, &buf) && (buf.st_size == 0))
{
*pResOut = 0;
}
}
return SQLITE_OK;
}
static int _rtthread_vfs_fullpathname(sqlite3_vfs* pvfs, const char *file_path, int nOut, char *zOut)
{
assert(pvfs->mxPathname == RTTHREAD_MAX_PATHNAME);
zOut[nOut - 1] = '\0';
if (file_path[0] == '/')
{
sqlite3_snprintf(nOut, zOut, "%s", file_path);
}
else
{
int nCwd;
if (getcwd(zOut, nOut - 1) == 0)
{
return _RTTHREAD_LOG_ERROR(SQLITE_CANTOPEN_BKPT, "getcwd", file_path);
}
nCwd = (int)strlen(zOut);
sqlite3_snprintf(nOut - nCwd, &zOut[nCwd], "/%s", file_path);
}
return SQLITE_OK;
}
static int _rtthread_vfs_randomness(sqlite3_vfs* pvfs, int nByte, char *zOut)
{
assert((size_t)nByte >= (sizeof(time_t) + sizeof(int)));
memset(zOut, 0, nByte);
{
int i;
char tick8, tick16;
tick8 = (char)rt_tick_get();
tick16 = (char)(rt_tick_get() >> 8);
for (i = 0; i < nByte; i++)
{
zOut[i] = (char)(i ^ tick8 ^ tick16);
tick8 = zOut[i];
tick16 = ~(tick8 ^ tick16);
}
}
return nByte;
}
static int _rtthread_vfs_sleep(sqlite3_vfs* pvfs, int microseconds)
{
int millisecond = (microseconds + 999) / 1000;
rt_thread_delay(rt_tick_from_millisecond(millisecond));
return millisecond * 1000;
}
static int _rtthread_vfs_current_time_int64(sqlite3_vfs*, sqlite3_int64*);
static int _rtthread_vfs_current_time(sqlite3_vfs* pvfs, double* pnow)
{
sqlite3_int64 i = 0;
int rc;
rc = _rtthread_vfs_current_time_int64(0, &i);
*pnow = i / 86400000.0;
return rc;
}
static int _rtthread_vfs_get_last_error(sqlite3_vfs* pvfs, int nBuf, char *zBuf)
{
return 0;
}
static int _rtthread_vfs_current_time_int64(sqlite3_vfs* pvfs, sqlite3_int64*pnow)
{
#ifndef NO_GETTOD
#define NO_GETTOD 1
#endif
static const sqlite3_int64 rtthreadEpoch = 24405875 * (sqlite3_int64)8640000;
int rc = SQLITE_OK;
#if defined(NO_GETTOD)
time_t t;
time(&t);
*pnow = ((sqlite3_int64)t) * 1000 + rtthreadEpoch;
#else
struct timeval sNow;
if (gettimeofday(&sNow, 0) == 0)
{
*pnow = rtthreadEpoch + 1000 * (sqlite3_int64)sNow.tv_sec + sNow.tv_usec / 1000;
}
else
{
rc = SQLITE_ERROR;
}
#endif
#ifdef SQLITE_TEST
if( sqlite3_current_time )
{
*pnow = 1000 * (sqlite3_int64)sqlite3_current_time + rtthreadEpoch;
}
#endif
return rc;
}
static int _rtthread_vfs_set_system_call(sqlite3_vfs* pvfs, const char *file_path, sqlite3_syscall_ptr pfn)
{
return SQLITE_NOTFOUND;
}
static sqlite3_syscall_ptr _rtthread_vfs_get_system_call(sqlite3_vfs* pvfs, const char *file_path)
{
return 0;
}
static const char* _rtthread_vfs_next_system_call(sqlite3_vfs *pvfs, const char *file_path)
{
return 0;
}
/*
** Initialize and deinitialize the operating system interface.
*/
SQLITE_API int sqlite3_os_init(void)
{
static sqlite3_vfs _rtthread_vfs = {
3, /* iVersion */
sizeof(RTTHREAD_SQLITE_FILE_T), /* szOsFile */
RTTHREAD_MAX_PATHNAME, /* mxPathname */
0, /* pNext */
"rt-thread", /* zName */
0, /* pAppData */
_rtthread_vfs_open, /* xOpen */
_rtthread_vfs_delete, /* xDelete */
_rtthread_vfs_access, /* xAccess */
_rtthread_vfs_fullpathname, /* xFullPathname */
0, /* xDlOpen */
0, /* xDlError */
0, /* xDlSym */
0, /* xDlClose */
_rtthread_vfs_randomness, /* xRandomness */
_rtthread_vfs_sleep, /* xSleep */
_rtthread_vfs_current_time, /* xCurrentTime */
_rtthread_vfs_get_last_error, /* xGetLastError */
_rtthread_vfs_current_time_int64, /* xCurrentTimeInt64 */
_rtthread_vfs_set_system_call, /* xSetSystemCall */
_rtthread_vfs_get_system_call, /* xGetSystemCall */
_rtthread_vfs_next_system_call, /* xNextSystemCall */
};
sqlite3_vfs_register(&_rtthread_vfs, 1);
return SQLITE_OK;
}
SQLITE_API int sqlite3_os_end(void)
{
return SQLITE_OK;
}
#endif /* SQLITE_OS_RTTHREAD */