1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Merge latest trunk changes into this branch.

FossilOrigin-Name: 790c56d493c66a2136e24d349d169639809d70bfab6996975a403be568a267a5
This commit is contained in:
dan
2024-11-01 19:41:22 +00:00
75 changed files with 888 additions and 15869 deletions

View File

@ -1,170 +0,0 @@
NOTE (2012-11-29):
The functionality implemented by this extension has been superseded
by WAL-mode. This module is no longer supported or maintained. The
code is retained for historical reference only.
------------------------------------------------------------------------------
Normally, when SQLite writes to a database file, it waits until the write
operation is finished before returning control to the calling application.
Since writing to the file-system is usually very slow compared with CPU
bound operations, this can be a performance bottleneck. This directory
contains an extension that causes SQLite to perform all write requests
using a separate thread running in the background. Although this does not
reduce the overall system resources (CPU, disk bandwidth etc.) at all, it
allows SQLite to return control to the caller quickly even when writing to
the database, eliminating the bottleneck.
1. Functionality
1.1 How it Works
1.2 Limitations
1.3 Locking and Concurrency
2. Compilation and Usage
3. Porting
1. FUNCTIONALITY
With asynchronous I/O, write requests are handled by a separate thread
running in the background. This means that the thread that initiates
a database write does not have to wait for (sometimes slow) disk I/O
to occur. The write seems to happen very quickly, though in reality
it is happening at its usual slow pace in the background.
Asynchronous I/O appears to give better responsiveness, but at a price.
You lose the Durable property. With the default I/O backend of SQLite,
once a write completes, you know that the information you wrote is
safely on disk. With the asynchronous I/O, this is not the case. If
your program crashes or if a power loss occurs after the database
write but before the asynchronous write thread has completed, then the
database change might never make it to disk and the next user of the
database might not see your change.
You lose Durability with asynchronous I/O, but you still retain the
other parts of ACID: Atomic, Consistent, and Isolated. Many
appliations get along fine without the Durablity.
1.1 How it Works
Asynchronous I/O works by creating a special SQLite "vfs" structure
and registering it with sqlite3_vfs_register(). When files opened via
this vfs are written to (using the vfs xWrite() method), the data is not
written directly to disk, but is placed in the "write-queue" to be
handled by the background thread.
When files opened with the asynchronous vfs are read from
(using the vfs xRead() method), the data is read from the file on
disk and the write-queue, so that from the point of view of
the vfs reader the xWrite() appears to have already completed.
The special vfs is registered (and unregistered) by calls to the
API functions sqlite3async_initialize() and sqlite3async_shutdown().
See section "Compilation and Usage" below for details.
1.2 Limitations
In order to gain experience with the main ideas surrounding asynchronous
IO, this implementation is deliberately kept simple. Additional
capabilities may be added in the future.
For example, as currently implemented, if writes are happening at a
steady stream that exceeds the I/O capability of the background writer
thread, the queue of pending write operations will grow without bound.
If this goes on for long enough, the host system could run out of memory.
A more sophisticated module could to keep track of the quantity of
pending writes and stop accepting new write requests when the queue of
pending writes grows too large.
1.3 Locking and Concurrency
Multiple connections from within a single process that use this
implementation of asynchronous IO may access a single database
file concurrently. From the point of view of the user, if all
connections are from within a single process, there is no difference
between the concurrency offered by "normal" SQLite and SQLite
using the asynchronous backend.
If file-locking is enabled (it is enabled by default), then connections
from multiple processes may also read and write the database file.
However concurrency is reduced as follows:
* When a connection using asynchronous IO begins a database
transaction, the database is locked immediately. However the
lock is not released until after all relevant operations
in the write-queue have been flushed to disk. This means
(for example) that the database may remain locked for some
time after a "COMMIT" or "ROLLBACK" is issued.
* If an application using asynchronous IO executes transactions
in quick succession, other database users may be effectively
locked out of the database. This is because when a BEGIN
is executed, a database lock is established immediately. But
when the corresponding COMMIT or ROLLBACK occurs, the lock
is not released until the relevant part of the write-queue
has been flushed through. As a result, if a COMMIT is followed
by a BEGIN before the write-queue is flushed through, the database
is never unlocked,preventing other processes from accessing
the database.
File-locking may be disabled at runtime using the sqlite3async_control()
API (see below). This may improve performance when an NFS or other
network file-system, as the synchronous round-trips to the server be
required to establish file locks are avoided. However, if multiple
connections attempt to access the same database file when file-locking
is disabled, application crashes and database corruption is a likely
outcome.
2. COMPILATION AND USAGE
The asynchronous IO extension consists of a single file of C code
(sqlite3async.c), and a header file (sqlite3async.h) that defines the
C API used by applications to activate and control the modules
functionality.
To use the asynchronous IO extension, compile sqlite3async.c as
part of the application that uses SQLite. Then use the API defined
in sqlite3async.h to initialize and configure the module.
The asynchronous IO VFS API is described in detail in comments in
sqlite3async.h. Using the API usually consists of the following steps:
1. Register the asynchronous IO VFS with SQLite by calling the
sqlite3async_initialize() function.
2. Create a background thread to perform write operations and call
sqlite3async_run().
3. Use the normal SQLite API to read and write to databases via
the asynchronous IO VFS.
Refer to sqlite3async.h for details.
3. PORTING
Currently the asynchronous IO extension is compatible with win32 systems
and systems that support the pthreads interface, including Mac OSX, Linux,
and other varieties of Unix.
To port the asynchronous IO extension to another platform, the user must
implement mutex and condition variable primitives for the new platform.
Currently there is no externally available interface to allow this, but
modifying the code within sqlite3async.c to include the new platforms
concurrency primitives is relatively easy. Search within sqlite3async.c
for the comment string "PORTING FUNCTIONS" for details. Then implement
new versions of each of the following:
static void async_mutex_enter(int eMutex);
static void async_mutex_leave(int eMutex);
static void async_cond_wait(int eCond, int eMutex);
static void async_cond_signal(int eCond);
static void async_sched_yield(void);
The functionality required of each of the above functions is described
in comments in sqlite3async.c.

File diff suppressed because it is too large Load Diff

View File

@ -1,222 +0,0 @@
#ifndef __SQLITEASYNC_H_
#define __SQLITEASYNC_H_ 1
/*
** Make sure we can call this stuff from C++.
*/
#ifdef __cplusplus
extern "C" {
#endif
#define SQLITEASYNC_VFSNAME "sqlite3async"
/*
** THREAD SAFETY NOTES:
**
** Of the four API functions in this file, the following are not threadsafe:
**
** sqlite3async_initialize()
** sqlite3async_shutdown()
**
** Care must be taken that neither of these functions is called while
** another thread may be calling either any sqlite3async_XXX() function
** or an sqlite3_XXX() API function related to a database handle that
** is using the asynchronous IO VFS.
**
** These functions:
**
** sqlite3async_run()
** sqlite3async_control()
**
** are threadsafe. It is quite safe to call either of these functions even
** if another thread may also be calling one of them or an sqlite3_XXX()
** function related to a database handle that uses the asynchronous IO VFS.
*/
/*
** Initialize the asynchronous IO VFS and register it with SQLite using
** sqlite3_vfs_register(). If the asynchronous VFS is already initialized
** and registered, this function is a no-op. The asynchronous IO VFS
** is registered as "sqlite3async".
**
** The asynchronous IO VFS does not make operating system IO requests
** directly. Instead, it uses an existing VFS implementation for all
** required file-system operations. If the first parameter to this function
** is NULL, then the current default VFS is used for IO. If it is not
** NULL, then it must be the name of an existing VFS. In other words, the
** first argument to this function is passed to sqlite3_vfs_find() to
** locate the VFS to use for all real IO operations. This VFS is known
** as the "parent VFS".
**
** If the second parameter to this function is non-zero, then the
** asynchronous IO VFS is registered as the default VFS for all SQLite
** database connections within the process. Otherwise, the asynchronous IO
** VFS is only used by connections opened using sqlite3_open_v2() that
** specifically request VFS "sqlite3async".
**
** If a parent VFS cannot be located, then SQLITE_ERROR is returned.
** In the unlikely event that operating system specific initialization
** fails (win32 systems create the required critical section and event
** objects within this function), then SQLITE_ERROR is also returned.
** Finally, if the call to sqlite3_vfs_register() returns an error, then
** the error code is returned to the user by this function. In all three
** of these cases, intialization has failed and the asynchronous IO VFS
** is not registered with SQLite.
**
** Otherwise, if no error occurs, SQLITE_OK is returned.
*/
int sqlite3async_initialize(const char *zParent, int isDefault);
/*
** This function unregisters the asynchronous IO VFS using
** sqlite3_vfs_unregister().
**
** On win32 platforms, this function also releases the small number of
** critical section and event objects created by sqlite3async_initialize().
*/
void sqlite3async_shutdown(void);
/*
** This function may only be called when the asynchronous IO VFS is
** installed (after a call to sqlite3async_initialize()). It processes
** zero or more queued write operations before returning. It is expected
** (but not required) that this function will be called by a different
** thread than those threads that use SQLite. The "background thread"
** that performs IO.
**
** How many queued write operations are performed before returning
** depends on the global setting configured by passing the SQLITEASYNC_HALT
** verb to sqlite3async_control() (see below for details). By default
** this function never returns - it processes all pending operations and
** then blocks waiting for new ones.
**
** If multiple simultaneous calls are made to sqlite3async_run() from two
** or more threads, then the calls are serialized internally.
*/
void sqlite3async_run(void);
/*
** This function may only be called when the asynchronous IO VFS is
** installed (after a call to sqlite3async_initialize()). It is used
** to query or configure various parameters that affect the operation
** of the asynchronous IO VFS. At present there are three parameters
** supported:
**
** * The "halt" parameter, which configures the circumstances under
** which the sqlite3async_run() parameter is configured.
**
** * The "delay" parameter. Setting the delay parameter to a non-zero
** value causes the sqlite3async_run() function to sleep for the
** configured number of milliseconds between each queued write
** operation.
**
** * The "lockfiles" parameter. This parameter determines whether or
** not the asynchronous IO VFS locks the database files it operates
** on. Disabling file locking can improve throughput.
**
** This function is always passed two arguments. When setting the value
** of a parameter, the first argument must be one of SQLITEASYNC_HALT,
** SQLITEASYNC_DELAY or SQLITEASYNC_LOCKFILES. The second argument must
** be passed the new value for the parameter as type "int".
**
** When querying the current value of a paramter, the first argument must
** be one of SQLITEASYNC_GET_HALT, GET_DELAY or GET_LOCKFILES. The second
** argument to this function must be of type (int *). The current value
** of the queried parameter is copied to the memory pointed to by the
** second argument. For example:
**
** int eCurrentHalt;
** int eNewHalt = SQLITEASYNC_HALT_IDLE;
**
** sqlite3async_control(SQLITEASYNC_HALT, eNewHalt);
** sqlite3async_control(SQLITEASYNC_GET_HALT, &eCurrentHalt);
** assert( eNewHalt==eCurrentHalt );
**
** See below for more detail on each configuration parameter.
**
** SQLITEASYNC_HALT:
**
** This is used to set the value of the "halt" parameter. The second
** argument must be one of the SQLITEASYNC_HALT_XXX symbols defined
** below (either NEVER, IDLE and NOW).
**
** If the parameter is set to NEVER, then calls to sqlite3async_run()
** never return. This is the default setting. If the parameter is set
** to IDLE, then calls to sqlite3async_run() return as soon as the
** queue of pending write operations is empty. If the parameter is set
** to NOW, then calls to sqlite3async_run() return as quickly as
** possible, without processing any pending write requests.
**
** If an attempt is made to set this parameter to an integer value other
** than SQLITEASYNC_HALT_NEVER, IDLE or NOW, then sqlite3async_control()
** returns SQLITE_MISUSE and the current value of the parameter is not
** modified.
**
** Modifying the "halt" parameter affects calls to sqlite3async_run()
** made by other threads that are currently in progress.
**
** SQLITEASYNC_DELAY:
**
** This is used to set the value of the "delay" parameter. If set to
** a non-zero value, then after completing a pending write request, the
** sqlite3async_run() function sleeps for the configured number of
** milliseconds.
**
** If an attempt is made to set this parameter to a negative value,
** sqlite3async_control() returns SQLITE_MISUSE and the current value
** of the parameter is not modified.
**
** Modifying the "delay" parameter affects calls to sqlite3async_run()
** made by other threads that are currently in progress.
**
** SQLITEASYNC_LOCKFILES:
**
** This is used to set the value of the "lockfiles" parameter. This
** parameter must be set to either 0 or 1. If set to 1, then the
** asynchronous IO VFS uses the xLock() and xUnlock() methods of the
** parent VFS to lock database files being read and/or written. If
** the parameter is set to 0, then these locks are omitted.
**
** This parameter may only be set when there are no open database
** connections using the VFS and the queue of pending write requests
** is empty. Attempting to set it when this is not true, or to set it
** to a value other than 0 or 1 causes sqlite3async_control() to return
** SQLITE_MISUSE and the value of the parameter to remain unchanged.
**
** If this parameter is set to zero, then it is only safe to access the
** database via the asynchronous IO VFS from within a single process. If
** while writing to the database via the asynchronous IO VFS the database
** is also read or written from within another process, or via another
** connection that does not use the asynchronous IO VFS within the same
** process, the results are undefined (and may include crashes or database
** corruption).
**
** Alternatively, if this parameter is set to 1, then it is safe to access
** the database from multiple connections within multiple processes using
** either the asynchronous IO VFS or the parent VFS directly.
*/
int sqlite3async_control(int op, ...);
/*
** Values that can be used as the first argument to sqlite3async_control().
*/
#define SQLITEASYNC_HALT 1
#define SQLITEASYNC_GET_HALT 2
#define SQLITEASYNC_DELAY 3
#define SQLITEASYNC_GET_DELAY 4
#define SQLITEASYNC_LOCKFILES 5
#define SQLITEASYNC_GET_LOCKFILES 6
/*
** If the first argument to sqlite3async_control() is SQLITEASYNC_HALT,
** the second argument should be one of the following.
*/
#define SQLITEASYNC_HALT_NEVER 0 /* Never halt (default value) */
#define SQLITEASYNC_HALT_NOW 1 /* Halt as soon as possible */
#define SQLITEASYNC_HALT_IDLE 2 /* Halt when write-queue is empty */
#ifdef __cplusplus
} /* End of the 'extern "C"' block */
#endif
#endif /* ifndef __SQLITEASYNC_H_ */

View File

@ -1,773 +0,0 @@
/*
** 2023 November 4
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
********************************************************************************
** This file implements various interfaces used for console and stream I/O
** by the SQLite project command-line tools, as explained in console_io.h .
** Functions prefixed by "SQLITE_INTERNAL_LINKAGE" behave as described there.
*/
#ifndef SQLITE_CDECL
# define SQLITE_CDECL
#endif
#ifndef SHELL_NO_SYSINC
# include <stdarg.h>
# include <string.h>
# include <stdlib.h>
# include <limits.h>
# include <assert.h>
# include "sqlite3.h"
#endif
#ifndef HAVE_CONSOLE_IO_H
# include "console_io.h"
#endif
#if defined(_MSC_VER)
# pragma warning(disable : 4204)
#endif
#ifndef SQLITE_CIO_NO_TRANSLATE
# if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT
# ifndef SHELL_NO_SYSINC
# include <io.h>
# include <fcntl.h>
# undef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# endif
# define CIO_WIN_WC_XLATE 1 /* Use WCHAR Windows APIs for console I/O */
# else
# ifndef SHELL_NO_SYSINC
# include <unistd.h>
# endif
# define CIO_WIN_WC_XLATE 0 /* Use plain C library stream I/O at console */
# endif
#else
# define CIO_WIN_WC_XLATE 0 /* Not exposing translation routines at all */
#endif
#if CIO_WIN_WC_XLATE
static HANDLE handleOfFile(FILE *pf){
int fileDesc = _fileno(pf);
union { intptr_t osfh; HANDLE fh; } fid = {
(fileDesc>=0)? _get_osfhandle(fileDesc) : (intptr_t)INVALID_HANDLE_VALUE
};
return fid.fh;
}
#endif
#ifndef SQLITE_CIO_NO_TRANSLATE
typedef struct PerStreamTags {
# if CIO_WIN_WC_XLATE
HANDLE hx;
DWORD consMode;
char acIncomplete[4];
# else
short reachesConsole;
# endif
FILE *pf;
} PerStreamTags;
/* Define NULL-like value for things which can validly be 0. */
# define SHELL_INVALID_FILE_PTR ((FILE *)~0)
# if CIO_WIN_WC_XLATE
# define SHELL_INVALID_CONS_MODE 0xFFFF0000
# endif
# if CIO_WIN_WC_XLATE
# define PST_INITIALIZER { INVALID_HANDLE_VALUE, SHELL_INVALID_CONS_MODE, \
{0,0,0,0}, SHELL_INVALID_FILE_PTR }
# else
# define PST_INITIALIZER { 0, SHELL_INVALID_FILE_PTR }
# endif
/* Quickly say whether a known output is going to the console. */
# if CIO_WIN_WC_XLATE
static short pstReachesConsole(PerStreamTags *ppst){
return (ppst->hx != INVALID_HANDLE_VALUE);
}
# else
# define pstReachesConsole(ppst) 0
# endif
# if CIO_WIN_WC_XLATE
static void restoreConsoleArb(PerStreamTags *ppst){
if( pstReachesConsole(ppst) ) SetConsoleMode(ppst->hx, ppst->consMode);
}
# else
# define restoreConsoleArb(ppst)
# endif
/* Say whether FILE* appears to be a console, collect associated info. */
static short streamOfConsole(FILE *pf, /* out */ PerStreamTags *ppst){
# if CIO_WIN_WC_XLATE
short rv = 0;
DWORD dwCM = SHELL_INVALID_CONS_MODE;
HANDLE fh = handleOfFile(pf);
ppst->pf = pf;
if( INVALID_HANDLE_VALUE != fh ){
rv = (GetFileType(fh) == FILE_TYPE_CHAR && GetConsoleMode(fh,&dwCM));
}
ppst->hx = (rv)? fh : INVALID_HANDLE_VALUE;
ppst->consMode = dwCM;
return rv;
# else
ppst->pf = pf;
ppst->reachesConsole = ( (short)isatty(fileno(pf)) );
return ppst->reachesConsole;
# endif
}
# ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
# define ENABLE_VIRTUAL_TERMINAL_PROCESSING (0x4)
# endif
# if CIO_WIN_WC_XLATE
/* Define console modes for use with the Windows Console API. */
# define SHELL_CONI_MODE \
(ENABLE_ECHO_INPUT | ENABLE_INSERT_MODE | ENABLE_LINE_INPUT | 0x80 \
| ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_PROCESSED_INPUT)
# define SHELL_CONO_MODE (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT \
| ENABLE_VIRTUAL_TERMINAL_PROCESSING)
# endif
typedef struct ConsoleInfo {
PerStreamTags pstSetup[3];
PerStreamTags pstDesignated[3];
StreamsAreConsole sacSetup;
} ConsoleInfo;
static short isValidStreamInfo(PerStreamTags *ppst){
return (ppst->pf != SHELL_INVALID_FILE_PTR);
}
static ConsoleInfo consoleInfo = {
{ /* pstSetup */ PST_INITIALIZER, PST_INITIALIZER, PST_INITIALIZER },
{ /* pstDesignated[] */ PST_INITIALIZER, PST_INITIALIZER, PST_INITIALIZER },
SAC_NoConsole /* sacSetup */
};
SQLITE_INTERNAL_LINKAGE FILE* invalidFileStream = (FILE *)~0;
# if CIO_WIN_WC_XLATE
static void maybeSetupAsConsole(PerStreamTags *ppst, short odir){
if( pstReachesConsole(ppst) ){
DWORD cm = odir? SHELL_CONO_MODE : SHELL_CONI_MODE;
SetConsoleMode(ppst->hx, cm);
}
}
# else
# define maybeSetupAsConsole(ppst,odir)
# endif
SQLITE_INTERNAL_LINKAGE void consoleRenewSetup(void){
# if CIO_WIN_WC_XLATE
int ix = 0;
while( ix < 6 ){
PerStreamTags *ppst = (ix<3)?
&consoleInfo.pstSetup[ix] : &consoleInfo.pstDesignated[ix-3];
maybeSetupAsConsole(ppst, (ix % 3)>0);
++ix;
}
# endif
}
SQLITE_INTERNAL_LINKAGE StreamsAreConsole
consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ){
StreamsAreConsole rv = SAC_NoConsole;
FILE* apf[3] = { pfIn, pfOut, pfErr };
int ix;
for( ix = 2; ix >= 0; --ix ){
PerStreamTags *ppst = &consoleInfo.pstSetup[ix];
if( streamOfConsole(apf[ix], ppst) ){
rv |= (SAC_InConsole<<ix);
}
consoleInfo.pstDesignated[ix] = *ppst;
if( ix > 0 ) fflush(apf[ix]);
}
consoleInfo.sacSetup = rv;
consoleRenewSetup();
return rv;
}
SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ){
# if CIO_WIN_WC_XLATE
static ConsoleInfo *pci = &consoleInfo;
if( pci->sacSetup ){
int ix;
for( ix=0; ix<3; ++ix ){
if( pci->sacSetup & (SAC_InConsole<<ix) ){
PerStreamTags *ppst = &pci->pstSetup[ix];
SetConsoleMode(ppst->hx, ppst->consMode);
}
}
}
# endif
}
#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */
#ifdef SQLITE_CIO_INPUT_REDIR
/* Say whether given FILE* is among those known, via either
** consoleClassifySetup() or set{Output,Error}Stream, as
** readable, and return an associated PerStreamTags pointer
** if so. Otherwise, return 0.
*/
static PerStreamTags * isKnownReadable(FILE *pf){
static PerStreamTags *apst[] = {
&consoleInfo.pstDesignated[0], &consoleInfo.pstSetup[0], 0
};
int ix = 0;
do {
if( apst[ix]->pf == pf ) break;
} while( apst[++ix] != 0 );
return apst[ix];
}
#endif
#ifndef SQLITE_CIO_NO_TRANSLATE
/* Say whether given FILE* is among those known, via either
** consoleClassifySetup() or set{Output,Error}Stream, as
** writable, and return an associated PerStreamTags pointer
** if so. Otherwise, return 0.
*/
static PerStreamTags * isKnownWritable(FILE *pf){
static PerStreamTags *apst[] = {
&consoleInfo.pstDesignated[1], &consoleInfo.pstDesignated[2],
&consoleInfo.pstSetup[1], &consoleInfo.pstSetup[2], 0
};
int ix = 0;
do {
if( apst[ix]->pf == pf ) break;
} while( apst[++ix] != 0 );
return apst[ix];
}
static FILE *designateEmitStream(FILE *pf, unsigned chix){
FILE *rv = consoleInfo.pstDesignated[chix].pf;
if( pf == invalidFileStream ) return rv;
else{
/* Setting a possibly new output stream. */
PerStreamTags *ppst = isKnownWritable(pf);
if( ppst != 0 ){
PerStreamTags pst = *ppst;
consoleInfo.pstDesignated[chix] = pst;
}else streamOfConsole(pf, &consoleInfo.pstDesignated[chix]);
}
return rv;
}
SQLITE_INTERNAL_LINKAGE FILE *setOutputStream(FILE *pf){
return designateEmitStream(pf, 1);
}
# ifdef CONSIO_SET_ERROR_STREAM
SQLITE_INTERNAL_LINKAGE FILE *setErrorStream(FILE *pf){
return designateEmitStream(pf, 2);
}
# endif
#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */
#ifndef SQLITE_CIO_NO_SETMODE
# if CIO_WIN_WC_XLATE
static void setModeFlushQ(FILE *pf, short bFlush, int mode){
if( bFlush ) fflush(pf);
_setmode(_fileno(pf), mode);
}
# else
# define setModeFlushQ(f, b, m) if(b) fflush(f)
# endif
SQLITE_INTERNAL_LINKAGE void setBinaryMode(FILE *pf, short bFlush){
setModeFlushQ(pf, bFlush, _O_BINARY);
}
SQLITE_INTERNAL_LINKAGE void setTextMode(FILE *pf, short bFlush){
setModeFlushQ(pf, bFlush, _O_TEXT);
}
# undef setModeFlushQ
#else /* defined(SQLITE_CIO_NO_SETMODE) */
# define setBinaryMode(f, bFlush) do{ if((bFlush)) fflush(f); }while(0)
# define setTextMode(f, bFlush) do{ if((bFlush)) fflush(f); }while(0)
#endif /* defined(SQLITE_CIO_NO_SETMODE) */
#ifndef SQLITE_CIO_NO_TRANSLATE
# if CIO_WIN_WC_XLATE
/* Write buffer cBuf as output to stream known to reach console,
** limited to ncTake char's. Return ncTake on success, else 0. */
static int conZstrEmit(PerStreamTags *ppst, const char *z, int ncTake){
int rv = 0;
if( z!=NULL ){
int nwc = MultiByteToWideChar(CP_UTF8,0, z,ncTake, 0,0);
if( nwc > 0 ){
WCHAR *zw = sqlite3_malloc64(nwc*sizeof(WCHAR));
if( zw!=NULL ){
nwc = MultiByteToWideChar(CP_UTF8,0, z,ncTake, zw,nwc);
if( nwc > 0 ){
/* Translation from UTF-8 to UTF-16, then WCHARs out. */
if( WriteConsoleW(ppst->hx, zw,nwc, 0, NULL) ){
rv = ncTake;
}
}
sqlite3_free(zw);
}
}
}
return rv;
}
/* For {f,o,e}PrintfUtf8() when stream is known to reach console. */
static int conioVmPrintf(PerStreamTags *ppst, const char *zFormat, va_list ap){
char *z = sqlite3_vmprintf(zFormat, ap);
if( z ){
int rv = conZstrEmit(ppst, z, (int)strlen(z));
sqlite3_free(z);
return rv;
}else return 0;
}
# endif /* CIO_WIN_WC_XLATE */
# ifdef CONSIO_GET_EMIT_STREAM
static PerStreamTags * getDesignatedEmitStream(FILE *pf, unsigned chix,
PerStreamTags *ppst){
PerStreamTags *rv = isKnownWritable(pf);
short isValid = (rv!=0)? isValidStreamInfo(rv) : 0;
if( rv != 0 && isValid ) return rv;
streamOfConsole(pf, ppst);
return ppst;
}
# endif
/* Get stream info, either for designated output or error stream when
** chix equals 1 or 2, or for an arbitrary stream when chix == 0.
** In either case, ppst references a caller-owned PerStreamTags
** struct which may be filled in if none of the known writable
** streams is being held by consoleInfo. The ppf parameter is a
** byref output when chix!=0 and a byref input when chix==0.
*/
static PerStreamTags *
getEmitStreamInfo(unsigned chix, PerStreamTags *ppst,
/* in/out */ FILE **ppf){
PerStreamTags *ppstTry;
FILE *pfEmit;
if( chix > 0 ){
ppstTry = &consoleInfo.pstDesignated[chix];
if( !isValidStreamInfo(ppstTry) ){
ppstTry = &consoleInfo.pstSetup[chix];
pfEmit = ppst->pf;
}else pfEmit = ppstTry->pf;
if( !isValidStreamInfo(ppstTry) ){
pfEmit = (chix > 1)? stderr : stdout;
ppstTry = ppst;
streamOfConsole(pfEmit, ppstTry);
}
*ppf = pfEmit;
}else{
ppstTry = isKnownWritable(*ppf);
if( ppstTry != 0 ) return ppstTry;
streamOfConsole(*ppf, ppst);
return ppst;
}
return ppstTry;
}
SQLITE_INTERNAL_LINKAGE int oPrintfUtf8(const char *zFormat, ...){
va_list ap;
int rv;
FILE *pfOut;
PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
# if CIO_WIN_WC_XLATE
PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut);
# else
getEmitStreamInfo(1, &pst, &pfOut);
# endif
assert(zFormat!=0);
va_start(ap, zFormat);
# if CIO_WIN_WC_XLATE
if( pstReachesConsole(ppst) ){
rv = conioVmPrintf(ppst, zFormat, ap);
}else{
# endif
rv = vfprintf(pfOut, zFormat, ap);
# if CIO_WIN_WC_XLATE
}
# endif
va_end(ap);
return rv;
}
SQLITE_INTERNAL_LINKAGE int ePrintfUtf8(const char *zFormat, ...){
va_list ap;
int rv;
FILE *pfErr;
PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
# if CIO_WIN_WC_XLATE
PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr);
# else
getEmitStreamInfo(2, &pst, &pfErr);
# endif
assert(zFormat!=0);
va_start(ap, zFormat);
# if CIO_WIN_WC_XLATE
if( pstReachesConsole(ppst) ){
rv = conioVmPrintf(ppst, zFormat, ap);
}else{
# endif
rv = vfprintf(pfErr, zFormat, ap);
# if CIO_WIN_WC_XLATE
}
# endif
va_end(ap);
return rv;
}
SQLITE_INTERNAL_LINKAGE int fPrintfUtf8(FILE *pfO, const char *zFormat, ...){
va_list ap;
int rv;
PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
# if CIO_WIN_WC_XLATE
PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO);
# else
getEmitStreamInfo(0, &pst, &pfO);
# endif
assert(zFormat!=0);
va_start(ap, zFormat);
# if CIO_WIN_WC_XLATE
if( pstReachesConsole(ppst) ){
maybeSetupAsConsole(ppst, 1);
rv = conioVmPrintf(ppst, zFormat, ap);
if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst);
}else{
# endif
rv = vfprintf(pfO, zFormat, ap);
# if CIO_WIN_WC_XLATE
}
# endif
va_end(ap);
return rv;
}
SQLITE_INTERNAL_LINKAGE int fPutsUtf8(const char *z, FILE *pfO){
PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
# if CIO_WIN_WC_XLATE
PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO);
# else
getEmitStreamInfo(0, &pst, &pfO);
# endif
assert(z!=0);
# if CIO_WIN_WC_XLATE
if( pstReachesConsole(ppst) ){
int rv;
maybeSetupAsConsole(ppst, 1);
rv = conZstrEmit(ppst, z, (int)strlen(z));
if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst);
return rv;
}else {
# endif
return (fputs(z, pfO)<0)? 0 : (int)strlen(z);
# if CIO_WIN_WC_XLATE
}
# endif
}
SQLITE_INTERNAL_LINKAGE int ePutsUtf8(const char *z){
FILE *pfErr;
PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
# if CIO_WIN_WC_XLATE
PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr);
# else
getEmitStreamInfo(2, &pst, &pfErr);
# endif
assert(z!=0);
# if CIO_WIN_WC_XLATE
if( pstReachesConsole(ppst) ) return conZstrEmit(ppst, z, (int)strlen(z));
else {
# endif
return (fputs(z, pfErr)<0)? 0 : (int)strlen(z);
# if CIO_WIN_WC_XLATE
}
# endif
}
SQLITE_INTERNAL_LINKAGE int oPutsUtf8(const char *z){
FILE *pfOut;
PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
# if CIO_WIN_WC_XLATE
PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut);
# else
getEmitStreamInfo(1, &pst, &pfOut);
# endif
assert(z!=0);
# if CIO_WIN_WC_XLATE
if( pstReachesConsole(ppst) ) return conZstrEmit(ppst, z, (int)strlen(z));
else {
# endif
return (fputs(z, pfOut)<0)? 0 : (int)strlen(z);
# if CIO_WIN_WC_XLATE
}
# endif
}
#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */
#if !(defined(SQLITE_CIO_NO_UTF8SCAN) && defined(SQLITE_CIO_NO_TRANSLATE))
/* Skip over as much z[] input char sequence as is valid UTF-8,
** limited per nAccept char's or whole characters and containing
** no char cn such that ((1<<cn) & ccm)!=0. On return, the
** sequence z:return (inclusive:exclusive) is validated UTF-8.
** Limit: nAccept>=0 => char count, nAccept<0 => character
*/
SQLITE_INTERNAL_LINKAGE const char*
zSkipValidUtf8(const char *z, int nAccept, long ccm){
int ng = (nAccept<0)? -nAccept : 0;
const char *pcLimit = (nAccept>=0)? z+nAccept : 0;
assert(z!=0);
while( (pcLimit)? (z<pcLimit) : (ng-- != 0) ){
char c = *z;
if( (c & 0x80) == 0 ){
if( ccm != 0L && c < 0x20 && ((1L<<c) & ccm) != 0 ) return z;
++z; /* ASCII */
}else if( (c & 0xC0) != 0xC0 ) return z; /* not a lead byte */
else{
const char *zt = z+1; /* Got lead byte, look at trail bytes.*/
do{
if( pcLimit && zt >= pcLimit ) return z;
else{
char ct = *zt++;
if( ct==0 || (zt-z)>4 || (ct & 0xC0)!=0x80 ){
/* Trailing bytes are too few, too many, or invalid. */
return z;
}
}
} while( ((c <<= 1) & 0x40) == 0x40 ); /* Eat lead byte's count. */
z = zt;
}
}
return z;
}
#endif /*!(defined(SQLITE_CIO_NO_UTF8SCAN)&&defined(SQLITE_CIO_NO_TRANSLATE))*/
#ifndef SQLITE_CIO_NO_TRANSLATE
# ifdef CONSIO_SPUTB
SQLITE_INTERNAL_LINKAGE int
fPutbUtf8(FILE *pfO, const char *cBuf, int nAccept){
assert(pfO!=0);
# if CIO_WIN_WC_XLATE
PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO);
if( pstReachesConsole(ppst) ){
int rv;
maybeSetupAsConsole(ppst, 1);
rv = conZstrEmit(ppst, cBuf, nAccept);
if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst);
return rv;
}else {
# endif
return (int)fwrite(cBuf, 1, nAccept, pfO);
# if CIO_WIN_WC_XLATE
}
# endif
}
# endif
SQLITE_INTERNAL_LINKAGE int
oPutbUtf8(const char *cBuf, int nAccept){
FILE *pfOut;
PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
# if CIO_WIN_WC_XLATE
PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut);
# else
getEmitStreamInfo(1, &pst, &pfOut);
# endif
# if CIO_WIN_WC_XLATE
if( pstReachesConsole(ppst) ){
return conZstrEmit(ppst, cBuf, nAccept);
}else {
# endif
return (int)fwrite(cBuf, 1, nAccept, pfOut);
# if CIO_WIN_WC_XLATE
}
# endif
}
/*
** Flush the given output stream. Return non-zero for success, else 0.
*/
#if !defined(SQLITE_CIO_NO_FLUSH) && !defined(SQLITE_CIO_NO_SETMODE)
SQLITE_INTERNAL_LINKAGE int
fFlushBuffer(FILE *pfOut){
# if CIO_WIN_WC_XLATE && !defined(SHELL_OMIT_FIO_DUPE)
return FlushFileBuffers(handleOfFile(pfOut))? 1 : 0;
# else
return fflush(pfOut);
# endif
}
#endif
#if CIO_WIN_WC_XLATE \
&& !defined(SHELL_OMIT_FIO_DUPE) \
&& defined(SQLITE_USE_ONLY_WIN32)
static struct FileAltIds {
int fd;
HANDLE fh;
} altIdsOfFile(FILE *pf){
struct FileAltIds rv = { _fileno(pf) };
union { intptr_t osfh; HANDLE fh; } fid = {
(rv.fd>=0)? _get_osfhandle(rv.fd) : (intptr_t)INVALID_HANDLE_VALUE
};
rv.fh = fid.fh;
return rv;
}
SQLITE_INTERNAL_LINKAGE size_t
cfWrite(const void *buf, size_t osz, size_t ocnt, FILE *pf){
size_t rv = 0;
struct FileAltIds fai = altIdsOfFile(pf);
int fmode = _setmode(fai.fd, _O_BINARY);
_setmode(fai.fd, fmode);
while( rv < ocnt ){
size_t nbo = osz;
while( nbo > 0 ){
DWORD dwno = (nbo>(1L<<24))? 1L<<24 : (DWORD)nbo;
BOOL wrc = TRUE;
BOOL genCR = (fmode & _O_TEXT)!=0;
if( genCR ){
const char *pnl = (const char*)memchr(buf, '\n', nbo);
if( pnl ) nbo = pnl - (const char*)buf;
else genCR = 0;
}
if( dwno>0 ) wrc = WriteFile(fai.fh, buf, dwno, 0,0);
if( genCR && wrc ){
wrc = WriteFile(fai.fh, "\r\n", 2, 0,0);
++dwno; /* Skip over the LF */
}
if( !wrc ) return rv;
buf = (const char*)buf + dwno;
nbo += dwno;
}
++rv;
}
return rv;
}
/* An fgets() equivalent, using Win32 file API for actual input.
** Input ends when given buffer is filled or a newline is read.
** If the FILE object is in text mode, swallows 0x0D. (ASCII CR)
*/
SQLITE_INTERNAL_LINKAGE char *
cfGets(char *cBuf, int n, FILE *pf){
int nci = 0;
struct FileAltIds fai = altIdsOfFile(pf);
int fmode = _setmode(fai.fd, _O_BINARY);
BOOL eatCR = (fmode & _O_TEXT)!=0;
_setmode(fai.fd, fmode);
while( nci < n-1 ){
DWORD nr;
if( !ReadFile(fai.fh, cBuf+nci, 1, &nr, 0) || nr==0 ) break;
if( nr>0 && (!eatCR || cBuf[nci]!='\r') ){
nci += nr;
if( cBuf[nci-nr]=='\n' ) break;
}
}
if( nci < n ) cBuf[nci] = 0;
return (nci>0)? cBuf : 0;
}
# else
# define cfWrite(b,os,no,f) fwrite(b,os,no,f)
# define cfGets(b,n,f) fgets(b,n,f)
# endif
# ifdef CONSIO_EPUTB
SQLITE_INTERNAL_LINKAGE int
ePutbUtf8(const char *cBuf, int nAccept){
FILE *pfErr;
PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr);
# if CIO_WIN_WC_XLATE
if( pstReachesConsole(ppst) ){
return conZstrEmit(ppst, cBuf, nAccept);
}else {
# endif
return (int)cfWrite(cBuf, 1, nAccept, pfErr);
# if CIO_WIN_WC_XLATE
}
# endif
}
# endif /* defined(CONSIO_EPUTB) */
SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn){
if( pfIn==0 ) pfIn = stdin;
# if CIO_WIN_WC_XLATE
if( pfIn == consoleInfo.pstSetup[0].pf
&& (consoleInfo.sacSetup & SAC_InConsole)!=0 ){
# if CIO_WIN_WC_XLATE==1
# define SHELL_GULP 150 /* Count of WCHARS to be gulped at a time */
WCHAR wcBuf[SHELL_GULP+1];
int lend = 0, noc = 0;
if( ncMax > 0 ) cBuf[0] = 0;
while( noc < ncMax-8-1 && !lend ){
/* There is room for at least 2 more characters and a 0-terminator. */
int na = (ncMax > SHELL_GULP*4+1 + noc)? SHELL_GULP : (ncMax-1 - noc)/4;
# undef SHELL_GULP
DWORD nbr = 0;
BOOL bRC = ReadConsoleW(consoleInfo.pstSetup[0].hx, wcBuf, na, &nbr, 0);
if( bRC && nbr>0 && (wcBuf[nbr-1]&0xF800)==0xD800 ){
/* Last WHAR read is first of a UTF-16 surrogate pair. Grab its mate. */
DWORD nbrx;
bRC &= ReadConsoleW(consoleInfo.pstSetup[0].hx, wcBuf+nbr, 1, &nbrx, 0);
if( bRC ) nbr += nbrx;
}
if( !bRC || (noc==0 && nbr==0) ) return 0;
if( nbr > 0 ){
int nmb = WideCharToMultiByte(CP_UTF8, 0, wcBuf,nbr,0,0,0,0);
if( nmb != 0 && noc+nmb <= ncMax ){
int iseg = noc;
nmb = WideCharToMultiByte(CP_UTF8, 0, wcBuf,nbr,cBuf+noc,nmb,0,0);
noc += nmb;
/* Fixup line-ends as coded by Windows for CR (or "Enter".)
** This is done without regard for any setMode{Text,Binary}()
** call that might have been done on the interactive input.
*/
if( noc > 0 ){
if( cBuf[noc-1]=='\n' ){
lend = 1;
if( noc > 1 && cBuf[noc-2]=='\r' ) cBuf[--noc-1] = '\n';
}
}
/* Check for ^Z (anywhere in line) too, to act as EOF. */
while( iseg < noc ){
if( cBuf[iseg]=='\x1a' ){
noc = iseg; /* Chop ^Z and anything following. */
lend = 1; /* Counts as end of line too. */
break;
}
++iseg;
}
}else break; /* Drop apparent garbage in. (Could assert.) */
}else break;
}
/* If got nothing, (after ^Z chop), must be at end-of-file. */
if( noc > 0 ){
cBuf[noc] = 0;
return cBuf;
}else return 0;
# endif
}else{
# endif
return cfGets(cBuf, ncMax, pfIn);
# if CIO_WIN_WC_XLATE
}
# endif
}
#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */
#if defined(_MSC_VER)
# pragma warning(default : 4204)
#endif
#undef SHELL_INVALID_FILE_PTR

View File

@ -1,287 +0,0 @@
/*
** 2023 November 1
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
********************************************************************************
** This file exposes various interfaces used for console and other I/O
** by the SQLite project command-line tools. These interfaces are used
** at either source conglomeration time, compilation time, or run time.
** This source provides for either inclusion into conglomerated,
** "single-source" forms or separate compilation then linking.
**
** Platform dependencies are "hidden" here by various stratagems so
** that, provided certain conditions are met, the programs using this
** source or object code compiled from it need no explicit conditional
** compilation in their source for their console and stream I/O.
**
** The symbols and functionality exposed here are not a public API.
** This code may change in tandem with other project code as needed.
**
** When this .h file and its companion .c are directly incorporated into
** a source conglomeration (such as shell.c), the preprocessor symbol
** CIO_WIN_WC_XLATE is defined as 0 or 1, reflecting whether console I/O
** translation for Windows is effected for the build.
*/
#define HAVE_CONSOLE_IO_H 1
#ifndef SQLITE_INTERNAL_LINKAGE
# define SQLITE_INTERNAL_LINKAGE extern /* external to translation unit */
# include <stdio.h>
#else
# define SHELL_NO_SYSINC /* Better yet, modify mkshellc.tcl for this. */
#endif
#ifndef SQLITE3_H
# include "sqlite3.h"
#endif
#ifndef SQLITE_CIO_NO_CLASSIFY
/* Define enum for use with following function. */
typedef enum StreamsAreConsole {
SAC_NoConsole = 0,
SAC_InConsole = 1, SAC_OutConsole = 2, SAC_ErrConsole = 4,
SAC_AnyConsole = 0x7
} StreamsAreConsole;
/*
** Classify the three standard I/O streams according to whether
** they are connected to a console attached to the process.
**
** Returns the bit-wise OR of SAC_{In,Out,Err}Console values,
** or SAC_NoConsole if none of the streams reaches a console.
**
** This function should be called before any I/O is done with
** the given streams. As a side-effect, the given inputs are
** recorded so that later I/O operations on them may be done
** differently than the C library FILE* I/O would be done,
** iff the stream is used for the I/O functions that follow,
** and to support the ones that use an implicit stream.
**
** On some platforms, stream or console mode alteration (aka
** "Setup") may be made which is undone by consoleRestore().
*/
SQLITE_INTERNAL_LINKAGE StreamsAreConsole
consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr );
/* A usual call for convenience: */
#define SQLITE_STD_CONSOLE_INIT() consoleClassifySetup(stdin,stdout,stderr)
/*
** After an initial call to consoleClassifySetup(...), renew
** the same setup it effected. (A call not after is an error.)
** This will restore state altered by consoleRestore();
**
** Applications which run an inferior (child) process which
** inherits the same I/O streams may call this function after
** such a process exits to guard against console mode changes.
*/
SQLITE_INTERNAL_LINKAGE void consoleRenewSetup(void);
/*
** Undo any side-effects left by consoleClassifySetup(...).
**
** This should be called after consoleClassifySetup() and
** before the process terminates normally. It is suitable
** for use with the atexit() C library procedure. After
** this call, no console I/O should be done until one of
** console{Classify or Renew}Setup(...) is called again.
**
** Applications which run an inferior (child) process that
** inherits the same I/O streams might call this procedure
** before so that said process will have a console setup
** however users have configured it or come to expect.
*/
SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void );
#else /* defined(SQLITE_CIO_NO_CLASSIFY) */
# define consoleClassifySetup(i,o,e)
# define consoleRenewSetup()
# define consoleRestore()
#endif /* defined(SQLITE_CIO_NO_CLASSIFY) */
#ifndef SQLITE_CIO_NO_REDIRECT
/*
** Set stream to be used for the functions below which write
** to "the designated X stream", where X is Output or Error.
** Returns the previous value.
**
** Alternatively, pass the special value, invalidFileStream,
** to get the designated stream value without setting it.
**
** Before the designated streams are set, they default to
** those passed to consoleClassifySetup(...), and before
** that is called they default to stdout and stderr.
**
** It is error to close a stream so designated, then, without
** designating another, use the corresponding {o,e}Emit(...).
*/
SQLITE_INTERNAL_LINKAGE FILE *invalidFileStream;
SQLITE_INTERNAL_LINKAGE FILE *setOutputStream(FILE *pf);
# ifdef CONSIO_SET_ERROR_STREAM
SQLITE_INTERNAL_LINKAGE FILE *setErrorStream(FILE *pf);
# endif
#else
# define setOutputStream(pf)
# define setErrorStream(pf)
#endif /* !defined(SQLITE_CIO_NO_REDIRECT) */
#ifndef SQLITE_CIO_NO_TRANSLATE
/*
** Emit output like fprintf(). If the output is going to the
** console and translation from UTF-8 is necessary, perform
** the needed translation. Otherwise, write formatted output
** to the provided stream almost as-is, possibly with newline
** translation as specified by set{Binary,Text}Mode().
*/
SQLITE_INTERNAL_LINKAGE int fPrintfUtf8(FILE *pfO, const char *zFormat, ...);
/* Like fPrintfUtf8 except stream is always the designated output. */
SQLITE_INTERNAL_LINKAGE int oPrintfUtf8(const char *zFormat, ...);
/* Like fPrintfUtf8 except stream is always the designated error. */
SQLITE_INTERNAL_LINKAGE int ePrintfUtf8(const char *zFormat, ...);
/*
** Emit output like fputs(). If the output is going to the
** console and translation from UTF-8 is necessary, perform
** the needed translation. Otherwise, write given text to the
** provided stream almost as-is, possibly with newline
** translation as specified by set{Binary,Text}Mode().
*/
SQLITE_INTERNAL_LINKAGE int fPutsUtf8(const char *z, FILE *pfO);
/* Like fPutsUtf8 except stream is always the designated output. */
SQLITE_INTERNAL_LINKAGE int oPutsUtf8(const char *z);
/* Like fPutsUtf8 except stream is always the designated error. */
SQLITE_INTERNAL_LINKAGE int ePutsUtf8(const char *z);
/*
** Emit output like fPutsUtf8(), except that the length of the
** accepted char or character sequence is limited by nAccept.
**
** Returns the number of accepted char values.
*/
#ifdef CONSIO_SPUTB
SQLITE_INTERNAL_LINKAGE int
fPutbUtf8(FILE *pfOut, const char *cBuf, int nAccept);
/* Like fPutbUtf8 except stream is always the designated output. */
#endif
SQLITE_INTERNAL_LINKAGE int
oPutbUtf8(const char *cBuf, int nAccept);
/* Like fPutbUtf8 except stream is always the designated error. */
#ifdef CONSIO_EPUTB
SQLITE_INTERNAL_LINKAGE int
ePutbUtf8(const char *cBuf, int nAccept);
#endif
/*
** Flush the given output stream. Return non-zero for success, else 0.
*/
#if !defined(SQLITE_CIO_NO_FLUSH) && !defined(SQLITE_CIO_NO_SETMODE)
SQLITE_INTERNAL_LINKAGE int
fFlushBuffer(FILE *pfOut);
#endif
/*
** Collect input like fgets(...) with special provisions for input
** from the console on such platforms as require same. Newline
** translation may be done as set by set{Binary,Text}Mode().
** As a convenience, pfIn==NULL is treated as stdin.
*/
SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn);
/* Like fGetsUtf8 except stream is always the designated input. */
/* SQLITE_INTERNAL_LINKAGE char* iGetsUtf8(char *cBuf, int ncMax); */
#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */
#ifndef SQLITE_CIO_NO_SETMODE
/*
** Set given stream for binary mode, where newline translation is
** not done, or for text mode where, for some platforms, newlines
** are translated to the platform's conventional char sequence.
** If bFlush true, flush the stream.
**
** An additional side-effect is that if the stream is one passed
** to consoleClassifySetup() as an output, it is flushed first.
**
** Note that binary/text mode has no effect on console I/O
** translation. On all platforms, newline to the console starts
** a new line and CR,LF chars from the console become a newline.
*/
SQLITE_INTERNAL_LINKAGE void setBinaryMode(FILE *, short bFlush);
SQLITE_INTERNAL_LINKAGE void setTextMode(FILE *, short bFlush);
#endif
#ifdef SQLITE_CIO_PROMPTED_IN
typedef struct Prompts {
int numPrompts;
const char **azPrompts;
} Prompts;
/*
** Macros for use of a line editor.
**
** The following macros define operations involving use of a
** line-editing library or simple console interaction.
** A "T" argument is a text (char *) buffer or filename.
** A "N" argument is an integer.
**
** SHELL_ADD_HISTORY(T) // Record text as line(s) of history.
** SHELL_READ_HISTORY(T) // Read history from file named by T.
** SHELL_WRITE_HISTORY(T) // Write history to file named by T.
** SHELL_STIFLE_HISTORY(N) // Limit history to N entries.
**
** A console program which does interactive console input is
** expected to call:
** SHELL_READ_HISTORY(T) before collecting such input;
** SHELL_ADD_HISTORY(T) as record-worthy input is taken;
** SHELL_STIFLE_HISTORY(N) after console input ceases; then
** SHELL_WRITE_HISTORY(T) before the program exits.
*/
/*
** Retrieve a single line of input text from an input stream.
**
** If pfIn is the input stream passed to consoleClassifySetup(),
** and azPrompt is not NULL, then a prompt is issued before the
** line is collected, as selected by the isContinuation flag.
** Array azPrompt[{0,1}] holds the {main,continuation} prompt.
**
** If zBufPrior is not NULL then it is a buffer from a prior
** call to this routine that can be reused, or will be freed.
**
** The result is stored in space obtained from malloc() and
** must either be freed by the caller or else passed back to
** this function as zBufPrior for reuse.
**
** This function may call upon services of a line-editing
** library to interactively collect line edited input.
*/
SQLITE_INTERNAL_LINKAGE char *
shellGetLine(FILE *pfIn, char *zBufPrior, int nLen,
short isContinuation, Prompts azPrompt);
#endif /* defined(SQLITE_CIO_PROMPTED_IN) */
/*
** TBD: Define an interface for application(s) to generate
** completion candidates for use by the line-editor.
**
** This may be premature; the CLI is the only application
** that does this. Yet, getting line-editing melded into
** console I/O is desirable because a line-editing library
** may have to establish console operating mode, possibly
** in a way that interferes with the above functionality.
*/
#if !(defined(SQLITE_CIO_NO_UTF8SCAN)&&defined(SQLITE_CIO_NO_TRANSLATE))
/* Skip over as much z[] input char sequence as is valid UTF-8,
** limited per nAccept char's or whole characters and containing
** no char cn such that ((1<<cn) & ccm)!=0. On return, the
** sequence z:return (inclusive:exclusive) is validated UTF-8.
** Limit: nAccept>=0 => char count, nAccept<0 => character
*/
SQLITE_INTERNAL_LINKAGE const char*
zSkipValidUtf8(const char *z, int nAccept, long ccm);
#endif

View File

@ -1,96 +0,0 @@
/*
** 2014-09-08
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
**
** This file contains the application interface definitions for the
** user-authentication extension feature.
**
** To compile with the user-authentication feature, append this file to
** end of an SQLite amalgamation header file ("sqlite3.h"), then add
** the SQLITE_USER_AUTHENTICATION compile-time option. See the
** user-auth.txt file in the same source directory as this file for
** additional information.
*/
#ifdef SQLITE_USER_AUTHENTICATION
#ifdef __cplusplus
extern "C" {
#endif
/*
** If a database contains the SQLITE_USER table, then the
** sqlite3_user_authenticate() interface must be invoked with an
** appropriate username and password prior to enable read and write
** access to the database.
**
** Return SQLITE_OK on success or SQLITE_ERROR if the username/password
** combination is incorrect or unknown.
**
** If the SQLITE_USER table is not present in the database file, then
** this interface is a harmless no-op returnning SQLITE_OK.
*/
int sqlite3_user_authenticate(
sqlite3 *db, /* The database connection */
const char *zUsername, /* Username */
const char *aPW, /* Password or credentials */
int nPW /* Number of bytes in aPW[] */
);
/*
** The sqlite3_user_add() interface can be used (by an admin user only)
** to create a new user. When called on a no-authentication-required
** database, this routine converts the database into an authentication-
** required database, automatically makes the added user an
** administrator, and logs in the current connection as that user.
** The sqlite3_user_add() interface only works for the "main" database, not
** for any ATTACH-ed databases. Any call to sqlite3_user_add() by a
** non-admin user results in an error.
*/
int sqlite3_user_add(
sqlite3 *db, /* Database connection */
const char *zUsername, /* Username to be added */
const char *aPW, /* Password or credentials */
int nPW, /* Number of bytes in aPW[] */
int isAdmin /* True to give new user admin privilege */
);
/*
** The sqlite3_user_change() interface can be used to change a users
** login credentials or admin privilege. Any user can change their own
** login credentials. Only an admin user can change another users login
** credentials or admin privilege setting. No user may change their own
** admin privilege setting.
*/
int sqlite3_user_change(
sqlite3 *db, /* Database connection */
const char *zUsername, /* Username to change */
const char *aPW, /* New password or credentials */
int nPW, /* Number of bytes in aPW[] */
int isAdmin /* Modified admin privilege for the user */
);
/*
** The sqlite3_user_delete() interface can be used (by an admin user only)
** to delete a user. The currently logged-in user cannot be deleted,
** which guarantees that there is always an admin user and hence that
** the database cannot be converted into a no-authentication-required
** database.
*/
int sqlite3_user_delete(
sqlite3 *db, /* Database connection */
const char *zUsername /* Username to remove */
);
#ifdef __cplusplus
} /* end of the 'extern "C"' block */
#endif
#endif /* SQLITE_USER_AUTHENTICATION */

View File

@ -1,176 +0,0 @@
*********************************** NOTICE ************************************
* This extension is deprecated. The SQLite developers do not maintain this *
* extension. At some point in the future, it might disappear from the source *
* tree. *
* *
* If you are using this extension and think it should be supported moving *
* forward, visit the SQLite Forum (https://sqlite.org/forum) and argue your *
* case there. *
* *
* This deprecation notice was added on 2024-01-22. *
*******************************************************************************
Activate the user authentication logic by including the
ext/userauth/userauth.c source code file in the build and
adding the -DSQLITE_USER_AUTHENTICATION compile-time option.
The ext/userauth/sqlite3userauth.h header file is available to
applications to define the interface.
When using the SQLite amalgamation, it is sufficient to append
the ext/userauth/userauth.c source file onto the end of the
amalgamation.
The following new APIs are available when user authentication is
activated:
int sqlite3_user_authenticate(
sqlite3 *db, /* The database connection */
const char *zUsername, /* Username */
const char *aPW, /* Password or credentials */
int nPW /* Number of bytes in aPW[] */
);
int sqlite3_user_add(
sqlite3 *db, /* Database connection */
const char *zUsername, /* Username to be added */
const char *aPW, /* Password or credentials */
int nPW, /* Number of bytes in aPW[] */
int isAdmin /* True to give new user admin privilege */
);
int sqlite3_user_change(
sqlite3 *db, /* Database connection */
const char *zUsername, /* Username to change */
const void *aPW, /* Modified password or credentials */
int nPW, /* Number of bytes in aPW[] */
int isAdmin /* Modified admin privilege for the user */
);
int sqlite3_user_delete(
sqlite3 *db, /* Database connection */
const char *zUsername /* Username to remove */
);
With this extension, a database can be marked as requiring authentication.
By default a database does not require authentication.
The sqlite3_open(), sqlite3_open16(), and sqlite3_open_v2() interfaces
work as before: they open a new database connection. However, if the
database being opened requires authentication, then attempts to read
or write from the database will fail with an SQLITE_AUTH error until
after sqlite3_user_authenticate() has been called successfully. The
sqlite3_user_authenticate() call will return SQLITE_OK if the
authentication credentials are accepted and SQLITE_ERROR if not.
Calling sqlite3_user_authenticate() on a no-authentication-required
database connection is a harmless no-op.
If the database is encrypted, then sqlite3_key_v2() must be called first,
with the correct decryption key, prior to invoking sqlite3_user_authenticate().
To recapitulate: When opening an existing unencrypted authentication-
required database, the call sequence is:
sqlite3_open_v2()
sqlite3_user_authenticate();
/* Database is now usable */
To open an existing, encrypted, authentication-required database, the
call sequence is:
sqlite3_open_v2();
sqlite3_key_v2();
sqlite3_user_authenticate();
/* Database is now usable */
When opening a no-authentication-required database, the database
connection is treated as if it was authenticated as an admin user.
When ATTACH-ing new database files to a connection, each newly attached
database that is an authentication-required database is checked using
the same username and password as supplied to the main database. If that
check fails, then the ATTACH command fails with an SQLITE_AUTH error.
The sqlite3_user_add() interface can be used (by an admin user only)
to create a new user. When called on a no-authentication-required
database and when A is true, the sqlite3_user_add(D,U,P,N,A) routine
converts the database into an authentication-required database and
logs in the database connection D as user U with password P,N.
To convert a no-authentication-required database into an authentication-
required database, the isAdmin parameter must be true. If
sqlite3_user_add(D,U,P,N,A) is called on a no-authentication-required
database and A is false, then the call fails with an SQLITE_AUTH error.
Any call to sqlite3_user_add() by a non-admin user results in an error.
Hence, to create a new, unencrypted, authentication-required database,
the call sequence is:
sqlite3_open_v2();
sqlite3_user_add();
And to create a new, encrypted, authentication-required database, the call
sequence is:
sqlite3_open_v2();
sqlite3_key_v2();
sqlite3_user_add();
The sqlite3_user_delete() interface can be used (by an admin user only)
to delete a user. The currently logged-in user cannot be deleted,
which guarantees that there is always an admin user and hence that
the database cannot be converted into a no-authentication-required
database.
The sqlite3_user_change() interface can be used to change a users
login credentials or admin privilege. Any user can change their own
password. Only an admin user can change another users login
credentials or admin privilege setting. No user may change their own
admin privilege setting.
The sqlite3_set_authorizer() callback is modified to take a 7th parameter
which is the username of the currently logged in user, or NULL for a
no-authentication-required database.
-----------------------------------------------------------------------------
Implementation notes:
An authentication-required database is identified by the presence of a
new table:
CREATE TABLE sqlite_user(
uname TEXT PRIMARY KEY,
isAdmin BOOLEAN,
pw BLOB
) WITHOUT ROWID;
The sqlite_user table is inaccessible (unreadable and unwriteable) to
non-admin users and is read-only for admin users. However, if the same
database file is opened by a version of SQLite that omits
the -DSQLITE_USER_AUTHENTICATION compile-time option, then the sqlite_user
table will be readable by anybody and writeable by anybody if
the "PRAGMA writable_schema=ON" statement is run first.
The sqlite_user.pw field is encoded by a built-in SQL function
"sqlite_crypt(X,Y)". The two arguments are both BLOBs. The first argument
is the plaintext password supplied to the sqlite3_user_authenticate()
interface. The second argument is the sqlite_user.pw value and is supplied
so that the function can extract the "salt" used by the password encoder.
The result of sqlite_crypt(X,Y) is another blob which is the value that
ends up being stored in sqlite_user.pw. To verify credentials X supplied
by the sqlite3_user_authenticate() routine, SQLite runs:
sqlite_user.pw == sqlite_crypt(X, sqlite_user.pw)
To compute an appropriate sqlite_user.pw value from a new or modified
password X, sqlite_crypt(X,NULL) is run. A new random salt is selected
when the second argument is NULL.
The built-in version of of sqlite_crypt() uses a simple Ceasar-cypher
which prevents passwords from being revealed by searching the raw database
for ASCII text, but is otherwise trivally broken. For better password
security, the database should be encrypted using the SQLite Encryption
Extension or similar technology. Or, the application can use the
sqlite3_create_function() interface to provide an alternative
implementation of sqlite_crypt() that computes a stronger password hash,
perhaps using a cryptographic hash function like SHA1.

View File

@ -1,355 +0,0 @@
/*
** 2014-09-08
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
**
** This file contains the bulk of the implementation of the
** user-authentication extension feature. Some parts of the user-
** authentication code are contained within the SQLite core (in the
** src/ subdirectory of the main source code tree) but those parts
** that could reasonable be separated out are moved into this file.
**
** To compile with the user-authentication feature, append this file to
** end of an SQLite amalgamation, then add the SQLITE_USER_AUTHENTICATION
** compile-time option. See the user-auth.txt file in the same source
** directory as this file for additional information.
*/
#ifdef SQLITE_USER_AUTHENTICATION
#ifndef SQLITEINT_H
# include "sqliteInt.h"
#endif
/*
** Prepare an SQL statement for use by the user authentication logic.
** Return a pointer to the prepared statement on success. Return a
** NULL pointer if there is an error of any kind.
*/
static sqlite3_stmt *sqlite3UserAuthPrepare(
sqlite3 *db,
const char *zFormat,
...
){
sqlite3_stmt *pStmt;
char *zSql;
int rc;
va_list ap;
u64 savedFlags = db->flags;
va_start(ap, zFormat);
zSql = sqlite3_vmprintf(zFormat, ap);
va_end(ap);
if( zSql==0 ) return 0;
db->flags |= SQLITE_WriteSchema;
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
db->flags = savedFlags;
sqlite3_free(zSql);
if( rc ){
sqlite3_finalize(pStmt);
pStmt = 0;
}
return pStmt;
}
/*
** Check to see if the sqlite_user table exists in database zDb.
*/
static int userTableExists(sqlite3 *db, const char *zDb){
int rc;
sqlite3_mutex_enter(db->mutex);
sqlite3BtreeEnterAll(db);
if( db->init.busy==0 ){
char *zErr = 0;
sqlite3Init(db, &zErr);
sqlite3DbFree(db, zErr);
}
rc = sqlite3FindTable(db, "sqlite_user", zDb)!=0;
sqlite3BtreeLeaveAll(db);
sqlite3_mutex_leave(db->mutex);
return rc;
}
/*
** Check to see if database zDb has a "sqlite_user" table and if it does
** whether that table can authenticate zUser with nPw,zPw. Write one of
** the UAUTH_* user authorization level codes into *peAuth and return a
** result code.
*/
static int userAuthCheckLogin(
sqlite3 *db, /* The database connection to check */
const char *zDb, /* Name of specific database to check */
u8 *peAuth /* OUT: One of UAUTH_* constants */
){
sqlite3_stmt *pStmt;
int rc;
*peAuth = UAUTH_Unknown;
if( !userTableExists(db, "main") ){
*peAuth = UAUTH_Admin; /* No sqlite_user table. Everybody is admin. */
return SQLITE_OK;
}
if( db->auth.zAuthUser==0 ){
*peAuth = UAUTH_Fail;
return SQLITE_OK;
}
pStmt = sqlite3UserAuthPrepare(db,
"SELECT pw=sqlite_crypt(?1,pw), isAdmin FROM \"%w\".sqlite_user"
" WHERE uname=?2", zDb);
if( pStmt==0 ) return SQLITE_NOMEM;
sqlite3_bind_blob(pStmt, 1, db->auth.zAuthPW, db->auth.nAuthPW,SQLITE_STATIC);
sqlite3_bind_text(pStmt, 2, db->auth.zAuthUser, -1, SQLITE_STATIC);
rc = sqlite3_step(pStmt);
if( rc==SQLITE_ROW && sqlite3_column_int(pStmt,0) ){
*peAuth = sqlite3_column_int(pStmt, 1) + UAUTH_User;
}else{
*peAuth = UAUTH_Fail;
}
return sqlite3_finalize(pStmt);
}
int sqlite3UserAuthCheckLogin(
sqlite3 *db, /* The database connection to check */
const char *zDb, /* Name of specific database to check */
u8 *peAuth /* OUT: One of UAUTH_* constants */
){
int rc;
u8 savedAuthLevel;
assert( zDb!=0 );
assert( peAuth!=0 );
savedAuthLevel = db->auth.authLevel;
db->auth.authLevel = UAUTH_Admin;
rc = userAuthCheckLogin(db, zDb, peAuth);
db->auth.authLevel = savedAuthLevel;
return rc;
}
/*
** If the current authLevel is UAUTH_Unknown, the take actions to figure
** out what authLevel should be
*/
void sqlite3UserAuthInit(sqlite3 *db){
if( db->auth.authLevel==UAUTH_Unknown ){
u8 authLevel = UAUTH_Fail;
sqlite3UserAuthCheckLogin(db, "main", &authLevel);
db->auth.authLevel = authLevel;
if( authLevel<UAUTH_Admin ) db->flags &= ~SQLITE_WriteSchema;
}
}
/*
** Implementation of the sqlite_crypt(X,Y) function.
**
** If Y is NULL then generate a new hash for password X and return that
** hash. If Y is not null, then generate a hash for password X using the
** same salt as the previous hash Y and return the new hash.
*/
void sqlite3CryptFunc(
sqlite3_context *context,
int NotUsed,
sqlite3_value **argv
){
const char *zIn;
int nIn, ii;
u8 *zOut;
char zSalt[8];
zIn = sqlite3_value_blob(argv[0]);
nIn = sqlite3_value_bytes(argv[0]);
if( sqlite3_value_type(argv[1])==SQLITE_BLOB
&& sqlite3_value_bytes(argv[1])==nIn+sizeof(zSalt)
){
memcpy(zSalt, sqlite3_value_blob(argv[1]), sizeof(zSalt));
}else{
sqlite3_randomness(sizeof(zSalt), zSalt);
}
zOut = sqlite3_malloc( nIn+sizeof(zSalt) );
if( zOut==0 ){
sqlite3_result_error_nomem(context);
}else{
memcpy(zOut, zSalt, sizeof(zSalt));
for(ii=0; ii<nIn; ii++){
zOut[ii+sizeof(zSalt)] = zIn[ii]^zSalt[ii&0x7];
}
sqlite3_result_blob(context, zOut, nIn+sizeof(zSalt), sqlite3_free);
}
}
/*
** If a database contains the SQLITE_USER table, then the
** sqlite3_user_authenticate() interface must be invoked with an
** appropriate username and password prior to enable read and write
** access to the database.
**
** Return SQLITE_OK on success or SQLITE_ERROR if the username/password
** combination is incorrect or unknown.
**
** If the SQLITE_USER table is not present in the database file, then
** this interface is a harmless no-op returnning SQLITE_OK.
*/
int sqlite3_user_authenticate(
sqlite3 *db, /* The database connection */
const char *zUsername, /* Username */
const char *zPW, /* Password or credentials */
int nPW /* Number of bytes in aPW[] */
){
int rc;
u8 authLevel = UAUTH_Fail;
db->auth.authLevel = UAUTH_Unknown;
sqlite3_free(db->auth.zAuthUser);
sqlite3_free(db->auth.zAuthPW);
memset(&db->auth, 0, sizeof(db->auth));
db->auth.zAuthUser = sqlite3_mprintf("%s", zUsername);
if( db->auth.zAuthUser==0 ) return SQLITE_NOMEM;
db->auth.zAuthPW = sqlite3_malloc( nPW+1 );
if( db->auth.zAuthPW==0 ) return SQLITE_NOMEM;
memcpy(db->auth.zAuthPW,zPW,nPW);
db->auth.nAuthPW = nPW;
rc = sqlite3UserAuthCheckLogin(db, "main", &authLevel);
db->auth.authLevel = authLevel;
sqlite3ExpirePreparedStatements(db, 0);
if( rc ){
return rc; /* OOM error, I/O error, etc. */
}
if( authLevel<UAUTH_User ){
return SQLITE_AUTH; /* Incorrect username and/or password */
}
return SQLITE_OK; /* Successful login */
}
/*
** The sqlite3_user_add() interface can be used (by an admin user only)
** to create a new user. When called on a no-authentication-required
** database, this routine converts the database into an authentication-
** required database, automatically makes the added user an
** administrator, and logs in the current connection as that user.
** The sqlite3_user_add() interface only works for the "main" database, not
** for any ATTACH-ed databases. Any call to sqlite3_user_add() by a
** non-admin user results in an error.
*/
int sqlite3_user_add(
sqlite3 *db, /* Database connection */
const char *zUsername, /* Username to be added */
const char *aPW, /* Password or credentials */
int nPW, /* Number of bytes in aPW[] */
int isAdmin /* True to give new user admin privilege */
){
sqlite3_stmt *pStmt;
int rc;
sqlite3UserAuthInit(db);
if( db->auth.authLevel<UAUTH_Admin ) return SQLITE_AUTH;
if( !userTableExists(db, "main") ){
if( !isAdmin ) return SQLITE_AUTH;
pStmt = sqlite3UserAuthPrepare(db,
"CREATE TABLE sqlite_user(\n"
" uname TEXT PRIMARY KEY,\n"
" isAdmin BOOLEAN,\n"
" pw BLOB\n"
") WITHOUT ROWID;");
if( pStmt==0 ) return SQLITE_NOMEM;
sqlite3_step(pStmt);
rc = sqlite3_finalize(pStmt);
if( rc ) return rc;
}
pStmt = sqlite3UserAuthPrepare(db,
"INSERT INTO sqlite_user(uname,isAdmin,pw)"
" VALUES(%Q,%d,sqlite_crypt(?1,NULL))",
zUsername, isAdmin!=0);
if( pStmt==0 ) return SQLITE_NOMEM;
sqlite3_bind_blob(pStmt, 1, aPW, nPW, SQLITE_STATIC);
sqlite3_step(pStmt);
rc = sqlite3_finalize(pStmt);
if( rc ) return rc;
if( db->auth.zAuthUser==0 ){
assert( isAdmin!=0 );
sqlite3_user_authenticate(db, zUsername, aPW, nPW);
}
return SQLITE_OK;
}
/*
** The sqlite3_user_change() interface can be used to change a users
** login credentials or admin privilege. Any user can change their own
** login credentials. Only an admin user can change another users login
** credentials or admin privilege setting. No user may change their own
** admin privilege setting.
*/
int sqlite3_user_change(
sqlite3 *db, /* Database connection */
const char *zUsername, /* Username to change */
const char *aPW, /* Modified password or credentials */
int nPW, /* Number of bytes in aPW[] */
int isAdmin /* Modified admin privilege for the user */
){
sqlite3_stmt *pStmt;
int rc;
u8 authLevel;
authLevel = db->auth.authLevel;
if( authLevel<UAUTH_User ){
/* Must be logged in to make a change */
return SQLITE_AUTH;
}
if( strcmp(db->auth.zAuthUser, zUsername)!=0 ){
if( db->auth.authLevel<UAUTH_Admin ){
/* Must be an administrator to change a different user */
return SQLITE_AUTH;
}
}else if( isAdmin!=(authLevel==UAUTH_Admin) ){
/* Cannot change the isAdmin setting for self */
return SQLITE_AUTH;
}
db->auth.authLevel = UAUTH_Admin;
if( !userTableExists(db, "main") ){
/* This routine is a no-op if the user to be modified does not exist */
}else{
pStmt = sqlite3UserAuthPrepare(db,
"UPDATE sqlite_user SET isAdmin=%d, pw=sqlite_crypt(?1,NULL)"
" WHERE uname=%Q", isAdmin, zUsername);
if( pStmt==0 ){
rc = SQLITE_NOMEM;
}else{
sqlite3_bind_blob(pStmt, 1, aPW, nPW, SQLITE_STATIC);
sqlite3_step(pStmt);
rc = sqlite3_finalize(pStmt);
}
}
db->auth.authLevel = authLevel;
return rc;
}
/*
** The sqlite3_user_delete() interface can be used (by an admin user only)
** to delete a user. The currently logged-in user cannot be deleted,
** which guarantees that there is always an admin user and hence that
** the database cannot be converted into a no-authentication-required
** database.
*/
int sqlite3_user_delete(
sqlite3 *db, /* Database connection */
const char *zUsername /* Username to remove */
){
sqlite3_stmt *pStmt;
if( db->auth.authLevel<UAUTH_Admin ){
/* Must be an administrator to delete a user */
return SQLITE_AUTH;
}
if( strcmp(db->auth.zAuthUser, zUsername)==0 ){
/* Cannot delete self */
return SQLITE_AUTH;
}
if( !userTableExists(db, "main") ){
/* This routine is a no-op if the user to be deleted does not exist */
return SQLITE_OK;
}
pStmt = sqlite3UserAuthPrepare(db,
"DELETE FROM sqlite_user WHERE uname=%Q", zUsername);
if( pStmt==0 ) return SQLITE_NOMEM;
sqlite3_step(pStmt);
return sqlite3_finalize(pStmt);
}
#endif /* SQLITE_USER_AUTHENTICATION */