| Index: third_party/sqlite/amalgamation/sqlite3.01.c
|
| diff --git a/third_party/sqlite/amalgamation/sqlite3.01.c b/third_party/sqlite/amalgamation/sqlite3.01.c
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..483408fec51f587dc7f4dfb74d65b2ea2cf40e9b
|
| --- /dev/null
|
| +++ b/third_party/sqlite/amalgamation/sqlite3.01.c
|
| @@ -0,0 +1,26166 @@
|
| +/************** Begin file date.c ********************************************/
|
| +/*
|
| +** 2003 October 31
|
| +**
|
| +** 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 C functions that implement date and time
|
| +** functions for SQLite.
|
| +**
|
| +** There is only one exported symbol in this file - the function
|
| +** sqlite3RegisterDateTimeFunctions() found at the bottom of the file.
|
| +** All other code has file scope.
|
| +**
|
| +** SQLite processes all times and dates as julian day numbers. The
|
| +** dates and times are stored as the number of days since noon
|
| +** in Greenwich on November 24, 4714 B.C. according to the Gregorian
|
| +** calendar system.
|
| +**
|
| +** 1970-01-01 00:00:00 is JD 2440587.5
|
| +** 2000-01-01 00:00:00 is JD 2451544.5
|
| +**
|
| +** This implementation requires years to be expressed as a 4-digit number
|
| +** which means that only dates between 0000-01-01 and 9999-12-31 can
|
| +** be represented, even though julian day numbers allow a much wider
|
| +** range of dates.
|
| +**
|
| +** The Gregorian calendar system is used for all dates and times,
|
| +** even those that predate the Gregorian calendar. Historians usually
|
| +** use the julian calendar for dates prior to 1582-10-15 and for some
|
| +** dates afterwards, depending on locale. Beware of this difference.
|
| +**
|
| +** The conversion algorithms are implemented based on descriptions
|
| +** in the following text:
|
| +**
|
| +** Jean Meeus
|
| +** Astronomical Algorithms, 2nd Edition, 1998
|
| +** ISBM 0-943396-61-1
|
| +** Willmann-Bell, Inc
|
| +** Richmond, Virginia (USA)
|
| +*/
|
| +/* #include "sqliteInt.h" */
|
| +/* #include <stdlib.h> */
|
| +/* #include <assert.h> */
|
| +#include <time.h>
|
| +
|
| +#ifndef SQLITE_OMIT_DATETIME_FUNCS
|
| +
|
| +/*
|
| +** The MSVC CRT on Windows CE may not have a localtime() function.
|
| +** So declare a substitute. The substitute function itself is
|
| +** defined in "os_win.c".
|
| +*/
|
| +#if !defined(SQLITE_OMIT_LOCALTIME) && defined(_WIN32_WCE) && \
|
| + (!defined(SQLITE_MSVC_LOCALTIME_API) || !SQLITE_MSVC_LOCALTIME_API)
|
| +struct tm *__cdecl localtime(const time_t *);
|
| +#endif
|
| +
|
| +/*
|
| +** A structure for holding a single date and time.
|
| +*/
|
| +typedef struct DateTime DateTime;
|
| +struct DateTime {
|
| + sqlite3_int64 iJD; /* The julian day number times 86400000 */
|
| + int Y, M, D; /* Year, month, and day */
|
| + int h, m; /* Hour and minutes */
|
| + int tz; /* Timezone offset in minutes */
|
| + double s; /* Seconds */
|
| + char validJD; /* True (1) if iJD is valid */
|
| + char rawS; /* Raw numeric value stored in s */
|
| + char validYMD; /* True (1) if Y,M,D are valid */
|
| + char validHMS; /* True (1) if h,m,s are valid */
|
| + char validTZ; /* True (1) if tz is valid */
|
| + char tzSet; /* Timezone was set explicitly */
|
| + char isError; /* An overflow has occurred */
|
| +};
|
| +
|
| +
|
| +/*
|
| +** Convert zDate into one or more integers according to the conversion
|
| +** specifier zFormat.
|
| +**
|
| +** zFormat[] contains 4 characters for each integer converted, except for
|
| +** the last integer which is specified by three characters. The meaning
|
| +** of a four-character format specifiers ABCD is:
|
| +**
|
| +** A: number of digits to convert. Always "2" or "4".
|
| +** B: minimum value. Always "0" or "1".
|
| +** C: maximum value, decoded as:
|
| +** a: 12
|
| +** b: 14
|
| +** c: 24
|
| +** d: 31
|
| +** e: 59
|
| +** f: 9999
|
| +** D: the separator character, or \000 to indicate this is the
|
| +** last number to convert.
|
| +**
|
| +** Example: To translate an ISO-8601 date YYYY-MM-DD, the format would
|
| +** be "40f-21a-20c". The "40f-" indicates the 4-digit year followed by "-".
|
| +** The "21a-" indicates the 2-digit month followed by "-". The "20c" indicates
|
| +** the 2-digit day which is the last integer in the set.
|
| +**
|
| +** The function returns the number of successful conversions.
|
| +*/
|
| +static int getDigits(const char *zDate, const char *zFormat, ...){
|
| + /* The aMx[] array translates the 3rd character of each format
|
| + ** spec into a max size: a b c d e f */
|
| + static const u16 aMx[] = { 12, 14, 24, 31, 59, 9999 };
|
| + va_list ap;
|
| + int cnt = 0;
|
| + char nextC;
|
| + va_start(ap, zFormat);
|
| + do{
|
| + char N = zFormat[0] - '0';
|
| + char min = zFormat[1] - '0';
|
| + int val = 0;
|
| + u16 max;
|
| +
|
| + assert( zFormat[2]>='a' && zFormat[2]<='f' );
|
| + max = aMx[zFormat[2] - 'a'];
|
| + nextC = zFormat[3];
|
| + val = 0;
|
| + while( N-- ){
|
| + if( !sqlite3Isdigit(*zDate) ){
|
| + goto end_getDigits;
|
| + }
|
| + val = val*10 + *zDate - '0';
|
| + zDate++;
|
| + }
|
| + if( val<(int)min || val>(int)max || (nextC!=0 && nextC!=*zDate) ){
|
| + goto end_getDigits;
|
| + }
|
| + *va_arg(ap,int*) = val;
|
| + zDate++;
|
| + cnt++;
|
| + zFormat += 4;
|
| + }while( nextC );
|
| +end_getDigits:
|
| + va_end(ap);
|
| + return cnt;
|
| +}
|
| +
|
| +/*
|
| +** Parse a timezone extension on the end of a date-time.
|
| +** The extension is of the form:
|
| +**
|
| +** (+/-)HH:MM
|
| +**
|
| +** Or the "zulu" notation:
|
| +**
|
| +** Z
|
| +**
|
| +** If the parse is successful, write the number of minutes
|
| +** of change in p->tz and return 0. If a parser error occurs,
|
| +** return non-zero.
|
| +**
|
| +** A missing specifier is not considered an error.
|
| +*/
|
| +static int parseTimezone(const char *zDate, DateTime *p){
|
| + int sgn = 0;
|
| + int nHr, nMn;
|
| + int c;
|
| + while( sqlite3Isspace(*zDate) ){ zDate++; }
|
| + p->tz = 0;
|
| + c = *zDate;
|
| + if( c=='-' ){
|
| + sgn = -1;
|
| + }else if( c=='+' ){
|
| + sgn = +1;
|
| + }else if( c=='Z' || c=='z' ){
|
| + zDate++;
|
| + goto zulu_time;
|
| + }else{
|
| + return c!=0;
|
| + }
|
| + zDate++;
|
| + if( getDigits(zDate, "20b:20e", &nHr, &nMn)!=2 ){
|
| + return 1;
|
| + }
|
| + zDate += 5;
|
| + p->tz = sgn*(nMn + nHr*60);
|
| +zulu_time:
|
| + while( sqlite3Isspace(*zDate) ){ zDate++; }
|
| + p->tzSet = 1;
|
| + return *zDate!=0;
|
| +}
|
| +
|
| +/*
|
| +** Parse times of the form HH:MM or HH:MM:SS or HH:MM:SS.FFFF.
|
| +** The HH, MM, and SS must each be exactly 2 digits. The
|
| +** fractional seconds FFFF can be one or more digits.
|
| +**
|
| +** Return 1 if there is a parsing error and 0 on success.
|
| +*/
|
| +static int parseHhMmSs(const char *zDate, DateTime *p){
|
| + int h, m, s;
|
| + double ms = 0.0;
|
| + if( getDigits(zDate, "20c:20e", &h, &m)!=2 ){
|
| + return 1;
|
| + }
|
| + zDate += 5;
|
| + if( *zDate==':' ){
|
| + zDate++;
|
| + if( getDigits(zDate, "20e", &s)!=1 ){
|
| + return 1;
|
| + }
|
| + zDate += 2;
|
| + if( *zDate=='.' && sqlite3Isdigit(zDate[1]) ){
|
| + double rScale = 1.0;
|
| + zDate++;
|
| + while( sqlite3Isdigit(*zDate) ){
|
| + ms = ms*10.0 + *zDate - '0';
|
| + rScale *= 10.0;
|
| + zDate++;
|
| + }
|
| + ms /= rScale;
|
| + }
|
| + }else{
|
| + s = 0;
|
| + }
|
| + p->validJD = 0;
|
| + p->rawS = 0;
|
| + p->validHMS = 1;
|
| + p->h = h;
|
| + p->m = m;
|
| + p->s = s + ms;
|
| + if( parseTimezone(zDate, p) ) return 1;
|
| + p->validTZ = (p->tz!=0)?1:0;
|
| + return 0;
|
| +}
|
| +
|
| +/*
|
| +** Put the DateTime object into its error state.
|
| +*/
|
| +static void datetimeError(DateTime *p){
|
| + memset(p, 0, sizeof(*p));
|
| + p->isError = 1;
|
| +}
|
| +
|
| +/*
|
| +** Convert from YYYY-MM-DD HH:MM:SS to julian day. We always assume
|
| +** that the YYYY-MM-DD is according to the Gregorian calendar.
|
| +**
|
| +** Reference: Meeus page 61
|
| +*/
|
| +static void computeJD(DateTime *p){
|
| + int Y, M, D, A, B, X1, X2;
|
| +
|
| + if( p->validJD ) return;
|
| + if( p->validYMD ){
|
| + Y = p->Y;
|
| + M = p->M;
|
| + D = p->D;
|
| + }else{
|
| + Y = 2000; /* If no YMD specified, assume 2000-Jan-01 */
|
| + M = 1;
|
| + D = 1;
|
| + }
|
| + if( Y<-4713 || Y>9999 || p->rawS ){
|
| + datetimeError(p);
|
| + return;
|
| + }
|
| + if( M<=2 ){
|
| + Y--;
|
| + M += 12;
|
| + }
|
| + A = Y/100;
|
| + B = 2 - A + (A/4);
|
| + X1 = 36525*(Y+4716)/100;
|
| + X2 = 306001*(M+1)/10000;
|
| + p->iJD = (sqlite3_int64)((X1 + X2 + D + B - 1524.5 ) * 86400000);
|
| + p->validJD = 1;
|
| + if( p->validHMS ){
|
| + p->iJD += p->h*3600000 + p->m*60000 + (sqlite3_int64)(p->s*1000);
|
| + if( p->validTZ ){
|
| + p->iJD -= p->tz*60000;
|
| + p->validYMD = 0;
|
| + p->validHMS = 0;
|
| + p->validTZ = 0;
|
| + }
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Parse dates of the form
|
| +**
|
| +** YYYY-MM-DD HH:MM:SS.FFF
|
| +** YYYY-MM-DD HH:MM:SS
|
| +** YYYY-MM-DD HH:MM
|
| +** YYYY-MM-DD
|
| +**
|
| +** Write the result into the DateTime structure and return 0
|
| +** on success and 1 if the input string is not a well-formed
|
| +** date.
|
| +*/
|
| +static int parseYyyyMmDd(const char *zDate, DateTime *p){
|
| + int Y, M, D, neg;
|
| +
|
| + if( zDate[0]=='-' ){
|
| + zDate++;
|
| + neg = 1;
|
| + }else{
|
| + neg = 0;
|
| + }
|
| + if( getDigits(zDate, "40f-21a-21d", &Y, &M, &D)!=3 ){
|
| + return 1;
|
| + }
|
| + zDate += 10;
|
| + while( sqlite3Isspace(*zDate) || 'T'==*(u8*)zDate ){ zDate++; }
|
| + if( parseHhMmSs(zDate, p)==0 ){
|
| + /* We got the time */
|
| + }else if( *zDate==0 ){
|
| + p->validHMS = 0;
|
| + }else{
|
| + return 1;
|
| + }
|
| + p->validJD = 0;
|
| + p->validYMD = 1;
|
| + p->Y = neg ? -Y : Y;
|
| + p->M = M;
|
| + p->D = D;
|
| + if( p->validTZ ){
|
| + computeJD(p);
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| +/*
|
| +** Set the time to the current time reported by the VFS.
|
| +**
|
| +** Return the number of errors.
|
| +*/
|
| +static int setDateTimeToCurrent(sqlite3_context *context, DateTime *p){
|
| + p->iJD = sqlite3StmtCurrentTime(context);
|
| + if( p->iJD>0 ){
|
| + p->validJD = 1;
|
| + return 0;
|
| + }else{
|
| + return 1;
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Input "r" is a numeric quantity which might be a julian day number,
|
| +** or the number of seconds since 1970. If the value if r is within
|
| +** range of a julian day number, install it as such and set validJD.
|
| +** If the value is a valid unix timestamp, put it in p->s and set p->rawS.
|
| +*/
|
| +static void setRawDateNumber(DateTime *p, double r){
|
| + p->s = r;
|
| + p->rawS = 1;
|
| + if( r>=0.0 && r<5373484.5 ){
|
| + p->iJD = (sqlite3_int64)(r*86400000.0 + 0.5);
|
| + p->validJD = 1;
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Attempt to parse the given string into a julian day number. Return
|
| +** the number of errors.
|
| +**
|
| +** The following are acceptable forms for the input string:
|
| +**
|
| +** YYYY-MM-DD HH:MM:SS.FFF +/-HH:MM
|
| +** DDDD.DD
|
| +** now
|
| +**
|
| +** In the first form, the +/-HH:MM is always optional. The fractional
|
| +** seconds extension (the ".FFF") is optional. The seconds portion
|
| +** (":SS.FFF") is option. The year and date can be omitted as long
|
| +** as there is a time string. The time string can be omitted as long
|
| +** as there is a year and date.
|
| +*/
|
| +static int parseDateOrTime(
|
| + sqlite3_context *context,
|
| + const char *zDate,
|
| + DateTime *p
|
| +){
|
| + double r;
|
| + if( parseYyyyMmDd(zDate,p)==0 ){
|
| + return 0;
|
| + }else if( parseHhMmSs(zDate, p)==0 ){
|
| + return 0;
|
| + }else if( sqlite3StrICmp(zDate,"now")==0){
|
| + return setDateTimeToCurrent(context, p);
|
| + }else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8) ){
|
| + setRawDateNumber(p, r);
|
| + return 0;
|
| + }
|
| + return 1;
|
| +}
|
| +
|
| +/* The julian day number for 9999-12-31 23:59:59.999 is 5373484.4999999.
|
| +** Multiplying this by 86400000 gives 464269060799999 as the maximum value
|
| +** for DateTime.iJD.
|
| +**
|
| +** But some older compilers (ex: gcc 4.2.1 on older Macs) cannot deal with
|
| +** such a large integer literal, so we have to encode it.
|
| +*/
|
| +#define INT_464269060799999 ((((i64)0x1a640)<<32)|0x1072fdff)
|
| +
|
| +/*
|
| +** Return TRUE if the given julian day number is within range.
|
| +**
|
| +** The input is the JulianDay times 86400000.
|
| +*/
|
| +static int validJulianDay(sqlite3_int64 iJD){
|
| + return iJD>=0 && iJD<=INT_464269060799999;
|
| +}
|
| +
|
| +/*
|
| +** Compute the Year, Month, and Day from the julian day number.
|
| +*/
|
| +static void computeYMD(DateTime *p){
|
| + int Z, A, B, C, D, E, X1;
|
| + if( p->validYMD ) return;
|
| + if( !p->validJD ){
|
| + p->Y = 2000;
|
| + p->M = 1;
|
| + p->D = 1;
|
| + }else{
|
| + assert( validJulianDay(p->iJD) );
|
| + Z = (int)((p->iJD + 43200000)/86400000);
|
| + A = (int)((Z - 1867216.25)/36524.25);
|
| + A = Z + 1 + A - (A/4);
|
| + B = A + 1524;
|
| + C = (int)((B - 122.1)/365.25);
|
| + D = (36525*(C&32767))/100;
|
| + E = (int)((B-D)/30.6001);
|
| + X1 = (int)(30.6001*E);
|
| + p->D = B - D - X1;
|
| + p->M = E<14 ? E-1 : E-13;
|
| + p->Y = p->M>2 ? C - 4716 : C - 4715;
|
| + }
|
| + p->validYMD = 1;
|
| +}
|
| +
|
| +/*
|
| +** Compute the Hour, Minute, and Seconds from the julian day number.
|
| +*/
|
| +static void computeHMS(DateTime *p){
|
| + int s;
|
| + if( p->validHMS ) return;
|
| + computeJD(p);
|
| + s = (int)((p->iJD + 43200000) % 86400000);
|
| + p->s = s/1000.0;
|
| + s = (int)p->s;
|
| + p->s -= s;
|
| + p->h = s/3600;
|
| + s -= p->h*3600;
|
| + p->m = s/60;
|
| + p->s += s - p->m*60;
|
| + p->rawS = 0;
|
| + p->validHMS = 1;
|
| +}
|
| +
|
| +/*
|
| +** Compute both YMD and HMS
|
| +*/
|
| +static void computeYMD_HMS(DateTime *p){
|
| + computeYMD(p);
|
| + computeHMS(p);
|
| +}
|
| +
|
| +/*
|
| +** Clear the YMD and HMS and the TZ
|
| +*/
|
| +static void clearYMD_HMS_TZ(DateTime *p){
|
| + p->validYMD = 0;
|
| + p->validHMS = 0;
|
| + p->validTZ = 0;
|
| +}
|
| +
|
| +#ifndef SQLITE_OMIT_LOCALTIME
|
| +/*
|
| +** On recent Windows platforms, the localtime_s() function is available
|
| +** as part of the "Secure CRT". It is essentially equivalent to
|
| +** localtime_r() available under most POSIX platforms, except that the
|
| +** order of the parameters is reversed.
|
| +**
|
| +** See http://msdn.microsoft.com/en-us/library/a442x3ye(VS.80).aspx.
|
| +**
|
| +** If the user has not indicated to use localtime_r() or localtime_s()
|
| +** already, check for an MSVC build environment that provides
|
| +** localtime_s().
|
| +*/
|
| +#if !HAVE_LOCALTIME_R && !HAVE_LOCALTIME_S \
|
| + && defined(_MSC_VER) && defined(_CRT_INSECURE_DEPRECATE)
|
| +#undef HAVE_LOCALTIME_S
|
| +#define HAVE_LOCALTIME_S 1
|
| +#endif
|
| +
|
| +/*
|
| +** The following routine implements the rough equivalent of localtime_r()
|
| +** using whatever operating-system specific localtime facility that
|
| +** is available. This routine returns 0 on success and
|
| +** non-zero on any kind of error.
|
| +**
|
| +** If the sqlite3GlobalConfig.bLocaltimeFault variable is true then this
|
| +** routine will always fail.
|
| +**
|
| +** EVIDENCE-OF: R-62172-00036 In this implementation, the standard C
|
| +** library function localtime_r() is used to assist in the calculation of
|
| +** local time.
|
| +*/
|
| +static int osLocaltime(time_t *t, struct tm *pTm){
|
| + int rc;
|
| +#if !HAVE_LOCALTIME_R && !HAVE_LOCALTIME_S
|
| + struct tm *pX;
|
| +#if SQLITE_THREADSAFE>0
|
| + sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
| +#endif
|
| + sqlite3_mutex_enter(mutex);
|
| + pX = localtime(t);
|
| +#ifndef SQLITE_UNTESTABLE
|
| + if( sqlite3GlobalConfig.bLocaltimeFault ) pX = 0;
|
| +#endif
|
| + if( pX ) *pTm = *pX;
|
| + sqlite3_mutex_leave(mutex);
|
| + rc = pX==0;
|
| +#else
|
| +#ifndef SQLITE_UNTESTABLE
|
| + if( sqlite3GlobalConfig.bLocaltimeFault ) return 1;
|
| +#endif
|
| +#if HAVE_LOCALTIME_R
|
| + rc = localtime_r(t, pTm)==0;
|
| +#else
|
| + rc = localtime_s(pTm, t);
|
| +#endif /* HAVE_LOCALTIME_R */
|
| +#endif /* HAVE_LOCALTIME_R || HAVE_LOCALTIME_S */
|
| + return rc;
|
| +}
|
| +#endif /* SQLITE_OMIT_LOCALTIME */
|
| +
|
| +
|
| +#ifndef SQLITE_OMIT_LOCALTIME
|
| +/*
|
| +** Compute the difference (in milliseconds) between localtime and UTC
|
| +** (a.k.a. GMT) for the time value p where p is in UTC. If no error occurs,
|
| +** return this value and set *pRc to SQLITE_OK.
|
| +**
|
| +** Or, if an error does occur, set *pRc to SQLITE_ERROR. The returned value
|
| +** is undefined in this case.
|
| +*/
|
| +static sqlite3_int64 localtimeOffset(
|
| + DateTime *p, /* Date at which to calculate offset */
|
| + sqlite3_context *pCtx, /* Write error here if one occurs */
|
| + int *pRc /* OUT: Error code. SQLITE_OK or ERROR */
|
| +){
|
| + DateTime x, y;
|
| + time_t t;
|
| + struct tm sLocal;
|
| +
|
| + /* Initialize the contents of sLocal to avoid a compiler warning. */
|
| + memset(&sLocal, 0, sizeof(sLocal));
|
| +
|
| + x = *p;
|
| + computeYMD_HMS(&x);
|
| + if( x.Y<1971 || x.Y>=2038 ){
|
| + /* EVIDENCE-OF: R-55269-29598 The localtime_r() C function normally only
|
| + ** works for years between 1970 and 2037. For dates outside this range,
|
| + ** SQLite attempts to map the year into an equivalent year within this
|
| + ** range, do the calculation, then map the year back.
|
| + */
|
| + x.Y = 2000;
|
| + x.M = 1;
|
| + x.D = 1;
|
| + x.h = 0;
|
| + x.m = 0;
|
| + x.s = 0.0;
|
| + } else {
|
| + int s = (int)(x.s + 0.5);
|
| + x.s = s;
|
| + }
|
| + x.tz = 0;
|
| + x.validJD = 0;
|
| + computeJD(&x);
|
| + t = (time_t)(x.iJD/1000 - 21086676*(i64)10000);
|
| + if( osLocaltime(&t, &sLocal) ){
|
| + sqlite3_result_error(pCtx, "local time unavailable", -1);
|
| + *pRc = SQLITE_ERROR;
|
| + return 0;
|
| + }
|
| + y.Y = sLocal.tm_year + 1900;
|
| + y.M = sLocal.tm_mon + 1;
|
| + y.D = sLocal.tm_mday;
|
| + y.h = sLocal.tm_hour;
|
| + y.m = sLocal.tm_min;
|
| + y.s = sLocal.tm_sec;
|
| + y.validYMD = 1;
|
| + y.validHMS = 1;
|
| + y.validJD = 0;
|
| + y.rawS = 0;
|
| + y.validTZ = 0;
|
| + y.isError = 0;
|
| + computeJD(&y);
|
| + *pRc = SQLITE_OK;
|
| + return y.iJD - x.iJD;
|
| +}
|
| +#endif /* SQLITE_OMIT_LOCALTIME */
|
| +
|
| +/*
|
| +** The following table defines various date transformations of the form
|
| +**
|
| +** 'NNN days'
|
| +**
|
| +** Where NNN is an arbitrary floating-point number and "days" can be one
|
| +** of several units of time.
|
| +*/
|
| +static const struct {
|
| + u8 eType; /* Transformation type code */
|
| + u8 nName; /* Length of th name */
|
| + char *zName; /* Name of the transformation */
|
| + double rLimit; /* Maximum NNN value for this transform */
|
| + double rXform; /* Constant used for this transform */
|
| +} aXformType[] = {
|
| + { 0, 6, "second", 464269060800.0, 86400000.0/(24.0*60.0*60.0) },
|
| + { 0, 6, "minute", 7737817680.0, 86400000.0/(24.0*60.0) },
|
| + { 0, 4, "hour", 128963628.0, 86400000.0/24.0 },
|
| + { 0, 3, "day", 5373485.0, 86400000.0 },
|
| + { 1, 5, "month", 176546.0, 30.0*86400000.0 },
|
| + { 2, 4, "year", 14713.0, 365.0*86400000.0 },
|
| +};
|
| +
|
| +/*
|
| +** Process a modifier to a date-time stamp. The modifiers are
|
| +** as follows:
|
| +**
|
| +** NNN days
|
| +** NNN hours
|
| +** NNN minutes
|
| +** NNN.NNNN seconds
|
| +** NNN months
|
| +** NNN years
|
| +** start of month
|
| +** start of year
|
| +** start of week
|
| +** start of day
|
| +** weekday N
|
| +** unixepoch
|
| +** localtime
|
| +** utc
|
| +**
|
| +** Return 0 on success and 1 if there is any kind of error. If the error
|
| +** is in a system call (i.e. localtime()), then an error message is written
|
| +** to context pCtx. If the error is an unrecognized modifier, no error is
|
| +** written to pCtx.
|
| +*/
|
| +static int parseModifier(
|
| + sqlite3_context *pCtx, /* Function context */
|
| + const char *z, /* The text of the modifier */
|
| + int n, /* Length of zMod in bytes */
|
| + DateTime *p /* The date/time value to be modified */
|
| +){
|
| + int rc = 1;
|
| + double r;
|
| + switch(sqlite3UpperToLower[(u8)z[0]] ){
|
| +#ifndef SQLITE_OMIT_LOCALTIME
|
| + case 'l': {
|
| + /* localtime
|
| + **
|
| + ** Assuming the current time value is UTC (a.k.a. GMT), shift it to
|
| + ** show local time.
|
| + */
|
| + if( sqlite3_stricmp(z, "localtime")==0 ){
|
| + computeJD(p);
|
| + p->iJD += localtimeOffset(p, pCtx, &rc);
|
| + clearYMD_HMS_TZ(p);
|
| + }
|
| + break;
|
| + }
|
| +#endif
|
| + case 'u': {
|
| + /*
|
| + ** unixepoch
|
| + **
|
| + ** Treat the current value of p->s as the number of
|
| + ** seconds since 1970. Convert to a real julian day number.
|
| + */
|
| + if( sqlite3_stricmp(z, "unixepoch")==0 && p->rawS ){
|
| + r = p->s*1000.0 + 210866760000000.0;
|
| + if( r>=0.0 && r<464269060800000.0 ){
|
| + clearYMD_HMS_TZ(p);
|
| + p->iJD = (sqlite3_int64)r;
|
| + p->validJD = 1;
|
| + p->rawS = 0;
|
| + rc = 0;
|
| + }
|
| + }
|
| +#ifndef SQLITE_OMIT_LOCALTIME
|
| + else if( sqlite3_stricmp(z, "utc")==0 ){
|
| + if( p->tzSet==0 ){
|
| + sqlite3_int64 c1;
|
| + computeJD(p);
|
| + c1 = localtimeOffset(p, pCtx, &rc);
|
| + if( rc==SQLITE_OK ){
|
| + p->iJD -= c1;
|
| + clearYMD_HMS_TZ(p);
|
| + p->iJD += c1 - localtimeOffset(p, pCtx, &rc);
|
| + }
|
| + p->tzSet = 1;
|
| + }else{
|
| + rc = SQLITE_OK;
|
| + }
|
| + }
|
| +#endif
|
| + break;
|
| + }
|
| + case 'w': {
|
| + /*
|
| + ** weekday N
|
| + **
|
| + ** Move the date to the same time on the next occurrence of
|
| + ** weekday N where 0==Sunday, 1==Monday, and so forth. If the
|
| + ** date is already on the appropriate weekday, this is a no-op.
|
| + */
|
| + if( sqlite3_strnicmp(z, "weekday ", 8)==0
|
| + && sqlite3AtoF(&z[8], &r, sqlite3Strlen30(&z[8]), SQLITE_UTF8)
|
| + && (n=(int)r)==r && n>=0 && r<7 ){
|
| + sqlite3_int64 Z;
|
| + computeYMD_HMS(p);
|
| + p->validTZ = 0;
|
| + p->validJD = 0;
|
| + computeJD(p);
|
| + Z = ((p->iJD + 129600000)/86400000) % 7;
|
| + if( Z>n ) Z -= 7;
|
| + p->iJD += (n - Z)*86400000;
|
| + clearYMD_HMS_TZ(p);
|
| + rc = 0;
|
| + }
|
| + break;
|
| + }
|
| + case 's': {
|
| + /*
|
| + ** start of TTTTT
|
| + **
|
| + ** Move the date backwards to the beginning of the current day,
|
| + ** or month or year.
|
| + */
|
| + if( sqlite3_strnicmp(z, "start of ", 9)!=0 ) break;
|
| + z += 9;
|
| + computeYMD(p);
|
| + p->validHMS = 1;
|
| + p->h = p->m = 0;
|
| + p->s = 0.0;
|
| + p->validTZ = 0;
|
| + p->validJD = 0;
|
| + if( sqlite3_stricmp(z,"month")==0 ){
|
| + p->D = 1;
|
| + rc = 0;
|
| + }else if( sqlite3_stricmp(z,"year")==0 ){
|
| + computeYMD(p);
|
| + p->M = 1;
|
| + p->D = 1;
|
| + rc = 0;
|
| + }else if( sqlite3_stricmp(z,"day")==0 ){
|
| + rc = 0;
|
| + }
|
| + break;
|
| + }
|
| + case '+':
|
| + case '-':
|
| + case '0':
|
| + case '1':
|
| + case '2':
|
| + case '3':
|
| + case '4':
|
| + case '5':
|
| + case '6':
|
| + case '7':
|
| + case '8':
|
| + case '9': {
|
| + double rRounder;
|
| + int i;
|
| + for(n=1; z[n] && z[n]!=':' && !sqlite3Isspace(z[n]); n++){}
|
| + if( !sqlite3AtoF(z, &r, n, SQLITE_UTF8) ){
|
| + rc = 1;
|
| + break;
|
| + }
|
| + if( z[n]==':' ){
|
| + /* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the
|
| + ** specified number of hours, minutes, seconds, and fractional seconds
|
| + ** to the time. The ".FFF" may be omitted. The ":SS.FFF" may be
|
| + ** omitted.
|
| + */
|
| + const char *z2 = z;
|
| + DateTime tx;
|
| + sqlite3_int64 day;
|
| + if( !sqlite3Isdigit(*z2) ) z2++;
|
| + memset(&tx, 0, sizeof(tx));
|
| + if( parseHhMmSs(z2, &tx) ) break;
|
| + computeJD(&tx);
|
| + tx.iJD -= 43200000;
|
| + day = tx.iJD/86400000;
|
| + tx.iJD -= day*86400000;
|
| + if( z[0]=='-' ) tx.iJD = -tx.iJD;
|
| + computeJD(p);
|
| + clearYMD_HMS_TZ(p);
|
| + p->iJD += tx.iJD;
|
| + rc = 0;
|
| + break;
|
| + }
|
| +
|
| + /* If control reaches this point, it means the transformation is
|
| + ** one of the forms like "+NNN days". */
|
| + z += n;
|
| + while( sqlite3Isspace(*z) ) z++;
|
| + n = sqlite3Strlen30(z);
|
| + if( n>10 || n<3 ) break;
|
| + if( sqlite3UpperToLower[(u8)z[n-1]]=='s' ) n--;
|
| + computeJD(p);
|
| + rc = 1;
|
| + rRounder = r<0 ? -0.5 : +0.5;
|
| + for(i=0; i<ArraySize(aXformType); i++){
|
| + if( aXformType[i].nName==n
|
| + && sqlite3_strnicmp(aXformType[i].zName, z, n)==0
|
| + && r>-aXformType[i].rLimit && r<aXformType[i].rLimit
|
| + ){
|
| + switch( aXformType[i].eType ){
|
| + case 1: { /* Special processing to add months */
|
| + int x;
|
| + computeYMD_HMS(p);
|
| + p->M += (int)r;
|
| + x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12;
|
| + p->Y += x;
|
| + p->M -= x*12;
|
| + p->validJD = 0;
|
| + r -= (int)r;
|
| + break;
|
| + }
|
| + case 2: { /* Special processing to add years */
|
| + int y = (int)r;
|
| + computeYMD_HMS(p);
|
| + p->Y += y;
|
| + p->validJD = 0;
|
| + r -= (int)r;
|
| + break;
|
| + }
|
| + }
|
| + computeJD(p);
|
| + p->iJD += (sqlite3_int64)(r*aXformType[i].rXform + rRounder);
|
| + rc = 0;
|
| + break;
|
| + }
|
| + }
|
| + clearYMD_HMS_TZ(p);
|
| + break;
|
| + }
|
| + default: {
|
| + break;
|
| + }
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Process time function arguments. argv[0] is a date-time stamp.
|
| +** argv[1] and following are modifiers. Parse them all and write
|
| +** the resulting time into the DateTime structure p. Return 0
|
| +** on success and 1 if there are any errors.
|
| +**
|
| +** If there are zero parameters (if even argv[0] is undefined)
|
| +** then assume a default value of "now" for argv[0].
|
| +*/
|
| +static int isDate(
|
| + sqlite3_context *context,
|
| + int argc,
|
| + sqlite3_value **argv,
|
| + DateTime *p
|
| +){
|
| + int i, n;
|
| + const unsigned char *z;
|
| + int eType;
|
| + memset(p, 0, sizeof(*p));
|
| + if( argc==0 ){
|
| + return setDateTimeToCurrent(context, p);
|
| + }
|
| + if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT
|
| + || eType==SQLITE_INTEGER ){
|
| + setRawDateNumber(p, sqlite3_value_double(argv[0]));
|
| + }else{
|
| + z = sqlite3_value_text(argv[0]);
|
| + if( !z || parseDateOrTime(context, (char*)z, p) ){
|
| + return 1;
|
| + }
|
| + }
|
| + for(i=1; i<argc; i++){
|
| + z = sqlite3_value_text(argv[i]);
|
| + n = sqlite3_value_bytes(argv[i]);
|
| + if( z==0 || parseModifier(context, (char*)z, n, p) ) return 1;
|
| + }
|
| + computeJD(p);
|
| + if( p->isError || !validJulianDay(p->iJD) ) return 1;
|
| + return 0;
|
| +}
|
| +
|
| +
|
| +/*
|
| +** The following routines implement the various date and time functions
|
| +** of SQLite.
|
| +*/
|
| +
|
| +/*
|
| +** julianday( TIMESTRING, MOD, MOD, ...)
|
| +**
|
| +** Return the julian day number of the date specified in the arguments
|
| +*/
|
| +static void juliandayFunc(
|
| + sqlite3_context *context,
|
| + int argc,
|
| + sqlite3_value **argv
|
| +){
|
| + DateTime x;
|
| + if( isDate(context, argc, argv, &x)==0 ){
|
| + computeJD(&x);
|
| + sqlite3_result_double(context, x.iJD/86400000.0);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** datetime( TIMESTRING, MOD, MOD, ...)
|
| +**
|
| +** Return YYYY-MM-DD HH:MM:SS
|
| +*/
|
| +static void datetimeFunc(
|
| + sqlite3_context *context,
|
| + int argc,
|
| + sqlite3_value **argv
|
| +){
|
| + DateTime x;
|
| + if( isDate(context, argc, argv, &x)==0 ){
|
| + char zBuf[100];
|
| + computeYMD_HMS(&x);
|
| + sqlite3_snprintf(sizeof(zBuf), zBuf, "%04d-%02d-%02d %02d:%02d:%02d",
|
| + x.Y, x.M, x.D, x.h, x.m, (int)(x.s));
|
| + sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** time( TIMESTRING, MOD, MOD, ...)
|
| +**
|
| +** Return HH:MM:SS
|
| +*/
|
| +static void timeFunc(
|
| + sqlite3_context *context,
|
| + int argc,
|
| + sqlite3_value **argv
|
| +){
|
| + DateTime x;
|
| + if( isDate(context, argc, argv, &x)==0 ){
|
| + char zBuf[100];
|
| + computeHMS(&x);
|
| + sqlite3_snprintf(sizeof(zBuf), zBuf, "%02d:%02d:%02d", x.h, x.m, (int)x.s);
|
| + sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** date( TIMESTRING, MOD, MOD, ...)
|
| +**
|
| +** Return YYYY-MM-DD
|
| +*/
|
| +static void dateFunc(
|
| + sqlite3_context *context,
|
| + int argc,
|
| + sqlite3_value **argv
|
| +){
|
| + DateTime x;
|
| + if( isDate(context, argc, argv, &x)==0 ){
|
| + char zBuf[100];
|
| + computeYMD(&x);
|
| + sqlite3_snprintf(sizeof(zBuf), zBuf, "%04d-%02d-%02d", x.Y, x.M, x.D);
|
| + sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** strftime( FORMAT, TIMESTRING, MOD, MOD, ...)
|
| +**
|
| +** Return a string described by FORMAT. Conversions as follows:
|
| +**
|
| +** %d day of month
|
| +** %f ** fractional seconds SS.SSS
|
| +** %H hour 00-24
|
| +** %j day of year 000-366
|
| +** %J ** julian day number
|
| +** %m month 01-12
|
| +** %M minute 00-59
|
| +** %s seconds since 1970-01-01
|
| +** %S seconds 00-59
|
| +** %w day of week 0-6 sunday==0
|
| +** %W week of year 00-53
|
| +** %Y year 0000-9999
|
| +** %% %
|
| +*/
|
| +static void strftimeFunc(
|
| + sqlite3_context *context,
|
| + int argc,
|
| + sqlite3_value **argv
|
| +){
|
| + DateTime x;
|
| + u64 n;
|
| + size_t i,j;
|
| + char *z;
|
| + sqlite3 *db;
|
| + const char *zFmt;
|
| + char zBuf[100];
|
| + if( argc==0 ) return;
|
| + zFmt = (const char*)sqlite3_value_text(argv[0]);
|
| + if( zFmt==0 || isDate(context, argc-1, argv+1, &x) ) return;
|
| + db = sqlite3_context_db_handle(context);
|
| + for(i=0, n=1; zFmt[i]; i++, n++){
|
| + if( zFmt[i]=='%' ){
|
| + switch( zFmt[i+1] ){
|
| + case 'd':
|
| + case 'H':
|
| + case 'm':
|
| + case 'M':
|
| + case 'S':
|
| + case 'W':
|
| + n++;
|
| + /* fall thru */
|
| + case 'w':
|
| + case '%':
|
| + break;
|
| + case 'f':
|
| + n += 8;
|
| + break;
|
| + case 'j':
|
| + n += 3;
|
| + break;
|
| + case 'Y':
|
| + n += 8;
|
| + break;
|
| + case 's':
|
| + case 'J':
|
| + n += 50;
|
| + break;
|
| + default:
|
| + return; /* ERROR. return a NULL */
|
| + }
|
| + i++;
|
| + }
|
| + }
|
| + testcase( n==sizeof(zBuf)-1 );
|
| + testcase( n==sizeof(zBuf) );
|
| + testcase( n==(u64)db->aLimit[SQLITE_LIMIT_LENGTH]+1 );
|
| + testcase( n==(u64)db->aLimit[SQLITE_LIMIT_LENGTH] );
|
| + if( n<sizeof(zBuf) ){
|
| + z = zBuf;
|
| + }else if( n>(u64)db->aLimit[SQLITE_LIMIT_LENGTH] ){
|
| + sqlite3_result_error_toobig(context);
|
| + return;
|
| + }else{
|
| + z = sqlite3DbMallocRawNN(db, (int)n);
|
| + if( z==0 ){
|
| + sqlite3_result_error_nomem(context);
|
| + return;
|
| + }
|
| + }
|
| + computeJD(&x);
|
| + computeYMD_HMS(&x);
|
| + for(i=j=0; zFmt[i]; i++){
|
| + if( zFmt[i]!='%' ){
|
| + z[j++] = zFmt[i];
|
| + }else{
|
| + i++;
|
| + switch( zFmt[i] ){
|
| + case 'd': sqlite3_snprintf(3, &z[j],"%02d",x.D); j+=2; break;
|
| + case 'f': {
|
| + double s = x.s;
|
| + if( s>59.999 ) s = 59.999;
|
| + sqlite3_snprintf(7, &z[j],"%06.3f", s);
|
| + j += sqlite3Strlen30(&z[j]);
|
| + break;
|
| + }
|
| + case 'H': sqlite3_snprintf(3, &z[j],"%02d",x.h); j+=2; break;
|
| + case 'W': /* Fall thru */
|
| + case 'j': {
|
| + int nDay; /* Number of days since 1st day of year */
|
| + DateTime y = x;
|
| + y.validJD = 0;
|
| + y.M = 1;
|
| + y.D = 1;
|
| + computeJD(&y);
|
| + nDay = (int)((x.iJD-y.iJD+43200000)/86400000);
|
| + if( zFmt[i]=='W' ){
|
| + int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */
|
| + wd = (int)(((x.iJD+43200000)/86400000)%7);
|
| + sqlite3_snprintf(3, &z[j],"%02d",(nDay+7-wd)/7);
|
| + j += 2;
|
| + }else{
|
| + sqlite3_snprintf(4, &z[j],"%03d",nDay+1);
|
| + j += 3;
|
| + }
|
| + break;
|
| + }
|
| + case 'J': {
|
| + sqlite3_snprintf(20, &z[j],"%.16g",x.iJD/86400000.0);
|
| + j+=sqlite3Strlen30(&z[j]);
|
| + break;
|
| + }
|
| + case 'm': sqlite3_snprintf(3, &z[j],"%02d",x.M); j+=2; break;
|
| + case 'M': sqlite3_snprintf(3, &z[j],"%02d",x.m); j+=2; break;
|
| + case 's': {
|
| + sqlite3_snprintf(30,&z[j],"%lld",
|
| + (i64)(x.iJD/1000 - 21086676*(i64)10000));
|
| + j += sqlite3Strlen30(&z[j]);
|
| + break;
|
| + }
|
| + case 'S': sqlite3_snprintf(3,&z[j],"%02d",(int)x.s); j+=2; break;
|
| + case 'w': {
|
| + z[j++] = (char)(((x.iJD+129600000)/86400000) % 7) + '0';
|
| + break;
|
| + }
|
| + case 'Y': {
|
| + sqlite3_snprintf(5,&z[j],"%04d",x.Y); j+=sqlite3Strlen30(&z[j]);
|
| + break;
|
| + }
|
| + default: z[j++] = '%'; break;
|
| + }
|
| + }
|
| + }
|
| + z[j] = 0;
|
| + sqlite3_result_text(context, z, -1,
|
| + z==zBuf ? SQLITE_TRANSIENT : SQLITE_DYNAMIC);
|
| +}
|
| +
|
| +/*
|
| +** current_time()
|
| +**
|
| +** This function returns the same value as time('now').
|
| +*/
|
| +static void ctimeFunc(
|
| + sqlite3_context *context,
|
| + int NotUsed,
|
| + sqlite3_value **NotUsed2
|
| +){
|
| + UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
| + timeFunc(context, 0, 0);
|
| +}
|
| +
|
| +/*
|
| +** current_date()
|
| +**
|
| +** This function returns the same value as date('now').
|
| +*/
|
| +static void cdateFunc(
|
| + sqlite3_context *context,
|
| + int NotUsed,
|
| + sqlite3_value **NotUsed2
|
| +){
|
| + UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
| + dateFunc(context, 0, 0);
|
| +}
|
| +
|
| +/*
|
| +** current_timestamp()
|
| +**
|
| +** This function returns the same value as datetime('now').
|
| +*/
|
| +static void ctimestampFunc(
|
| + sqlite3_context *context,
|
| + int NotUsed,
|
| + sqlite3_value **NotUsed2
|
| +){
|
| + UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
| + datetimeFunc(context, 0, 0);
|
| +}
|
| +#endif /* !defined(SQLITE_OMIT_DATETIME_FUNCS) */
|
| +
|
| +#ifdef SQLITE_OMIT_DATETIME_FUNCS
|
| +/*
|
| +** If the library is compiled to omit the full-scale date and time
|
| +** handling (to get a smaller binary), the following minimal version
|
| +** of the functions current_time(), current_date() and current_timestamp()
|
| +** are included instead. This is to support column declarations that
|
| +** include "DEFAULT CURRENT_TIME" etc.
|
| +**
|
| +** This function uses the C-library functions time(), gmtime()
|
| +** and strftime(). The format string to pass to strftime() is supplied
|
| +** as the user-data for the function.
|
| +*/
|
| +static void currentTimeFunc(
|
| + sqlite3_context *context,
|
| + int argc,
|
| + sqlite3_value **argv
|
| +){
|
| + time_t t;
|
| + char *zFormat = (char *)sqlite3_user_data(context);
|
| + sqlite3_int64 iT;
|
| + struct tm *pTm;
|
| + struct tm sNow;
|
| + char zBuf[20];
|
| +
|
| + UNUSED_PARAMETER(argc);
|
| + UNUSED_PARAMETER(argv);
|
| +
|
| + iT = sqlite3StmtCurrentTime(context);
|
| + if( iT<=0 ) return;
|
| + t = iT/1000 - 10000*(sqlite3_int64)21086676;
|
| +#if HAVE_GMTIME_R
|
| + pTm = gmtime_r(&t, &sNow);
|
| +#else
|
| + sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
|
| + pTm = gmtime(&t);
|
| + if( pTm ) memcpy(&sNow, pTm, sizeof(sNow));
|
| + sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
|
| +#endif
|
| + if( pTm ){
|
| + strftime(zBuf, 20, zFormat, &sNow);
|
| + sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
|
| + }
|
| +}
|
| +#endif
|
| +
|
| +/*
|
| +** This function registered all of the above C functions as SQL
|
| +** functions. This should be the only routine in this file with
|
| +** external linkage.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void){
|
| + static FuncDef aDateTimeFuncs[] = {
|
| +#ifndef SQLITE_OMIT_DATETIME_FUNCS
|
| + DFUNCTION(julianday, -1, 0, 0, juliandayFunc ),
|
| + DFUNCTION(date, -1, 0, 0, dateFunc ),
|
| + DFUNCTION(time, -1, 0, 0, timeFunc ),
|
| + DFUNCTION(datetime, -1, 0, 0, datetimeFunc ),
|
| + DFUNCTION(strftime, -1, 0, 0, strftimeFunc ),
|
| + DFUNCTION(current_time, 0, 0, 0, ctimeFunc ),
|
| + DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc),
|
| + DFUNCTION(current_date, 0, 0, 0, cdateFunc ),
|
| +#else
|
| + STR_FUNCTION(current_time, 0, "%H:%M:%S", 0, currentTimeFunc),
|
| + STR_FUNCTION(current_date, 0, "%Y-%m-%d", 0, currentTimeFunc),
|
| + STR_FUNCTION(current_timestamp, 0, "%Y-%m-%d %H:%M:%S", 0, currentTimeFunc),
|
| +#endif
|
| + };
|
| + sqlite3InsertBuiltinFuncs(aDateTimeFuncs, ArraySize(aDateTimeFuncs));
|
| +}
|
| +
|
| +/************** End of date.c ************************************************/
|
| +/************** Begin file os.c **********************************************/
|
| +/*
|
| +** 2005 November 29
|
| +**
|
| +** 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 OS interface code that is common to all
|
| +** architectures.
|
| +*/
|
| +/* #include "sqliteInt.h" */
|
| +
|
| +/*
|
| +** If we compile with the SQLITE_TEST macro set, then the following block
|
| +** of code will give us the ability to simulate a disk I/O error. This
|
| +** is used for testing the I/O recovery logic.
|
| +*/
|
| +#if defined(SQLITE_TEST)
|
| +SQLITE_API int sqlite3_io_error_hit = 0; /* Total number of I/O Errors */
|
| +SQLITE_API int sqlite3_io_error_hardhit = 0; /* Number of non-benign errors */
|
| +SQLITE_API int sqlite3_io_error_pending = 0; /* Count down to first I/O error */
|
| +SQLITE_API int sqlite3_io_error_persist = 0; /* True if I/O errors persist */
|
| +SQLITE_API int sqlite3_io_error_benign = 0; /* True if errors are benign */
|
| +SQLITE_API int sqlite3_diskfull_pending = 0;
|
| +SQLITE_API int sqlite3_diskfull = 0;
|
| +#endif /* defined(SQLITE_TEST) */
|
| +
|
| +/*
|
| +** When testing, also keep a count of the number of open files.
|
| +*/
|
| +#if defined(SQLITE_TEST)
|
| +SQLITE_API int sqlite3_open_file_count = 0;
|
| +#endif /* defined(SQLITE_TEST) */
|
| +
|
| +/*
|
| +** The default SQLite sqlite3_vfs implementations do not allocate
|
| +** memory (actually, os_unix.c allocates a small amount of memory
|
| +** from within OsOpen()), but some third-party implementations may.
|
| +** So we test the effects of a malloc() failing and the sqlite3OsXXX()
|
| +** function returning SQLITE_IOERR_NOMEM using the DO_OS_MALLOC_TEST macro.
|
| +**
|
| +** The following functions are instrumented for malloc() failure
|
| +** testing:
|
| +**
|
| +** sqlite3OsRead()
|
| +** sqlite3OsWrite()
|
| +** sqlite3OsSync()
|
| +** sqlite3OsFileSize()
|
| +** sqlite3OsLock()
|
| +** sqlite3OsCheckReservedLock()
|
| +** sqlite3OsFileControl()
|
| +** sqlite3OsShmMap()
|
| +** sqlite3OsOpen()
|
| +** sqlite3OsDelete()
|
| +** sqlite3OsAccess()
|
| +** sqlite3OsFullPathname()
|
| +**
|
| +*/
|
| +#if defined(SQLITE_TEST)
|
| +SQLITE_API int sqlite3_memdebug_vfs_oom_test = 1;
|
| + #define DO_OS_MALLOC_TEST(x) \
|
| + if (sqlite3_memdebug_vfs_oom_test && (!x || !sqlite3JournalIsInMemory(x))) { \
|
| + void *pTstAlloc = sqlite3Malloc(10); \
|
| + if (!pTstAlloc) return SQLITE_IOERR_NOMEM_BKPT; \
|
| + sqlite3_free(pTstAlloc); \
|
| + }
|
| +#else
|
| + #define DO_OS_MALLOC_TEST(x)
|
| +#endif
|
| +
|
| +/*
|
| +** The following routines are convenience wrappers around methods
|
| +** of the sqlite3_file object. This is mostly just syntactic sugar. All
|
| +** of this would be completely automatic if SQLite were coded using
|
| +** C++ instead of plain old C.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3OsClose(sqlite3_file *pId){
|
| + if( pId->pMethods ){
|
| + pId->pMethods->xClose(pId);
|
| + pId->pMethods = 0;
|
| + }
|
| +}
|
| +SQLITE_PRIVATE int sqlite3OsRead(sqlite3_file *id, void *pBuf, int amt, i64 offset){
|
| + DO_OS_MALLOC_TEST(id);
|
| + return id->pMethods->xRead(id, pBuf, amt, offset);
|
| +}
|
| +SQLITE_PRIVATE int sqlite3OsWrite(sqlite3_file *id, const void *pBuf, int amt, i64 offset){
|
| + DO_OS_MALLOC_TEST(id);
|
| + return id->pMethods->xWrite(id, pBuf, amt, offset);
|
| +}
|
| +SQLITE_PRIVATE int sqlite3OsTruncate(sqlite3_file *id, i64 size){
|
| + return id->pMethods->xTruncate(id, size);
|
| +}
|
| +SQLITE_PRIVATE int sqlite3OsSync(sqlite3_file *id, int flags){
|
| + DO_OS_MALLOC_TEST(id);
|
| + return id->pMethods->xSync(id, flags);
|
| +}
|
| +SQLITE_PRIVATE int sqlite3OsFileSize(sqlite3_file *id, i64 *pSize){
|
| + DO_OS_MALLOC_TEST(id);
|
| + return id->pMethods->xFileSize(id, pSize);
|
| +}
|
| +SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file *id, int lockType){
|
| + DO_OS_MALLOC_TEST(id);
|
| + return id->pMethods->xLock(id, lockType);
|
| +}
|
| +SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file *id, int lockType){
|
| + return id->pMethods->xUnlock(id, lockType);
|
| +}
|
| +SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut){
|
| + DO_OS_MALLOC_TEST(id);
|
| + return id->pMethods->xCheckReservedLock(id, pResOut);
|
| +}
|
| +
|
| +/*
|
| +** Use sqlite3OsFileControl() when we are doing something that might fail
|
| +** and we need to know about the failures. Use sqlite3OsFileControlHint()
|
| +** when simply tossing information over the wall to the VFS and we do not
|
| +** really care if the VFS receives and understands the information since it
|
| +** is only a hint and can be safely ignored. The sqlite3OsFileControlHint()
|
| +** routine has no return value since the return value would be meaningless.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){
|
| +#ifdef SQLITE_TEST
|
| + if( op!=SQLITE_FCNTL_COMMIT_PHASETWO ){
|
| + /* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite
|
| + ** is using a regular VFS, it is called after the corresponding
|
| + ** transaction has been committed. Injecting a fault at this point
|
| + ** confuses the test scripts - the COMMIT comand returns SQLITE_NOMEM
|
| + ** but the transaction is committed anyway.
|
| + **
|
| + ** The core must call OsFileControl() though, not OsFileControlHint(),
|
| + ** as if a custom VFS (e.g. zipvfs) returns an error here, it probably
|
| + ** means the commit really has failed and an error should be returned
|
| + ** to the user. */
|
| + DO_OS_MALLOC_TEST(id);
|
| + }
|
| +#endif
|
| + return id->pMethods->xFileControl(id, op, pArg);
|
| +}
|
| +SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file *id, int op, void *pArg){
|
| + (void)id->pMethods->xFileControl(id, op, pArg);
|
| +}
|
| +
|
| +SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id){
|
| + int (*xSectorSize)(sqlite3_file*) = id->pMethods->xSectorSize;
|
| + return (xSectorSize ? xSectorSize(id) : SQLITE_DEFAULT_SECTOR_SIZE);
|
| +}
|
| +SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id){
|
| + return id->pMethods->xDeviceCharacteristics(id);
|
| +}
|
| +SQLITE_PRIVATE int sqlite3OsShmLock(sqlite3_file *id, int offset, int n, int flags){
|
| + return id->pMethods->xShmLock(id, offset, n, flags);
|
| +}
|
| +SQLITE_PRIVATE void sqlite3OsShmBarrier(sqlite3_file *id){
|
| + id->pMethods->xShmBarrier(id);
|
| +}
|
| +SQLITE_PRIVATE int sqlite3OsShmUnmap(sqlite3_file *id, int deleteFlag){
|
| + return id->pMethods->xShmUnmap(id, deleteFlag);
|
| +}
|
| +SQLITE_PRIVATE int sqlite3OsShmMap(
|
| + sqlite3_file *id, /* Database file handle */
|
| + int iPage,
|
| + int pgsz,
|
| + int bExtend, /* True to extend file if necessary */
|
| + void volatile **pp /* OUT: Pointer to mapping */
|
| +){
|
| + DO_OS_MALLOC_TEST(id);
|
| + return id->pMethods->xShmMap(id, iPage, pgsz, bExtend, pp);
|
| +}
|
| +
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| +/* The real implementation of xFetch and xUnfetch */
|
| +SQLITE_PRIVATE int sqlite3OsFetch(sqlite3_file *id, i64 iOff, int iAmt, void **pp){
|
| + DO_OS_MALLOC_TEST(id);
|
| + return id->pMethods->xFetch(id, iOff, iAmt, pp);
|
| +}
|
| +SQLITE_PRIVATE int sqlite3OsUnfetch(sqlite3_file *id, i64 iOff, void *p){
|
| + return id->pMethods->xUnfetch(id, iOff, p);
|
| +}
|
| +#else
|
| +/* No-op stubs to use when memory-mapped I/O is disabled */
|
| +SQLITE_PRIVATE int sqlite3OsFetch(sqlite3_file *id, i64 iOff, int iAmt, void **pp){
|
| + *pp = 0;
|
| + return SQLITE_OK;
|
| +}
|
| +SQLITE_PRIVATE int sqlite3OsUnfetch(sqlite3_file *id, i64 iOff, void *p){
|
| + return SQLITE_OK;
|
| +}
|
| +#endif
|
| +
|
| +/*
|
| +** The next group of routines are convenience wrappers around the
|
| +** VFS methods.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3OsOpen(
|
| + sqlite3_vfs *pVfs,
|
| + const char *zPath,
|
| + sqlite3_file *pFile,
|
| + int flags,
|
| + int *pFlagsOut
|
| +){
|
| + int rc;
|
| + DO_OS_MALLOC_TEST(0);
|
| + /* 0x87f7f is a mask of SQLITE_OPEN_ flags that are valid to be passed
|
| + ** down into the VFS layer. Some SQLITE_OPEN_ flags (for example,
|
| + ** SQLITE_OPEN_FULLMUTEX or SQLITE_OPEN_SHAREDCACHE) are blocked before
|
| + ** reaching the VFS. */
|
| + rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x87f7f, pFlagsOut);
|
| + assert( rc==SQLITE_OK || pFile->pMethods==0 );
|
| + return rc;
|
| +}
|
| +SQLITE_PRIVATE int sqlite3OsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
|
| + DO_OS_MALLOC_TEST(0);
|
| + assert( dirSync==0 || dirSync==1 );
|
| + return pVfs->xDelete(pVfs, zPath, dirSync);
|
| +}
|
| +SQLITE_PRIVATE int sqlite3OsAccess(
|
| + sqlite3_vfs *pVfs,
|
| + const char *zPath,
|
| + int flags,
|
| + int *pResOut
|
| +){
|
| + DO_OS_MALLOC_TEST(0);
|
| + return pVfs->xAccess(pVfs, zPath, flags, pResOut);
|
| +}
|
| +SQLITE_PRIVATE int sqlite3OsFullPathname(
|
| + sqlite3_vfs *pVfs,
|
| + const char *zPath,
|
| + int nPathOut,
|
| + char *zPathOut
|
| +){
|
| + DO_OS_MALLOC_TEST(0);
|
| + zPathOut[0] = 0;
|
| + return pVfs->xFullPathname(pVfs, zPath, nPathOut, zPathOut);
|
| +}
|
| +#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
| +SQLITE_PRIVATE void *sqlite3OsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
|
| + return pVfs->xDlOpen(pVfs, zPath);
|
| +}
|
| +SQLITE_PRIVATE void sqlite3OsDlError(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
|
| + pVfs->xDlError(pVfs, nByte, zBufOut);
|
| +}
|
| +SQLITE_PRIVATE void (*sqlite3OsDlSym(sqlite3_vfs *pVfs, void *pHdle, const char *zSym))(void){
|
| + return pVfs->xDlSym(pVfs, pHdle, zSym);
|
| +}
|
| +SQLITE_PRIVATE void sqlite3OsDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
| + pVfs->xDlClose(pVfs, pHandle);
|
| +}
|
| +#endif /* SQLITE_OMIT_LOAD_EXTENSION */
|
| +SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
|
| + return pVfs->xRandomness(pVfs, nByte, zBufOut);
|
| +}
|
| +SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *pVfs, int nMicro){
|
| + return pVfs->xSleep(pVfs, nMicro);
|
| +}
|
| +SQLITE_PRIVATE int sqlite3OsGetLastError(sqlite3_vfs *pVfs){
|
| + return pVfs->xGetLastError ? pVfs->xGetLastError(pVfs, 0, 0) : 0;
|
| +}
|
| +SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
|
| + int rc;
|
| + /* IMPLEMENTATION-OF: R-49045-42493 SQLite will use the xCurrentTimeInt64()
|
| + ** method to get the current date and time if that method is available
|
| + ** (if iVersion is 2 or greater and the function pointer is not NULL) and
|
| + ** will fall back to xCurrentTime() if xCurrentTimeInt64() is
|
| + ** unavailable.
|
| + */
|
| + if( pVfs->iVersion>=2 && pVfs->xCurrentTimeInt64 ){
|
| + rc = pVfs->xCurrentTimeInt64(pVfs, pTimeOut);
|
| + }else{
|
| + double r;
|
| + rc = pVfs->xCurrentTime(pVfs, &r);
|
| + *pTimeOut = (sqlite3_int64)(r*86400000.0);
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +SQLITE_PRIVATE int sqlite3OsOpenMalloc(
|
| + sqlite3_vfs *pVfs,
|
| + const char *zFile,
|
| + sqlite3_file **ppFile,
|
| + int flags,
|
| + int *pOutFlags
|
| +){
|
| + int rc;
|
| + sqlite3_file *pFile;
|
| + pFile = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile);
|
| + if( pFile ){
|
| + rc = sqlite3OsOpen(pVfs, zFile, pFile, flags, pOutFlags);
|
| + if( rc!=SQLITE_OK ){
|
| + sqlite3_free(pFile);
|
| + }else{
|
| + *ppFile = pFile;
|
| + }
|
| + }else{
|
| + rc = SQLITE_NOMEM_BKPT;
|
| + }
|
| + return rc;
|
| +}
|
| +SQLITE_PRIVATE void sqlite3OsCloseFree(sqlite3_file *pFile){
|
| + assert( pFile );
|
| + sqlite3OsClose(pFile);
|
| + sqlite3_free(pFile);
|
| +}
|
| +
|
| +/*
|
| +** This function is a wrapper around the OS specific implementation of
|
| +** sqlite3_os_init(). The purpose of the wrapper is to provide the
|
| +** ability to simulate a malloc failure, so that the handling of an
|
| +** error in sqlite3_os_init() by the upper layers can be tested.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3OsInit(void){
|
| + void *p = sqlite3_malloc(10);
|
| + if( p==0 ) return SQLITE_NOMEM_BKPT;
|
| + sqlite3_free(p);
|
| + return sqlite3_os_init();
|
| +}
|
| +
|
| +/*
|
| +** The list of all registered VFS implementations.
|
| +*/
|
| +static sqlite3_vfs * SQLITE_WSD vfsList = 0;
|
| +#define vfsList GLOBAL(sqlite3_vfs *, vfsList)
|
| +
|
| +/*
|
| +** Locate a VFS by name. If no name is given, simply return the
|
| +** first VFS on the list.
|
| +*/
|
| +SQLITE_API sqlite3_vfs *sqlite3_vfs_find(const char *zVfs){
|
| + sqlite3_vfs *pVfs = 0;
|
| +#if SQLITE_THREADSAFE
|
| + sqlite3_mutex *mutex;
|
| +#endif
|
| +#ifndef SQLITE_OMIT_AUTOINIT
|
| + int rc = sqlite3_initialize();
|
| + if( rc ) return 0;
|
| +#endif
|
| +#if SQLITE_THREADSAFE
|
| + mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
| +#endif
|
| + sqlite3_mutex_enter(mutex);
|
| + for(pVfs = vfsList; pVfs; pVfs=pVfs->pNext){
|
| + if( zVfs==0 ) break;
|
| + if( strcmp(zVfs, pVfs->zName)==0 ) break;
|
| + }
|
| + sqlite3_mutex_leave(mutex);
|
| + return pVfs;
|
| +}
|
| +
|
| +/*
|
| +** Unlink a VFS from the linked list
|
| +*/
|
| +static void vfsUnlink(sqlite3_vfs *pVfs){
|
| + assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)) );
|
| + if( pVfs==0 ){
|
| + /* No-op */
|
| + }else if( vfsList==pVfs ){
|
| + vfsList = pVfs->pNext;
|
| + }else if( vfsList ){
|
| + sqlite3_vfs *p = vfsList;
|
| + while( p->pNext && p->pNext!=pVfs ){
|
| + p = p->pNext;
|
| + }
|
| + if( p->pNext==pVfs ){
|
| + p->pNext = pVfs->pNext;
|
| + }
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Register a VFS with the system. It is harmless to register the same
|
| +** VFS multiple times. The new VFS becomes the default if makeDflt is
|
| +** true.
|
| +*/
|
| +SQLITE_API int sqlite3_vfs_register(sqlite3_vfs *pVfs, int makeDflt){
|
| + MUTEX_LOGIC(sqlite3_mutex *mutex;)
|
| +#ifndef SQLITE_OMIT_AUTOINIT
|
| + int rc = sqlite3_initialize();
|
| + if( rc ) return rc;
|
| +#endif
|
| +#ifdef SQLITE_ENABLE_API_ARMOR
|
| + if( pVfs==0 ) return SQLITE_MISUSE_BKPT;
|
| +#endif
|
| +
|
| + MUTEX_LOGIC( mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
|
| + sqlite3_mutex_enter(mutex);
|
| + vfsUnlink(pVfs);
|
| + if( makeDflt || vfsList==0 ){
|
| + pVfs->pNext = vfsList;
|
| + vfsList = pVfs;
|
| + }else{
|
| + pVfs->pNext = vfsList->pNext;
|
| + vfsList->pNext = pVfs;
|
| + }
|
| + assert(vfsList);
|
| + sqlite3_mutex_leave(mutex);
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Unregister a VFS so that it is no longer accessible.
|
| +*/
|
| +SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs *pVfs){
|
| +#if SQLITE_THREADSAFE
|
| + sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
|
| +#endif
|
| + sqlite3_mutex_enter(mutex);
|
| + vfsUnlink(pVfs);
|
| + sqlite3_mutex_leave(mutex);
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/************** End of os.c **************************************************/
|
| +/************** Begin file fault.c *******************************************/
|
| +/*
|
| +** 2008 Jan 22
|
| +**
|
| +** 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 code to support the concept of "benign"
|
| +** malloc failures (when the xMalloc() or xRealloc() method of the
|
| +** sqlite3_mem_methods structure fails to allocate a block of memory
|
| +** and returns 0).
|
| +**
|
| +** Most malloc failures are non-benign. After they occur, SQLite
|
| +** abandons the current operation and returns an error code (usually
|
| +** SQLITE_NOMEM) to the user. However, sometimes a fault is not necessarily
|
| +** fatal. For example, if a malloc fails while resizing a hash table, this
|
| +** is completely recoverable simply by not carrying out the resize. The
|
| +** hash table will continue to function normally. So a malloc failure
|
| +** during a hash table resize is a benign fault.
|
| +*/
|
| +
|
| +/* #include "sqliteInt.h" */
|
| +
|
| +#ifndef SQLITE_UNTESTABLE
|
| +
|
| +/*
|
| +** Global variables.
|
| +*/
|
| +typedef struct BenignMallocHooks BenignMallocHooks;
|
| +static SQLITE_WSD struct BenignMallocHooks {
|
| + void (*xBenignBegin)(void);
|
| + void (*xBenignEnd)(void);
|
| +} sqlite3Hooks = { 0, 0 };
|
| +
|
| +/* The "wsdHooks" macro will resolve to the appropriate BenignMallocHooks
|
| +** structure. If writable static data is unsupported on the target,
|
| +** we have to locate the state vector at run-time. In the more common
|
| +** case where writable static data is supported, wsdHooks can refer directly
|
| +** to the "sqlite3Hooks" state vector declared above.
|
| +*/
|
| +#ifdef SQLITE_OMIT_WSD
|
| +# define wsdHooksInit \
|
| + BenignMallocHooks *x = &GLOBAL(BenignMallocHooks,sqlite3Hooks)
|
| +# define wsdHooks x[0]
|
| +#else
|
| +# define wsdHooksInit
|
| +# define wsdHooks sqlite3Hooks
|
| +#endif
|
| +
|
| +
|
| +/*
|
| +** Register hooks to call when sqlite3BeginBenignMalloc() and
|
| +** sqlite3EndBenignMalloc() are called, respectively.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3BenignMallocHooks(
|
| + void (*xBenignBegin)(void),
|
| + void (*xBenignEnd)(void)
|
| +){
|
| + wsdHooksInit;
|
| + wsdHooks.xBenignBegin = xBenignBegin;
|
| + wsdHooks.xBenignEnd = xBenignEnd;
|
| +}
|
| +
|
| +/*
|
| +** This (sqlite3EndBenignMalloc()) is called by SQLite code to indicate that
|
| +** subsequent malloc failures are benign. A call to sqlite3EndBenignMalloc()
|
| +** indicates that subsequent malloc failures are non-benign.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3BeginBenignMalloc(void){
|
| + wsdHooksInit;
|
| + if( wsdHooks.xBenignBegin ){
|
| + wsdHooks.xBenignBegin();
|
| + }
|
| +}
|
| +SQLITE_PRIVATE void sqlite3EndBenignMalloc(void){
|
| + wsdHooksInit;
|
| + if( wsdHooks.xBenignEnd ){
|
| + wsdHooks.xBenignEnd();
|
| + }
|
| +}
|
| +
|
| +#endif /* #ifndef SQLITE_UNTESTABLE */
|
| +
|
| +/************** End of fault.c ***********************************************/
|
| +/************** Begin file mem0.c ********************************************/
|
| +/*
|
| +** 2008 October 28
|
| +**
|
| +** 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 a no-op memory allocation drivers for use when
|
| +** SQLITE_ZERO_MALLOC is defined. The allocation drivers implemented
|
| +** here always fail. SQLite will not operate with these drivers. These
|
| +** are merely placeholders. Real drivers must be substituted using
|
| +** sqlite3_config() before SQLite will operate.
|
| +*/
|
| +/* #include "sqliteInt.h" */
|
| +
|
| +/*
|
| +** This version of the memory allocator is the default. It is
|
| +** used when no other memory allocator is specified using compile-time
|
| +** macros.
|
| +*/
|
| +#ifdef SQLITE_ZERO_MALLOC
|
| +
|
| +/*
|
| +** No-op versions of all memory allocation routines
|
| +*/
|
| +static void *sqlite3MemMalloc(int nByte){ return 0; }
|
| +static void sqlite3MemFree(void *pPrior){ return; }
|
| +static void *sqlite3MemRealloc(void *pPrior, int nByte){ return 0; }
|
| +static int sqlite3MemSize(void *pPrior){ return 0; }
|
| +static int sqlite3MemRoundup(int n){ return n; }
|
| +static int sqlite3MemInit(void *NotUsed){ return SQLITE_OK; }
|
| +static void sqlite3MemShutdown(void *NotUsed){ return; }
|
| +
|
| +/*
|
| +** This routine is the only routine in this file with external linkage.
|
| +**
|
| +** Populate the low-level memory allocation function pointers in
|
| +** sqlite3GlobalConfig.m with pointers to the routines in this file.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3MemSetDefault(void){
|
| + static const sqlite3_mem_methods defaultMethods = {
|
| + sqlite3MemMalloc,
|
| + sqlite3MemFree,
|
| + sqlite3MemRealloc,
|
| + sqlite3MemSize,
|
| + sqlite3MemRoundup,
|
| + sqlite3MemInit,
|
| + sqlite3MemShutdown,
|
| + 0
|
| + };
|
| + sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods);
|
| +}
|
| +
|
| +#endif /* SQLITE_ZERO_MALLOC */
|
| +
|
| +/************** End of mem0.c ************************************************/
|
| +/************** Begin file mem1.c ********************************************/
|
| +/*
|
| +** 2007 August 14
|
| +**
|
| +** 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 low-level memory allocation drivers for when
|
| +** SQLite will use the standard C-library malloc/realloc/free interface
|
| +** to obtain the memory it needs.
|
| +**
|
| +** This file contains implementations of the low-level memory allocation
|
| +** routines specified in the sqlite3_mem_methods object. The content of
|
| +** this file is only used if SQLITE_SYSTEM_MALLOC is defined. The
|
| +** SQLITE_SYSTEM_MALLOC macro is defined automatically if neither the
|
| +** SQLITE_MEMDEBUG nor the SQLITE_WIN32_MALLOC macros are defined. The
|
| +** default configuration is to use memory allocation routines in this
|
| +** file.
|
| +**
|
| +** C-preprocessor macro summary:
|
| +**
|
| +** HAVE_MALLOC_USABLE_SIZE The configure script sets this symbol if
|
| +** the malloc_usable_size() interface exists
|
| +** on the target platform. Or, this symbol
|
| +** can be set manually, if desired.
|
| +** If an equivalent interface exists by
|
| +** a different name, using a separate -D
|
| +** option to rename it.
|
| +**
|
| +** SQLITE_WITHOUT_ZONEMALLOC Some older macs lack support for the zone
|
| +** memory allocator. Set this symbol to enable
|
| +** building on older macs.
|
| +**
|
| +** SQLITE_WITHOUT_MSIZE Set this symbol to disable the use of
|
| +** _msize() on windows systems. This might
|
| +** be necessary when compiling for Delphi,
|
| +** for example.
|
| +*/
|
| +/* #include "sqliteInt.h" */
|
| +
|
| +/*
|
| +** This version of the memory allocator is the default. It is
|
| +** used when no other memory allocator is specified using compile-time
|
| +** macros.
|
| +*/
|
| +#ifdef SQLITE_SYSTEM_MALLOC
|
| +#if defined(__APPLE__) && !defined(SQLITE_WITHOUT_ZONEMALLOC)
|
| +
|
| +/*
|
| +** Use the zone allocator available on apple products unless the
|
| +** SQLITE_WITHOUT_ZONEMALLOC symbol is defined.
|
| +*/
|
| +#include <sys/sysctl.h>
|
| +#include <malloc/malloc.h>
|
| +#include <libkern/OSAtomic.h>
|
| +static malloc_zone_t* _sqliteZone_;
|
| +#define SQLITE_MALLOC(x) malloc_zone_malloc(_sqliteZone_, (x))
|
| +#define SQLITE_FREE(x) malloc_zone_free(_sqliteZone_, (x));
|
| +#define SQLITE_REALLOC(x,y) malloc_zone_realloc(_sqliteZone_, (x), (y))
|
| +#define SQLITE_MALLOCSIZE(x) \
|
| + (_sqliteZone_ ? _sqliteZone_->size(_sqliteZone_,x) : malloc_size(x))
|
| +
|
| +#else /* if not __APPLE__ */
|
| +
|
| +/*
|
| +** Use standard C library malloc and free on non-Apple systems.
|
| +** Also used by Apple systems if SQLITE_WITHOUT_ZONEMALLOC is defined.
|
| +*/
|
| +#define SQLITE_MALLOC(x) malloc(x)
|
| +#define SQLITE_FREE(x) free(x)
|
| +#define SQLITE_REALLOC(x,y) realloc((x),(y))
|
| +
|
| +/*
|
| +** The malloc.h header file is needed for malloc_usable_size() function
|
| +** on some systems (e.g. Linux).
|
| +*/
|
| +#if HAVE_MALLOC_H && HAVE_MALLOC_USABLE_SIZE
|
| +# define SQLITE_USE_MALLOC_H 1
|
| +# define SQLITE_USE_MALLOC_USABLE_SIZE 1
|
| +/*
|
| +** The MSVCRT has malloc_usable_size(), but it is called _msize(). The
|
| +** use of _msize() is automatic, but can be disabled by compiling with
|
| +** -DSQLITE_WITHOUT_MSIZE. Using the _msize() function also requires
|
| +** the malloc.h header file.
|
| +*/
|
| +#elif defined(_MSC_VER) && !defined(SQLITE_WITHOUT_MSIZE)
|
| +# define SQLITE_USE_MALLOC_H
|
| +# define SQLITE_USE_MSIZE
|
| +#endif
|
| +
|
| +/*
|
| +** Include the malloc.h header file, if necessary. Also set define macro
|
| +** SQLITE_MALLOCSIZE to the appropriate function name, which is _msize()
|
| +** for MSVC and malloc_usable_size() for most other systems (e.g. Linux).
|
| +** The memory size function can always be overridden manually by defining
|
| +** the macro SQLITE_MALLOCSIZE to the desired function name.
|
| +*/
|
| +#if defined(SQLITE_USE_MALLOC_H)
|
| +# include <malloc.h>
|
| +# if defined(SQLITE_USE_MALLOC_USABLE_SIZE)
|
| +# if !defined(SQLITE_MALLOCSIZE)
|
| +# define SQLITE_MALLOCSIZE(x) malloc_usable_size(x)
|
| +# endif
|
| +# elif defined(SQLITE_USE_MSIZE)
|
| +# if !defined(SQLITE_MALLOCSIZE)
|
| +# define SQLITE_MALLOCSIZE _msize
|
| +# endif
|
| +# endif
|
| +#endif /* defined(SQLITE_USE_MALLOC_H) */
|
| +
|
| +#endif /* __APPLE__ or not __APPLE__ */
|
| +
|
| +/*
|
| +** Like malloc(), but remember the size of the allocation
|
| +** so that we can find it later using sqlite3MemSize().
|
| +**
|
| +** For this low-level routine, we are guaranteed that nByte>0 because
|
| +** cases of nByte<=0 will be intercepted and dealt with by higher level
|
| +** routines.
|
| +*/
|
| +static void *sqlite3MemMalloc(int nByte){
|
| +#ifdef SQLITE_MALLOCSIZE
|
| + void *p;
|
| + testcase( ROUND8(nByte)==nByte );
|
| + p = SQLITE_MALLOC( nByte );
|
| + if( p==0 ){
|
| + testcase( sqlite3GlobalConfig.xLog!=0 );
|
| + sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes of memory", nByte);
|
| + }
|
| + return p;
|
| +#else
|
| + sqlite3_int64 *p;
|
| + assert( nByte>0 );
|
| + testcase( ROUND8(nByte)!=nByte );
|
| + p = SQLITE_MALLOC( nByte+8 );
|
| + if( p ){
|
| + p[0] = nByte;
|
| + p++;
|
| + }else{
|
| + testcase( sqlite3GlobalConfig.xLog!=0 );
|
| + sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes of memory", nByte);
|
| + }
|
| + return (void *)p;
|
| +#endif
|
| +}
|
| +
|
| +/*
|
| +** Like free() but works for allocations obtained from sqlite3MemMalloc()
|
| +** or sqlite3MemRealloc().
|
| +**
|
| +** For this low-level routine, we already know that pPrior!=0 since
|
| +** cases where pPrior==0 will have been intecepted and dealt with
|
| +** by higher-level routines.
|
| +*/
|
| +static void sqlite3MemFree(void *pPrior){
|
| +#ifdef SQLITE_MALLOCSIZE
|
| + SQLITE_FREE(pPrior);
|
| +#else
|
| + sqlite3_int64 *p = (sqlite3_int64*)pPrior;
|
| + assert( pPrior!=0 );
|
| + p--;
|
| + SQLITE_FREE(p);
|
| +#endif
|
| +}
|
| +
|
| +/*
|
| +** Report the allocated size of a prior return from xMalloc()
|
| +** or xRealloc().
|
| +*/
|
| +static int sqlite3MemSize(void *pPrior){
|
| +#ifdef SQLITE_MALLOCSIZE
|
| + assert( pPrior!=0 );
|
| + return (int)SQLITE_MALLOCSIZE(pPrior);
|
| +#else
|
| + sqlite3_int64 *p;
|
| + assert( pPrior!=0 );
|
| + p = (sqlite3_int64*)pPrior;
|
| + p--;
|
| + return (int)p[0];
|
| +#endif
|
| +}
|
| +
|
| +/*
|
| +** Like realloc(). Resize an allocation previously obtained from
|
| +** sqlite3MemMalloc().
|
| +**
|
| +** For this low-level interface, we know that pPrior!=0. Cases where
|
| +** pPrior==0 while have been intercepted by higher-level routine and
|
| +** redirected to xMalloc. Similarly, we know that nByte>0 because
|
| +** cases where nByte<=0 will have been intercepted by higher-level
|
| +** routines and redirected to xFree.
|
| +*/
|
| +static void *sqlite3MemRealloc(void *pPrior, int nByte){
|
| +#ifdef SQLITE_MALLOCSIZE
|
| + void *p = SQLITE_REALLOC(pPrior, nByte);
|
| + if( p==0 ){
|
| + testcase( sqlite3GlobalConfig.xLog!=0 );
|
| + sqlite3_log(SQLITE_NOMEM,
|
| + "failed memory resize %u to %u bytes",
|
| + SQLITE_MALLOCSIZE(pPrior), nByte);
|
| + }
|
| + return p;
|
| +#else
|
| + sqlite3_int64 *p = (sqlite3_int64*)pPrior;
|
| + assert( pPrior!=0 && nByte>0 );
|
| + assert( nByte==ROUND8(nByte) ); /* EV: R-46199-30249 */
|
| + p--;
|
| + p = SQLITE_REALLOC(p, nByte+8 );
|
| + if( p ){
|
| + p[0] = nByte;
|
| + p++;
|
| + }else{
|
| + testcase( sqlite3GlobalConfig.xLog!=0 );
|
| + sqlite3_log(SQLITE_NOMEM,
|
| + "failed memory resize %u to %u bytes",
|
| + sqlite3MemSize(pPrior), nByte);
|
| + }
|
| + return (void*)p;
|
| +#endif
|
| +}
|
| +
|
| +/*
|
| +** Round up a request size to the next valid allocation size.
|
| +*/
|
| +static int sqlite3MemRoundup(int n){
|
| + return ROUND8(n);
|
| +}
|
| +
|
| +/*
|
| +** Initialize this module.
|
| +*/
|
| +static int sqlite3MemInit(void *NotUsed){
|
| +#if defined(__APPLE__) && !defined(SQLITE_WITHOUT_ZONEMALLOC)
|
| + int cpuCount;
|
| + size_t len;
|
| + if( _sqliteZone_ ){
|
| + return SQLITE_OK;
|
| + }
|
| + len = sizeof(cpuCount);
|
| + /* One usually wants to use hw.acctivecpu for MT decisions, but not here */
|
| + sysctlbyname("hw.ncpu", &cpuCount, &len, NULL, 0);
|
| + if( cpuCount>1 ){
|
| + /* defer MT decisions to system malloc */
|
| + _sqliteZone_ = malloc_default_zone();
|
| + }else{
|
| + /* only 1 core, use our own zone to contention over global locks,
|
| + ** e.g. we have our own dedicated locks */
|
| + bool success;
|
| + malloc_zone_t* newzone = malloc_create_zone(4096, 0);
|
| + malloc_set_zone_name(newzone, "Sqlite_Heap");
|
| + do{
|
| + success = OSAtomicCompareAndSwapPtrBarrier(NULL, newzone,
|
| + (void * volatile *)&_sqliteZone_);
|
| + }while(!_sqliteZone_);
|
| + if( !success ){
|
| + /* somebody registered a zone first */
|
| + malloc_destroy_zone(newzone);
|
| + }
|
| + }
|
| +#endif
|
| + UNUSED_PARAMETER(NotUsed);
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Deinitialize this module.
|
| +*/
|
| +static void sqlite3MemShutdown(void *NotUsed){
|
| + UNUSED_PARAMETER(NotUsed);
|
| + return;
|
| +}
|
| +
|
| +/*
|
| +** This routine is the only routine in this file with external linkage.
|
| +**
|
| +** Populate the low-level memory allocation function pointers in
|
| +** sqlite3GlobalConfig.m with pointers to the routines in this file.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3MemSetDefault(void){
|
| + static const sqlite3_mem_methods defaultMethods = {
|
| + sqlite3MemMalloc,
|
| + sqlite3MemFree,
|
| + sqlite3MemRealloc,
|
| + sqlite3MemSize,
|
| + sqlite3MemRoundup,
|
| + sqlite3MemInit,
|
| + sqlite3MemShutdown,
|
| + 0
|
| + };
|
| + sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods);
|
| +}
|
| +
|
| +#endif /* SQLITE_SYSTEM_MALLOC */
|
| +
|
| +/************** End of mem1.c ************************************************/
|
| +/************** Begin file mem2.c ********************************************/
|
| +/*
|
| +** 2007 August 15
|
| +**
|
| +** 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 low-level memory allocation drivers for when
|
| +** SQLite will use the standard C-library malloc/realloc/free interface
|
| +** to obtain the memory it needs while adding lots of additional debugging
|
| +** information to each allocation in order to help detect and fix memory
|
| +** leaks and memory usage errors.
|
| +**
|
| +** This file contains implementations of the low-level memory allocation
|
| +** routines specified in the sqlite3_mem_methods object.
|
| +*/
|
| +/* #include "sqliteInt.h" */
|
| +
|
| +/*
|
| +** This version of the memory allocator is used only if the
|
| +** SQLITE_MEMDEBUG macro is defined
|
| +*/
|
| +#ifdef SQLITE_MEMDEBUG
|
| +
|
| +/*
|
| +** The backtrace functionality is only available with GLIBC
|
| +*/
|
| +#ifdef __GLIBC__
|
| + extern int backtrace(void**,int);
|
| + extern void backtrace_symbols_fd(void*const*,int,int);
|
| +#else
|
| +# define backtrace(A,B) 1
|
| +# define backtrace_symbols_fd(A,B,C)
|
| +#endif
|
| +/* #include <stdio.h> */
|
| +
|
| +/*
|
| +** Each memory allocation looks like this:
|
| +**
|
| +** ------------------------------------------------------------------------
|
| +** | Title | backtrace pointers | MemBlockHdr | allocation | EndGuard |
|
| +** ------------------------------------------------------------------------
|
| +**
|
| +** The application code sees only a pointer to the allocation. We have
|
| +** to back up from the allocation pointer to find the MemBlockHdr. The
|
| +** MemBlockHdr tells us the size of the allocation and the number of
|
| +** backtrace pointers. There is also a guard word at the end of the
|
| +** MemBlockHdr.
|
| +*/
|
| +struct MemBlockHdr {
|
| + i64 iSize; /* Size of this allocation */
|
| + struct MemBlockHdr *pNext, *pPrev; /* Linked list of all unfreed memory */
|
| + char nBacktrace; /* Number of backtraces on this alloc */
|
| + char nBacktraceSlots; /* Available backtrace slots */
|
| + u8 nTitle; /* Bytes of title; includes '\0' */
|
| + u8 eType; /* Allocation type code */
|
| + int iForeGuard; /* Guard word for sanity */
|
| +};
|
| +
|
| +/*
|
| +** Guard words
|
| +*/
|
| +#define FOREGUARD 0x80F5E153
|
| +#define REARGUARD 0xE4676B53
|
| +
|
| +/*
|
| +** Number of malloc size increments to track.
|
| +*/
|
| +#define NCSIZE 1000
|
| +
|
| +/*
|
| +** All of the static variables used by this module are collected
|
| +** into a single structure named "mem". This is to keep the
|
| +** static variables organized and to reduce namespace pollution
|
| +** when this module is combined with other in the amalgamation.
|
| +*/
|
| +static struct {
|
| +
|
| + /*
|
| + ** Mutex to control access to the memory allocation subsystem.
|
| + */
|
| + sqlite3_mutex *mutex;
|
| +
|
| + /*
|
| + ** Head and tail of a linked list of all outstanding allocations
|
| + */
|
| + struct MemBlockHdr *pFirst;
|
| + struct MemBlockHdr *pLast;
|
| +
|
| + /*
|
| + ** The number of levels of backtrace to save in new allocations.
|
| + */
|
| + int nBacktrace;
|
| + void (*xBacktrace)(int, int, void **);
|
| +
|
| + /*
|
| + ** Title text to insert in front of each block
|
| + */
|
| + int nTitle; /* Bytes of zTitle to save. Includes '\0' and padding */
|
| + char zTitle[100]; /* The title text */
|
| +
|
| + /*
|
| + ** sqlite3MallocDisallow() increments the following counter.
|
| + ** sqlite3MallocAllow() decrements it.
|
| + */
|
| + int disallow; /* Do not allow memory allocation */
|
| +
|
| + /*
|
| + ** Gather statistics on the sizes of memory allocations.
|
| + ** nAlloc[i] is the number of allocation attempts of i*8
|
| + ** bytes. i==NCSIZE is the number of allocation attempts for
|
| + ** sizes more than NCSIZE*8 bytes.
|
| + */
|
| + int nAlloc[NCSIZE]; /* Total number of allocations */
|
| + int nCurrent[NCSIZE]; /* Current number of allocations */
|
| + int mxCurrent[NCSIZE]; /* Highwater mark for nCurrent */
|
| +
|
| +} mem;
|
| +
|
| +
|
| +/*
|
| +** Adjust memory usage statistics
|
| +*/
|
| +static void adjustStats(int iSize, int increment){
|
| + int i = ROUND8(iSize)/8;
|
| + if( i>NCSIZE-1 ){
|
| + i = NCSIZE - 1;
|
| + }
|
| + if( increment>0 ){
|
| + mem.nAlloc[i]++;
|
| + mem.nCurrent[i]++;
|
| + if( mem.nCurrent[i]>mem.mxCurrent[i] ){
|
| + mem.mxCurrent[i] = mem.nCurrent[i];
|
| + }
|
| + }else{
|
| + mem.nCurrent[i]--;
|
| + assert( mem.nCurrent[i]>=0 );
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Given an allocation, find the MemBlockHdr for that allocation.
|
| +**
|
| +** This routine checks the guards at either end of the allocation and
|
| +** if they are incorrect it asserts.
|
| +*/
|
| +static struct MemBlockHdr *sqlite3MemsysGetHeader(void *pAllocation){
|
| + struct MemBlockHdr *p;
|
| + int *pInt;
|
| + u8 *pU8;
|
| + int nReserve;
|
| +
|
| + p = (struct MemBlockHdr*)pAllocation;
|
| + p--;
|
| + assert( p->iForeGuard==(int)FOREGUARD );
|
| + nReserve = ROUND8(p->iSize);
|
| + pInt = (int*)pAllocation;
|
| + pU8 = (u8*)pAllocation;
|
| + assert( pInt[nReserve/sizeof(int)]==(int)REARGUARD );
|
| + /* This checks any of the "extra" bytes allocated due
|
| + ** to rounding up to an 8 byte boundary to ensure
|
| + ** they haven't been overwritten.
|
| + */
|
| + while( nReserve-- > p->iSize ) assert( pU8[nReserve]==0x65 );
|
| + return p;
|
| +}
|
| +
|
| +/*
|
| +** Return the number of bytes currently allocated at address p.
|
| +*/
|
| +static int sqlite3MemSize(void *p){
|
| + struct MemBlockHdr *pHdr;
|
| + if( !p ){
|
| + return 0;
|
| + }
|
| + pHdr = sqlite3MemsysGetHeader(p);
|
| + return (int)pHdr->iSize;
|
| +}
|
| +
|
| +/*
|
| +** Initialize the memory allocation subsystem.
|
| +*/
|
| +static int sqlite3MemInit(void *NotUsed){
|
| + UNUSED_PARAMETER(NotUsed);
|
| + assert( (sizeof(struct MemBlockHdr)&7) == 0 );
|
| + if( !sqlite3GlobalConfig.bMemstat ){
|
| + /* If memory status is enabled, then the malloc.c wrapper will already
|
| + ** hold the STATIC_MEM mutex when the routines here are invoked. */
|
| + mem.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
|
| + }
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Deinitialize the memory allocation subsystem.
|
| +*/
|
| +static void sqlite3MemShutdown(void *NotUsed){
|
| + UNUSED_PARAMETER(NotUsed);
|
| + mem.mutex = 0;
|
| +}
|
| +
|
| +/*
|
| +** Round up a request size to the next valid allocation size.
|
| +*/
|
| +static int sqlite3MemRoundup(int n){
|
| + return ROUND8(n);
|
| +}
|
| +
|
| +/*
|
| +** Fill a buffer with pseudo-random bytes. This is used to preset
|
| +** the content of a new memory allocation to unpredictable values and
|
| +** to clear the content of a freed allocation to unpredictable values.
|
| +*/
|
| +static void randomFill(char *pBuf, int nByte){
|
| + unsigned int x, y, r;
|
| + x = SQLITE_PTR_TO_INT(pBuf);
|
| + y = nByte | 1;
|
| + while( nByte >= 4 ){
|
| + x = (x>>1) ^ (-(int)(x&1) & 0xd0000001);
|
| + y = y*1103515245 + 12345;
|
| + r = x ^ y;
|
| + *(int*)pBuf = r;
|
| + pBuf += 4;
|
| + nByte -= 4;
|
| + }
|
| + while( nByte-- > 0 ){
|
| + x = (x>>1) ^ (-(int)(x&1) & 0xd0000001);
|
| + y = y*1103515245 + 12345;
|
| + r = x ^ y;
|
| + *(pBuf++) = r & 0xff;
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Allocate nByte bytes of memory.
|
| +*/
|
| +static void *sqlite3MemMalloc(int nByte){
|
| + struct MemBlockHdr *pHdr;
|
| + void **pBt;
|
| + char *z;
|
| + int *pInt;
|
| + void *p = 0;
|
| + int totalSize;
|
| + int nReserve;
|
| + sqlite3_mutex_enter(mem.mutex);
|
| + assert( mem.disallow==0 );
|
| + nReserve = ROUND8(nByte);
|
| + totalSize = nReserve + sizeof(*pHdr) + sizeof(int) +
|
| + mem.nBacktrace*sizeof(void*) + mem.nTitle;
|
| + p = malloc(totalSize);
|
| + if( p ){
|
| + z = p;
|
| + pBt = (void**)&z[mem.nTitle];
|
| + pHdr = (struct MemBlockHdr*)&pBt[mem.nBacktrace];
|
| + pHdr->pNext = 0;
|
| + pHdr->pPrev = mem.pLast;
|
| + if( mem.pLast ){
|
| + mem.pLast->pNext = pHdr;
|
| + }else{
|
| + mem.pFirst = pHdr;
|
| + }
|
| + mem.pLast = pHdr;
|
| + pHdr->iForeGuard = FOREGUARD;
|
| + pHdr->eType = MEMTYPE_HEAP;
|
| + pHdr->nBacktraceSlots = mem.nBacktrace;
|
| + pHdr->nTitle = mem.nTitle;
|
| + if( mem.nBacktrace ){
|
| + void *aAddr[40];
|
| + pHdr->nBacktrace = backtrace(aAddr, mem.nBacktrace+1)-1;
|
| + memcpy(pBt, &aAddr[1], pHdr->nBacktrace*sizeof(void*));
|
| + assert(pBt[0]);
|
| + if( mem.xBacktrace ){
|
| + mem.xBacktrace(nByte, pHdr->nBacktrace-1, &aAddr[1]);
|
| + }
|
| + }else{
|
| + pHdr->nBacktrace = 0;
|
| + }
|
| + if( mem.nTitle ){
|
| + memcpy(z, mem.zTitle, mem.nTitle);
|
| + }
|
| + pHdr->iSize = nByte;
|
| + adjustStats(nByte, +1);
|
| + pInt = (int*)&pHdr[1];
|
| + pInt[nReserve/sizeof(int)] = REARGUARD;
|
| + randomFill((char*)pInt, nByte);
|
| + memset(((char*)pInt)+nByte, 0x65, nReserve-nByte);
|
| + p = (void*)pInt;
|
| + }
|
| + sqlite3_mutex_leave(mem.mutex);
|
| + return p;
|
| +}
|
| +
|
| +/*
|
| +** Free memory.
|
| +*/
|
| +static void sqlite3MemFree(void *pPrior){
|
| + struct MemBlockHdr *pHdr;
|
| + void **pBt;
|
| + char *z;
|
| + assert( sqlite3GlobalConfig.bMemstat || sqlite3GlobalConfig.bCoreMutex==0
|
| + || mem.mutex!=0 );
|
| + pHdr = sqlite3MemsysGetHeader(pPrior);
|
| + pBt = (void**)pHdr;
|
| + pBt -= pHdr->nBacktraceSlots;
|
| + sqlite3_mutex_enter(mem.mutex);
|
| + if( pHdr->pPrev ){
|
| + assert( pHdr->pPrev->pNext==pHdr );
|
| + pHdr->pPrev->pNext = pHdr->pNext;
|
| + }else{
|
| + assert( mem.pFirst==pHdr );
|
| + mem.pFirst = pHdr->pNext;
|
| + }
|
| + if( pHdr->pNext ){
|
| + assert( pHdr->pNext->pPrev==pHdr );
|
| + pHdr->pNext->pPrev = pHdr->pPrev;
|
| + }else{
|
| + assert( mem.pLast==pHdr );
|
| + mem.pLast = pHdr->pPrev;
|
| + }
|
| + z = (char*)pBt;
|
| + z -= pHdr->nTitle;
|
| + adjustStats((int)pHdr->iSize, -1);
|
| + randomFill(z, sizeof(void*)*pHdr->nBacktraceSlots + sizeof(*pHdr) +
|
| + (int)pHdr->iSize + sizeof(int) + pHdr->nTitle);
|
| + free(z);
|
| + sqlite3_mutex_leave(mem.mutex);
|
| +}
|
| +
|
| +/*
|
| +** Change the size of an existing memory allocation.
|
| +**
|
| +** For this debugging implementation, we *always* make a copy of the
|
| +** allocation into a new place in memory. In this way, if the
|
| +** higher level code is using pointer to the old allocation, it is
|
| +** much more likely to break and we are much more liking to find
|
| +** the error.
|
| +*/
|
| +static void *sqlite3MemRealloc(void *pPrior, int nByte){
|
| + struct MemBlockHdr *pOldHdr;
|
| + void *pNew;
|
| + assert( mem.disallow==0 );
|
| + assert( (nByte & 7)==0 ); /* EV: R-46199-30249 */
|
| + pOldHdr = sqlite3MemsysGetHeader(pPrior);
|
| + pNew = sqlite3MemMalloc(nByte);
|
| + if( pNew ){
|
| + memcpy(pNew, pPrior, (int)(nByte<pOldHdr->iSize ? nByte : pOldHdr->iSize));
|
| + if( nByte>pOldHdr->iSize ){
|
| + randomFill(&((char*)pNew)[pOldHdr->iSize], nByte - (int)pOldHdr->iSize);
|
| + }
|
| + sqlite3MemFree(pPrior);
|
| + }
|
| + return pNew;
|
| +}
|
| +
|
| +/*
|
| +** Populate the low-level memory allocation function pointers in
|
| +** sqlite3GlobalConfig.m with pointers to the routines in this file.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3MemSetDefault(void){
|
| + static const sqlite3_mem_methods defaultMethods = {
|
| + sqlite3MemMalloc,
|
| + sqlite3MemFree,
|
| + sqlite3MemRealloc,
|
| + sqlite3MemSize,
|
| + sqlite3MemRoundup,
|
| + sqlite3MemInit,
|
| + sqlite3MemShutdown,
|
| + 0
|
| + };
|
| + sqlite3_config(SQLITE_CONFIG_MALLOC, &defaultMethods);
|
| +}
|
| +
|
| +/*
|
| +** Set the "type" of an allocation.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3MemdebugSetType(void *p, u8 eType){
|
| + if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){
|
| + struct MemBlockHdr *pHdr;
|
| + pHdr = sqlite3MemsysGetHeader(p);
|
| + assert( pHdr->iForeGuard==FOREGUARD );
|
| + pHdr->eType = eType;
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Return TRUE if the mask of type in eType matches the type of the
|
| +** allocation p. Also return true if p==NULL.
|
| +**
|
| +** This routine is designed for use within an assert() statement, to
|
| +** verify the type of an allocation. For example:
|
| +**
|
| +** assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3MemdebugHasType(void *p, u8 eType){
|
| + int rc = 1;
|
| + if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){
|
| + struct MemBlockHdr *pHdr;
|
| + pHdr = sqlite3MemsysGetHeader(p);
|
| + assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */
|
| + if( (pHdr->eType&eType)==0 ){
|
| + rc = 0;
|
| + }
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Return TRUE if the mask of type in eType matches no bits of the type of the
|
| +** allocation p. Also return true if p==NULL.
|
| +**
|
| +** This routine is designed for use within an assert() statement, to
|
| +** verify the type of an allocation. For example:
|
| +**
|
| +** assert( sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) );
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3MemdebugNoType(void *p, u8 eType){
|
| + int rc = 1;
|
| + if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){
|
| + struct MemBlockHdr *pHdr;
|
| + pHdr = sqlite3MemsysGetHeader(p);
|
| + assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */
|
| + if( (pHdr->eType&eType)!=0 ){
|
| + rc = 0;
|
| + }
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Set the number of backtrace levels kept for each allocation.
|
| +** A value of zero turns off backtracing. The number is always rounded
|
| +** up to a multiple of 2.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3MemdebugBacktrace(int depth){
|
| + if( depth<0 ){ depth = 0; }
|
| + if( depth>20 ){ depth = 20; }
|
| + depth = (depth+1)&0xfe;
|
| + mem.nBacktrace = depth;
|
| +}
|
| +
|
| +SQLITE_PRIVATE void sqlite3MemdebugBacktraceCallback(void (*xBacktrace)(int, int, void **)){
|
| + mem.xBacktrace = xBacktrace;
|
| +}
|
| +
|
| +/*
|
| +** Set the title string for subsequent allocations.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3MemdebugSettitle(const char *zTitle){
|
| + unsigned int n = sqlite3Strlen30(zTitle) + 1;
|
| + sqlite3_mutex_enter(mem.mutex);
|
| + if( n>=sizeof(mem.zTitle) ) n = sizeof(mem.zTitle)-1;
|
| + memcpy(mem.zTitle, zTitle, n);
|
| + mem.zTitle[n] = 0;
|
| + mem.nTitle = ROUND8(n);
|
| + sqlite3_mutex_leave(mem.mutex);
|
| +}
|
| +
|
| +SQLITE_PRIVATE void sqlite3MemdebugSync(){
|
| + struct MemBlockHdr *pHdr;
|
| + for(pHdr=mem.pFirst; pHdr; pHdr=pHdr->pNext){
|
| + void **pBt = (void**)pHdr;
|
| + pBt -= pHdr->nBacktraceSlots;
|
| + mem.xBacktrace((int)pHdr->iSize, pHdr->nBacktrace-1, &pBt[1]);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Open the file indicated and write a log of all unfreed memory
|
| +** allocations into that log.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3MemdebugDump(const char *zFilename){
|
| + FILE *out;
|
| + struct MemBlockHdr *pHdr;
|
| + void **pBt;
|
| + int i;
|
| + out = fopen(zFilename, "w");
|
| + if( out==0 ){
|
| + fprintf(stderr, "** Unable to output memory debug output log: %s **\n",
|
| + zFilename);
|
| + return;
|
| + }
|
| + for(pHdr=mem.pFirst; pHdr; pHdr=pHdr->pNext){
|
| + char *z = (char*)pHdr;
|
| + z -= pHdr->nBacktraceSlots*sizeof(void*) + pHdr->nTitle;
|
| + fprintf(out, "**** %lld bytes at %p from %s ****\n",
|
| + pHdr->iSize, &pHdr[1], pHdr->nTitle ? z : "???");
|
| + if( pHdr->nBacktrace ){
|
| + fflush(out);
|
| + pBt = (void**)pHdr;
|
| + pBt -= pHdr->nBacktraceSlots;
|
| + backtrace_symbols_fd(pBt, pHdr->nBacktrace, fileno(out));
|
| + fprintf(out, "\n");
|
| + }
|
| + }
|
| + fprintf(out, "COUNTS:\n");
|
| + for(i=0; i<NCSIZE-1; i++){
|
| + if( mem.nAlloc[i] ){
|
| + fprintf(out, " %5d: %10d %10d %10d\n",
|
| + i*8, mem.nAlloc[i], mem.nCurrent[i], mem.mxCurrent[i]);
|
| + }
|
| + }
|
| + if( mem.nAlloc[NCSIZE-1] ){
|
| + fprintf(out, " %5d: %10d %10d %10d\n",
|
| + NCSIZE*8-8, mem.nAlloc[NCSIZE-1],
|
| + mem.nCurrent[NCSIZE-1], mem.mxCurrent[NCSIZE-1]);
|
| + }
|
| + fclose(out);
|
| +}
|
| +
|
| +/*
|
| +** Return the number of times sqlite3MemMalloc() has been called.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3MemdebugMallocCount(){
|
| + int i;
|
| + int nTotal = 0;
|
| + for(i=0; i<NCSIZE; i++){
|
| + nTotal += mem.nAlloc[i];
|
| + }
|
| + return nTotal;
|
| +}
|
| +
|
| +
|
| +#endif /* SQLITE_MEMDEBUG */
|
| +
|
| +/************** End of mem2.c ************************************************/
|
| +/************** Begin file mem3.c ********************************************/
|
| +/*
|
| +** 2007 October 14
|
| +**
|
| +** 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 C functions that implement a memory
|
| +** allocation subsystem for use by SQLite.
|
| +**
|
| +** This version of the memory allocation subsystem omits all
|
| +** use of malloc(). The SQLite user supplies a block of memory
|
| +** before calling sqlite3_initialize() from which allocations
|
| +** are made and returned by the xMalloc() and xRealloc()
|
| +** implementations. Once sqlite3_initialize() has been called,
|
| +** the amount of memory available to SQLite is fixed and cannot
|
| +** be changed.
|
| +**
|
| +** This version of the memory allocation subsystem is included
|
| +** in the build only if SQLITE_ENABLE_MEMSYS3 is defined.
|
| +*/
|
| +/* #include "sqliteInt.h" */
|
| +
|
| +/*
|
| +** This version of the memory allocator is only built into the library
|
| +** SQLITE_ENABLE_MEMSYS3 is defined. Defining this symbol does not
|
| +** mean that the library will use a memory-pool by default, just that
|
| +** it is available. The mempool allocator is activated by calling
|
| +** sqlite3_config().
|
| +*/
|
| +#ifdef SQLITE_ENABLE_MEMSYS3
|
| +
|
| +/*
|
| +** Maximum size (in Mem3Blocks) of a "small" chunk.
|
| +*/
|
| +#define MX_SMALL 10
|
| +
|
| +
|
| +/*
|
| +** Number of freelist hash slots
|
| +*/
|
| +#define N_HASH 61
|
| +
|
| +/*
|
| +** A memory allocation (also called a "chunk") consists of two or
|
| +** more blocks where each block is 8 bytes. The first 8 bytes are
|
| +** a header that is not returned to the user.
|
| +**
|
| +** A chunk is two or more blocks that is either checked out or
|
| +** free. The first block has format u.hdr. u.hdr.size4x is 4 times the
|
| +** size of the allocation in blocks if the allocation is free.
|
| +** The u.hdr.size4x&1 bit is true if the chunk is checked out and
|
| +** false if the chunk is on the freelist. The u.hdr.size4x&2 bit
|
| +** is true if the previous chunk is checked out and false if the
|
| +** previous chunk is free. The u.hdr.prevSize field is the size of
|
| +** the previous chunk in blocks if the previous chunk is on the
|
| +** freelist. If the previous chunk is checked out, then
|
| +** u.hdr.prevSize can be part of the data for that chunk and should
|
| +** not be read or written.
|
| +**
|
| +** We often identify a chunk by its index in mem3.aPool[]. When
|
| +** this is done, the chunk index refers to the second block of
|
| +** the chunk. In this way, the first chunk has an index of 1.
|
| +** A chunk index of 0 means "no such chunk" and is the equivalent
|
| +** of a NULL pointer.
|
| +**
|
| +** The second block of free chunks is of the form u.list. The
|
| +** two fields form a double-linked list of chunks of related sizes.
|
| +** Pointers to the head of the list are stored in mem3.aiSmall[]
|
| +** for smaller chunks and mem3.aiHash[] for larger chunks.
|
| +**
|
| +** The second block of a chunk is user data if the chunk is checked
|
| +** out. If a chunk is checked out, the user data may extend into
|
| +** the u.hdr.prevSize value of the following chunk.
|
| +*/
|
| +typedef struct Mem3Block Mem3Block;
|
| +struct Mem3Block {
|
| + union {
|
| + struct {
|
| + u32 prevSize; /* Size of previous chunk in Mem3Block elements */
|
| + u32 size4x; /* 4x the size of current chunk in Mem3Block elements */
|
| + } hdr;
|
| + struct {
|
| + u32 next; /* Index in mem3.aPool[] of next free chunk */
|
| + u32 prev; /* Index in mem3.aPool[] of previous free chunk */
|
| + } list;
|
| + } u;
|
| +};
|
| +
|
| +/*
|
| +** All of the static variables used by this module are collected
|
| +** into a single structure named "mem3". This is to keep the
|
| +** static variables organized and to reduce namespace pollution
|
| +** when this module is combined with other in the amalgamation.
|
| +*/
|
| +static SQLITE_WSD struct Mem3Global {
|
| + /*
|
| + ** Memory available for allocation. nPool is the size of the array
|
| + ** (in Mem3Blocks) pointed to by aPool less 2.
|
| + */
|
| + u32 nPool;
|
| + Mem3Block *aPool;
|
| +
|
| + /*
|
| + ** True if we are evaluating an out-of-memory callback.
|
| + */
|
| + int alarmBusy;
|
| +
|
| + /*
|
| + ** Mutex to control access to the memory allocation subsystem.
|
| + */
|
| + sqlite3_mutex *mutex;
|
| +
|
| + /*
|
| + ** The minimum amount of free space that we have seen.
|
| + */
|
| + u32 mnMaster;
|
| +
|
| + /*
|
| + ** iMaster is the index of the master chunk. Most new allocations
|
| + ** occur off of this chunk. szMaster is the size (in Mem3Blocks)
|
| + ** of the current master. iMaster is 0 if there is not master chunk.
|
| + ** The master chunk is not in either the aiHash[] or aiSmall[].
|
| + */
|
| + u32 iMaster;
|
| + u32 szMaster;
|
| +
|
| + /*
|
| + ** Array of lists of free blocks according to the block size
|
| + ** for smaller chunks, or a hash on the block size for larger
|
| + ** chunks.
|
| + */
|
| + u32 aiSmall[MX_SMALL-1]; /* For sizes 2 through MX_SMALL, inclusive */
|
| + u32 aiHash[N_HASH]; /* For sizes MX_SMALL+1 and larger */
|
| +} mem3 = { 97535575 };
|
| +
|
| +#define mem3 GLOBAL(struct Mem3Global, mem3)
|
| +
|
| +/*
|
| +** Unlink the chunk at mem3.aPool[i] from list it is currently
|
| +** on. *pRoot is the list that i is a member of.
|
| +*/
|
| +static void memsys3UnlinkFromList(u32 i, u32 *pRoot){
|
| + u32 next = mem3.aPool[i].u.list.next;
|
| + u32 prev = mem3.aPool[i].u.list.prev;
|
| + assert( sqlite3_mutex_held(mem3.mutex) );
|
| + if( prev==0 ){
|
| + *pRoot = next;
|
| + }else{
|
| + mem3.aPool[prev].u.list.next = next;
|
| + }
|
| + if( next ){
|
| + mem3.aPool[next].u.list.prev = prev;
|
| + }
|
| + mem3.aPool[i].u.list.next = 0;
|
| + mem3.aPool[i].u.list.prev = 0;
|
| +}
|
| +
|
| +/*
|
| +** Unlink the chunk at index i from
|
| +** whatever list is currently a member of.
|
| +*/
|
| +static void memsys3Unlink(u32 i){
|
| + u32 size, hash;
|
| + assert( sqlite3_mutex_held(mem3.mutex) );
|
| + assert( (mem3.aPool[i-1].u.hdr.size4x & 1)==0 );
|
| + assert( i>=1 );
|
| + size = mem3.aPool[i-1].u.hdr.size4x/4;
|
| + assert( size==mem3.aPool[i+size-1].u.hdr.prevSize );
|
| + assert( size>=2 );
|
| + if( size <= MX_SMALL ){
|
| + memsys3UnlinkFromList(i, &mem3.aiSmall[size-2]);
|
| + }else{
|
| + hash = size % N_HASH;
|
| + memsys3UnlinkFromList(i, &mem3.aiHash[hash]);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Link the chunk at mem3.aPool[i] so that is on the list rooted
|
| +** at *pRoot.
|
| +*/
|
| +static void memsys3LinkIntoList(u32 i, u32 *pRoot){
|
| + assert( sqlite3_mutex_held(mem3.mutex) );
|
| + mem3.aPool[i].u.list.next = *pRoot;
|
| + mem3.aPool[i].u.list.prev = 0;
|
| + if( *pRoot ){
|
| + mem3.aPool[*pRoot].u.list.prev = i;
|
| + }
|
| + *pRoot = i;
|
| +}
|
| +
|
| +/*
|
| +** Link the chunk at index i into either the appropriate
|
| +** small chunk list, or into the large chunk hash table.
|
| +*/
|
| +static void memsys3Link(u32 i){
|
| + u32 size, hash;
|
| + assert( sqlite3_mutex_held(mem3.mutex) );
|
| + assert( i>=1 );
|
| + assert( (mem3.aPool[i-1].u.hdr.size4x & 1)==0 );
|
| + size = mem3.aPool[i-1].u.hdr.size4x/4;
|
| + assert( size==mem3.aPool[i+size-1].u.hdr.prevSize );
|
| + assert( size>=2 );
|
| + if( size <= MX_SMALL ){
|
| + memsys3LinkIntoList(i, &mem3.aiSmall[size-2]);
|
| + }else{
|
| + hash = size % N_HASH;
|
| + memsys3LinkIntoList(i, &mem3.aiHash[hash]);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** If the STATIC_MEM mutex is not already held, obtain it now. The mutex
|
| +** will already be held (obtained by code in malloc.c) if
|
| +** sqlite3GlobalConfig.bMemStat is true.
|
| +*/
|
| +static void memsys3Enter(void){
|
| + if( sqlite3GlobalConfig.bMemstat==0 && mem3.mutex==0 ){
|
| + mem3.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
|
| + }
|
| + sqlite3_mutex_enter(mem3.mutex);
|
| +}
|
| +static void memsys3Leave(void){
|
| + sqlite3_mutex_leave(mem3.mutex);
|
| +}
|
| +
|
| +/*
|
| +** Called when we are unable to satisfy an allocation of nBytes.
|
| +*/
|
| +static void memsys3OutOfMemory(int nByte){
|
| + if( !mem3.alarmBusy ){
|
| + mem3.alarmBusy = 1;
|
| + assert( sqlite3_mutex_held(mem3.mutex) );
|
| + sqlite3_mutex_leave(mem3.mutex);
|
| + sqlite3_release_memory(nByte);
|
| + sqlite3_mutex_enter(mem3.mutex);
|
| + mem3.alarmBusy = 0;
|
| + }
|
| +}
|
| +
|
| +
|
| +/*
|
| +** Chunk i is a free chunk that has been unlinked. Adjust its
|
| +** size parameters for check-out and return a pointer to the
|
| +** user portion of the chunk.
|
| +*/
|
| +static void *memsys3Checkout(u32 i, u32 nBlock){
|
| + u32 x;
|
| + assert( sqlite3_mutex_held(mem3.mutex) );
|
| + assert( i>=1 );
|
| + assert( mem3.aPool[i-1].u.hdr.size4x/4==nBlock );
|
| + assert( mem3.aPool[i+nBlock-1].u.hdr.prevSize==nBlock );
|
| + x = mem3.aPool[i-1].u.hdr.size4x;
|
| + mem3.aPool[i-1].u.hdr.size4x = nBlock*4 | 1 | (x&2);
|
| + mem3.aPool[i+nBlock-1].u.hdr.prevSize = nBlock;
|
| + mem3.aPool[i+nBlock-1].u.hdr.size4x |= 2;
|
| + return &mem3.aPool[i];
|
| +}
|
| +
|
| +/*
|
| +** Carve a piece off of the end of the mem3.iMaster free chunk.
|
| +** Return a pointer to the new allocation. Or, if the master chunk
|
| +** is not large enough, return 0.
|
| +*/
|
| +static void *memsys3FromMaster(u32 nBlock){
|
| + assert( sqlite3_mutex_held(mem3.mutex) );
|
| + assert( mem3.szMaster>=nBlock );
|
| + if( nBlock>=mem3.szMaster-1 ){
|
| + /* Use the entire master */
|
| + void *p = memsys3Checkout(mem3.iMaster, mem3.szMaster);
|
| + mem3.iMaster = 0;
|
| + mem3.szMaster = 0;
|
| + mem3.mnMaster = 0;
|
| + return p;
|
| + }else{
|
| + /* Split the master block. Return the tail. */
|
| + u32 newi, x;
|
| + newi = mem3.iMaster + mem3.szMaster - nBlock;
|
| + assert( newi > mem3.iMaster+1 );
|
| + mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.prevSize = nBlock;
|
| + mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.size4x |= 2;
|
| + mem3.aPool[newi-1].u.hdr.size4x = nBlock*4 + 1;
|
| + mem3.szMaster -= nBlock;
|
| + mem3.aPool[newi-1].u.hdr.prevSize = mem3.szMaster;
|
| + x = mem3.aPool[mem3.iMaster-1].u.hdr.size4x & 2;
|
| + mem3.aPool[mem3.iMaster-1].u.hdr.size4x = mem3.szMaster*4 | x;
|
| + if( mem3.szMaster < mem3.mnMaster ){
|
| + mem3.mnMaster = mem3.szMaster;
|
| + }
|
| + return (void*)&mem3.aPool[newi];
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** *pRoot is the head of a list of free chunks of the same size
|
| +** or same size hash. In other words, *pRoot is an entry in either
|
| +** mem3.aiSmall[] or mem3.aiHash[].
|
| +**
|
| +** This routine examines all entries on the given list and tries
|
| +** to coalesce each entries with adjacent free chunks.
|
| +**
|
| +** If it sees a chunk that is larger than mem3.iMaster, it replaces
|
| +** the current mem3.iMaster with the new larger chunk. In order for
|
| +** this mem3.iMaster replacement to work, the master chunk must be
|
| +** linked into the hash tables. That is not the normal state of
|
| +** affairs, of course. The calling routine must link the master
|
| +** chunk before invoking this routine, then must unlink the (possibly
|
| +** changed) master chunk once this routine has finished.
|
| +*/
|
| +static void memsys3Merge(u32 *pRoot){
|
| + u32 iNext, prev, size, i, x;
|
| +
|
| + assert( sqlite3_mutex_held(mem3.mutex) );
|
| + for(i=*pRoot; i>0; i=iNext){
|
| + iNext = mem3.aPool[i].u.list.next;
|
| + size = mem3.aPool[i-1].u.hdr.size4x;
|
| + assert( (size&1)==0 );
|
| + if( (size&2)==0 ){
|
| + memsys3UnlinkFromList(i, pRoot);
|
| + assert( i > mem3.aPool[i-1].u.hdr.prevSize );
|
| + prev = i - mem3.aPool[i-1].u.hdr.prevSize;
|
| + if( prev==iNext ){
|
| + iNext = mem3.aPool[prev].u.list.next;
|
| + }
|
| + memsys3Unlink(prev);
|
| + size = i + size/4 - prev;
|
| + x = mem3.aPool[prev-1].u.hdr.size4x & 2;
|
| + mem3.aPool[prev-1].u.hdr.size4x = size*4 | x;
|
| + mem3.aPool[prev+size-1].u.hdr.prevSize = size;
|
| + memsys3Link(prev);
|
| + i = prev;
|
| + }else{
|
| + size /= 4;
|
| + }
|
| + if( size>mem3.szMaster ){
|
| + mem3.iMaster = i;
|
| + mem3.szMaster = size;
|
| + }
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Return a block of memory of at least nBytes in size.
|
| +** Return NULL if unable.
|
| +**
|
| +** This function assumes that the necessary mutexes, if any, are
|
| +** already held by the caller. Hence "Unsafe".
|
| +*/
|
| +static void *memsys3MallocUnsafe(int nByte){
|
| + u32 i;
|
| + u32 nBlock;
|
| + u32 toFree;
|
| +
|
| + assert( sqlite3_mutex_held(mem3.mutex) );
|
| + assert( sizeof(Mem3Block)==8 );
|
| + if( nByte<=12 ){
|
| + nBlock = 2;
|
| + }else{
|
| + nBlock = (nByte + 11)/8;
|
| + }
|
| + assert( nBlock>=2 );
|
| +
|
| + /* STEP 1:
|
| + ** Look for an entry of the correct size in either the small
|
| + ** chunk table or in the large chunk hash table. This is
|
| + ** successful most of the time (about 9 times out of 10).
|
| + */
|
| + if( nBlock <= MX_SMALL ){
|
| + i = mem3.aiSmall[nBlock-2];
|
| + if( i>0 ){
|
| + memsys3UnlinkFromList(i, &mem3.aiSmall[nBlock-2]);
|
| + return memsys3Checkout(i, nBlock);
|
| + }
|
| + }else{
|
| + int hash = nBlock % N_HASH;
|
| + for(i=mem3.aiHash[hash]; i>0; i=mem3.aPool[i].u.list.next){
|
| + if( mem3.aPool[i-1].u.hdr.size4x/4==nBlock ){
|
| + memsys3UnlinkFromList(i, &mem3.aiHash[hash]);
|
| + return memsys3Checkout(i, nBlock);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /* STEP 2:
|
| + ** Try to satisfy the allocation by carving a piece off of the end
|
| + ** of the master chunk. This step usually works if step 1 fails.
|
| + */
|
| + if( mem3.szMaster>=nBlock ){
|
| + return memsys3FromMaster(nBlock);
|
| + }
|
| +
|
| +
|
| + /* STEP 3:
|
| + ** Loop through the entire memory pool. Coalesce adjacent free
|
| + ** chunks. Recompute the master chunk as the largest free chunk.
|
| + ** Then try again to satisfy the allocation by carving a piece off
|
| + ** of the end of the master chunk. This step happens very
|
| + ** rarely (we hope!)
|
| + */
|
| + for(toFree=nBlock*16; toFree<(mem3.nPool*16); toFree *= 2){
|
| + memsys3OutOfMemory(toFree);
|
| + if( mem3.iMaster ){
|
| + memsys3Link(mem3.iMaster);
|
| + mem3.iMaster = 0;
|
| + mem3.szMaster = 0;
|
| + }
|
| + for(i=0; i<N_HASH; i++){
|
| + memsys3Merge(&mem3.aiHash[i]);
|
| + }
|
| + for(i=0; i<MX_SMALL-1; i++){
|
| + memsys3Merge(&mem3.aiSmall[i]);
|
| + }
|
| + if( mem3.szMaster ){
|
| + memsys3Unlink(mem3.iMaster);
|
| + if( mem3.szMaster>=nBlock ){
|
| + return memsys3FromMaster(nBlock);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /* If none of the above worked, then we fail. */
|
| + return 0;
|
| +}
|
| +
|
| +/*
|
| +** Free an outstanding memory allocation.
|
| +**
|
| +** This function assumes that the necessary mutexes, if any, are
|
| +** already held by the caller. Hence "Unsafe".
|
| +*/
|
| +static void memsys3FreeUnsafe(void *pOld){
|
| + Mem3Block *p = (Mem3Block*)pOld;
|
| + int i;
|
| + u32 size, x;
|
| + assert( sqlite3_mutex_held(mem3.mutex) );
|
| + assert( p>mem3.aPool && p<&mem3.aPool[mem3.nPool] );
|
| + i = p - mem3.aPool;
|
| + assert( (mem3.aPool[i-1].u.hdr.size4x&1)==1 );
|
| + size = mem3.aPool[i-1].u.hdr.size4x/4;
|
| + assert( i+size<=mem3.nPool+1 );
|
| + mem3.aPool[i-1].u.hdr.size4x &= ~1;
|
| + mem3.aPool[i+size-1].u.hdr.prevSize = size;
|
| + mem3.aPool[i+size-1].u.hdr.size4x &= ~2;
|
| + memsys3Link(i);
|
| +
|
| + /* Try to expand the master using the newly freed chunk */
|
| + if( mem3.iMaster ){
|
| + while( (mem3.aPool[mem3.iMaster-1].u.hdr.size4x&2)==0 ){
|
| + size = mem3.aPool[mem3.iMaster-1].u.hdr.prevSize;
|
| + mem3.iMaster -= size;
|
| + mem3.szMaster += size;
|
| + memsys3Unlink(mem3.iMaster);
|
| + x = mem3.aPool[mem3.iMaster-1].u.hdr.size4x & 2;
|
| + mem3.aPool[mem3.iMaster-1].u.hdr.size4x = mem3.szMaster*4 | x;
|
| + mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.prevSize = mem3.szMaster;
|
| + }
|
| + x = mem3.aPool[mem3.iMaster-1].u.hdr.size4x & 2;
|
| + while( (mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.size4x&1)==0 ){
|
| + memsys3Unlink(mem3.iMaster+mem3.szMaster);
|
| + mem3.szMaster += mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.size4x/4;
|
| + mem3.aPool[mem3.iMaster-1].u.hdr.size4x = mem3.szMaster*4 | x;
|
| + mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.prevSize = mem3.szMaster;
|
| + }
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Return the size of an outstanding allocation, in bytes. The
|
| +** size returned omits the 8-byte header overhead. This only
|
| +** works for chunks that are currently checked out.
|
| +*/
|
| +static int memsys3Size(void *p){
|
| + Mem3Block *pBlock;
|
| + assert( p!=0 );
|
| + pBlock = (Mem3Block*)p;
|
| + assert( (pBlock[-1].u.hdr.size4x&1)!=0 );
|
| + return (pBlock[-1].u.hdr.size4x&~3)*2 - 4;
|
| +}
|
| +
|
| +/*
|
| +** Round up a request size to the next valid allocation size.
|
| +*/
|
| +static int memsys3Roundup(int n){
|
| + if( n<=12 ){
|
| + return 12;
|
| + }else{
|
| + return ((n+11)&~7) - 4;
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Allocate nBytes of memory.
|
| +*/
|
| +static void *memsys3Malloc(int nBytes){
|
| + sqlite3_int64 *p;
|
| + assert( nBytes>0 ); /* malloc.c filters out 0 byte requests */
|
| + memsys3Enter();
|
| + p = memsys3MallocUnsafe(nBytes);
|
| + memsys3Leave();
|
| + return (void*)p;
|
| +}
|
| +
|
| +/*
|
| +** Free memory.
|
| +*/
|
| +static void memsys3Free(void *pPrior){
|
| + assert( pPrior );
|
| + memsys3Enter();
|
| + memsys3FreeUnsafe(pPrior);
|
| + memsys3Leave();
|
| +}
|
| +
|
| +/*
|
| +** Change the size of an existing memory allocation
|
| +*/
|
| +static void *memsys3Realloc(void *pPrior, int nBytes){
|
| + int nOld;
|
| + void *p;
|
| + if( pPrior==0 ){
|
| + return sqlite3_malloc(nBytes);
|
| + }
|
| + if( nBytes<=0 ){
|
| + sqlite3_free(pPrior);
|
| + return 0;
|
| + }
|
| + nOld = memsys3Size(pPrior);
|
| + if( nBytes<=nOld && nBytes>=nOld-128 ){
|
| + return pPrior;
|
| + }
|
| + memsys3Enter();
|
| + p = memsys3MallocUnsafe(nBytes);
|
| + if( p ){
|
| + if( nOld<nBytes ){
|
| + memcpy(p, pPrior, nOld);
|
| + }else{
|
| + memcpy(p, pPrior, nBytes);
|
| + }
|
| + memsys3FreeUnsafe(pPrior);
|
| + }
|
| + memsys3Leave();
|
| + return p;
|
| +}
|
| +
|
| +/*
|
| +** Initialize this module.
|
| +*/
|
| +static int memsys3Init(void *NotUsed){
|
| + UNUSED_PARAMETER(NotUsed);
|
| + if( !sqlite3GlobalConfig.pHeap ){
|
| + return SQLITE_ERROR;
|
| + }
|
| +
|
| + /* Store a pointer to the memory block in global structure mem3. */
|
| + assert( sizeof(Mem3Block)==8 );
|
| + mem3.aPool = (Mem3Block *)sqlite3GlobalConfig.pHeap;
|
| + mem3.nPool = (sqlite3GlobalConfig.nHeap / sizeof(Mem3Block)) - 2;
|
| +
|
| + /* Initialize the master block. */
|
| + mem3.szMaster = mem3.nPool;
|
| + mem3.mnMaster = mem3.szMaster;
|
| + mem3.iMaster = 1;
|
| + mem3.aPool[0].u.hdr.size4x = (mem3.szMaster<<2) + 2;
|
| + mem3.aPool[mem3.nPool].u.hdr.prevSize = mem3.nPool;
|
| + mem3.aPool[mem3.nPool].u.hdr.size4x = 1;
|
| +
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Deinitialize this module.
|
| +*/
|
| +static void memsys3Shutdown(void *NotUsed){
|
| + UNUSED_PARAMETER(NotUsed);
|
| + mem3.mutex = 0;
|
| + return;
|
| +}
|
| +
|
| +
|
| +
|
| +/*
|
| +** Open the file indicated and write a log of all unfreed memory
|
| +** allocations into that log.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3Memsys3Dump(const char *zFilename){
|
| +#ifdef SQLITE_DEBUG
|
| + FILE *out;
|
| + u32 i, j;
|
| + u32 size;
|
| + if( zFilename==0 || zFilename[0]==0 ){
|
| + out = stdout;
|
| + }else{
|
| + out = fopen(zFilename, "w");
|
| + if( out==0 ){
|
| + fprintf(stderr, "** Unable to output memory debug output log: %s **\n",
|
| + zFilename);
|
| + return;
|
| + }
|
| + }
|
| + memsys3Enter();
|
| + fprintf(out, "CHUNKS:\n");
|
| + for(i=1; i<=mem3.nPool; i+=size/4){
|
| + size = mem3.aPool[i-1].u.hdr.size4x;
|
| + if( size/4<=1 ){
|
| + fprintf(out, "%p size error\n", &mem3.aPool[i]);
|
| + assert( 0 );
|
| + break;
|
| + }
|
| + if( (size&1)==0 && mem3.aPool[i+size/4-1].u.hdr.prevSize!=size/4 ){
|
| + fprintf(out, "%p tail size does not match\n", &mem3.aPool[i]);
|
| + assert( 0 );
|
| + break;
|
| + }
|
| + if( ((mem3.aPool[i+size/4-1].u.hdr.size4x&2)>>1)!=(size&1) ){
|
| + fprintf(out, "%p tail checkout bit is incorrect\n", &mem3.aPool[i]);
|
| + assert( 0 );
|
| + break;
|
| + }
|
| + if( size&1 ){
|
| + fprintf(out, "%p %6d bytes checked out\n", &mem3.aPool[i], (size/4)*8-8);
|
| + }else{
|
| + fprintf(out, "%p %6d bytes free%s\n", &mem3.aPool[i], (size/4)*8-8,
|
| + i==mem3.iMaster ? " **master**" : "");
|
| + }
|
| + }
|
| + for(i=0; i<MX_SMALL-1; i++){
|
| + if( mem3.aiSmall[i]==0 ) continue;
|
| + fprintf(out, "small(%2d):", i);
|
| + for(j = mem3.aiSmall[i]; j>0; j=mem3.aPool[j].u.list.next){
|
| + fprintf(out, " %p(%d)", &mem3.aPool[j],
|
| + (mem3.aPool[j-1].u.hdr.size4x/4)*8-8);
|
| + }
|
| + fprintf(out, "\n");
|
| + }
|
| + for(i=0; i<N_HASH; i++){
|
| + if( mem3.aiHash[i]==0 ) continue;
|
| + fprintf(out, "hash(%2d):", i);
|
| + for(j = mem3.aiHash[i]; j>0; j=mem3.aPool[j].u.list.next){
|
| + fprintf(out, " %p(%d)", &mem3.aPool[j],
|
| + (mem3.aPool[j-1].u.hdr.size4x/4)*8-8);
|
| + }
|
| + fprintf(out, "\n");
|
| + }
|
| + fprintf(out, "master=%d\n", mem3.iMaster);
|
| + fprintf(out, "nowUsed=%d\n", mem3.nPool*8 - mem3.szMaster*8);
|
| + fprintf(out, "mxUsed=%d\n", mem3.nPool*8 - mem3.mnMaster*8);
|
| + sqlite3_mutex_leave(mem3.mutex);
|
| + if( out==stdout ){
|
| + fflush(stdout);
|
| + }else{
|
| + fclose(out);
|
| + }
|
| +#else
|
| + UNUSED_PARAMETER(zFilename);
|
| +#endif
|
| +}
|
| +
|
| +/*
|
| +** This routine is the only routine in this file with external
|
| +** linkage.
|
| +**
|
| +** Populate the low-level memory allocation function pointers in
|
| +** sqlite3GlobalConfig.m with pointers to the routines in this file. The
|
| +** arguments specify the block of memory to manage.
|
| +**
|
| +** This routine is only called by sqlite3_config(), and therefore
|
| +** is not required to be threadsafe (it is not).
|
| +*/
|
| +SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys3(void){
|
| + static const sqlite3_mem_methods mempoolMethods = {
|
| + memsys3Malloc,
|
| + memsys3Free,
|
| + memsys3Realloc,
|
| + memsys3Size,
|
| + memsys3Roundup,
|
| + memsys3Init,
|
| + memsys3Shutdown,
|
| + 0
|
| + };
|
| + return &mempoolMethods;
|
| +}
|
| +
|
| +#endif /* SQLITE_ENABLE_MEMSYS3 */
|
| +
|
| +/************** End of mem3.c ************************************************/
|
| +/************** Begin file mem5.c ********************************************/
|
| +/*
|
| +** 2007 October 14
|
| +**
|
| +** 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 C functions that implement a memory
|
| +** allocation subsystem for use by SQLite.
|
| +**
|
| +** This version of the memory allocation subsystem omits all
|
| +** use of malloc(). The application gives SQLite a block of memory
|
| +** before calling sqlite3_initialize() from which allocations
|
| +** are made and returned by the xMalloc() and xRealloc()
|
| +** implementations. Once sqlite3_initialize() has been called,
|
| +** the amount of memory available to SQLite is fixed and cannot
|
| +** be changed.
|
| +**
|
| +** This version of the memory allocation subsystem is included
|
| +** in the build only if SQLITE_ENABLE_MEMSYS5 is defined.
|
| +**
|
| +** This memory allocator uses the following algorithm:
|
| +**
|
| +** 1. All memory allocation sizes are rounded up to a power of 2.
|
| +**
|
| +** 2. If two adjacent free blocks are the halves of a larger block,
|
| +** then the two blocks are coalesced into the single larger block.
|
| +**
|
| +** 3. New memory is allocated from the first available free block.
|
| +**
|
| +** This algorithm is described in: J. M. Robson. "Bounds for Some Functions
|
| +** Concerning Dynamic Storage Allocation". Journal of the Association for
|
| +** Computing Machinery, Volume 21, Number 8, July 1974, pages 491-499.
|
| +**
|
| +** Let n be the size of the largest allocation divided by the minimum
|
| +** allocation size (after rounding all sizes up to a power of 2.) Let M
|
| +** be the maximum amount of memory ever outstanding at one time. Let
|
| +** N be the total amount of memory available for allocation. Robson
|
| +** proved that this memory allocator will never breakdown due to
|
| +** fragmentation as long as the following constraint holds:
|
| +**
|
| +** N >= M*(1 + log2(n)/2) - n + 1
|
| +**
|
| +** The sqlite3_status() logic tracks the maximum values of n and M so
|
| +** that an application can, at any time, verify this constraint.
|
| +*/
|
| +/* #include "sqliteInt.h" */
|
| +
|
| +/*
|
| +** This version of the memory allocator is used only when
|
| +** SQLITE_ENABLE_MEMSYS5 is defined.
|
| +*/
|
| +#ifdef SQLITE_ENABLE_MEMSYS5
|
| +
|
| +/*
|
| +** A minimum allocation is an instance of the following structure.
|
| +** Larger allocations are an array of these structures where the
|
| +** size of the array is a power of 2.
|
| +**
|
| +** The size of this object must be a power of two. That fact is
|
| +** verified in memsys5Init().
|
| +*/
|
| +typedef struct Mem5Link Mem5Link;
|
| +struct Mem5Link {
|
| + int next; /* Index of next free chunk */
|
| + int prev; /* Index of previous free chunk */
|
| +};
|
| +
|
| +/*
|
| +** Maximum size of any allocation is ((1<<LOGMAX)*mem5.szAtom). Since
|
| +** mem5.szAtom is always at least 8 and 32-bit integers are used,
|
| +** it is not actually possible to reach this limit.
|
| +*/
|
| +#define LOGMAX 30
|
| +
|
| +/*
|
| +** Masks used for mem5.aCtrl[] elements.
|
| +*/
|
| +#define CTRL_LOGSIZE 0x1f /* Log2 Size of this block */
|
| +#define CTRL_FREE 0x20 /* True if not checked out */
|
| +
|
| +/*
|
| +** All of the static variables used by this module are collected
|
| +** into a single structure named "mem5". This is to keep the
|
| +** static variables organized and to reduce namespace pollution
|
| +** when this module is combined with other in the amalgamation.
|
| +*/
|
| +static SQLITE_WSD struct Mem5Global {
|
| + /*
|
| + ** Memory available for allocation
|
| + */
|
| + int szAtom; /* Smallest possible allocation in bytes */
|
| + int nBlock; /* Number of szAtom sized blocks in zPool */
|
| + u8 *zPool; /* Memory available to be allocated */
|
| +
|
| + /*
|
| + ** Mutex to control access to the memory allocation subsystem.
|
| + */
|
| + sqlite3_mutex *mutex;
|
| +
|
| +#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
| + /*
|
| + ** Performance statistics
|
| + */
|
| + u64 nAlloc; /* Total number of calls to malloc */
|
| + u64 totalAlloc; /* Total of all malloc calls - includes internal frag */
|
| + u64 totalExcess; /* Total internal fragmentation */
|
| + u32 currentOut; /* Current checkout, including internal fragmentation */
|
| + u32 currentCount; /* Current number of distinct checkouts */
|
| + u32 maxOut; /* Maximum instantaneous currentOut */
|
| + u32 maxCount; /* Maximum instantaneous currentCount */
|
| + u32 maxRequest; /* Largest allocation (exclusive of internal frag) */
|
| +#endif
|
| +
|
| + /*
|
| + ** Lists of free blocks. aiFreelist[0] is a list of free blocks of
|
| + ** size mem5.szAtom. aiFreelist[1] holds blocks of size szAtom*2.
|
| + ** aiFreelist[2] holds free blocks of size szAtom*4. And so forth.
|
| + */
|
| + int aiFreelist[LOGMAX+1];
|
| +
|
| + /*
|
| + ** Space for tracking which blocks are checked out and the size
|
| + ** of each block. One byte per block.
|
| + */
|
| + u8 *aCtrl;
|
| +
|
| +} mem5;
|
| +
|
| +/*
|
| +** Access the static variable through a macro for SQLITE_OMIT_WSD.
|
| +*/
|
| +#define mem5 GLOBAL(struct Mem5Global, mem5)
|
| +
|
| +/*
|
| +** Assuming mem5.zPool is divided up into an array of Mem5Link
|
| +** structures, return a pointer to the idx-th such link.
|
| +*/
|
| +#define MEM5LINK(idx) ((Mem5Link *)(&mem5.zPool[(idx)*mem5.szAtom]))
|
| +
|
| +/*
|
| +** Unlink the chunk at mem5.aPool[i] from list it is currently
|
| +** on. It should be found on mem5.aiFreelist[iLogsize].
|
| +*/
|
| +static void memsys5Unlink(int i, int iLogsize){
|
| + int next, prev;
|
| + assert( i>=0 && i<mem5.nBlock );
|
| + assert( iLogsize>=0 && iLogsize<=LOGMAX );
|
| + assert( (mem5.aCtrl[i] & CTRL_LOGSIZE)==iLogsize );
|
| +
|
| + next = MEM5LINK(i)->next;
|
| + prev = MEM5LINK(i)->prev;
|
| + if( prev<0 ){
|
| + mem5.aiFreelist[iLogsize] = next;
|
| + }else{
|
| + MEM5LINK(prev)->next = next;
|
| + }
|
| + if( next>=0 ){
|
| + MEM5LINK(next)->prev = prev;
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Link the chunk at mem5.aPool[i] so that is on the iLogsize
|
| +** free list.
|
| +*/
|
| +static void memsys5Link(int i, int iLogsize){
|
| + int x;
|
| + assert( sqlite3_mutex_held(mem5.mutex) );
|
| + assert( i>=0 && i<mem5.nBlock );
|
| + assert( iLogsize>=0 && iLogsize<=LOGMAX );
|
| + assert( (mem5.aCtrl[i] & CTRL_LOGSIZE)==iLogsize );
|
| +
|
| + x = MEM5LINK(i)->next = mem5.aiFreelist[iLogsize];
|
| + MEM5LINK(i)->prev = -1;
|
| + if( x>=0 ){
|
| + assert( x<mem5.nBlock );
|
| + MEM5LINK(x)->prev = i;
|
| + }
|
| + mem5.aiFreelist[iLogsize] = i;
|
| +}
|
| +
|
| +/*
|
| +** Obtain or release the mutex needed to access global data structures.
|
| +*/
|
| +static void memsys5Enter(void){
|
| + sqlite3_mutex_enter(mem5.mutex);
|
| +}
|
| +static void memsys5Leave(void){
|
| + sqlite3_mutex_leave(mem5.mutex);
|
| +}
|
| +
|
| +/*
|
| +** Return the size of an outstanding allocation, in bytes.
|
| +** This only works for chunks that are currently checked out.
|
| +*/
|
| +static int memsys5Size(void *p){
|
| + int iSize, i;
|
| + assert( p!=0 );
|
| + i = (int)(((u8 *)p-mem5.zPool)/mem5.szAtom);
|
| + assert( i>=0 && i<mem5.nBlock );
|
| + iSize = mem5.szAtom * (1 << (mem5.aCtrl[i]&CTRL_LOGSIZE));
|
| + return iSize;
|
| +}
|
| +
|
| +/*
|
| +** Return a block of memory of at least nBytes in size.
|
| +** Return NULL if unable. Return NULL if nBytes==0.
|
| +**
|
| +** The caller guarantees that nByte is positive.
|
| +**
|
| +** The caller has obtained a mutex prior to invoking this
|
| +** routine so there is never any chance that two or more
|
| +** threads can be in this routine at the same time.
|
| +*/
|
| +static void *memsys5MallocUnsafe(int nByte){
|
| + int i; /* Index of a mem5.aPool[] slot */
|
| + int iBin; /* Index into mem5.aiFreelist[] */
|
| + int iFullSz; /* Size of allocation rounded up to power of 2 */
|
| + int iLogsize; /* Log2 of iFullSz/POW2_MIN */
|
| +
|
| + /* nByte must be a positive */
|
| + assert( nByte>0 );
|
| +
|
| + /* No more than 1GiB per allocation */
|
| + if( nByte > 0x40000000 ) return 0;
|
| +
|
| +#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
| + /* Keep track of the maximum allocation request. Even unfulfilled
|
| + ** requests are counted */
|
| + if( (u32)nByte>mem5.maxRequest ){
|
| + mem5.maxRequest = nByte;
|
| + }
|
| +#endif
|
| +
|
| +
|
| + /* Round nByte up to the next valid power of two */
|
| + for(iFullSz=mem5.szAtom,iLogsize=0; iFullSz<nByte; iFullSz*=2,iLogsize++){}
|
| +
|
| + /* Make sure mem5.aiFreelist[iLogsize] contains at least one free
|
| + ** block. If not, then split a block of the next larger power of
|
| + ** two in order to create a new free block of size iLogsize.
|
| + */
|
| + for(iBin=iLogsize; iBin<=LOGMAX && mem5.aiFreelist[iBin]<0; iBin++){}
|
| + if( iBin>LOGMAX ){
|
| + testcase( sqlite3GlobalConfig.xLog!=0 );
|
| + sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes", nByte);
|
| + return 0;
|
| + }
|
| + i = mem5.aiFreelist[iBin];
|
| + memsys5Unlink(i, iBin);
|
| + while( iBin>iLogsize ){
|
| + int newSize;
|
| +
|
| + iBin--;
|
| + newSize = 1 << iBin;
|
| + mem5.aCtrl[i+newSize] = CTRL_FREE | iBin;
|
| + memsys5Link(i+newSize, iBin);
|
| + }
|
| + mem5.aCtrl[i] = iLogsize;
|
| +
|
| +#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
| + /* Update allocator performance statistics. */
|
| + mem5.nAlloc++;
|
| + mem5.totalAlloc += iFullSz;
|
| + mem5.totalExcess += iFullSz - nByte;
|
| + mem5.currentCount++;
|
| + mem5.currentOut += iFullSz;
|
| + if( mem5.maxCount<mem5.currentCount ) mem5.maxCount = mem5.currentCount;
|
| + if( mem5.maxOut<mem5.currentOut ) mem5.maxOut = mem5.currentOut;
|
| +#endif
|
| +
|
| +#ifdef SQLITE_DEBUG
|
| + /* Make sure the allocated memory does not assume that it is set to zero
|
| + ** or retains a value from a previous allocation */
|
| + memset(&mem5.zPool[i*mem5.szAtom], 0xAA, iFullSz);
|
| +#endif
|
| +
|
| + /* Return a pointer to the allocated memory. */
|
| + return (void*)&mem5.zPool[i*mem5.szAtom];
|
| +}
|
| +
|
| +/*
|
| +** Free an outstanding memory allocation.
|
| +*/
|
| +static void memsys5FreeUnsafe(void *pOld){
|
| + u32 size, iLogsize;
|
| + int iBlock;
|
| +
|
| + /* Set iBlock to the index of the block pointed to by pOld in
|
| + ** the array of mem5.szAtom byte blocks pointed to by mem5.zPool.
|
| + */
|
| + iBlock = (int)(((u8 *)pOld-mem5.zPool)/mem5.szAtom);
|
| +
|
| + /* Check that the pointer pOld points to a valid, non-free block. */
|
| + assert( iBlock>=0 && iBlock<mem5.nBlock );
|
| + assert( ((u8 *)pOld-mem5.zPool)%mem5.szAtom==0 );
|
| + assert( (mem5.aCtrl[iBlock] & CTRL_FREE)==0 );
|
| +
|
| + iLogsize = mem5.aCtrl[iBlock] & CTRL_LOGSIZE;
|
| + size = 1<<iLogsize;
|
| + assert( iBlock+size-1<(u32)mem5.nBlock );
|
| +
|
| + mem5.aCtrl[iBlock] |= CTRL_FREE;
|
| + mem5.aCtrl[iBlock+size-1] |= CTRL_FREE;
|
| +
|
| +#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
| + assert( mem5.currentCount>0 );
|
| + assert( mem5.currentOut>=(size*mem5.szAtom) );
|
| + mem5.currentCount--;
|
| + mem5.currentOut -= size*mem5.szAtom;
|
| + assert( mem5.currentOut>0 || mem5.currentCount==0 );
|
| + assert( mem5.currentCount>0 || mem5.currentOut==0 );
|
| +#endif
|
| +
|
| + mem5.aCtrl[iBlock] = CTRL_FREE | iLogsize;
|
| + while( ALWAYS(iLogsize<LOGMAX) ){
|
| + int iBuddy;
|
| + if( (iBlock>>iLogsize) & 1 ){
|
| + iBuddy = iBlock - size;
|
| + assert( iBuddy>=0 );
|
| + }else{
|
| + iBuddy = iBlock + size;
|
| + if( iBuddy>=mem5.nBlock ) break;
|
| + }
|
| + if( mem5.aCtrl[iBuddy]!=(CTRL_FREE | iLogsize) ) break;
|
| + memsys5Unlink(iBuddy, iLogsize);
|
| + iLogsize++;
|
| + if( iBuddy<iBlock ){
|
| + mem5.aCtrl[iBuddy] = CTRL_FREE | iLogsize;
|
| + mem5.aCtrl[iBlock] = 0;
|
| + iBlock = iBuddy;
|
| + }else{
|
| + mem5.aCtrl[iBlock] = CTRL_FREE | iLogsize;
|
| + mem5.aCtrl[iBuddy] = 0;
|
| + }
|
| + size *= 2;
|
| + }
|
| +
|
| +#ifdef SQLITE_DEBUG
|
| + /* Overwrite freed memory with the 0x55 bit pattern to verify that it is
|
| + ** not used after being freed */
|
| + memset(&mem5.zPool[iBlock*mem5.szAtom], 0x55, size);
|
| +#endif
|
| +
|
| + memsys5Link(iBlock, iLogsize);
|
| +}
|
| +
|
| +/*
|
| +** Allocate nBytes of memory.
|
| +*/
|
| +static void *memsys5Malloc(int nBytes){
|
| + sqlite3_int64 *p = 0;
|
| + if( nBytes>0 ){
|
| + memsys5Enter();
|
| + p = memsys5MallocUnsafe(nBytes);
|
| + memsys5Leave();
|
| + }
|
| + return (void*)p;
|
| +}
|
| +
|
| +/*
|
| +** Free memory.
|
| +**
|
| +** The outer layer memory allocator prevents this routine from
|
| +** being called with pPrior==0.
|
| +*/
|
| +static void memsys5Free(void *pPrior){
|
| + assert( pPrior!=0 );
|
| + memsys5Enter();
|
| + memsys5FreeUnsafe(pPrior);
|
| + memsys5Leave();
|
| +}
|
| +
|
| +/*
|
| +** Change the size of an existing memory allocation.
|
| +**
|
| +** The outer layer memory allocator prevents this routine from
|
| +** being called with pPrior==0.
|
| +**
|
| +** nBytes is always a value obtained from a prior call to
|
| +** memsys5Round(). Hence nBytes is always a non-negative power
|
| +** of two. If nBytes==0 that means that an oversize allocation
|
| +** (an allocation larger than 0x40000000) was requested and this
|
| +** routine should return 0 without freeing pPrior.
|
| +*/
|
| +static void *memsys5Realloc(void *pPrior, int nBytes){
|
| + int nOld;
|
| + void *p;
|
| + assert( pPrior!=0 );
|
| + assert( (nBytes&(nBytes-1))==0 ); /* EV: R-46199-30249 */
|
| + assert( nBytes>=0 );
|
| + if( nBytes==0 ){
|
| + return 0;
|
| + }
|
| + nOld = memsys5Size(pPrior);
|
| + if( nBytes<=nOld ){
|
| + return pPrior;
|
| + }
|
| + p = memsys5Malloc(nBytes);
|
| + if( p ){
|
| + memcpy(p, pPrior, nOld);
|
| + memsys5Free(pPrior);
|
| + }
|
| + return p;
|
| +}
|
| +
|
| +/*
|
| +** Round up a request size to the next valid allocation size. If
|
| +** the allocation is too large to be handled by this allocation system,
|
| +** return 0.
|
| +**
|
| +** All allocations must be a power of two and must be expressed by a
|
| +** 32-bit signed integer. Hence the largest allocation is 0x40000000
|
| +** or 1073741824 bytes.
|
| +*/
|
| +static int memsys5Roundup(int n){
|
| + int iFullSz;
|
| + if( n > 0x40000000 ) return 0;
|
| + for(iFullSz=mem5.szAtom; iFullSz<n; iFullSz *= 2);
|
| + return iFullSz;
|
| +}
|
| +
|
| +/*
|
| +** Return the ceiling of the logarithm base 2 of iValue.
|
| +**
|
| +** Examples: memsys5Log(1) -> 0
|
| +** memsys5Log(2) -> 1
|
| +** memsys5Log(4) -> 2
|
| +** memsys5Log(5) -> 3
|
| +** memsys5Log(8) -> 3
|
| +** memsys5Log(9) -> 4
|
| +*/
|
| +static int memsys5Log(int iValue){
|
| + int iLog;
|
| + for(iLog=0; (iLog<(int)((sizeof(int)*8)-1)) && (1<<iLog)<iValue; iLog++);
|
| + return iLog;
|
| +}
|
| +
|
| +/*
|
| +** Initialize the memory allocator.
|
| +**
|
| +** This routine is not threadsafe. The caller must be holding a mutex
|
| +** to prevent multiple threads from entering at the same time.
|
| +*/
|
| +static int memsys5Init(void *NotUsed){
|
| + int ii; /* Loop counter */
|
| + int nByte; /* Number of bytes of memory available to this allocator */
|
| + u8 *zByte; /* Memory usable by this allocator */
|
| + int nMinLog; /* Log base 2 of minimum allocation size in bytes */
|
| + int iOffset; /* An offset into mem5.aCtrl[] */
|
| +
|
| + UNUSED_PARAMETER(NotUsed);
|
| +
|
| + /* For the purposes of this routine, disable the mutex */
|
| + mem5.mutex = 0;
|
| +
|
| + /* The size of a Mem5Link object must be a power of two. Verify that
|
| + ** this is case.
|
| + */
|
| + assert( (sizeof(Mem5Link)&(sizeof(Mem5Link)-1))==0 );
|
| +
|
| + nByte = sqlite3GlobalConfig.nHeap;
|
| + zByte = (u8*)sqlite3GlobalConfig.pHeap;
|
| + assert( zByte!=0 ); /* sqlite3_config() does not allow otherwise */
|
| +
|
| + /* boundaries on sqlite3GlobalConfig.mnReq are enforced in sqlite3_config() */
|
| + nMinLog = memsys5Log(sqlite3GlobalConfig.mnReq);
|
| + mem5.szAtom = (1<<nMinLog);
|
| + while( (int)sizeof(Mem5Link)>mem5.szAtom ){
|
| + mem5.szAtom = mem5.szAtom << 1;
|
| + }
|
| +
|
| + mem5.nBlock = (nByte / (mem5.szAtom+sizeof(u8)));
|
| + mem5.zPool = zByte;
|
| + mem5.aCtrl = (u8 *)&mem5.zPool[mem5.nBlock*mem5.szAtom];
|
| +
|
| + for(ii=0; ii<=LOGMAX; ii++){
|
| + mem5.aiFreelist[ii] = -1;
|
| + }
|
| +
|
| + iOffset = 0;
|
| + for(ii=LOGMAX; ii>=0; ii--){
|
| + int nAlloc = (1<<ii);
|
| + if( (iOffset+nAlloc)<=mem5.nBlock ){
|
| + mem5.aCtrl[iOffset] = ii | CTRL_FREE;
|
| + memsys5Link(iOffset, ii);
|
| + iOffset += nAlloc;
|
| + }
|
| + assert((iOffset+nAlloc)>mem5.nBlock);
|
| + }
|
| +
|
| + /* If a mutex is required for normal operation, allocate one */
|
| + if( sqlite3GlobalConfig.bMemstat==0 ){
|
| + mem5.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
|
| + }
|
| +
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Deinitialize this module.
|
| +*/
|
| +static void memsys5Shutdown(void *NotUsed){
|
| + UNUSED_PARAMETER(NotUsed);
|
| + mem5.mutex = 0;
|
| + return;
|
| +}
|
| +
|
| +#ifdef SQLITE_TEST
|
| +/*
|
| +** Open the file indicated and write a log of all unfreed memory
|
| +** allocations into that log.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3Memsys5Dump(const char *zFilename){
|
| + FILE *out;
|
| + int i, j, n;
|
| + int nMinLog;
|
| +
|
| + if( zFilename==0 || zFilename[0]==0 ){
|
| + out = stdout;
|
| + }else{
|
| + out = fopen(zFilename, "w");
|
| + if( out==0 ){
|
| + fprintf(stderr, "** Unable to output memory debug output log: %s **\n",
|
| + zFilename);
|
| + return;
|
| + }
|
| + }
|
| + memsys5Enter();
|
| + nMinLog = memsys5Log(mem5.szAtom);
|
| + for(i=0; i<=LOGMAX && i+nMinLog<32; i++){
|
| + for(n=0, j=mem5.aiFreelist[i]; j>=0; j = MEM5LINK(j)->next, n++){}
|
| + fprintf(out, "freelist items of size %d: %d\n", mem5.szAtom << i, n);
|
| + }
|
| + fprintf(out, "mem5.nAlloc = %llu\n", mem5.nAlloc);
|
| + fprintf(out, "mem5.totalAlloc = %llu\n", mem5.totalAlloc);
|
| + fprintf(out, "mem5.totalExcess = %llu\n", mem5.totalExcess);
|
| + fprintf(out, "mem5.currentOut = %u\n", mem5.currentOut);
|
| + fprintf(out, "mem5.currentCount = %u\n", mem5.currentCount);
|
| + fprintf(out, "mem5.maxOut = %u\n", mem5.maxOut);
|
| + fprintf(out, "mem5.maxCount = %u\n", mem5.maxCount);
|
| + fprintf(out, "mem5.maxRequest = %u\n", mem5.maxRequest);
|
| + memsys5Leave();
|
| + if( out==stdout ){
|
| + fflush(stdout);
|
| + }else{
|
| + fclose(out);
|
| + }
|
| +}
|
| +#endif
|
| +
|
| +/*
|
| +** This routine is the only routine in this file with external
|
| +** linkage. It returns a pointer to a static sqlite3_mem_methods
|
| +** struct populated with the memsys5 methods.
|
| +*/
|
| +SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys5(void){
|
| + static const sqlite3_mem_methods memsys5Methods = {
|
| + memsys5Malloc,
|
| + memsys5Free,
|
| + memsys5Realloc,
|
| + memsys5Size,
|
| + memsys5Roundup,
|
| + memsys5Init,
|
| + memsys5Shutdown,
|
| + 0
|
| + };
|
| + return &memsys5Methods;
|
| +}
|
| +
|
| +#endif /* SQLITE_ENABLE_MEMSYS5 */
|
| +
|
| +/************** End of mem5.c ************************************************/
|
| +/************** Begin file mutex.c *******************************************/
|
| +/*
|
| +** 2007 August 14
|
| +**
|
| +** 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 C functions that implement mutexes.
|
| +**
|
| +** This file contains code that is common across all mutex implementations.
|
| +*/
|
| +/* #include "sqliteInt.h" */
|
| +
|
| +#if defined(SQLITE_DEBUG) && !defined(SQLITE_MUTEX_OMIT)
|
| +/*
|
| +** For debugging purposes, record when the mutex subsystem is initialized
|
| +** and uninitialized so that we can assert() if there is an attempt to
|
| +** allocate a mutex while the system is uninitialized.
|
| +*/
|
| +static SQLITE_WSD int mutexIsInit = 0;
|
| +#endif /* SQLITE_DEBUG && !defined(SQLITE_MUTEX_OMIT) */
|
| +
|
| +
|
| +#ifndef SQLITE_MUTEX_OMIT
|
| +/*
|
| +** Initialize the mutex system.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3MutexInit(void){
|
| + int rc = SQLITE_OK;
|
| + if( !sqlite3GlobalConfig.mutex.xMutexAlloc ){
|
| + /* If the xMutexAlloc method has not been set, then the user did not
|
| + ** install a mutex implementation via sqlite3_config() prior to
|
| + ** sqlite3_initialize() being called. This block copies pointers to
|
| + ** the default implementation into the sqlite3GlobalConfig structure.
|
| + */
|
| + sqlite3_mutex_methods const *pFrom;
|
| + sqlite3_mutex_methods *pTo = &sqlite3GlobalConfig.mutex;
|
| +
|
| + if( sqlite3GlobalConfig.bCoreMutex ){
|
| + pFrom = sqlite3DefaultMutex();
|
| + }else{
|
| + pFrom = sqlite3NoopMutex();
|
| + }
|
| + pTo->xMutexInit = pFrom->xMutexInit;
|
| + pTo->xMutexEnd = pFrom->xMutexEnd;
|
| + pTo->xMutexFree = pFrom->xMutexFree;
|
| + pTo->xMutexEnter = pFrom->xMutexEnter;
|
| + pTo->xMutexTry = pFrom->xMutexTry;
|
| + pTo->xMutexLeave = pFrom->xMutexLeave;
|
| + pTo->xMutexHeld = pFrom->xMutexHeld;
|
| + pTo->xMutexNotheld = pFrom->xMutexNotheld;
|
| + sqlite3MemoryBarrier();
|
| + pTo->xMutexAlloc = pFrom->xMutexAlloc;
|
| + }
|
| + assert( sqlite3GlobalConfig.mutex.xMutexInit );
|
| + rc = sqlite3GlobalConfig.mutex.xMutexInit();
|
| +
|
| +#ifdef SQLITE_DEBUG
|
| + GLOBAL(int, mutexIsInit) = 1;
|
| +#endif
|
| +
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Shutdown the mutex system. This call frees resources allocated by
|
| +** sqlite3MutexInit().
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3MutexEnd(void){
|
| + int rc = SQLITE_OK;
|
| + if( sqlite3GlobalConfig.mutex.xMutexEnd ){
|
| + rc = sqlite3GlobalConfig.mutex.xMutexEnd();
|
| + }
|
| +
|
| +#ifdef SQLITE_DEBUG
|
| + GLOBAL(int, mutexIsInit) = 0;
|
| +#endif
|
| +
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Retrieve a pointer to a static mutex or allocate a new dynamic one.
|
| +*/
|
| +SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int id){
|
| +#ifndef SQLITE_OMIT_AUTOINIT
|
| + if( id<=SQLITE_MUTEX_RECURSIVE && sqlite3_initialize() ) return 0;
|
| + if( id>SQLITE_MUTEX_RECURSIVE && sqlite3MutexInit() ) return 0;
|
| +#endif
|
| + assert( sqlite3GlobalConfig.mutex.xMutexAlloc );
|
| + return sqlite3GlobalConfig.mutex.xMutexAlloc(id);
|
| +}
|
| +
|
| +SQLITE_PRIVATE sqlite3_mutex *sqlite3MutexAlloc(int id){
|
| + if( !sqlite3GlobalConfig.bCoreMutex ){
|
| + return 0;
|
| + }
|
| + assert( GLOBAL(int, mutexIsInit) );
|
| + assert( sqlite3GlobalConfig.mutex.xMutexAlloc );
|
| + return sqlite3GlobalConfig.mutex.xMutexAlloc(id);
|
| +}
|
| +
|
| +/*
|
| +** Free a dynamic mutex.
|
| +*/
|
| +SQLITE_API void sqlite3_mutex_free(sqlite3_mutex *p){
|
| + if( p ){
|
| + assert( sqlite3GlobalConfig.mutex.xMutexFree );
|
| + sqlite3GlobalConfig.mutex.xMutexFree(p);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Obtain the mutex p. If some other thread already has the mutex, block
|
| +** until it can be obtained.
|
| +*/
|
| +SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex *p){
|
| + if( p ){
|
| + assert( sqlite3GlobalConfig.mutex.xMutexEnter );
|
| + sqlite3GlobalConfig.mutex.xMutexEnter(p);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Obtain the mutex p. If successful, return SQLITE_OK. Otherwise, if another
|
| +** thread holds the mutex and it cannot be obtained, return SQLITE_BUSY.
|
| +*/
|
| +SQLITE_API int sqlite3_mutex_try(sqlite3_mutex *p){
|
| + int rc = SQLITE_OK;
|
| + if( p ){
|
| + assert( sqlite3GlobalConfig.mutex.xMutexTry );
|
| + return sqlite3GlobalConfig.mutex.xMutexTry(p);
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** The sqlite3_mutex_leave() routine exits a mutex that was previously
|
| +** entered by the same thread. The behavior is undefined if the mutex
|
| +** is not currently entered. If a NULL pointer is passed as an argument
|
| +** this function is a no-op.
|
| +*/
|
| +SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex *p){
|
| + if( p ){
|
| + assert( sqlite3GlobalConfig.mutex.xMutexLeave );
|
| + sqlite3GlobalConfig.mutex.xMutexLeave(p);
|
| + }
|
| +}
|
| +
|
| +#ifndef NDEBUG
|
| +/*
|
| +** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
|
| +** intended for use inside assert() statements.
|
| +*/
|
| +SQLITE_API int sqlite3_mutex_held(sqlite3_mutex *p){
|
| + assert( p==0 || sqlite3GlobalConfig.mutex.xMutexHeld );
|
| + return p==0 || sqlite3GlobalConfig.mutex.xMutexHeld(p);
|
| +}
|
| +SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex *p){
|
| + assert( p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld );
|
| + return p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld(p);
|
| +}
|
| +#endif
|
| +
|
| +#endif /* !defined(SQLITE_MUTEX_OMIT) */
|
| +
|
| +/************** End of mutex.c ***********************************************/
|
| +/************** Begin file mutex_noop.c **************************************/
|
| +/*
|
| +** 2008 October 07
|
| +**
|
| +** 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 C functions that implement mutexes.
|
| +**
|
| +** This implementation in this file does not provide any mutual
|
| +** exclusion and is thus suitable for use only in applications
|
| +** that use SQLite in a single thread. The routines defined
|
| +** here are place-holders. Applications can substitute working
|
| +** mutex routines at start-time using the
|
| +**
|
| +** sqlite3_config(SQLITE_CONFIG_MUTEX,...)
|
| +**
|
| +** interface.
|
| +**
|
| +** If compiled with SQLITE_DEBUG, then additional logic is inserted
|
| +** that does error checking on mutexes to make sure they are being
|
| +** called correctly.
|
| +*/
|
| +/* #include "sqliteInt.h" */
|
| +
|
| +#ifndef SQLITE_MUTEX_OMIT
|
| +
|
| +#ifndef SQLITE_DEBUG
|
| +/*
|
| +** Stub routines for all mutex methods.
|
| +**
|
| +** This routines provide no mutual exclusion or error checking.
|
| +*/
|
| +static int noopMutexInit(void){ return SQLITE_OK; }
|
| +static int noopMutexEnd(void){ return SQLITE_OK; }
|
| +static sqlite3_mutex *noopMutexAlloc(int id){
|
| + UNUSED_PARAMETER(id);
|
| + return (sqlite3_mutex*)8;
|
| +}
|
| +static void noopMutexFree(sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; }
|
| +static void noopMutexEnter(sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; }
|
| +static int noopMutexTry(sqlite3_mutex *p){
|
| + UNUSED_PARAMETER(p);
|
| + return SQLITE_OK;
|
| +}
|
| +static void noopMutexLeave(sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; }
|
| +
|
| +SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3NoopMutex(void){
|
| + static const sqlite3_mutex_methods sMutex = {
|
| + noopMutexInit,
|
| + noopMutexEnd,
|
| + noopMutexAlloc,
|
| + noopMutexFree,
|
| + noopMutexEnter,
|
| + noopMutexTry,
|
| + noopMutexLeave,
|
| +
|
| + 0,
|
| + 0,
|
| + };
|
| +
|
| + return &sMutex;
|
| +}
|
| +#endif /* !SQLITE_DEBUG */
|
| +
|
| +#ifdef SQLITE_DEBUG
|
| +/*
|
| +** In this implementation, error checking is provided for testing
|
| +** and debugging purposes. The mutexes still do not provide any
|
| +** mutual exclusion.
|
| +*/
|
| +
|
| +/*
|
| +** The mutex object
|
| +*/
|
| +typedef struct sqlite3_debug_mutex {
|
| + int id; /* The mutex type */
|
| + int cnt; /* Number of entries without a matching leave */
|
| +} sqlite3_debug_mutex;
|
| +
|
| +/*
|
| +** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
|
| +** intended for use inside assert() statements.
|
| +*/
|
| +static int debugMutexHeld(sqlite3_mutex *pX){
|
| + sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX;
|
| + return p==0 || p->cnt>0;
|
| +}
|
| +static int debugMutexNotheld(sqlite3_mutex *pX){
|
| + sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX;
|
| + return p==0 || p->cnt==0;
|
| +}
|
| +
|
| +/*
|
| +** Initialize and deinitialize the mutex subsystem.
|
| +*/
|
| +static int debugMutexInit(void){ return SQLITE_OK; }
|
| +static int debugMutexEnd(void){ return SQLITE_OK; }
|
| +
|
| +/*
|
| +** The sqlite3_mutex_alloc() routine allocates a new
|
| +** mutex and returns a pointer to it. If it returns NULL
|
| +** that means that a mutex could not be allocated.
|
| +*/
|
| +static sqlite3_mutex *debugMutexAlloc(int id){
|
| + static sqlite3_debug_mutex aStatic[SQLITE_MUTEX_STATIC_VFS3 - 1];
|
| + sqlite3_debug_mutex *pNew = 0;
|
| + switch( id ){
|
| + case SQLITE_MUTEX_FAST:
|
| + case SQLITE_MUTEX_RECURSIVE: {
|
| + pNew = sqlite3Malloc(sizeof(*pNew));
|
| + if( pNew ){
|
| + pNew->id = id;
|
| + pNew->cnt = 0;
|
| + }
|
| + break;
|
| + }
|
| + default: {
|
| +#ifdef SQLITE_ENABLE_API_ARMOR
|
| + if( id-2<0 || id-2>=ArraySize(aStatic) ){
|
| + (void)SQLITE_MISUSE_BKPT;
|
| + return 0;
|
| + }
|
| +#endif
|
| + pNew = &aStatic[id-2];
|
| + pNew->id = id;
|
| + break;
|
| + }
|
| + }
|
| + return (sqlite3_mutex*)pNew;
|
| +}
|
| +
|
| +/*
|
| +** This routine deallocates a previously allocated mutex.
|
| +*/
|
| +static void debugMutexFree(sqlite3_mutex *pX){
|
| + sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX;
|
| + assert( p->cnt==0 );
|
| + if( p->id==SQLITE_MUTEX_RECURSIVE || p->id==SQLITE_MUTEX_FAST ){
|
| + sqlite3_free(p);
|
| + }else{
|
| +#ifdef SQLITE_ENABLE_API_ARMOR
|
| + (void)SQLITE_MISUSE_BKPT;
|
| +#endif
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt
|
| +** to enter a mutex. If another thread is already within the mutex,
|
| +** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return
|
| +** SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK
|
| +** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can
|
| +** be entered multiple times by the same thread. In such cases the,
|
| +** mutex must be exited an equal number of times before another thread
|
| +** can enter. If the same thread tries to enter any other kind of mutex
|
| +** more than once, the behavior is undefined.
|
| +*/
|
| +static void debugMutexEnter(sqlite3_mutex *pX){
|
| + sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX;
|
| + assert( p->id==SQLITE_MUTEX_RECURSIVE || debugMutexNotheld(pX) );
|
| + p->cnt++;
|
| +}
|
| +static int debugMutexTry(sqlite3_mutex *pX){
|
| + sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX;
|
| + assert( p->id==SQLITE_MUTEX_RECURSIVE || debugMutexNotheld(pX) );
|
| + p->cnt++;
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** The sqlite3_mutex_leave() routine exits a mutex that was
|
| +** previously entered by the same thread. The behavior
|
| +** is undefined if the mutex is not currently entered or
|
| +** is not currently allocated. SQLite will never do either.
|
| +*/
|
| +static void debugMutexLeave(sqlite3_mutex *pX){
|
| + sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX;
|
| + assert( debugMutexHeld(pX) );
|
| + p->cnt--;
|
| + assert( p->id==SQLITE_MUTEX_RECURSIVE || debugMutexNotheld(pX) );
|
| +}
|
| +
|
| +SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3NoopMutex(void){
|
| + static const sqlite3_mutex_methods sMutex = {
|
| + debugMutexInit,
|
| + debugMutexEnd,
|
| + debugMutexAlloc,
|
| + debugMutexFree,
|
| + debugMutexEnter,
|
| + debugMutexTry,
|
| + debugMutexLeave,
|
| +
|
| + debugMutexHeld,
|
| + debugMutexNotheld
|
| + };
|
| +
|
| + return &sMutex;
|
| +}
|
| +#endif /* SQLITE_DEBUG */
|
| +
|
| +/*
|
| +** If compiled with SQLITE_MUTEX_NOOP, then the no-op mutex implementation
|
| +** is used regardless of the run-time threadsafety setting.
|
| +*/
|
| +#ifdef SQLITE_MUTEX_NOOP
|
| +SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
|
| + return sqlite3NoopMutex();
|
| +}
|
| +#endif /* defined(SQLITE_MUTEX_NOOP) */
|
| +#endif /* !defined(SQLITE_MUTEX_OMIT) */
|
| +
|
| +/************** End of mutex_noop.c ******************************************/
|
| +/************** Begin file mutex_unix.c **************************************/
|
| +/*
|
| +** 2007 August 28
|
| +**
|
| +** 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 C functions that implement mutexes for pthreads
|
| +*/
|
| +/* #include "sqliteInt.h" */
|
| +
|
| +/*
|
| +** The code in this file is only used if we are compiling threadsafe
|
| +** under unix with pthreads.
|
| +**
|
| +** Note that this implementation requires a version of pthreads that
|
| +** supports recursive mutexes.
|
| +*/
|
| +#ifdef SQLITE_MUTEX_PTHREADS
|
| +
|
| +#include <pthread.h>
|
| +
|
| +/*
|
| +** The sqlite3_mutex.id, sqlite3_mutex.nRef, and sqlite3_mutex.owner fields
|
| +** are necessary under two condidtions: (1) Debug builds and (2) using
|
| +** home-grown mutexes. Encapsulate these conditions into a single #define.
|
| +*/
|
| +#if defined(SQLITE_DEBUG) || defined(SQLITE_HOMEGROWN_RECURSIVE_MUTEX)
|
| +# define SQLITE_MUTEX_NREF 1
|
| +#else
|
| +# define SQLITE_MUTEX_NREF 0
|
| +#endif
|
| +
|
| +/*
|
| +** Each recursive mutex is an instance of the following structure.
|
| +*/
|
| +struct sqlite3_mutex {
|
| + pthread_mutex_t mutex; /* Mutex controlling the lock */
|
| +#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR)
|
| + int id; /* Mutex type */
|
| +#endif
|
| +#if SQLITE_MUTEX_NREF
|
| + volatile int nRef; /* Number of entrances */
|
| + volatile pthread_t owner; /* Thread that is within this mutex */
|
| + int trace; /* True to trace changes */
|
| +#endif
|
| +};
|
| +#if SQLITE_MUTEX_NREF
|
| +#define SQLITE3_MUTEX_INITIALIZER {PTHREAD_MUTEX_INITIALIZER,0,0,(pthread_t)0,0}
|
| +#elif defined(SQLITE_ENABLE_API_ARMOR)
|
| +#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0 }
|
| +#else
|
| +#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER }
|
| +#endif
|
| +
|
| +/*
|
| +** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
|
| +** intended for use only inside assert() statements. On some platforms,
|
| +** there might be race conditions that can cause these routines to
|
| +** deliver incorrect results. In particular, if pthread_equal() is
|
| +** not an atomic operation, then these routines might delivery
|
| +** incorrect results. On most platforms, pthread_equal() is a
|
| +** comparison of two integers and is therefore atomic. But we are
|
| +** told that HPUX is not such a platform. If so, then these routines
|
| +** will not always work correctly on HPUX.
|
| +**
|
| +** On those platforms where pthread_equal() is not atomic, SQLite
|
| +** should be compiled without -DSQLITE_DEBUG and with -DNDEBUG to
|
| +** make sure no assert() statements are evaluated and hence these
|
| +** routines are never called.
|
| +*/
|
| +#if !defined(NDEBUG) || defined(SQLITE_DEBUG)
|
| +static int pthreadMutexHeld(sqlite3_mutex *p){
|
| + return (p->nRef!=0 && pthread_equal(p->owner, pthread_self()));
|
| +}
|
| +static int pthreadMutexNotheld(sqlite3_mutex *p){
|
| + return p->nRef==0 || pthread_equal(p->owner, pthread_self())==0;
|
| +}
|
| +#endif
|
| +
|
| +/*
|
| +** Try to provide a memory barrier operation, needed for initialization
|
| +** and also for the implementation of xShmBarrier in the VFS in cases
|
| +** where SQLite is compiled without mutexes.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3MemoryBarrier(void){
|
| +#if defined(SQLITE_MEMORY_BARRIER)
|
| + SQLITE_MEMORY_BARRIER;
|
| +#elif defined(__GNUC__) && GCC_VERSION>=4001000
|
| + __sync_synchronize();
|
| +#endif
|
| +}
|
| +
|
| +/*
|
| +** Initialize and deinitialize the mutex subsystem.
|
| +*/
|
| +static int pthreadMutexInit(void){ return SQLITE_OK; }
|
| +static int pthreadMutexEnd(void){ return SQLITE_OK; }
|
| +
|
| +/*
|
| +** The sqlite3_mutex_alloc() routine allocates a new
|
| +** mutex and returns a pointer to it. If it returns NULL
|
| +** that means that a mutex could not be allocated. SQLite
|
| +** will unwind its stack and return an error. The argument
|
| +** to sqlite3_mutex_alloc() is one of these integer constants:
|
| +**
|
| +** <ul>
|
| +** <li> SQLITE_MUTEX_FAST
|
| +** <li> SQLITE_MUTEX_RECURSIVE
|
| +** <li> SQLITE_MUTEX_STATIC_MASTER
|
| +** <li> SQLITE_MUTEX_STATIC_MEM
|
| +** <li> SQLITE_MUTEX_STATIC_OPEN
|
| +** <li> SQLITE_MUTEX_STATIC_PRNG
|
| +** <li> SQLITE_MUTEX_STATIC_LRU
|
| +** <li> SQLITE_MUTEX_STATIC_PMEM
|
| +** <li> SQLITE_MUTEX_STATIC_APP1
|
| +** <li> SQLITE_MUTEX_STATIC_APP2
|
| +** <li> SQLITE_MUTEX_STATIC_APP3
|
| +** <li> SQLITE_MUTEX_STATIC_VFS1
|
| +** <li> SQLITE_MUTEX_STATIC_VFS2
|
| +** <li> SQLITE_MUTEX_STATIC_VFS3
|
| +** </ul>
|
| +**
|
| +** The first two constants 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. But 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() each return
|
| +** a pointer to a static preexisting mutex. Six 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. But for the static
|
| +** mutex types, the same mutex is returned on every call that has
|
| +** the same type number.
|
| +*/
|
| +static sqlite3_mutex *pthreadMutexAlloc(int iType){
|
| + static sqlite3_mutex staticMutexes[] = {
|
| + SQLITE3_MUTEX_INITIALIZER,
|
| + SQLITE3_MUTEX_INITIALIZER,
|
| + SQLITE3_MUTEX_INITIALIZER,
|
| + SQLITE3_MUTEX_INITIALIZER,
|
| + SQLITE3_MUTEX_INITIALIZER,
|
| + SQLITE3_MUTEX_INITIALIZER,
|
| + SQLITE3_MUTEX_INITIALIZER,
|
| + SQLITE3_MUTEX_INITIALIZER,
|
| + SQLITE3_MUTEX_INITIALIZER,
|
| + SQLITE3_MUTEX_INITIALIZER,
|
| + SQLITE3_MUTEX_INITIALIZER,
|
| + SQLITE3_MUTEX_INITIALIZER
|
| + };
|
| + sqlite3_mutex *p;
|
| + switch( iType ){
|
| + case SQLITE_MUTEX_RECURSIVE: {
|
| + p = sqlite3MallocZero( sizeof(*p) );
|
| + if( p ){
|
| +#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
|
| + /* If recursive mutexes are not available, we will have to
|
| + ** build our own. See below. */
|
| + pthread_mutex_init(&p->mutex, 0);
|
| +#else
|
| + /* Use a recursive mutex if it is available */
|
| + pthread_mutexattr_t recursiveAttr;
|
| + pthread_mutexattr_init(&recursiveAttr);
|
| + pthread_mutexattr_settype(&recursiveAttr, PTHREAD_MUTEX_RECURSIVE);
|
| + pthread_mutex_init(&p->mutex, &recursiveAttr);
|
| + pthread_mutexattr_destroy(&recursiveAttr);
|
| +#endif
|
| + }
|
| + break;
|
| + }
|
| + case SQLITE_MUTEX_FAST: {
|
| + p = sqlite3MallocZero( sizeof(*p) );
|
| + if( p ){
|
| + pthread_mutex_init(&p->mutex, 0);
|
| + }
|
| + break;
|
| + }
|
| + default: {
|
| +#ifdef SQLITE_ENABLE_API_ARMOR
|
| + if( iType-2<0 || iType-2>=ArraySize(staticMutexes) ){
|
| + (void)SQLITE_MISUSE_BKPT;
|
| + return 0;
|
| + }
|
| +#endif
|
| + p = &staticMutexes[iType-2];
|
| + break;
|
| + }
|
| + }
|
| +#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR)
|
| + if( p ) p->id = iType;
|
| +#endif
|
| + return p;
|
| +}
|
| +
|
| +
|
| +/*
|
| +** This routine deallocates a previously
|
| +** allocated mutex. SQLite is careful to deallocate every
|
| +** mutex that it allocates.
|
| +*/
|
| +static void pthreadMutexFree(sqlite3_mutex *p){
|
| + assert( p->nRef==0 );
|
| +#if SQLITE_ENABLE_API_ARMOR
|
| + if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE )
|
| +#endif
|
| + {
|
| + pthread_mutex_destroy(&p->mutex);
|
| + sqlite3_free(p);
|
| + }
|
| +#ifdef SQLITE_ENABLE_API_ARMOR
|
| + else{
|
| + (void)SQLITE_MISUSE_BKPT;
|
| + }
|
| +#endif
|
| +}
|
| +
|
| +/*
|
| +** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt
|
| +** to enter a mutex. If another thread is already within the mutex,
|
| +** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return
|
| +** SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK
|
| +** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can
|
| +** be entered multiple times by the same thread. In such cases the,
|
| +** mutex must be exited an equal number of times before another thread
|
| +** can enter. If the same thread tries to enter any other kind of mutex
|
| +** more than once, the behavior is undefined.
|
| +*/
|
| +static void pthreadMutexEnter(sqlite3_mutex *p){
|
| + assert( p->id==SQLITE_MUTEX_RECURSIVE || pthreadMutexNotheld(p) );
|
| +
|
| +#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
|
| + /* If recursive mutexes are not available, then we have to grow
|
| + ** our own. This implementation assumes that pthread_equal()
|
| + ** is atomic - that it cannot be deceived into thinking self
|
| + ** and p->owner are equal if p->owner changes between two values
|
| + ** that are not equal to self while the comparison is taking place.
|
| + ** This implementation also assumes a coherent cache - that
|
| + ** separate processes cannot read different values from the same
|
| + ** address at the same time. If either of these two conditions
|
| + ** are not met, then the mutexes will fail and problems will result.
|
| + */
|
| + {
|
| + pthread_t self = pthread_self();
|
| + if( p->nRef>0 && pthread_equal(p->owner, self) ){
|
| + p->nRef++;
|
| + }else{
|
| + pthread_mutex_lock(&p->mutex);
|
| + assert( p->nRef==0 );
|
| + p->owner = self;
|
| + p->nRef = 1;
|
| + }
|
| + }
|
| +#else
|
| + /* Use the built-in recursive mutexes if they are available.
|
| + */
|
| + pthread_mutex_lock(&p->mutex);
|
| +#if SQLITE_MUTEX_NREF
|
| + assert( p->nRef>0 || p->owner==0 );
|
| + p->owner = pthread_self();
|
| + p->nRef++;
|
| +#endif
|
| +#endif
|
| +
|
| +#ifdef SQLITE_DEBUG
|
| + if( p->trace ){
|
| + printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
|
| + }
|
| +#endif
|
| +}
|
| +static int pthreadMutexTry(sqlite3_mutex *p){
|
| + int rc;
|
| + assert( p->id==SQLITE_MUTEX_RECURSIVE || pthreadMutexNotheld(p) );
|
| +
|
| +#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
|
| + /* If recursive mutexes are not available, then we have to grow
|
| + ** our own. This implementation assumes that pthread_equal()
|
| + ** is atomic - that it cannot be deceived into thinking self
|
| + ** and p->owner are equal if p->owner changes between two values
|
| + ** that are not equal to self while the comparison is taking place.
|
| + ** This implementation also assumes a coherent cache - that
|
| + ** separate processes cannot read different values from the same
|
| + ** address at the same time. If either of these two conditions
|
| + ** are not met, then the mutexes will fail and problems will result.
|
| + */
|
| + {
|
| + pthread_t self = pthread_self();
|
| + if( p->nRef>0 && pthread_equal(p->owner, self) ){
|
| + p->nRef++;
|
| + rc = SQLITE_OK;
|
| + }else if( pthread_mutex_trylock(&p->mutex)==0 ){
|
| + assert( p->nRef==0 );
|
| + p->owner = self;
|
| + p->nRef = 1;
|
| + rc = SQLITE_OK;
|
| + }else{
|
| + rc = SQLITE_BUSY;
|
| + }
|
| + }
|
| +#else
|
| + /* Use the built-in recursive mutexes if they are available.
|
| + */
|
| + if( pthread_mutex_trylock(&p->mutex)==0 ){
|
| +#if SQLITE_MUTEX_NREF
|
| + p->owner = pthread_self();
|
| + p->nRef++;
|
| +#endif
|
| + rc = SQLITE_OK;
|
| + }else{
|
| + rc = SQLITE_BUSY;
|
| + }
|
| +#endif
|
| +
|
| +#ifdef SQLITE_DEBUG
|
| + if( rc==SQLITE_OK && p->trace ){
|
| + printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
|
| + }
|
| +#endif
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** The sqlite3_mutex_leave() routine exits a mutex that was
|
| +** previously entered by the same thread. The behavior
|
| +** is undefined if the mutex is not currently entered or
|
| +** is not currently allocated. SQLite will never do either.
|
| +*/
|
| +static void pthreadMutexLeave(sqlite3_mutex *p){
|
| + assert( pthreadMutexHeld(p) );
|
| +#if SQLITE_MUTEX_NREF
|
| + p->nRef--;
|
| + if( p->nRef==0 ) p->owner = 0;
|
| +#endif
|
| + assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
|
| +
|
| +#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
|
| + if( p->nRef==0 ){
|
| + pthread_mutex_unlock(&p->mutex);
|
| + }
|
| +#else
|
| + pthread_mutex_unlock(&p->mutex);
|
| +#endif
|
| +
|
| +#ifdef SQLITE_DEBUG
|
| + if( p->trace ){
|
| + printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
|
| + }
|
| +#endif
|
| +}
|
| +
|
| +SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
|
| + static const sqlite3_mutex_methods sMutex = {
|
| + pthreadMutexInit,
|
| + pthreadMutexEnd,
|
| + pthreadMutexAlloc,
|
| + pthreadMutexFree,
|
| + pthreadMutexEnter,
|
| + pthreadMutexTry,
|
| + pthreadMutexLeave,
|
| +#ifdef SQLITE_DEBUG
|
| + pthreadMutexHeld,
|
| + pthreadMutexNotheld
|
| +#else
|
| + 0,
|
| + 0
|
| +#endif
|
| + };
|
| +
|
| + return &sMutex;
|
| +}
|
| +
|
| +#endif /* SQLITE_MUTEX_PTHREADS */
|
| +
|
| +/************** End of mutex_unix.c ******************************************/
|
| +/************** Begin file mutex_w32.c ***************************************/
|
| +/*
|
| +** 2007 August 14
|
| +**
|
| +** 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 C functions that implement mutexes for Win32.
|
| +*/
|
| +/* #include "sqliteInt.h" */
|
| +
|
| +#if SQLITE_OS_WIN
|
| +/*
|
| +** Include code that is common to all os_*.c files
|
| +*/
|
| +/************** Include os_common.h in the middle of mutex_w32.c *************/
|
| +/************** Begin file os_common.h ***************************************/
|
| +/*
|
| +** 2004 May 22
|
| +**
|
| +** 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 macros and a little bit of code that is common to
|
| +** all of the platform-specific files (os_*.c) and is #included into those
|
| +** files.
|
| +**
|
| +** This file should be #included by the os_*.c files only. It is not a
|
| +** general purpose header file.
|
| +*/
|
| +#ifndef _OS_COMMON_H_
|
| +#define _OS_COMMON_H_
|
| +
|
| +/*
|
| +** At least two bugs have slipped in because we changed the MEMORY_DEBUG
|
| +** macro to SQLITE_DEBUG and some older makefiles have not yet made the
|
| +** switch. The following code should catch this problem at compile-time.
|
| +*/
|
| +#ifdef MEMORY_DEBUG
|
| +# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
|
| +#endif
|
| +
|
| +/*
|
| +** Macros for performance tracing. Normally turned off. Only works
|
| +** on i486 hardware.
|
| +*/
|
| +#ifdef SQLITE_PERFORMANCE_TRACE
|
| +
|
| +/*
|
| +** hwtime.h contains inline assembler code for implementing
|
| +** high-performance timing routines.
|
| +*/
|
| +/************** Include hwtime.h in the middle of os_common.h ****************/
|
| +/************** Begin file hwtime.h ******************************************/
|
| +/*
|
| +** 2008 May 27
|
| +**
|
| +** 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 inline asm code for retrieving "high-performance"
|
| +** counters for x86 class CPUs.
|
| +*/
|
| +#ifndef SQLITE_HWTIME_H
|
| +#define SQLITE_HWTIME_H
|
| +
|
| +/*
|
| +** The following routine only works on pentium-class (or newer) processors.
|
| +** It uses the RDTSC opcode to read the cycle count value out of the
|
| +** processor and returns that value. This can be used for high-res
|
| +** profiling.
|
| +*/
|
| +#if (defined(__GNUC__) || defined(_MSC_VER)) && \
|
| + (defined(i386) || defined(__i386__) || defined(_M_IX86))
|
| +
|
| + #if defined(__GNUC__)
|
| +
|
| + __inline__ sqlite_uint64 sqlite3Hwtime(void){
|
| + unsigned int lo, hi;
|
| + __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
|
| + return (sqlite_uint64)hi << 32 | lo;
|
| + }
|
| +
|
| + #elif defined(_MSC_VER)
|
| +
|
| + __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){
|
| + __asm {
|
| + rdtsc
|
| + ret ; return value at EDX:EAX
|
| + }
|
| + }
|
| +
|
| + #endif
|
| +
|
| +#elif (defined(__GNUC__) && defined(__x86_64__))
|
| +
|
| + __inline__ sqlite_uint64 sqlite3Hwtime(void){
|
| + unsigned long val;
|
| + __asm__ __volatile__ ("rdtsc" : "=A" (val));
|
| + return val;
|
| + }
|
| +
|
| +#elif (defined(__GNUC__) && defined(__ppc__))
|
| +
|
| + __inline__ sqlite_uint64 sqlite3Hwtime(void){
|
| + unsigned long long retval;
|
| + unsigned long junk;
|
| + __asm__ __volatile__ ("\n\
|
| + 1: mftbu %1\n\
|
| + mftb %L0\n\
|
| + mftbu %0\n\
|
| + cmpw %0,%1\n\
|
| + bne 1b"
|
| + : "=r" (retval), "=r" (junk));
|
| + return retval;
|
| + }
|
| +
|
| +#else
|
| +
|
| + #error Need implementation of sqlite3Hwtime() for your platform.
|
| +
|
| + /*
|
| + ** To compile without implementing sqlite3Hwtime() for your platform,
|
| + ** you can remove the above #error and use the following
|
| + ** stub function. You will lose timing support for many
|
| + ** of the debugging and testing utilities, but it should at
|
| + ** least compile and run.
|
| + */
|
| +SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); }
|
| +
|
| +#endif
|
| +
|
| +#endif /* !defined(SQLITE_HWTIME_H) */
|
| +
|
| +/************** End of hwtime.h **********************************************/
|
| +/************** Continuing where we left off in os_common.h ******************/
|
| +
|
| +static sqlite_uint64 g_start;
|
| +static sqlite_uint64 g_elapsed;
|
| +#define TIMER_START g_start=sqlite3Hwtime()
|
| +#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start
|
| +#define TIMER_ELAPSED g_elapsed
|
| +#else
|
| +#define TIMER_START
|
| +#define TIMER_END
|
| +#define TIMER_ELAPSED ((sqlite_uint64)0)
|
| +#endif
|
| +
|
| +/*
|
| +** If we compile with the SQLITE_TEST macro set, then the following block
|
| +** of code will give us the ability to simulate a disk I/O error. This
|
| +** is used for testing the I/O recovery logic.
|
| +*/
|
| +#if defined(SQLITE_TEST)
|
| +SQLITE_API extern int sqlite3_io_error_hit;
|
| +SQLITE_API extern int sqlite3_io_error_hardhit;
|
| +SQLITE_API extern int sqlite3_io_error_pending;
|
| +SQLITE_API extern int sqlite3_io_error_persist;
|
| +SQLITE_API extern int sqlite3_io_error_benign;
|
| +SQLITE_API extern int sqlite3_diskfull_pending;
|
| +SQLITE_API extern int sqlite3_diskfull;
|
| +#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X)
|
| +#define SimulateIOError(CODE) \
|
| + if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \
|
| + || sqlite3_io_error_pending-- == 1 ) \
|
| + { local_ioerr(); CODE; }
|
| +static void local_ioerr(){
|
| + IOTRACE(("IOERR\n"));
|
| + sqlite3_io_error_hit++;
|
| + if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++;
|
| +}
|
| +#define SimulateDiskfullError(CODE) \
|
| + if( sqlite3_diskfull_pending ){ \
|
| + if( sqlite3_diskfull_pending == 1 ){ \
|
| + local_ioerr(); \
|
| + sqlite3_diskfull = 1; \
|
| + sqlite3_io_error_hit = 1; \
|
| + CODE; \
|
| + }else{ \
|
| + sqlite3_diskfull_pending--; \
|
| + } \
|
| + }
|
| +#else
|
| +#define SimulateIOErrorBenign(X)
|
| +#define SimulateIOError(A)
|
| +#define SimulateDiskfullError(A)
|
| +#endif /* defined(SQLITE_TEST) */
|
| +
|
| +/*
|
| +** When testing, keep a count of the number of open files.
|
| +*/
|
| +#if defined(SQLITE_TEST)
|
| +SQLITE_API extern int sqlite3_open_file_count;
|
| +#define OpenCounter(X) sqlite3_open_file_count+=(X)
|
| +#else
|
| +#define OpenCounter(X)
|
| +#endif /* defined(SQLITE_TEST) */
|
| +
|
| +#endif /* !defined(_OS_COMMON_H_) */
|
| +
|
| +/************** End of os_common.h *******************************************/
|
| +/************** Continuing where we left off in mutex_w32.c ******************/
|
| +
|
| +/*
|
| +** Include the header file for the Windows VFS.
|
| +*/
|
| +/************** Include os_win.h in the middle of mutex_w32.c ****************/
|
| +/************** Begin file os_win.h ******************************************/
|
| +/*
|
| +** 2013 November 25
|
| +**
|
| +** 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 code that is specific to Windows.
|
| +*/
|
| +#ifndef SQLITE_OS_WIN_H
|
| +#define SQLITE_OS_WIN_H
|
| +
|
| +/*
|
| +** Include the primary Windows SDK header file.
|
| +*/
|
| +#include "windows.h"
|
| +
|
| +#ifdef __CYGWIN__
|
| +# include <sys/cygwin.h>
|
| +# include <errno.h> /* amalgamator: dontcache */
|
| +#endif
|
| +
|
| +/*
|
| +** Determine if we are dealing with Windows NT.
|
| +**
|
| +** We ought to be able to determine if we are compiling for Windows 9x or
|
| +** Windows NT using the _WIN32_WINNT macro as follows:
|
| +**
|
| +** #if defined(_WIN32_WINNT)
|
| +** # define SQLITE_OS_WINNT 1
|
| +** #else
|
| +** # define SQLITE_OS_WINNT 0
|
| +** #endif
|
| +**
|
| +** However, Visual Studio 2005 does not set _WIN32_WINNT by default, as
|
| +** it ought to, so the above test does not work. We'll just assume that
|
| +** everything is Windows NT unless the programmer explicitly says otherwise
|
| +** by setting SQLITE_OS_WINNT to 0.
|
| +*/
|
| +#if SQLITE_OS_WIN && !defined(SQLITE_OS_WINNT)
|
| +# define SQLITE_OS_WINNT 1
|
| +#endif
|
| +
|
| +/*
|
| +** Determine if we are dealing with Windows CE - which has a much reduced
|
| +** API.
|
| +*/
|
| +#if defined(_WIN32_WCE)
|
| +# define SQLITE_OS_WINCE 1
|
| +#else
|
| +# define SQLITE_OS_WINCE 0
|
| +#endif
|
| +
|
| +/*
|
| +** Determine if we are dealing with WinRT, which provides only a subset of
|
| +** the full Win32 API.
|
| +*/
|
| +#if !defined(SQLITE_OS_WINRT)
|
| +# define SQLITE_OS_WINRT 0
|
| +#endif
|
| +
|
| +/*
|
| +** For WinCE, some API function parameters do not appear to be declared as
|
| +** volatile.
|
| +*/
|
| +#if SQLITE_OS_WINCE
|
| +# define SQLITE_WIN32_VOLATILE
|
| +#else
|
| +# define SQLITE_WIN32_VOLATILE volatile
|
| +#endif
|
| +
|
| +/*
|
| +** For some Windows sub-platforms, the _beginthreadex() / _endthreadex()
|
| +** functions are not available (e.g. those not using MSVC, Cygwin, etc).
|
| +*/
|
| +#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \
|
| + SQLITE_THREADSAFE>0 && !defined(__CYGWIN__)
|
| +# define SQLITE_OS_WIN_THREADS 1
|
| +#else
|
| +# define SQLITE_OS_WIN_THREADS 0
|
| +#endif
|
| +
|
| +#endif /* SQLITE_OS_WIN_H */
|
| +
|
| +/************** End of os_win.h **********************************************/
|
| +/************** Continuing where we left off in mutex_w32.c ******************/
|
| +#endif
|
| +
|
| +/*
|
| +** The code in this file is only used if we are compiling multithreaded
|
| +** on a Win32 system.
|
| +*/
|
| +#ifdef SQLITE_MUTEX_W32
|
| +
|
| +/*
|
| +** Each recursive mutex is an instance of the following structure.
|
| +*/
|
| +struct sqlite3_mutex {
|
| + CRITICAL_SECTION mutex; /* Mutex controlling the lock */
|
| + int id; /* Mutex type */
|
| +#ifdef SQLITE_DEBUG
|
| + volatile int nRef; /* Number of enterances */
|
| + volatile DWORD owner; /* Thread holding this mutex */
|
| + volatile int trace; /* True to trace changes */
|
| +#endif
|
| +};
|
| +
|
| +/*
|
| +** These are the initializer values used when declaring a "static" mutex
|
| +** on Win32. It should be noted that all mutexes require initialization
|
| +** on the Win32 platform.
|
| +*/
|
| +#define SQLITE_W32_MUTEX_INITIALIZER { 0 }
|
| +
|
| +#ifdef SQLITE_DEBUG
|
| +#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0, \
|
| + 0L, (DWORD)0, 0 }
|
| +#else
|
| +#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0 }
|
| +#endif
|
| +
|
| +#ifdef SQLITE_DEBUG
|
| +/*
|
| +** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
|
| +** intended for use only inside assert() statements.
|
| +*/
|
| +static int winMutexHeld(sqlite3_mutex *p){
|
| + return p->nRef!=0 && p->owner==GetCurrentThreadId();
|
| +}
|
| +
|
| +static int winMutexNotheld2(sqlite3_mutex *p, DWORD tid){
|
| + return p->nRef==0 || p->owner!=tid;
|
| +}
|
| +
|
| +static int winMutexNotheld(sqlite3_mutex *p){
|
| + DWORD tid = GetCurrentThreadId();
|
| + return winMutexNotheld2(p, tid);
|
| +}
|
| +#endif
|
| +
|
| +/*
|
| +** Try to provide a memory barrier operation, needed for initialization
|
| +** and also for the xShmBarrier method of the VFS in cases when SQLite is
|
| +** compiled without mutexes (SQLITE_THREADSAFE=0).
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3MemoryBarrier(void){
|
| +#if defined(SQLITE_MEMORY_BARRIER)
|
| + SQLITE_MEMORY_BARRIER;
|
| +#elif defined(__GNUC__)
|
| + __sync_synchronize();
|
| +#elif MSVC_VERSION>=1300
|
| + _ReadWriteBarrier();
|
| +#elif defined(MemoryBarrier)
|
| + MemoryBarrier();
|
| +#endif
|
| +}
|
| +
|
| +/*
|
| +** Initialize and deinitialize the mutex subsystem.
|
| +*/
|
| +static sqlite3_mutex winMutex_staticMutexes[] = {
|
| + SQLITE3_MUTEX_INITIALIZER,
|
| + SQLITE3_MUTEX_INITIALIZER,
|
| + SQLITE3_MUTEX_INITIALIZER,
|
| + SQLITE3_MUTEX_INITIALIZER,
|
| + SQLITE3_MUTEX_INITIALIZER,
|
| + SQLITE3_MUTEX_INITIALIZER,
|
| + SQLITE3_MUTEX_INITIALIZER,
|
| + SQLITE3_MUTEX_INITIALIZER,
|
| + SQLITE3_MUTEX_INITIALIZER,
|
| + SQLITE3_MUTEX_INITIALIZER,
|
| + SQLITE3_MUTEX_INITIALIZER,
|
| + SQLITE3_MUTEX_INITIALIZER
|
| +};
|
| +
|
| +static int winMutex_isInit = 0;
|
| +static int winMutex_isNt = -1; /* <0 means "need to query" */
|
| +
|
| +/* As the winMutexInit() and winMutexEnd() functions are called as part
|
| +** of the sqlite3_initialize() and sqlite3_shutdown() processing, the
|
| +** "interlocked" magic used here is probably not strictly necessary.
|
| +*/
|
| +static LONG SQLITE_WIN32_VOLATILE winMutex_lock = 0;
|
| +
|
| +SQLITE_API int sqlite3_win32_is_nt(void); /* os_win.c */
|
| +SQLITE_API void sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */
|
| +
|
| +static int winMutexInit(void){
|
| + /* The first to increment to 1 does actual initialization */
|
| + if( InterlockedCompareExchange(&winMutex_lock, 1, 0)==0 ){
|
| + int i;
|
| + for(i=0; i<ArraySize(winMutex_staticMutexes); i++){
|
| +#if SQLITE_OS_WINRT
|
| + InitializeCriticalSectionEx(&winMutex_staticMutexes[i].mutex, 0, 0);
|
| +#else
|
| + InitializeCriticalSection(&winMutex_staticMutexes[i].mutex);
|
| +#endif
|
| + }
|
| + winMutex_isInit = 1;
|
| + }else{
|
| + /* Another thread is (in the process of) initializing the static
|
| + ** mutexes */
|
| + while( !winMutex_isInit ){
|
| + sqlite3_win32_sleep(1);
|
| + }
|
| + }
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +static int winMutexEnd(void){
|
| + /* The first to decrement to 0 does actual shutdown
|
| + ** (which should be the last to shutdown.) */
|
| + if( InterlockedCompareExchange(&winMutex_lock, 0, 1)==1 ){
|
| + if( winMutex_isInit==1 ){
|
| + int i;
|
| + for(i=0; i<ArraySize(winMutex_staticMutexes); i++){
|
| + DeleteCriticalSection(&winMutex_staticMutexes[i].mutex);
|
| + }
|
| + winMutex_isInit = 0;
|
| + }
|
| + }
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** The sqlite3_mutex_alloc() routine allocates a new
|
| +** mutex and returns a pointer to it. If it returns NULL
|
| +** that means that a mutex could not be allocated. SQLite
|
| +** will unwind its stack and return an error. The argument
|
| +** to sqlite3_mutex_alloc() is one of these integer constants:
|
| +**
|
| +** <ul>
|
| +** <li> SQLITE_MUTEX_FAST
|
| +** <li> SQLITE_MUTEX_RECURSIVE
|
| +** <li> SQLITE_MUTEX_STATIC_MASTER
|
| +** <li> SQLITE_MUTEX_STATIC_MEM
|
| +** <li> SQLITE_MUTEX_STATIC_OPEN
|
| +** <li> SQLITE_MUTEX_STATIC_PRNG
|
| +** <li> SQLITE_MUTEX_STATIC_LRU
|
| +** <li> SQLITE_MUTEX_STATIC_PMEM
|
| +** <li> SQLITE_MUTEX_STATIC_APP1
|
| +** <li> SQLITE_MUTEX_STATIC_APP2
|
| +** <li> SQLITE_MUTEX_STATIC_APP3
|
| +** <li> SQLITE_MUTEX_STATIC_VFS1
|
| +** <li> SQLITE_MUTEX_STATIC_VFS2
|
| +** <li> SQLITE_MUTEX_STATIC_VFS3
|
| +** </ul>
|
| +**
|
| +** The first two constants 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. But 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() each return
|
| +** a pointer to a static preexisting mutex. Six 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. But for the static
|
| +** mutex types, the same mutex is returned on every call that has
|
| +** the same type number.
|
| +*/
|
| +static sqlite3_mutex *winMutexAlloc(int iType){
|
| + sqlite3_mutex *p;
|
| +
|
| + switch( iType ){
|
| + case SQLITE_MUTEX_FAST:
|
| + case SQLITE_MUTEX_RECURSIVE: {
|
| + p = sqlite3MallocZero( sizeof(*p) );
|
| + if( p ){
|
| + p->id = iType;
|
| +#ifdef SQLITE_DEBUG
|
| +#ifdef SQLITE_WIN32_MUTEX_TRACE_DYNAMIC
|
| + p->trace = 1;
|
| +#endif
|
| +#endif
|
| +#if SQLITE_OS_WINRT
|
| + InitializeCriticalSectionEx(&p->mutex, 0, 0);
|
| +#else
|
| + InitializeCriticalSection(&p->mutex);
|
| +#endif
|
| + }
|
| + break;
|
| + }
|
| + default: {
|
| +#ifdef SQLITE_ENABLE_API_ARMOR
|
| + if( iType-2<0 || iType-2>=ArraySize(winMutex_staticMutexes) ){
|
| + (void)SQLITE_MISUSE_BKPT;
|
| + return 0;
|
| + }
|
| +#endif
|
| + p = &winMutex_staticMutexes[iType-2];
|
| + p->id = iType;
|
| +#ifdef SQLITE_DEBUG
|
| +#ifdef SQLITE_WIN32_MUTEX_TRACE_STATIC
|
| + p->trace = 1;
|
| +#endif
|
| +#endif
|
| + break;
|
| + }
|
| + }
|
| + return p;
|
| +}
|
| +
|
| +
|
| +/*
|
| +** This routine deallocates a previously
|
| +** allocated mutex. SQLite is careful to deallocate every
|
| +** mutex that it allocates.
|
| +*/
|
| +static void winMutexFree(sqlite3_mutex *p){
|
| + assert( p );
|
| + assert( p->nRef==0 && p->owner==0 );
|
| + if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ){
|
| + DeleteCriticalSection(&p->mutex);
|
| + sqlite3_free(p);
|
| + }else{
|
| +#ifdef SQLITE_ENABLE_API_ARMOR
|
| + (void)SQLITE_MISUSE_BKPT;
|
| +#endif
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt
|
| +** to enter a mutex. If another thread is already within the mutex,
|
| +** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return
|
| +** SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK
|
| +** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can
|
| +** be entered multiple times by the same thread. In such cases the,
|
| +** mutex must be exited an equal number of times before another thread
|
| +** can enter. If the same thread tries to enter any other kind of mutex
|
| +** more than once, the behavior is undefined.
|
| +*/
|
| +static void winMutexEnter(sqlite3_mutex *p){
|
| +#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
| + DWORD tid = GetCurrentThreadId();
|
| +#endif
|
| +#ifdef SQLITE_DEBUG
|
| + assert( p );
|
| + assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) );
|
| +#else
|
| + assert( p );
|
| +#endif
|
| + assert( winMutex_isInit==1 );
|
| + EnterCriticalSection(&p->mutex);
|
| +#ifdef SQLITE_DEBUG
|
| + assert( p->nRef>0 || p->owner==0 );
|
| + p->owner = tid;
|
| + p->nRef++;
|
| + if( p->trace ){
|
| + OSTRACE(("ENTER-MUTEX tid=%lu, mutex=%p (%d), nRef=%d\n",
|
| + tid, p, p->trace, p->nRef));
|
| + }
|
| +#endif
|
| +}
|
| +
|
| +static int winMutexTry(sqlite3_mutex *p){
|
| +#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
| + DWORD tid = GetCurrentThreadId();
|
| +#endif
|
| + int rc = SQLITE_BUSY;
|
| + assert( p );
|
| + assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) );
|
| + /*
|
| + ** The sqlite3_mutex_try() routine is very rarely used, and when it
|
| + ** is used it is merely an optimization. So it is OK for it to always
|
| + ** fail.
|
| + **
|
| + ** The TryEnterCriticalSection() interface is only available on WinNT.
|
| + ** And some windows compilers complain if you try to use it without
|
| + ** first doing some #defines that prevent SQLite from building on Win98.
|
| + ** For that reason, we will omit this optimization for now. See
|
| + ** ticket #2685.
|
| + */
|
| +#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0400
|
| + assert( winMutex_isInit==1 );
|
| + assert( winMutex_isNt>=-1 && winMutex_isNt<=1 );
|
| + if( winMutex_isNt<0 ){
|
| + winMutex_isNt = sqlite3_win32_is_nt();
|
| + }
|
| + assert( winMutex_isNt==0 || winMutex_isNt==1 );
|
| + if( winMutex_isNt && TryEnterCriticalSection(&p->mutex) ){
|
| +#ifdef SQLITE_DEBUG
|
| + p->owner = tid;
|
| + p->nRef++;
|
| +#endif
|
| + rc = SQLITE_OK;
|
| + }
|
| +#else
|
| + UNUSED_PARAMETER(p);
|
| +#endif
|
| +#ifdef SQLITE_DEBUG
|
| + if( p->trace ){
|
| + OSTRACE(("TRY-MUTEX tid=%lu, mutex=%p (%d), owner=%lu, nRef=%d, rc=%s\n",
|
| + tid, p, p->trace, p->owner, p->nRef, sqlite3ErrName(rc)));
|
| + }
|
| +#endif
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** The sqlite3_mutex_leave() routine exits a mutex that was
|
| +** previously entered by the same thread. The behavior
|
| +** is undefined if the mutex is not currently entered or
|
| +** is not currently allocated. SQLite will never do either.
|
| +*/
|
| +static void winMutexLeave(sqlite3_mutex *p){
|
| +#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
| + DWORD tid = GetCurrentThreadId();
|
| +#endif
|
| + assert( p );
|
| +#ifdef SQLITE_DEBUG
|
| + assert( p->nRef>0 );
|
| + assert( p->owner==tid );
|
| + p->nRef--;
|
| + if( p->nRef==0 ) p->owner = 0;
|
| + assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
|
| +#endif
|
| + assert( winMutex_isInit==1 );
|
| + LeaveCriticalSection(&p->mutex);
|
| +#ifdef SQLITE_DEBUG
|
| + if( p->trace ){
|
| + OSTRACE(("LEAVE-MUTEX tid=%lu, mutex=%p (%d), nRef=%d\n",
|
| + tid, p, p->trace, p->nRef));
|
| + }
|
| +#endif
|
| +}
|
| +
|
| +SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
|
| + static const sqlite3_mutex_methods sMutex = {
|
| + winMutexInit,
|
| + winMutexEnd,
|
| + winMutexAlloc,
|
| + winMutexFree,
|
| + winMutexEnter,
|
| + winMutexTry,
|
| + winMutexLeave,
|
| +#ifdef SQLITE_DEBUG
|
| + winMutexHeld,
|
| + winMutexNotheld
|
| +#else
|
| + 0,
|
| + 0
|
| +#endif
|
| + };
|
| + return &sMutex;
|
| +}
|
| +
|
| +#endif /* SQLITE_MUTEX_W32 */
|
| +
|
| +/************** End of mutex_w32.c *******************************************/
|
| +/************** Begin file malloc.c ******************************************/
|
| +/*
|
| +** 2001 September 15
|
| +**
|
| +** 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.
|
| +**
|
| +*************************************************************************
|
| +**
|
| +** Memory allocation functions used throughout sqlite.
|
| +*/
|
| +/* #include "sqliteInt.h" */
|
| +/* #include <stdarg.h> */
|
| +
|
| +/*
|
| +** Attempt to release up to n bytes of non-essential memory currently
|
| +** held by SQLite. An example of non-essential memory is memory used to
|
| +** cache database pages that are not currently in use.
|
| +*/
|
| +SQLITE_API int sqlite3_release_memory(int n){
|
| +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
| + return sqlite3PcacheReleaseMemory(n);
|
| +#else
|
| + /* IMPLEMENTATION-OF: R-34391-24921 The sqlite3_release_memory() routine
|
| + ** is a no-op returning zero if SQLite is not compiled with
|
| + ** SQLITE_ENABLE_MEMORY_MANAGEMENT. */
|
| + UNUSED_PARAMETER(n);
|
| + return 0;
|
| +#endif
|
| +}
|
| +
|
| +/*
|
| +** An instance of the following object records the location of
|
| +** each unused scratch buffer.
|
| +*/
|
| +typedef struct ScratchFreeslot {
|
| + struct ScratchFreeslot *pNext; /* Next unused scratch buffer */
|
| +} ScratchFreeslot;
|
| +
|
| +/*
|
| +** State information local to the memory allocation subsystem.
|
| +*/
|
| +static SQLITE_WSD struct Mem0Global {
|
| + sqlite3_mutex *mutex; /* Mutex to serialize access */
|
| + sqlite3_int64 alarmThreshold; /* The soft heap limit */
|
| +
|
| + /*
|
| + ** Pointers to the end of sqlite3GlobalConfig.pScratch memory
|
| + ** (so that a range test can be used to determine if an allocation
|
| + ** being freed came from pScratch) and a pointer to the list of
|
| + ** unused scratch allocations.
|
| + */
|
| + void *pScratchEnd;
|
| + ScratchFreeslot *pScratchFree;
|
| + u32 nScratchFree;
|
| +
|
| + /*
|
| + ** True if heap is nearly "full" where "full" is defined by the
|
| + ** sqlite3_soft_heap_limit() setting.
|
| + */
|
| + int nearlyFull;
|
| +} mem0 = { 0, 0, 0, 0, 0, 0 };
|
| +
|
| +#define mem0 GLOBAL(struct Mem0Global, mem0)
|
| +
|
| +/*
|
| +** Return the memory allocator mutex. sqlite3_status() needs it.
|
| +*/
|
| +SQLITE_PRIVATE sqlite3_mutex *sqlite3MallocMutex(void){
|
| + return mem0.mutex;
|
| +}
|
| +
|
| +#ifndef SQLITE_OMIT_DEPRECATED
|
| +/*
|
| +** Deprecated external interface. It used to set an alarm callback
|
| +** that was invoked when memory usage grew too large. Now it is a
|
| +** no-op.
|
| +*/
|
| +SQLITE_API int sqlite3_memory_alarm(
|
| + void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
|
| + void *pArg,
|
| + sqlite3_int64 iThreshold
|
| +){
|
| + (void)xCallback;
|
| + (void)pArg;
|
| + (void)iThreshold;
|
| + return SQLITE_OK;
|
| +}
|
| +#endif
|
| +
|
| +/*
|
| +** Set the soft heap-size limit for the library. Passing a zero or
|
| +** negative value indicates no limit.
|
| +*/
|
| +SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){
|
| + sqlite3_int64 priorLimit;
|
| + sqlite3_int64 excess;
|
| + sqlite3_int64 nUsed;
|
| +#ifndef SQLITE_OMIT_AUTOINIT
|
| + int rc = sqlite3_initialize();
|
| + if( rc ) return -1;
|
| +#endif
|
| + sqlite3_mutex_enter(mem0.mutex);
|
| + priorLimit = mem0.alarmThreshold;
|
| + if( n<0 ){
|
| + sqlite3_mutex_leave(mem0.mutex);
|
| + return priorLimit;
|
| + }
|
| + mem0.alarmThreshold = n;
|
| + nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
|
| + mem0.nearlyFull = (n>0 && n<=nUsed);
|
| + sqlite3_mutex_leave(mem0.mutex);
|
| + excess = sqlite3_memory_used() - n;
|
| + if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff));
|
| + return priorLimit;
|
| +}
|
| +SQLITE_API void sqlite3_soft_heap_limit(int n){
|
| + if( n<0 ) n = 0;
|
| + sqlite3_soft_heap_limit64(n);
|
| +}
|
| +
|
| +/*
|
| +** Initialize the memory allocation subsystem.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3MallocInit(void){
|
| + int rc;
|
| + if( sqlite3GlobalConfig.m.xMalloc==0 ){
|
| + sqlite3MemSetDefault();
|
| + }
|
| + memset(&mem0, 0, sizeof(mem0));
|
| + mem0.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
|
| + if( sqlite3GlobalConfig.pScratch && sqlite3GlobalConfig.szScratch>=100
|
| + && sqlite3GlobalConfig.nScratch>0 ){
|
| + int i, n, sz;
|
| + ScratchFreeslot *pSlot;
|
| + sz = ROUNDDOWN8(sqlite3GlobalConfig.szScratch);
|
| + sqlite3GlobalConfig.szScratch = sz;
|
| + pSlot = (ScratchFreeslot*)sqlite3GlobalConfig.pScratch;
|
| + n = sqlite3GlobalConfig.nScratch;
|
| + mem0.pScratchFree = pSlot;
|
| + mem0.nScratchFree = n;
|
| + for(i=0; i<n-1; i++){
|
| + pSlot->pNext = (ScratchFreeslot*)(sz+(char*)pSlot);
|
| + pSlot = pSlot->pNext;
|
| + }
|
| + pSlot->pNext = 0;
|
| + mem0.pScratchEnd = (void*)&pSlot[1];
|
| + }else{
|
| + mem0.pScratchEnd = 0;
|
| + sqlite3GlobalConfig.pScratch = 0;
|
| + sqlite3GlobalConfig.szScratch = 0;
|
| + sqlite3GlobalConfig.nScratch = 0;
|
| + }
|
| + if( sqlite3GlobalConfig.pPage==0 || sqlite3GlobalConfig.szPage<512
|
| + || sqlite3GlobalConfig.nPage<=0 ){
|
| + sqlite3GlobalConfig.pPage = 0;
|
| + sqlite3GlobalConfig.szPage = 0;
|
| + }
|
| + rc = sqlite3GlobalConfig.m.xInit(sqlite3GlobalConfig.m.pAppData);
|
| + if( rc!=SQLITE_OK ) memset(&mem0, 0, sizeof(mem0));
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Return true if the heap is currently under memory pressure - in other
|
| +** words if the amount of heap used is close to the limit set by
|
| +** sqlite3_soft_heap_limit().
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3HeapNearlyFull(void){
|
| + return mem0.nearlyFull;
|
| +}
|
| +
|
| +/*
|
| +** Deinitialize the memory allocation subsystem.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3MallocEnd(void){
|
| + if( sqlite3GlobalConfig.m.xShutdown ){
|
| + sqlite3GlobalConfig.m.xShutdown(sqlite3GlobalConfig.m.pAppData);
|
| + }
|
| + memset(&mem0, 0, sizeof(mem0));
|
| +}
|
| +
|
| +/*
|
| +** Return the amount of memory currently checked out.
|
| +*/
|
| +SQLITE_API sqlite3_int64 sqlite3_memory_used(void){
|
| + sqlite3_int64 res, mx;
|
| + sqlite3_status64(SQLITE_STATUS_MEMORY_USED, &res, &mx, 0);
|
| + return res;
|
| +}
|
| +
|
| +/*
|
| +** Return the maximum amount of memory that has ever been
|
| +** checked out since either the beginning of this process
|
| +** or since the most recent reset.
|
| +*/
|
| +SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag){
|
| + sqlite3_int64 res, mx;
|
| + sqlite3_status64(SQLITE_STATUS_MEMORY_USED, &res, &mx, resetFlag);
|
| + return mx;
|
| +}
|
| +
|
| +/*
|
| +** Trigger the alarm
|
| +*/
|
| +static void sqlite3MallocAlarm(int nByte){
|
| + if( mem0.alarmThreshold<=0 ) return;
|
| + sqlite3_mutex_leave(mem0.mutex);
|
| + sqlite3_release_memory(nByte);
|
| + sqlite3_mutex_enter(mem0.mutex);
|
| +}
|
| +
|
| +/*
|
| +** Do a memory allocation with statistics and alarms. Assume the
|
| +** lock is already held.
|
| +*/
|
| +static void mallocWithAlarm(int n, void **pp){
|
| + void *p;
|
| + int nFull;
|
| + assert( sqlite3_mutex_held(mem0.mutex) );
|
| + assert( n>0 );
|
| +
|
| + /* In Firefox (circa 2017-02-08), xRoundup() is remapped to an internal
|
| + ** implementation of malloc_good_size(), which must be called in debug
|
| + ** mode and specifically when the DMD "Dark Matter Detector" is enabled
|
| + ** or else a crash results. Hence, do not attempt to optimize out the
|
| + ** following xRoundup() call. */
|
| + nFull = sqlite3GlobalConfig.m.xRoundup(n);
|
| +
|
| + sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, n);
|
| + if( mem0.alarmThreshold>0 ){
|
| + sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
|
| + if( nUsed >= mem0.alarmThreshold - nFull ){
|
| + mem0.nearlyFull = 1;
|
| + sqlite3MallocAlarm(nFull);
|
| + }else{
|
| + mem0.nearlyFull = 0;
|
| + }
|
| + }
|
| + p = sqlite3GlobalConfig.m.xMalloc(nFull);
|
| +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
| + if( p==0 && mem0.alarmThreshold>0 ){
|
| + sqlite3MallocAlarm(nFull);
|
| + p = sqlite3GlobalConfig.m.xMalloc(nFull);
|
| + }
|
| +#endif
|
| + if( p ){
|
| + nFull = sqlite3MallocSize(p);
|
| + sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nFull);
|
| + sqlite3StatusUp(SQLITE_STATUS_MALLOC_COUNT, 1);
|
| + }
|
| + *pp = p;
|
| +}
|
| +
|
| +/*
|
| +** Allocate memory. This routine is like sqlite3_malloc() except that it
|
| +** assumes the memory subsystem has already been initialized.
|
| +*/
|
| +SQLITE_PRIVATE void *sqlite3Malloc(u64 n){
|
| + void *p;
|
| + if( n==0 || n>=0x7fffff00 ){
|
| + /* A memory allocation of a number of bytes which is near the maximum
|
| + ** signed integer value might cause an integer overflow inside of the
|
| + ** xMalloc(). Hence we limit the maximum size to 0x7fffff00, giving
|
| + ** 255 bytes of overhead. SQLite itself will never use anything near
|
| + ** this amount. The only way to reach the limit is with sqlite3_malloc() */
|
| + p = 0;
|
| + }else if( sqlite3GlobalConfig.bMemstat ){
|
| + sqlite3_mutex_enter(mem0.mutex);
|
| + mallocWithAlarm((int)n, &p);
|
| + sqlite3_mutex_leave(mem0.mutex);
|
| + }else{
|
| + p = sqlite3GlobalConfig.m.xMalloc((int)n);
|
| + }
|
| + assert( EIGHT_BYTE_ALIGNMENT(p) ); /* IMP: R-11148-40995 */
|
| + return p;
|
| +}
|
| +
|
| +/*
|
| +** This version of the memory allocation is for use by the application.
|
| +** First make sure the memory subsystem is initialized, then do the
|
| +** allocation.
|
| +*/
|
| +SQLITE_API void *sqlite3_malloc(int n){
|
| +#ifndef SQLITE_OMIT_AUTOINIT
|
| + if( sqlite3_initialize() ) return 0;
|
| +#endif
|
| + return n<=0 ? 0 : sqlite3Malloc(n);
|
| +}
|
| +SQLITE_API void *sqlite3_malloc64(sqlite3_uint64 n){
|
| +#ifndef SQLITE_OMIT_AUTOINIT
|
| + if( sqlite3_initialize() ) return 0;
|
| +#endif
|
| + return sqlite3Malloc(n);
|
| +}
|
| +
|
| +/*
|
| +** Each thread may only have a single outstanding allocation from
|
| +** xScratchMalloc(). We verify this constraint in the single-threaded
|
| +** case by setting scratchAllocOut to 1 when an allocation
|
| +** is outstanding clearing it when the allocation is freed.
|
| +*/
|
| +#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
|
| +static int scratchAllocOut = 0;
|
| +#endif
|
| +
|
| +
|
| +/*
|
| +** Allocate memory that is to be used and released right away.
|
| +** This routine is similar to alloca() in that it is not intended
|
| +** for situations where the memory might be held long-term. This
|
| +** routine is intended to get memory to old large transient data
|
| +** structures that would not normally fit on the stack of an
|
| +** embedded processor.
|
| +*/
|
| +SQLITE_PRIVATE void *sqlite3ScratchMalloc(int n){
|
| + void *p;
|
| + assert( n>0 );
|
| +
|
| + sqlite3_mutex_enter(mem0.mutex);
|
| + sqlite3StatusHighwater(SQLITE_STATUS_SCRATCH_SIZE, n);
|
| + if( mem0.nScratchFree && sqlite3GlobalConfig.szScratch>=n ){
|
| + p = mem0.pScratchFree;
|
| + mem0.pScratchFree = mem0.pScratchFree->pNext;
|
| + mem0.nScratchFree--;
|
| + sqlite3StatusUp(SQLITE_STATUS_SCRATCH_USED, 1);
|
| + sqlite3_mutex_leave(mem0.mutex);
|
| + }else{
|
| + sqlite3_mutex_leave(mem0.mutex);
|
| + p = sqlite3Malloc(n);
|
| + if( sqlite3GlobalConfig.bMemstat && p ){
|
| + sqlite3_mutex_enter(mem0.mutex);
|
| + sqlite3StatusUp(SQLITE_STATUS_SCRATCH_OVERFLOW, sqlite3MallocSize(p));
|
| + sqlite3_mutex_leave(mem0.mutex);
|
| + }
|
| + sqlite3MemdebugSetType(p, MEMTYPE_SCRATCH);
|
| + }
|
| + assert( sqlite3_mutex_notheld(mem0.mutex) );
|
| +
|
| +
|
| +#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
|
| + /* EVIDENCE-OF: R-12970-05880 SQLite will not use more than one scratch
|
| + ** buffers per thread.
|
| + **
|
| + ** This can only be checked in single-threaded mode.
|
| + */
|
| + assert( scratchAllocOut==0 );
|
| + if( p ) scratchAllocOut++;
|
| +#endif
|
| +
|
| + return p;
|
| +}
|
| +SQLITE_PRIVATE void sqlite3ScratchFree(void *p){
|
| + if( p ){
|
| +
|
| +#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
|
| + /* Verify that no more than two scratch allocation per thread
|
| + ** is outstanding at one time. (This is only checked in the
|
| + ** single-threaded case since checking in the multi-threaded case
|
| + ** would be much more complicated.) */
|
| + assert( scratchAllocOut>=1 && scratchAllocOut<=2 );
|
| + scratchAllocOut--;
|
| +#endif
|
| +
|
| + if( SQLITE_WITHIN(p, sqlite3GlobalConfig.pScratch, mem0.pScratchEnd) ){
|
| + /* Release memory from the SQLITE_CONFIG_SCRATCH allocation */
|
| + ScratchFreeslot *pSlot;
|
| + pSlot = (ScratchFreeslot*)p;
|
| + sqlite3_mutex_enter(mem0.mutex);
|
| + pSlot->pNext = mem0.pScratchFree;
|
| + mem0.pScratchFree = pSlot;
|
| + mem0.nScratchFree++;
|
| + assert( mem0.nScratchFree <= (u32)sqlite3GlobalConfig.nScratch );
|
| + sqlite3StatusDown(SQLITE_STATUS_SCRATCH_USED, 1);
|
| + sqlite3_mutex_leave(mem0.mutex);
|
| + }else{
|
| + /* Release memory back to the heap */
|
| + assert( sqlite3MemdebugHasType(p, MEMTYPE_SCRATCH) );
|
| + assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_SCRATCH) );
|
| + sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
|
| + if( sqlite3GlobalConfig.bMemstat ){
|
| + int iSize = sqlite3MallocSize(p);
|
| + sqlite3_mutex_enter(mem0.mutex);
|
| + sqlite3StatusDown(SQLITE_STATUS_SCRATCH_OVERFLOW, iSize);
|
| + sqlite3StatusDown(SQLITE_STATUS_MEMORY_USED, iSize);
|
| + sqlite3StatusDown(SQLITE_STATUS_MALLOC_COUNT, 1);
|
| + sqlite3GlobalConfig.m.xFree(p);
|
| + sqlite3_mutex_leave(mem0.mutex);
|
| + }else{
|
| + sqlite3GlobalConfig.m.xFree(p);
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** TRUE if p is a lookaside memory allocation from db
|
| +*/
|
| +#ifndef SQLITE_OMIT_LOOKASIDE
|
| +static int isLookaside(sqlite3 *db, void *p){
|
| + return SQLITE_WITHIN(p, db->lookaside.pStart, db->lookaside.pEnd);
|
| +}
|
| +#else
|
| +#define isLookaside(A,B) 0
|
| +#endif
|
| +
|
| +/*
|
| +** Return the size of a memory allocation previously obtained from
|
| +** sqlite3Malloc() or sqlite3_malloc().
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3MallocSize(void *p){
|
| + assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
|
| + return sqlite3GlobalConfig.m.xSize(p);
|
| +}
|
| +SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, void *p){
|
| + assert( p!=0 );
|
| + if( db==0 || !isLookaside(db,p) ){
|
| +#if SQLITE_DEBUG
|
| + if( db==0 ){
|
| + assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) );
|
| + assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
|
| + }else{
|
| + assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
|
| + assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
|
| + }
|
| +#endif
|
| + return sqlite3GlobalConfig.m.xSize(p);
|
| + }else{
|
| + assert( sqlite3_mutex_held(db->mutex) );
|
| + return db->lookaside.sz;
|
| + }
|
| +}
|
| +SQLITE_API sqlite3_uint64 sqlite3_msize(void *p){
|
| + assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) );
|
| + assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
|
| + return p ? sqlite3GlobalConfig.m.xSize(p) : 0;
|
| +}
|
| +
|
| +/*
|
| +** Free memory previously obtained from sqlite3Malloc().
|
| +*/
|
| +SQLITE_API void sqlite3_free(void *p){
|
| + if( p==0 ) return; /* IMP: R-49053-54554 */
|
| + assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
|
| + assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) );
|
| + if( sqlite3GlobalConfig.bMemstat ){
|
| + sqlite3_mutex_enter(mem0.mutex);
|
| + sqlite3StatusDown(SQLITE_STATUS_MEMORY_USED, sqlite3MallocSize(p));
|
| + sqlite3StatusDown(SQLITE_STATUS_MALLOC_COUNT, 1);
|
| + sqlite3GlobalConfig.m.xFree(p);
|
| + sqlite3_mutex_leave(mem0.mutex);
|
| + }else{
|
| + sqlite3GlobalConfig.m.xFree(p);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Add the size of memory allocation "p" to the count in
|
| +** *db->pnBytesFreed.
|
| +*/
|
| +static SQLITE_NOINLINE void measureAllocationSize(sqlite3 *db, void *p){
|
| + *db->pnBytesFreed += sqlite3DbMallocSize(db,p);
|
| +}
|
| +
|
| +/*
|
| +** Free memory that might be associated with a particular database
|
| +** connection.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3DbFree(sqlite3 *db, void *p){
|
| + assert( db==0 || sqlite3_mutex_held(db->mutex) );
|
| + if( p==0 ) return;
|
| + if( db ){
|
| + if( db->pnBytesFreed ){
|
| + measureAllocationSize(db, p);
|
| + return;
|
| + }
|
| + if( isLookaside(db, p) ){
|
| + LookasideSlot *pBuf = (LookasideSlot*)p;
|
| +#if SQLITE_DEBUG
|
| + /* Trash all content in the buffer being freed */
|
| + memset(p, 0xaa, db->lookaside.sz);
|
| +#endif
|
| + pBuf->pNext = db->lookaside.pFree;
|
| + db->lookaside.pFree = pBuf;
|
| + db->lookaside.nOut--;
|
| + return;
|
| + }
|
| + }
|
| + assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
|
| + assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
|
| + assert( db!=0 || sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) );
|
| + sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
|
| + sqlite3_free(p);
|
| +}
|
| +
|
| +/*
|
| +** Change the size of an existing memory allocation
|
| +*/
|
| +SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, u64 nBytes){
|
| + int nOld, nNew, nDiff;
|
| + void *pNew;
|
| + assert( sqlite3MemdebugHasType(pOld, MEMTYPE_HEAP) );
|
| + assert( sqlite3MemdebugNoType(pOld, (u8)~MEMTYPE_HEAP) );
|
| + if( pOld==0 ){
|
| + return sqlite3Malloc(nBytes); /* IMP: R-04300-56712 */
|
| + }
|
| + if( nBytes==0 ){
|
| + sqlite3_free(pOld); /* IMP: R-26507-47431 */
|
| + return 0;
|
| + }
|
| + if( nBytes>=0x7fffff00 ){
|
| + /* The 0x7ffff00 limit term is explained in comments on sqlite3Malloc() */
|
| + return 0;
|
| + }
|
| + nOld = sqlite3MallocSize(pOld);
|
| + /* IMPLEMENTATION-OF: R-46199-30249 SQLite guarantees that the second
|
| + ** argument to xRealloc is always a value returned by a prior call to
|
| + ** xRoundup. */
|
| + nNew = sqlite3GlobalConfig.m.xRoundup((int)nBytes);
|
| + if( nOld==nNew ){
|
| + pNew = pOld;
|
| + }else if( sqlite3GlobalConfig.bMemstat ){
|
| + sqlite3_mutex_enter(mem0.mutex);
|
| + sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, (int)nBytes);
|
| + nDiff = nNew - nOld;
|
| + if( nDiff>0 && sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >=
|
| + mem0.alarmThreshold-nDiff ){
|
| + sqlite3MallocAlarm(nDiff);
|
| + }
|
| + pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
|
| + if( pNew==0 && mem0.alarmThreshold>0 ){
|
| + sqlite3MallocAlarm((int)nBytes);
|
| + pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
|
| + }
|
| + if( pNew ){
|
| + nNew = sqlite3MallocSize(pNew);
|
| + sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nNew-nOld);
|
| + }
|
| + sqlite3_mutex_leave(mem0.mutex);
|
| + }else{
|
| + pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
|
| + }
|
| + assert( EIGHT_BYTE_ALIGNMENT(pNew) ); /* IMP: R-11148-40995 */
|
| + return pNew;
|
| +}
|
| +
|
| +/*
|
| +** The public interface to sqlite3Realloc. Make sure that the memory
|
| +** subsystem is initialized prior to invoking sqliteRealloc.
|
| +*/
|
| +SQLITE_API void *sqlite3_realloc(void *pOld, int n){
|
| +#ifndef SQLITE_OMIT_AUTOINIT
|
| + if( sqlite3_initialize() ) return 0;
|
| +#endif
|
| + if( n<0 ) n = 0; /* IMP: R-26507-47431 */
|
| + return sqlite3Realloc(pOld, n);
|
| +}
|
| +SQLITE_API void *sqlite3_realloc64(void *pOld, sqlite3_uint64 n){
|
| +#ifndef SQLITE_OMIT_AUTOINIT
|
| + if( sqlite3_initialize() ) return 0;
|
| +#endif
|
| + return sqlite3Realloc(pOld, n);
|
| +}
|
| +
|
| +
|
| +/*
|
| +** Allocate and zero memory.
|
| +*/
|
| +SQLITE_PRIVATE void *sqlite3MallocZero(u64 n){
|
| + void *p = sqlite3Malloc(n);
|
| + if( p ){
|
| + memset(p, 0, (size_t)n);
|
| + }
|
| + return p;
|
| +}
|
| +
|
| +/*
|
| +** Allocate and zero memory. If the allocation fails, make
|
| +** the mallocFailed flag in the connection pointer.
|
| +*/
|
| +SQLITE_PRIVATE void *sqlite3DbMallocZero(sqlite3 *db, u64 n){
|
| + void *p;
|
| + testcase( db==0 );
|
| + p = sqlite3DbMallocRaw(db, n);
|
| + if( p ) memset(p, 0, (size_t)n);
|
| + return p;
|
| +}
|
| +
|
| +
|
| +/* Finish the work of sqlite3DbMallocRawNN for the unusual and
|
| +** slower case when the allocation cannot be fulfilled using lookaside.
|
| +*/
|
| +static SQLITE_NOINLINE void *dbMallocRawFinish(sqlite3 *db, u64 n){
|
| + void *p;
|
| + assert( db!=0 );
|
| + p = sqlite3Malloc(n);
|
| + if( !p ) sqlite3OomFault(db);
|
| + sqlite3MemdebugSetType(p,
|
| + (db->lookaside.bDisable==0) ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP);
|
| + return p;
|
| +}
|
| +
|
| +/*
|
| +** Allocate memory, either lookaside (if possible) or heap.
|
| +** If the allocation fails, set the mallocFailed flag in
|
| +** the connection pointer.
|
| +**
|
| +** If db!=0 and db->mallocFailed is true (indicating a prior malloc
|
| +** failure on the same database connection) then always return 0.
|
| +** Hence for a particular database connection, once malloc starts
|
| +** failing, it fails consistently until mallocFailed is reset.
|
| +** This is an important assumption. There are many places in the
|
| +** code that do things like this:
|
| +**
|
| +** int *a = (int*)sqlite3DbMallocRaw(db, 100);
|
| +** int *b = (int*)sqlite3DbMallocRaw(db, 200);
|
| +** if( b ) a[10] = 9;
|
| +**
|
| +** In other words, if a subsequent malloc (ex: "b") worked, it is assumed
|
| +** that all prior mallocs (ex: "a") worked too.
|
| +**
|
| +** The sqlite3MallocRawNN() variant guarantees that the "db" parameter is
|
| +** not a NULL pointer.
|
| +*/
|
| +SQLITE_PRIVATE void *sqlite3DbMallocRaw(sqlite3 *db, u64 n){
|
| + void *p;
|
| + if( db ) return sqlite3DbMallocRawNN(db, n);
|
| + p = sqlite3Malloc(n);
|
| + sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
|
| + return p;
|
| +}
|
| +SQLITE_PRIVATE void *sqlite3DbMallocRawNN(sqlite3 *db, u64 n){
|
| +#ifndef SQLITE_OMIT_LOOKASIDE
|
| + LookasideSlot *pBuf;
|
| + assert( db!=0 );
|
| + assert( sqlite3_mutex_held(db->mutex) );
|
| + assert( db->pnBytesFreed==0 );
|
| + if( db->lookaside.bDisable==0 ){
|
| + assert( db->mallocFailed==0 );
|
| + if( n>db->lookaside.sz ){
|
| + db->lookaside.anStat[1]++;
|
| + }else if( (pBuf = db->lookaside.pFree)==0 ){
|
| + db->lookaside.anStat[2]++;
|
| + }else{
|
| + db->lookaside.pFree = pBuf->pNext;
|
| + db->lookaside.nOut++;
|
| + db->lookaside.anStat[0]++;
|
| + if( db->lookaside.nOut>db->lookaside.mxOut ){
|
| + db->lookaside.mxOut = db->lookaside.nOut;
|
| + }
|
| + return (void*)pBuf;
|
| + }
|
| + }else if( db->mallocFailed ){
|
| + return 0;
|
| + }
|
| +#else
|
| + assert( db!=0 );
|
| + assert( sqlite3_mutex_held(db->mutex) );
|
| + assert( db->pnBytesFreed==0 );
|
| + if( db->mallocFailed ){
|
| + return 0;
|
| + }
|
| +#endif
|
| + return dbMallocRawFinish(db, n);
|
| +}
|
| +
|
| +/* Forward declaration */
|
| +static SQLITE_NOINLINE void *dbReallocFinish(sqlite3 *db, void *p, u64 n);
|
| +
|
| +/*
|
| +** Resize the block of memory pointed to by p to n bytes. If the
|
| +** resize fails, set the mallocFailed flag in the connection object.
|
| +*/
|
| +SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *db, void *p, u64 n){
|
| + assert( db!=0 );
|
| + if( p==0 ) return sqlite3DbMallocRawNN(db, n);
|
| + assert( sqlite3_mutex_held(db->mutex) );
|
| + if( isLookaside(db,p) && n<=db->lookaside.sz ) return p;
|
| + return dbReallocFinish(db, p, n);
|
| +}
|
| +static SQLITE_NOINLINE void *dbReallocFinish(sqlite3 *db, void *p, u64 n){
|
| + void *pNew = 0;
|
| + assert( db!=0 );
|
| + assert( p!=0 );
|
| + if( db->mallocFailed==0 ){
|
| + if( isLookaside(db, p) ){
|
| + pNew = sqlite3DbMallocRawNN(db, n);
|
| + if( pNew ){
|
| + memcpy(pNew, p, db->lookaside.sz);
|
| + sqlite3DbFree(db, p);
|
| + }
|
| + }else{
|
| + assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
|
| + assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
|
| + sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
|
| + pNew = sqlite3_realloc64(p, n);
|
| + if( !pNew ){
|
| + sqlite3OomFault(db);
|
| + }
|
| + sqlite3MemdebugSetType(pNew,
|
| + (db->lookaside.bDisable==0 ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP));
|
| + }
|
| + }
|
| + return pNew;
|
| +}
|
| +
|
| +/*
|
| +** Attempt to reallocate p. If the reallocation fails, then free p
|
| +** and set the mallocFailed flag in the database connection.
|
| +*/
|
| +SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *db, void *p, u64 n){
|
| + void *pNew;
|
| + pNew = sqlite3DbRealloc(db, p, n);
|
| + if( !pNew ){
|
| + sqlite3DbFree(db, p);
|
| + }
|
| + return pNew;
|
| +}
|
| +
|
| +/*
|
| +** Make a copy of a string in memory obtained from sqliteMalloc(). These
|
| +** functions call sqlite3MallocRaw() directly instead of sqliteMalloc(). This
|
| +** is because when memory debugging is turned on, these two functions are
|
| +** called via macros that record the current file and line number in the
|
| +** ThreadData structure.
|
| +*/
|
| +SQLITE_PRIVATE char *sqlite3DbStrDup(sqlite3 *db, const char *z){
|
| + char *zNew;
|
| + size_t n;
|
| + if( z==0 ){
|
| + return 0;
|
| + }
|
| + n = strlen(z) + 1;
|
| + zNew = sqlite3DbMallocRaw(db, n);
|
| + if( zNew ){
|
| + memcpy(zNew, z, n);
|
| + }
|
| + return zNew;
|
| +}
|
| +SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3 *db, const char *z, u64 n){
|
| + char *zNew;
|
| + assert( db!=0 );
|
| + if( z==0 ){
|
| + return 0;
|
| + }
|
| + assert( (n&0x7fffffff)==n );
|
| + zNew = sqlite3DbMallocRawNN(db, n+1);
|
| + if( zNew ){
|
| + memcpy(zNew, z, (size_t)n);
|
| + zNew[n] = 0;
|
| + }
|
| + return zNew;
|
| +}
|
| +
|
| +/*
|
| +** Free any prior content in *pz and replace it with a copy of zNew.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3SetString(char **pz, sqlite3 *db, const char *zNew){
|
| + sqlite3DbFree(db, *pz);
|
| + *pz = sqlite3DbStrDup(db, zNew);
|
| +}
|
| +
|
| +/*
|
| +** Call this routine to record the fact that an OOM (out-of-memory) error
|
| +** has happened. This routine will set db->mallocFailed, and also
|
| +** temporarily disable the lookaside memory allocator and interrupt
|
| +** any running VDBEs.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3OomFault(sqlite3 *db){
|
| + if( db->mallocFailed==0 && db->bBenignMalloc==0 ){
|
| + db->mallocFailed = 1;
|
| + if( db->nVdbeExec>0 ){
|
| + db->u1.isInterrupted = 1;
|
| + }
|
| + db->lookaside.bDisable++;
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** This routine reactivates the memory allocator and clears the
|
| +** db->mallocFailed flag as necessary.
|
| +**
|
| +** The memory allocator is not restarted if there are running
|
| +** VDBEs.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3OomClear(sqlite3 *db){
|
| + if( db->mallocFailed && db->nVdbeExec==0 ){
|
| + db->mallocFailed = 0;
|
| + db->u1.isInterrupted = 0;
|
| + assert( db->lookaside.bDisable>0 );
|
| + db->lookaside.bDisable--;
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Take actions at the end of an API call to indicate an OOM error
|
| +*/
|
| +static SQLITE_NOINLINE int apiOomError(sqlite3 *db){
|
| + sqlite3OomClear(db);
|
| + sqlite3Error(db, SQLITE_NOMEM);
|
| + return SQLITE_NOMEM_BKPT;
|
| +}
|
| +
|
| +/*
|
| +** This function must be called before exiting any API function (i.e.
|
| +** returning control to the user) that has called sqlite3_malloc or
|
| +** sqlite3_realloc.
|
| +**
|
| +** The returned value is normally a copy of the second argument to this
|
| +** function. However, if a malloc() failure has occurred since the previous
|
| +** invocation SQLITE_NOMEM is returned instead.
|
| +**
|
| +** If an OOM as occurred, then the connection error-code (the value
|
| +** returned by sqlite3_errcode()) is set to SQLITE_NOMEM.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3ApiExit(sqlite3* db, int rc){
|
| + /* If the db handle must hold the connection handle mutex here.
|
| + ** Otherwise the read (and possible write) of db->mallocFailed
|
| + ** is unsafe, as is the call to sqlite3Error().
|
| + */
|
| + assert( db!=0 );
|
| + assert( sqlite3_mutex_held(db->mutex) );
|
| + if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){
|
| + return apiOomError(db);
|
| + }
|
| + return rc & db->errMask;
|
| +}
|
| +
|
| +/************** End of malloc.c **********************************************/
|
| +/************** Begin file printf.c ******************************************/
|
| +/*
|
| +** The "printf" code that follows dates from the 1980's. It is in
|
| +** the public domain.
|
| +**
|
| +**************************************************************************
|
| +**
|
| +** This file contains code for a set of "printf"-like routines. These
|
| +** routines format strings much like the printf() from the standard C
|
| +** library, though the implementation here has enhancements to support
|
| +** SQLite.
|
| +*/
|
| +/* #include "sqliteInt.h" */
|
| +
|
| +/*
|
| +** Conversion types fall into various categories as defined by the
|
| +** following enumeration.
|
| +*/
|
| +#define etRADIX 0 /* Integer types. %d, %x, %o, and so forth */
|
| +#define etFLOAT 1 /* Floating point. %f */
|
| +#define etEXP 2 /* Exponentional notation. %e and %E */
|
| +#define etGENERIC 3 /* Floating or exponential, depending on exponent. %g */
|
| +#define etSIZE 4 /* Return number of characters processed so far. %n */
|
| +#define etSTRING 5 /* Strings. %s */
|
| +#define etDYNSTRING 6 /* Dynamically allocated strings. %z */
|
| +#define etPERCENT 7 /* Percent symbol. %% */
|
| +#define etCHARX 8 /* Characters. %c */
|
| +/* The rest are extensions, not normally found in printf() */
|
| +#define etSQLESCAPE 9 /* Strings with '\'' doubled. %q */
|
| +#define etSQLESCAPE2 10 /* Strings with '\'' doubled and enclosed in '',
|
| + NULL pointers replaced by SQL NULL. %Q */
|
| +#define etTOKEN 11 /* a pointer to a Token structure */
|
| +#define etSRCLIST 12 /* a pointer to a SrcList */
|
| +#define etPOINTER 13 /* The %p conversion */
|
| +#define etSQLESCAPE3 14 /* %w -> Strings with '\"' doubled */
|
| +#define etORDINAL 15 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */
|
| +
|
| +#define etINVALID 16 /* Any unrecognized conversion type */
|
| +
|
| +
|
| +/*
|
| +** An "etByte" is an 8-bit unsigned value.
|
| +*/
|
| +typedef unsigned char etByte;
|
| +
|
| +/*
|
| +** Each builtin conversion character (ex: the 'd' in "%d") is described
|
| +** by an instance of the following structure
|
| +*/
|
| +typedef struct et_info { /* Information about each format field */
|
| + char fmttype; /* The format field code letter */
|
| + etByte base; /* The base for radix conversion */
|
| + etByte flags; /* One or more of FLAG_ constants below */
|
| + etByte type; /* Conversion paradigm */
|
| + etByte charset; /* Offset into aDigits[] of the digits string */
|
| + etByte prefix; /* Offset into aPrefix[] of the prefix string */
|
| +} et_info;
|
| +
|
| +/*
|
| +** Allowed values for et_info.flags
|
| +*/
|
| +#define FLAG_SIGNED 1 /* True if the value to convert is signed */
|
| +#define FLAG_STRING 4 /* Allow infinity precision */
|
| +
|
| +
|
| +/*
|
| +** The following table is searched linearly, so it is good to put the
|
| +** most frequently used conversion types first.
|
| +*/
|
| +static const char aDigits[] = "0123456789ABCDEF0123456789abcdef";
|
| +static const char aPrefix[] = "-x0\000X0";
|
| +static const et_info fmtinfo[] = {
|
| + { 'd', 10, 1, etRADIX, 0, 0 },
|
| + { 's', 0, 4, etSTRING, 0, 0 },
|
| + { 'g', 0, 1, etGENERIC, 30, 0 },
|
| + { 'z', 0, 4, etDYNSTRING, 0, 0 },
|
| + { 'q', 0, 4, etSQLESCAPE, 0, 0 },
|
| + { 'Q', 0, 4, etSQLESCAPE2, 0, 0 },
|
| + { 'w', 0, 4, etSQLESCAPE3, 0, 0 },
|
| + { 'c', 0, 0, etCHARX, 0, 0 },
|
| + { 'o', 8, 0, etRADIX, 0, 2 },
|
| + { 'u', 10, 0, etRADIX, 0, 0 },
|
| + { 'x', 16, 0, etRADIX, 16, 1 },
|
| + { 'X', 16, 0, etRADIX, 0, 4 },
|
| +#ifndef SQLITE_OMIT_FLOATING_POINT
|
| + { 'f', 0, 1, etFLOAT, 0, 0 },
|
| + { 'e', 0, 1, etEXP, 30, 0 },
|
| + { 'E', 0, 1, etEXP, 14, 0 },
|
| + { 'G', 0, 1, etGENERIC, 14, 0 },
|
| +#endif
|
| + { 'i', 10, 1, etRADIX, 0, 0 },
|
| + { 'n', 0, 0, etSIZE, 0, 0 },
|
| + { '%', 0, 0, etPERCENT, 0, 0 },
|
| + { 'p', 16, 0, etPOINTER, 0, 1 },
|
| +
|
| + /* All the rest are undocumented and are for internal use only */
|
| + { 'T', 0, 0, etTOKEN, 0, 0 },
|
| + { 'S', 0, 0, etSRCLIST, 0, 0 },
|
| + { 'r', 10, 1, etORDINAL, 0, 0 },
|
| +};
|
| +
|
| +/*
|
| +** If SQLITE_OMIT_FLOATING_POINT is defined, then none of the floating point
|
| +** conversions will work.
|
| +*/
|
| +#ifndef SQLITE_OMIT_FLOATING_POINT
|
| +/*
|
| +** "*val" is a double such that 0.1 <= *val < 10.0
|
| +** Return the ascii code for the leading digit of *val, then
|
| +** multiply "*val" by 10.0 to renormalize.
|
| +**
|
| +** Example:
|
| +** input: *val = 3.14159
|
| +** output: *val = 1.4159 function return = '3'
|
| +**
|
| +** The counter *cnt is incremented each time. After counter exceeds
|
| +** 16 (the number of significant digits in a 64-bit float) '0' is
|
| +** always returned.
|
| +*/
|
| +static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
|
| + int digit;
|
| + LONGDOUBLE_TYPE d;
|
| + if( (*cnt)<=0 ) return '0';
|
| + (*cnt)--;
|
| + digit = (int)*val;
|
| + d = digit;
|
| + digit += '0';
|
| + *val = (*val - d)*10.0;
|
| + return (char)digit;
|
| +}
|
| +#endif /* SQLITE_OMIT_FLOATING_POINT */
|
| +
|
| +/*
|
| +** Set the StrAccum object to an error mode.
|
| +*/
|
| +static void setStrAccumError(StrAccum *p, u8 eError){
|
| + assert( eError==STRACCUM_NOMEM || eError==STRACCUM_TOOBIG );
|
| + p->accError = eError;
|
| + p->nAlloc = 0;
|
| +}
|
| +
|
| +/*
|
| +** Extra argument values from a PrintfArguments object
|
| +*/
|
| +static sqlite3_int64 getIntArg(PrintfArguments *p){
|
| + if( p->nArg<=p->nUsed ) return 0;
|
| + return sqlite3_value_int64(p->apArg[p->nUsed++]);
|
| +}
|
| +static double getDoubleArg(PrintfArguments *p){
|
| + if( p->nArg<=p->nUsed ) return 0.0;
|
| + return sqlite3_value_double(p->apArg[p->nUsed++]);
|
| +}
|
| +static char *getTextArg(PrintfArguments *p){
|
| + if( p->nArg<=p->nUsed ) return 0;
|
| + return (char*)sqlite3_value_text(p->apArg[p->nUsed++]);
|
| +}
|
| +
|
| +
|
| +/*
|
| +** On machines with a small stack size, you can redefine the
|
| +** SQLITE_PRINT_BUF_SIZE to be something smaller, if desired.
|
| +*/
|
| +#ifndef SQLITE_PRINT_BUF_SIZE
|
| +# define SQLITE_PRINT_BUF_SIZE 70
|
| +#endif
|
| +#define etBUFSIZE SQLITE_PRINT_BUF_SIZE /* Size of the output buffer */
|
| +
|
| +/*
|
| +** Render a string given by "fmt" into the StrAccum object.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3VXPrintf(
|
| + StrAccum *pAccum, /* Accumulate results here */
|
| + const char *fmt, /* Format string */
|
| + va_list ap /* arguments */
|
| +){
|
| + int c; /* Next character in the format string */
|
| + char *bufpt; /* Pointer to the conversion buffer */
|
| + int precision; /* Precision of the current field */
|
| + int length; /* Length of the field */
|
| + int idx; /* A general purpose loop counter */
|
| + int width; /* Width of the current field */
|
| + etByte flag_leftjustify; /* True if "-" flag is present */
|
| + etByte flag_plussign; /* True if "+" flag is present */
|
| + etByte flag_blanksign; /* True if " " flag is present */
|
| + etByte flag_alternateform; /* True if "#" flag is present */
|
| + etByte flag_altform2; /* True if "!" flag is present */
|
| + etByte flag_zeropad; /* True if field width constant starts with zero */
|
| + etByte flag_long; /* True if "l" flag is present */
|
| + etByte flag_longlong; /* True if the "ll" flag is present */
|
| + etByte done; /* Loop termination flag */
|
| + etByte xtype = etINVALID; /* Conversion paradigm */
|
| + u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */
|
| + char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
|
| + sqlite_uint64 longvalue; /* Value for integer types */
|
| + LONGDOUBLE_TYPE realvalue; /* Value for real types */
|
| + const et_info *infop; /* Pointer to the appropriate info structure */
|
| + char *zOut; /* Rendering buffer */
|
| + int nOut; /* Size of the rendering buffer */
|
| + char *zExtra = 0; /* Malloced memory used by some conversion */
|
| +#ifndef SQLITE_OMIT_FLOATING_POINT
|
| + int exp, e2; /* exponent of real numbers */
|
| + int nsd; /* Number of significant digits returned */
|
| + double rounder; /* Used for rounding floating point values */
|
| + etByte flag_dp; /* True if decimal point should be shown */
|
| + etByte flag_rtz; /* True if trailing zeros should be removed */
|
| +#endif
|
| + PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */
|
| + char buf[etBUFSIZE]; /* Conversion buffer */
|
| +
|
| + bufpt = 0;
|
| + if( (pAccum->printfFlags & SQLITE_PRINTF_SQLFUNC)!=0 ){
|
| + pArgList = va_arg(ap, PrintfArguments*);
|
| + bArgList = 1;
|
| + }else{
|
| + bArgList = 0;
|
| + }
|
| + for(; (c=(*fmt))!=0; ++fmt){
|
| + if( c!='%' ){
|
| + bufpt = (char *)fmt;
|
| +#if HAVE_STRCHRNUL
|
| + fmt = strchrnul(fmt, '%');
|
| +#else
|
| + do{ fmt++; }while( *fmt && *fmt != '%' );
|
| +#endif
|
| + sqlite3StrAccumAppend(pAccum, bufpt, (int)(fmt - bufpt));
|
| + if( *fmt==0 ) break;
|
| + }
|
| + if( (c=(*++fmt))==0 ){
|
| + sqlite3StrAccumAppend(pAccum, "%", 1);
|
| + break;
|
| + }
|
| + /* Find out what flags are present */
|
| + flag_leftjustify = flag_plussign = flag_blanksign =
|
| + flag_alternateform = flag_altform2 = flag_zeropad = 0;
|
| + done = 0;
|
| + do{
|
| + switch( c ){
|
| + case '-': flag_leftjustify = 1; break;
|
| + case '+': flag_plussign = 1; break;
|
| + case ' ': flag_blanksign = 1; break;
|
| + case '#': flag_alternateform = 1; break;
|
| + case '!': flag_altform2 = 1; break;
|
| + case '0': flag_zeropad = 1; break;
|
| + default: done = 1; break;
|
| + }
|
| + }while( !done && (c=(*++fmt))!=0 );
|
| + /* Get the field width */
|
| + if( c=='*' ){
|
| + if( bArgList ){
|
| + width = (int)getIntArg(pArgList);
|
| + }else{
|
| + width = va_arg(ap,int);
|
| + }
|
| + if( width<0 ){
|
| + flag_leftjustify = 1;
|
| + width = width >= -2147483647 ? -width : 0;
|
| + }
|
| + c = *++fmt;
|
| + }else{
|
| + unsigned wx = 0;
|
| + while( c>='0' && c<='9' ){
|
| + wx = wx*10 + c - '0';
|
| + c = *++fmt;
|
| + }
|
| + testcase( wx>0x7fffffff );
|
| + width = wx & 0x7fffffff;
|
| + }
|
| + assert( width>=0 );
|
| +#ifdef SQLITE_PRINTF_PRECISION_LIMIT
|
| + if( width>SQLITE_PRINTF_PRECISION_LIMIT ){
|
| + width = SQLITE_PRINTF_PRECISION_LIMIT;
|
| + }
|
| +#endif
|
| +
|
| + /* Get the precision */
|
| + if( c=='.' ){
|
| + c = *++fmt;
|
| + if( c=='*' ){
|
| + if( bArgList ){
|
| + precision = (int)getIntArg(pArgList);
|
| + }else{
|
| + precision = va_arg(ap,int);
|
| + }
|
| + c = *++fmt;
|
| + if( precision<0 ){
|
| + precision = precision >= -2147483647 ? -precision : -1;
|
| + }
|
| + }else{
|
| + unsigned px = 0;
|
| + while( c>='0' && c<='9' ){
|
| + px = px*10 + c - '0';
|
| + c = *++fmt;
|
| + }
|
| + testcase( px>0x7fffffff );
|
| + precision = px & 0x7fffffff;
|
| + }
|
| + }else{
|
| + precision = -1;
|
| + }
|
| + assert( precision>=(-1) );
|
| +#ifdef SQLITE_PRINTF_PRECISION_LIMIT
|
| + if( precision>SQLITE_PRINTF_PRECISION_LIMIT ){
|
| + precision = SQLITE_PRINTF_PRECISION_LIMIT;
|
| + }
|
| +#endif
|
| +
|
| +
|
| + /* Get the conversion type modifier */
|
| + if( c=='l' ){
|
| + flag_long = 1;
|
| + c = *++fmt;
|
| + if( c=='l' ){
|
| + flag_longlong = 1;
|
| + c = *++fmt;
|
| + }else{
|
| + flag_longlong = 0;
|
| + }
|
| + }else{
|
| + flag_long = flag_longlong = 0;
|
| + }
|
| + /* Fetch the info entry for the field */
|
| + infop = &fmtinfo[0];
|
| + xtype = etINVALID;
|
| + for(idx=0; idx<ArraySize(fmtinfo); idx++){
|
| + if( c==fmtinfo[idx].fmttype ){
|
| + infop = &fmtinfo[idx];
|
| + xtype = infop->type;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + /*
|
| + ** At this point, variables are initialized as follows:
|
| + **
|
| + ** flag_alternateform TRUE if a '#' is present.
|
| + ** flag_altform2 TRUE if a '!' is present.
|
| + ** flag_plussign TRUE if a '+' is present.
|
| + ** flag_leftjustify TRUE if a '-' is present or if the
|
| + ** field width was negative.
|
| + ** flag_zeropad TRUE if the width began with 0.
|
| + ** flag_long TRUE if the letter 'l' (ell) prefixed
|
| + ** the conversion character.
|
| + ** flag_longlong TRUE if the letter 'll' (ell ell) prefixed
|
| + ** the conversion character.
|
| + ** flag_blanksign TRUE if a ' ' is present.
|
| + ** width The specified field width. This is
|
| + ** always non-negative. Zero is the default.
|
| + ** precision The specified precision. The default
|
| + ** is -1.
|
| + ** xtype The class of the conversion.
|
| + ** infop Pointer to the appropriate info struct.
|
| + */
|
| + switch( xtype ){
|
| + case etPOINTER:
|
| + flag_longlong = sizeof(char*)==sizeof(i64);
|
| + flag_long = sizeof(char*)==sizeof(long int);
|
| + /* Fall through into the next case */
|
| + case etORDINAL:
|
| + case etRADIX:
|
| + if( infop->flags & FLAG_SIGNED ){
|
| + i64 v;
|
| + if( bArgList ){
|
| + v = getIntArg(pArgList);
|
| + }else if( flag_longlong ){
|
| + v = va_arg(ap,i64);
|
| + }else if( flag_long ){
|
| + v = va_arg(ap,long int);
|
| + }else{
|
| + v = va_arg(ap,int);
|
| + }
|
| + if( v<0 ){
|
| + if( v==SMALLEST_INT64 ){
|
| + longvalue = ((u64)1)<<63;
|
| + }else{
|
| + longvalue = -v;
|
| + }
|
| + prefix = '-';
|
| + }else{
|
| + longvalue = v;
|
| + if( flag_plussign ) prefix = '+';
|
| + else if( flag_blanksign ) prefix = ' ';
|
| + else prefix = 0;
|
| + }
|
| + }else{
|
| + if( bArgList ){
|
| + longvalue = (u64)getIntArg(pArgList);
|
| + }else if( flag_longlong ){
|
| + longvalue = va_arg(ap,u64);
|
| + }else if( flag_long ){
|
| + longvalue = va_arg(ap,unsigned long int);
|
| + }else{
|
| + longvalue = va_arg(ap,unsigned int);
|
| + }
|
| + prefix = 0;
|
| + }
|
| + if( longvalue==0 ) flag_alternateform = 0;
|
| + if( flag_zeropad && precision<width-(prefix!=0) ){
|
| + precision = width-(prefix!=0);
|
| + }
|
| + if( precision<etBUFSIZE-10 ){
|
| + nOut = etBUFSIZE;
|
| + zOut = buf;
|
| + }else{
|
| + nOut = precision + 10;
|
| + zOut = zExtra = sqlite3Malloc( nOut );
|
| + if( zOut==0 ){
|
| + setStrAccumError(pAccum, STRACCUM_NOMEM);
|
| + return;
|
| + }
|
| + }
|
| + bufpt = &zOut[nOut-1];
|
| + if( xtype==etORDINAL ){
|
| + static const char zOrd[] = "thstndrd";
|
| + int x = (int)(longvalue % 10);
|
| + if( x>=4 || (longvalue/10)%10==1 ){
|
| + x = 0;
|
| + }
|
| + *(--bufpt) = zOrd[x*2+1];
|
| + *(--bufpt) = zOrd[x*2];
|
| + }
|
| + {
|
| + const char *cset = &aDigits[infop->charset];
|
| + u8 base = infop->base;
|
| + do{ /* Convert to ascii */
|
| + *(--bufpt) = cset[longvalue%base];
|
| + longvalue = longvalue/base;
|
| + }while( longvalue>0 );
|
| + }
|
| + length = (int)(&zOut[nOut-1]-bufpt);
|
| + for(idx=precision-length; idx>0; idx--){
|
| + *(--bufpt) = '0'; /* Zero pad */
|
| + }
|
| + if( prefix ) *(--bufpt) = prefix; /* Add sign */
|
| + if( flag_alternateform && infop->prefix ){ /* Add "0" or "0x" */
|
| + const char *pre;
|
| + char x;
|
| + pre = &aPrefix[infop->prefix];
|
| + for(; (x=(*pre))!=0; pre++) *(--bufpt) = x;
|
| + }
|
| + length = (int)(&zOut[nOut-1]-bufpt);
|
| + break;
|
| + case etFLOAT:
|
| + case etEXP:
|
| + case etGENERIC:
|
| + if( bArgList ){
|
| + realvalue = getDoubleArg(pArgList);
|
| + }else{
|
| + realvalue = va_arg(ap,double);
|
| + }
|
| +#ifdef SQLITE_OMIT_FLOATING_POINT
|
| + length = 0;
|
| +#else
|
| + if( precision<0 ) precision = 6; /* Set default precision */
|
| + if( realvalue<0.0 ){
|
| + realvalue = -realvalue;
|
| + prefix = '-';
|
| + }else{
|
| + if( flag_plussign ) prefix = '+';
|
| + else if( flag_blanksign ) prefix = ' ';
|
| + else prefix = 0;
|
| + }
|
| + if( xtype==etGENERIC && precision>0 ) precision--;
|
| + testcase( precision>0xfff );
|
| + for(idx=precision&0xfff, rounder=0.5; idx>0; idx--, rounder*=0.1){}
|
| + if( xtype==etFLOAT ) realvalue += rounder;
|
| + /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
|
| + exp = 0;
|
| + if( sqlite3IsNaN((double)realvalue) ){
|
| + bufpt = "NaN";
|
| + length = 3;
|
| + break;
|
| + }
|
| + if( realvalue>0.0 ){
|
| + LONGDOUBLE_TYPE scale = 1.0;
|
| + while( realvalue>=1e100*scale && exp<=350 ){ scale *= 1e100;exp+=100;}
|
| + while( realvalue>=1e10*scale && exp<=350 ){ scale *= 1e10; exp+=10; }
|
| + while( realvalue>=10.0*scale && exp<=350 ){ scale *= 10.0; exp++; }
|
| + realvalue /= scale;
|
| + while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; }
|
| + while( realvalue<1.0 ){ realvalue *= 10.0; exp--; }
|
| + if( exp>350 ){
|
| + bufpt = buf;
|
| + buf[0] = prefix;
|
| + memcpy(buf+(prefix!=0),"Inf",4);
|
| + length = 3+(prefix!=0);
|
| + break;
|
| + }
|
| + }
|
| + bufpt = buf;
|
| + /*
|
| + ** If the field type is etGENERIC, then convert to either etEXP
|
| + ** or etFLOAT, as appropriate.
|
| + */
|
| + if( xtype!=etFLOAT ){
|
| + realvalue += rounder;
|
| + if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
|
| + }
|
| + if( xtype==etGENERIC ){
|
| + flag_rtz = !flag_alternateform;
|
| + if( exp<-4 || exp>precision ){
|
| + xtype = etEXP;
|
| + }else{
|
| + precision = precision - exp;
|
| + xtype = etFLOAT;
|
| + }
|
| + }else{
|
| + flag_rtz = flag_altform2;
|
| + }
|
| + if( xtype==etEXP ){
|
| + e2 = 0;
|
| + }else{
|
| + e2 = exp;
|
| + }
|
| + if( MAX(e2,0)+(i64)precision+(i64)width > etBUFSIZE - 15 ){
|
| + bufpt = zExtra
|
| + = sqlite3Malloc( MAX(e2,0)+(i64)precision+(i64)width+15 );
|
| + if( bufpt==0 ){
|
| + setStrAccumError(pAccum, STRACCUM_NOMEM);
|
| + return;
|
| + }
|
| + }
|
| + zOut = bufpt;
|
| + nsd = 16 + flag_altform2*10;
|
| + flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2;
|
| + /* The sign in front of the number */
|
| + if( prefix ){
|
| + *(bufpt++) = prefix;
|
| + }
|
| + /* Digits prior to the decimal point */
|
| + if( e2<0 ){
|
| + *(bufpt++) = '0';
|
| + }else{
|
| + for(; e2>=0; e2--){
|
| + *(bufpt++) = et_getdigit(&realvalue,&nsd);
|
| + }
|
| + }
|
| + /* The decimal point */
|
| + if( flag_dp ){
|
| + *(bufpt++) = '.';
|
| + }
|
| + /* "0" digits after the decimal point but before the first
|
| + ** significant digit of the number */
|
| + for(e2++; e2<0; precision--, e2++){
|
| + assert( precision>0 );
|
| + *(bufpt++) = '0';
|
| + }
|
| + /* Significant digits after the decimal point */
|
| + while( (precision--)>0 ){
|
| + *(bufpt++) = et_getdigit(&realvalue,&nsd);
|
| + }
|
| + /* Remove trailing zeros and the "." if no digits follow the "." */
|
| + if( flag_rtz && flag_dp ){
|
| + while( bufpt[-1]=='0' ) *(--bufpt) = 0;
|
| + assert( bufpt>zOut );
|
| + if( bufpt[-1]=='.' ){
|
| + if( flag_altform2 ){
|
| + *(bufpt++) = '0';
|
| + }else{
|
| + *(--bufpt) = 0;
|
| + }
|
| + }
|
| + }
|
| + /* Add the "eNNN" suffix */
|
| + if( xtype==etEXP ){
|
| + *(bufpt++) = aDigits[infop->charset];
|
| + if( exp<0 ){
|
| + *(bufpt++) = '-'; exp = -exp;
|
| + }else{
|
| + *(bufpt++) = '+';
|
| + }
|
| + if( exp>=100 ){
|
| + *(bufpt++) = (char)((exp/100)+'0'); /* 100's digit */
|
| + exp %= 100;
|
| + }
|
| + *(bufpt++) = (char)(exp/10+'0'); /* 10's digit */
|
| + *(bufpt++) = (char)(exp%10+'0'); /* 1's digit */
|
| + }
|
| + *bufpt = 0;
|
| +
|
| + /* The converted number is in buf[] and zero terminated. Output it.
|
| + ** Note that the number is in the usual order, not reversed as with
|
| + ** integer conversions. */
|
| + length = (int)(bufpt-zOut);
|
| + bufpt = zOut;
|
| +
|
| + /* Special case: Add leading zeros if the flag_zeropad flag is
|
| + ** set and we are not left justified */
|
| + if( flag_zeropad && !flag_leftjustify && length < width){
|
| + int i;
|
| + int nPad = width - length;
|
| + for(i=width; i>=nPad; i--){
|
| + bufpt[i] = bufpt[i-nPad];
|
| + }
|
| + i = prefix!=0;
|
| + while( nPad-- ) bufpt[i++] = '0';
|
| + length = width;
|
| + }
|
| +#endif /* !defined(SQLITE_OMIT_FLOATING_POINT) */
|
| + break;
|
| + case etSIZE:
|
| + if( !bArgList ){
|
| + *(va_arg(ap,int*)) = pAccum->nChar;
|
| + }
|
| + length = width = 0;
|
| + break;
|
| + case etPERCENT:
|
| + buf[0] = '%';
|
| + bufpt = buf;
|
| + length = 1;
|
| + break;
|
| + case etCHARX:
|
| + if( bArgList ){
|
| + bufpt = getTextArg(pArgList);
|
| + c = bufpt ? bufpt[0] : 0;
|
| + }else{
|
| + c = va_arg(ap,int);
|
| + }
|
| + if( precision>1 ){
|
| + width -= precision-1;
|
| + if( width>1 && !flag_leftjustify ){
|
| + sqlite3AppendChar(pAccum, width-1, ' ');
|
| + width = 0;
|
| + }
|
| + sqlite3AppendChar(pAccum, precision-1, c);
|
| + }
|
| + length = 1;
|
| + buf[0] = c;
|
| + bufpt = buf;
|
| + break;
|
| + case etSTRING:
|
| + case etDYNSTRING:
|
| + if( bArgList ){
|
| + bufpt = getTextArg(pArgList);
|
| + xtype = etSTRING;
|
| + }else{
|
| + bufpt = va_arg(ap,char*);
|
| + }
|
| + if( bufpt==0 ){
|
| + bufpt = "";
|
| + }else if( xtype==etDYNSTRING ){
|
| + zExtra = bufpt;
|
| + }
|
| + if( precision>=0 ){
|
| + for(length=0; length<precision && bufpt[length]; length++){}
|
| + }else{
|
| + length = sqlite3Strlen30(bufpt);
|
| + }
|
| + break;
|
| + case etSQLESCAPE: /* Escape ' characters */
|
| + case etSQLESCAPE2: /* Escape ' and enclose in '...' */
|
| + case etSQLESCAPE3: { /* Escape " characters */
|
| + int i, j, k, n, isnull;
|
| + int needQuote;
|
| + char ch;
|
| + char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */
|
| + char *escarg;
|
| +
|
| + if( bArgList ){
|
| + escarg = getTextArg(pArgList);
|
| + }else{
|
| + escarg = va_arg(ap,char*);
|
| + }
|
| + isnull = escarg==0;
|
| + if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)");
|
| + k = precision;
|
| + for(i=n=0; k!=0 && (ch=escarg[i])!=0; i++, k--){
|
| + if( ch==q ) n++;
|
| + }
|
| + needQuote = !isnull && xtype==etSQLESCAPE2;
|
| + n += i + 3;
|
| + if( n>etBUFSIZE ){
|
| + bufpt = zExtra = sqlite3Malloc( n );
|
| + if( bufpt==0 ){
|
| + setStrAccumError(pAccum, STRACCUM_NOMEM);
|
| + return;
|
| + }
|
| + }else{
|
| + bufpt = buf;
|
| + }
|
| + j = 0;
|
| + if( needQuote ) bufpt[j++] = q;
|
| + k = i;
|
| + for(i=0; i<k; i++){
|
| + bufpt[j++] = ch = escarg[i];
|
| + if( ch==q ) bufpt[j++] = ch;
|
| + }
|
| + if( needQuote ) bufpt[j++] = q;
|
| + bufpt[j] = 0;
|
| + length = j;
|
| + /* The precision in %q and %Q means how many input characters to
|
| + ** consume, not the length of the output...
|
| + ** if( precision>=0 && precision<length ) length = precision; */
|
| + break;
|
| + }
|
| + case etTOKEN: {
|
| + Token *pToken;
|
| + if( (pAccum->printfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return;
|
| + pToken = va_arg(ap, Token*);
|
| + assert( bArgList==0 );
|
| + if( pToken && pToken->n ){
|
| + sqlite3StrAccumAppend(pAccum, (const char*)pToken->z, pToken->n);
|
| + }
|
| + length = width = 0;
|
| + break;
|
| + }
|
| + case etSRCLIST: {
|
| + SrcList *pSrc;
|
| + int k;
|
| + struct SrcList_item *pItem;
|
| + if( (pAccum->printfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return;
|
| + pSrc = va_arg(ap, SrcList*);
|
| + k = va_arg(ap, int);
|
| + pItem = &pSrc->a[k];
|
| + assert( bArgList==0 );
|
| + assert( k>=0 && k<pSrc->nSrc );
|
| + if( pItem->zDatabase ){
|
| + sqlite3StrAccumAppendAll(pAccum, pItem->zDatabase);
|
| + sqlite3StrAccumAppend(pAccum, ".", 1);
|
| + }
|
| + sqlite3StrAccumAppendAll(pAccum, pItem->zName);
|
| + length = width = 0;
|
| + break;
|
| + }
|
| + default: {
|
| + assert( xtype==etINVALID );
|
| + return;
|
| + }
|
| + }/* End switch over the format type */
|
| + /*
|
| + ** The text of the conversion is pointed to by "bufpt" and is
|
| + ** "length" characters long. The field width is "width". Do
|
| + ** the output.
|
| + */
|
| + width -= length;
|
| + if( width>0 ){
|
| + if( !flag_leftjustify ) sqlite3AppendChar(pAccum, width, ' ');
|
| + sqlite3StrAccumAppend(pAccum, bufpt, length);
|
| + if( flag_leftjustify ) sqlite3AppendChar(pAccum, width, ' ');
|
| + }else{
|
| + sqlite3StrAccumAppend(pAccum, bufpt, length);
|
| + }
|
| +
|
| + if( zExtra ){
|
| + sqlite3DbFree(pAccum->db, zExtra);
|
| + zExtra = 0;
|
| + }
|
| + }/* End for loop over the format string */
|
| +} /* End of function */
|
| +
|
| +/*
|
| +** Enlarge the memory allocation on a StrAccum object so that it is
|
| +** able to accept at least N more bytes of text.
|
| +**
|
| +** Return the number of bytes of text that StrAccum is able to accept
|
| +** after the attempted enlargement. The value returned might be zero.
|
| +*/
|
| +static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
|
| + char *zNew;
|
| + assert( p->nChar+(i64)N >= p->nAlloc ); /* Only called if really needed */
|
| + if( p->accError ){
|
| + testcase(p->accError==STRACCUM_TOOBIG);
|
| + testcase(p->accError==STRACCUM_NOMEM);
|
| + return 0;
|
| + }
|
| + if( p->mxAlloc==0 ){
|
| + N = p->nAlloc - p->nChar - 1;
|
| + setStrAccumError(p, STRACCUM_TOOBIG);
|
| + return N;
|
| + }else{
|
| + char *zOld = isMalloced(p) ? p->zText : 0;
|
| + i64 szNew = p->nChar;
|
| + assert( (p->zText==0 || p->zText==p->zBase)==!isMalloced(p) );
|
| + szNew += N + 1;
|
| + if( szNew+p->nChar<=p->mxAlloc ){
|
| + /* Force exponential buffer size growth as long as it does not overflow,
|
| + ** to avoid having to call this routine too often */
|
| + szNew += p->nChar;
|
| + }
|
| + if( szNew > p->mxAlloc ){
|
| + sqlite3StrAccumReset(p);
|
| + setStrAccumError(p, STRACCUM_TOOBIG);
|
| + return 0;
|
| + }else{
|
| + p->nAlloc = (int)szNew;
|
| + }
|
| + if( p->db ){
|
| + zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc);
|
| + }else{
|
| + zNew = sqlite3_realloc64(zOld, p->nAlloc);
|
| + }
|
| + if( zNew ){
|
| + assert( p->zText!=0 || p->nChar==0 );
|
| + if( !isMalloced(p) && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar);
|
| + p->zText = zNew;
|
| + p->nAlloc = sqlite3DbMallocSize(p->db, zNew);
|
| + p->printfFlags |= SQLITE_PRINTF_MALLOCED;
|
| + }else{
|
| + sqlite3StrAccumReset(p);
|
| + setStrAccumError(p, STRACCUM_NOMEM);
|
| + return 0;
|
| + }
|
| + }
|
| + return N;
|
| +}
|
| +
|
| +/*
|
| +** Append N copies of character c to the given string buffer.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3AppendChar(StrAccum *p, int N, char c){
|
| + testcase( p->nChar + (i64)N > 0x7fffffff );
|
| + if( p->nChar+(i64)N >= p->nAlloc && (N = sqlite3StrAccumEnlarge(p, N))<=0 ){
|
| + return;
|
| + }
|
| + assert( (p->zText==p->zBase)==!isMalloced(p) );
|
| + while( (N--)>0 ) p->zText[p->nChar++] = c;
|
| +}
|
| +
|
| +/*
|
| +** The StrAccum "p" is not large enough to accept N new bytes of z[].
|
| +** So enlarge if first, then do the append.
|
| +**
|
| +** This is a helper routine to sqlite3StrAccumAppend() that does special-case
|
| +** work (enlarging the buffer) using tail recursion, so that the
|
| +** sqlite3StrAccumAppend() routine can use fast calling semantics.
|
| +*/
|
| +static void SQLITE_NOINLINE enlargeAndAppend(StrAccum *p, const char *z, int N){
|
| + N = sqlite3StrAccumEnlarge(p, N);
|
| + if( N>0 ){
|
| + memcpy(&p->zText[p->nChar], z, N);
|
| + p->nChar += N;
|
| + }
|
| + assert( (p->zText==0 || p->zText==p->zBase)==!isMalloced(p) );
|
| +}
|
| +
|
| +/*
|
| +** Append N bytes of text from z to the StrAccum object. Increase the
|
| +** size of the memory allocation for StrAccum if necessary.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
|
| + assert( z!=0 || N==0 );
|
| + assert( p->zText!=0 || p->nChar==0 || p->accError );
|
| + assert( N>=0 );
|
| + assert( p->accError==0 || p->nAlloc==0 );
|
| + if( p->nChar+N >= p->nAlloc ){
|
| + enlargeAndAppend(p,z,N);
|
| + }else if( N ){
|
| + assert( p->zText );
|
| + p->nChar += N;
|
| + memcpy(&p->zText[p->nChar-N], z, N);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Append the complete text of zero-terminated string z[] to the p string.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3StrAccumAppendAll(StrAccum *p, const char *z){
|
| + sqlite3StrAccumAppend(p, z, sqlite3Strlen30(z));
|
| +}
|
| +
|
| +
|
| +/*
|
| +** Finish off a string by making sure it is zero-terminated.
|
| +** Return a pointer to the resulting string. Return a NULL
|
| +** pointer if any kind of error was encountered.
|
| +*/
|
| +static SQLITE_NOINLINE char *strAccumFinishRealloc(StrAccum *p){
|
| + assert( p->mxAlloc>0 && !isMalloced(p) );
|
| + p->zText = sqlite3DbMallocRaw(p->db, p->nChar+1 );
|
| + if( p->zText ){
|
| + memcpy(p->zText, p->zBase, p->nChar+1);
|
| + p->printfFlags |= SQLITE_PRINTF_MALLOCED;
|
| + }else{
|
| + setStrAccumError(p, STRACCUM_NOMEM);
|
| + }
|
| + return p->zText;
|
| +}
|
| +SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum *p){
|
| + if( p->zText ){
|
| + assert( (p->zText==p->zBase)==!isMalloced(p) );
|
| + p->zText[p->nChar] = 0;
|
| + if( p->mxAlloc>0 && !isMalloced(p) ){
|
| + return strAccumFinishRealloc(p);
|
| + }
|
| + }
|
| + return p->zText;
|
| +}
|
| +
|
| +/*
|
| +** Reset an StrAccum string. Reclaim all malloced memory.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3StrAccumReset(StrAccum *p){
|
| + assert( (p->zText==0 || p->zText==p->zBase)==!isMalloced(p) );
|
| + if( isMalloced(p) ){
|
| + sqlite3DbFree(p->db, p->zText);
|
| + p->printfFlags &= ~SQLITE_PRINTF_MALLOCED;
|
| + }
|
| + p->zText = 0;
|
| +}
|
| +
|
| +/*
|
| +** Initialize a string accumulator.
|
| +**
|
| +** p: The accumulator to be initialized.
|
| +** db: Pointer to a database connection. May be NULL. Lookaside
|
| +** memory is used if not NULL. db->mallocFailed is set appropriately
|
| +** when not NULL.
|
| +** zBase: An initial buffer. May be NULL in which case the initial buffer
|
| +** is malloced.
|
| +** n: Size of zBase in bytes. If total space requirements never exceed
|
| +** n then no memory allocations ever occur.
|
| +** mx: Maximum number of bytes to accumulate. If mx==0 then no memory
|
| +** allocations will ever occur.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum *p, sqlite3 *db, char *zBase, int n, int mx){
|
| + p->zText = p->zBase = zBase;
|
| + p->db = db;
|
| + p->nChar = 0;
|
| + p->nAlloc = n;
|
| + p->mxAlloc = mx;
|
| + p->accError = 0;
|
| + p->printfFlags = 0;
|
| +}
|
| +
|
| +/*
|
| +** Print into memory obtained from sqliteMalloc(). Use the internal
|
| +** %-conversion extensions.
|
| +*/
|
| +SQLITE_PRIVATE char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list ap){
|
| + char *z;
|
| + char zBase[SQLITE_PRINT_BUF_SIZE];
|
| + StrAccum acc;
|
| + assert( db!=0 );
|
| + sqlite3StrAccumInit(&acc, db, zBase, sizeof(zBase),
|
| + db->aLimit[SQLITE_LIMIT_LENGTH]);
|
| + acc.printfFlags = SQLITE_PRINTF_INTERNAL;
|
| + sqlite3VXPrintf(&acc, zFormat, ap);
|
| + z = sqlite3StrAccumFinish(&acc);
|
| + if( acc.accError==STRACCUM_NOMEM ){
|
| + sqlite3OomFault(db);
|
| + }
|
| + return z;
|
| +}
|
| +
|
| +/*
|
| +** Print into memory obtained from sqliteMalloc(). Use the internal
|
| +** %-conversion extensions.
|
| +*/
|
| +SQLITE_PRIVATE char *sqlite3MPrintf(sqlite3 *db, const char *zFormat, ...){
|
| + va_list ap;
|
| + char *z;
|
| + va_start(ap, zFormat);
|
| + z = sqlite3VMPrintf(db, zFormat, ap);
|
| + va_end(ap);
|
| + return z;
|
| +}
|
| +
|
| +/*
|
| +** Print into memory obtained from sqlite3_malloc(). Omit the internal
|
| +** %-conversion extensions.
|
| +*/
|
| +SQLITE_API char *sqlite3_vmprintf(const char *zFormat, va_list ap){
|
| + char *z;
|
| + char zBase[SQLITE_PRINT_BUF_SIZE];
|
| + StrAccum acc;
|
| +
|
| +#ifdef SQLITE_ENABLE_API_ARMOR
|
| + if( zFormat==0 ){
|
| + (void)SQLITE_MISUSE_BKPT;
|
| + return 0;
|
| + }
|
| +#endif
|
| +#ifndef SQLITE_OMIT_AUTOINIT
|
| + if( sqlite3_initialize() ) return 0;
|
| +#endif
|
| + sqlite3StrAccumInit(&acc, 0, zBase, sizeof(zBase), SQLITE_MAX_LENGTH);
|
| + sqlite3VXPrintf(&acc, zFormat, ap);
|
| + z = sqlite3StrAccumFinish(&acc);
|
| + return z;
|
| +}
|
| +
|
| +/*
|
| +** Print into memory obtained from sqlite3_malloc()(). Omit the internal
|
| +** %-conversion extensions.
|
| +*/
|
| +SQLITE_API char *sqlite3_mprintf(const char *zFormat, ...){
|
| + va_list ap;
|
| + char *z;
|
| +#ifndef SQLITE_OMIT_AUTOINIT
|
| + if( sqlite3_initialize() ) return 0;
|
| +#endif
|
| + va_start(ap, zFormat);
|
| + z = sqlite3_vmprintf(zFormat, ap);
|
| + va_end(ap);
|
| + return z;
|
| +}
|
| +
|
| +/*
|
| +** sqlite3_snprintf() works like snprintf() except that it ignores the
|
| +** current locale settings. This is important for SQLite because we
|
| +** are not able to use a "," as the decimal point in place of "." as
|
| +** specified by some locales.
|
| +**
|
| +** Oops: The first two arguments of sqlite3_snprintf() are backwards
|
| +** from the snprintf() standard. Unfortunately, it is too late to change
|
| +** this without breaking compatibility, so we just have to live with the
|
| +** mistake.
|
| +**
|
| +** sqlite3_vsnprintf() is the varargs version.
|
| +*/
|
| +SQLITE_API char *sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_list ap){
|
| + StrAccum acc;
|
| + if( n<=0 ) return zBuf;
|
| +#ifdef SQLITE_ENABLE_API_ARMOR
|
| + if( zBuf==0 || zFormat==0 ) {
|
| + (void)SQLITE_MISUSE_BKPT;
|
| + if( zBuf ) zBuf[0] = 0;
|
| + return zBuf;
|
| + }
|
| +#endif
|
| + sqlite3StrAccumInit(&acc, 0, zBuf, n, 0);
|
| + sqlite3VXPrintf(&acc, zFormat, ap);
|
| + zBuf[acc.nChar] = 0;
|
| + return zBuf;
|
| +}
|
| +SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){
|
| + char *z;
|
| + va_list ap;
|
| + va_start(ap,zFormat);
|
| + z = sqlite3_vsnprintf(n, zBuf, zFormat, ap);
|
| + va_end(ap);
|
| + return z;
|
| +}
|
| +
|
| +/*
|
| +** This is the routine that actually formats the sqlite3_log() message.
|
| +** We house it in a separate routine from sqlite3_log() to avoid using
|
| +** stack space on small-stack systems when logging is disabled.
|
| +**
|
| +** sqlite3_log() must render into a static buffer. It cannot dynamically
|
| +** allocate memory because it might be called while the memory allocator
|
| +** mutex is held.
|
| +**
|
| +** sqlite3VXPrintf() might ask for *temporary* memory allocations for
|
| +** certain format characters (%q) or for very large precisions or widths.
|
| +** Care must be taken that any sqlite3_log() calls that occur while the
|
| +** memory mutex is held do not use these mechanisms.
|
| +*/
|
| +static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){
|
| + StrAccum acc; /* String accumulator */
|
| + char zMsg[SQLITE_PRINT_BUF_SIZE*3]; /* Complete log message */
|
| +
|
| + sqlite3StrAccumInit(&acc, 0, zMsg, sizeof(zMsg), 0);
|
| + sqlite3VXPrintf(&acc, zFormat, ap);
|
| + sqlite3GlobalConfig.xLog(sqlite3GlobalConfig.pLogArg, iErrCode,
|
| + sqlite3StrAccumFinish(&acc));
|
| +}
|
| +
|
| +/*
|
| +** Format and write a message to the log if logging is enabled.
|
| +*/
|
| +SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...){
|
| + va_list ap; /* Vararg list */
|
| + if( sqlite3GlobalConfig.xLog ){
|
| + va_start(ap, zFormat);
|
| + renderLogMsg(iErrCode, zFormat, ap);
|
| + va_end(ap);
|
| + }
|
| +}
|
| +
|
| +#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
|
| +/*
|
| +** A version of printf() that understands %lld. Used for debugging.
|
| +** The printf() built into some versions of windows does not understand %lld
|
| +** and segfaults if you give it a long long int.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3DebugPrintf(const char *zFormat, ...){
|
| + va_list ap;
|
| + StrAccum acc;
|
| + char zBuf[500];
|
| + sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
|
| + va_start(ap,zFormat);
|
| + sqlite3VXPrintf(&acc, zFormat, ap);
|
| + va_end(ap);
|
| + sqlite3StrAccumFinish(&acc);
|
| + fprintf(stdout,"%s", zBuf);
|
| + fflush(stdout);
|
| +}
|
| +#endif
|
| +
|
| +
|
| +/*
|
| +** variable-argument wrapper around sqlite3VXPrintf(). The bFlags argument
|
| +** can contain the bit SQLITE_PRINTF_INTERNAL enable internal formats.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3XPrintf(StrAccum *p, const char *zFormat, ...){
|
| + va_list ap;
|
| + va_start(ap,zFormat);
|
| + sqlite3VXPrintf(p, zFormat, ap);
|
| + va_end(ap);
|
| +}
|
| +
|
| +/************** End of printf.c **********************************************/
|
| +/************** Begin file treeview.c ****************************************/
|
| +/*
|
| +** 2015-06-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 C code to implement the TreeView debugging routines.
|
| +** These routines print a parse tree to standard output for debugging and
|
| +** analysis.
|
| +**
|
| +** The interfaces in this file is only available when compiling
|
| +** with SQLITE_DEBUG.
|
| +*/
|
| +/* #include "sqliteInt.h" */
|
| +#ifdef SQLITE_DEBUG
|
| +
|
| +/*
|
| +** Add a new subitem to the tree. The moreToFollow flag indicates that this
|
| +** is not the last item in the tree.
|
| +*/
|
| +static TreeView *sqlite3TreeViewPush(TreeView *p, u8 moreToFollow){
|
| + if( p==0 ){
|
| + p = sqlite3_malloc64( sizeof(*p) );
|
| + if( p==0 ) return 0;
|
| + memset(p, 0, sizeof(*p));
|
| + }else{
|
| + p->iLevel++;
|
| + }
|
| + assert( moreToFollow==0 || moreToFollow==1 );
|
| + if( p->iLevel<sizeof(p->bLine) ) p->bLine[p->iLevel] = moreToFollow;
|
| + return p;
|
| +}
|
| +
|
| +/*
|
| +** Finished with one layer of the tree
|
| +*/
|
| +static void sqlite3TreeViewPop(TreeView *p){
|
| + if( p==0 ) return;
|
| + p->iLevel--;
|
| + if( p->iLevel<0 ) sqlite3_free(p);
|
| +}
|
| +
|
| +/*
|
| +** Generate a single line of output for the tree, with a prefix that contains
|
| +** all the appropriate tree lines
|
| +*/
|
| +static void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){
|
| + va_list ap;
|
| + int i;
|
| + StrAccum acc;
|
| + char zBuf[500];
|
| + sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
|
| + if( p ){
|
| + for(i=0; i<p->iLevel && i<sizeof(p->bLine)-1; i++){
|
| + sqlite3StrAccumAppend(&acc, p->bLine[i] ? "| " : " ", 4);
|
| + }
|
| + sqlite3StrAccumAppend(&acc, p->bLine[i] ? "|-- " : "'-- ", 4);
|
| + }
|
| + va_start(ap, zFormat);
|
| + sqlite3VXPrintf(&acc, zFormat, ap);
|
| + va_end(ap);
|
| + assert( acc.nChar>0 );
|
| + if( zBuf[acc.nChar-1]!='\n' ) sqlite3StrAccumAppend(&acc, "\n", 1);
|
| + sqlite3StrAccumFinish(&acc);
|
| + fprintf(stdout,"%s", zBuf);
|
| + fflush(stdout);
|
| +}
|
| +
|
| +/*
|
| +** Shorthand for starting a new tree item that consists of a single label
|
| +*/
|
| +static void sqlite3TreeViewItem(TreeView *p, const char *zLabel,u8 moreFollows){
|
| + p = sqlite3TreeViewPush(p, moreFollows);
|
| + sqlite3TreeViewLine(p, "%s", zLabel);
|
| +}
|
| +
|
| +/*
|
| +** Generate a human-readable description of a WITH clause.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 moreToFollow){
|
| + int i;
|
| + if( pWith==0 ) return;
|
| + if( pWith->nCte==0 ) return;
|
| + if( pWith->pOuter ){
|
| + sqlite3TreeViewLine(pView, "WITH (0x%p, pOuter=0x%p)",pWith,pWith->pOuter);
|
| + }else{
|
| + sqlite3TreeViewLine(pView, "WITH (0x%p)", pWith);
|
| + }
|
| + if( pWith->nCte>0 ){
|
| + pView = sqlite3TreeViewPush(pView, 1);
|
| + for(i=0; i<pWith->nCte; i++){
|
| + StrAccum x;
|
| + char zLine[1000];
|
| + const struct Cte *pCte = &pWith->a[i];
|
| + sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0);
|
| + sqlite3XPrintf(&x, "%s", pCte->zName);
|
| + if( pCte->pCols && pCte->pCols->nExpr>0 ){
|
| + char cSep = '(';
|
| + int j;
|
| + for(j=0; j<pCte->pCols->nExpr; j++){
|
| + sqlite3XPrintf(&x, "%c%s", cSep, pCte->pCols->a[j].zName);
|
| + cSep = ',';
|
| + }
|
| + sqlite3XPrintf(&x, ")");
|
| + }
|
| + sqlite3XPrintf(&x, " AS");
|
| + sqlite3StrAccumFinish(&x);
|
| + sqlite3TreeViewItem(pView, zLine, i<pWith->nCte-1);
|
| + sqlite3TreeViewSelect(pView, pCte->pSelect, 0);
|
| + sqlite3TreeViewPop(pView);
|
| + }
|
| + sqlite3TreeViewPop(pView);
|
| + }
|
| +}
|
| +
|
| +
|
| +/*
|
| +** Generate a human-readable description of a Select object.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
|
| + int n = 0;
|
| + int cnt = 0;
|
| + pView = sqlite3TreeViewPush(pView, moreToFollow);
|
| + if( p->pWith ){
|
| + sqlite3TreeViewWith(pView, p->pWith, 1);
|
| + cnt = 1;
|
| + sqlite3TreeViewPush(pView, 1);
|
| + }
|
| + do{
|
| + sqlite3TreeViewLine(pView, "SELECT%s%s (0x%p) selFlags=0x%x nSelectRow=%d",
|
| + ((p->selFlags & SF_Distinct) ? " DISTINCT" : ""),
|
| + ((p->selFlags & SF_Aggregate) ? " agg_flag" : ""), p, p->selFlags,
|
| + (int)p->nSelectRow
|
| + );
|
| + if( cnt++ ) sqlite3TreeViewPop(pView);
|
| + if( p->pPrior ){
|
| + n = 1000;
|
| + }else{
|
| + n = 0;
|
| + if( p->pSrc && p->pSrc->nSrc ) n++;
|
| + if( p->pWhere ) n++;
|
| + if( p->pGroupBy ) n++;
|
| + if( p->pHaving ) n++;
|
| + if( p->pOrderBy ) n++;
|
| + if( p->pLimit ) n++;
|
| + if( p->pOffset ) n++;
|
| + }
|
| + sqlite3TreeViewExprList(pView, p->pEList, (n--)>0, "result-set");
|
| + if( p->pSrc && p->pSrc->nSrc ){
|
| + int i;
|
| + pView = sqlite3TreeViewPush(pView, (n--)>0);
|
| + sqlite3TreeViewLine(pView, "FROM");
|
| + for(i=0; i<p->pSrc->nSrc; i++){
|
| + struct SrcList_item *pItem = &p->pSrc->a[i];
|
| + StrAccum x;
|
| + char zLine[100];
|
| + sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0);
|
| + sqlite3XPrintf(&x, "{%d,*}", pItem->iCursor);
|
| + if( pItem->zDatabase ){
|
| + sqlite3XPrintf(&x, " %s.%s", pItem->zDatabase, pItem->zName);
|
| + }else if( pItem->zName ){
|
| + sqlite3XPrintf(&x, " %s", pItem->zName);
|
| + }
|
| + if( pItem->pTab ){
|
| + sqlite3XPrintf(&x, " tabname=%Q", pItem->pTab->zName);
|
| + }
|
| + if( pItem->zAlias ){
|
| + sqlite3XPrintf(&x, " (AS %s)", pItem->zAlias);
|
| + }
|
| + if( pItem->fg.jointype & JT_LEFT ){
|
| + sqlite3XPrintf(&x, " LEFT-JOIN");
|
| + }
|
| + sqlite3StrAccumFinish(&x);
|
| + sqlite3TreeViewItem(pView, zLine, i<p->pSrc->nSrc-1);
|
| + if( pItem->pSelect ){
|
| + sqlite3TreeViewSelect(pView, pItem->pSelect, 0);
|
| + }
|
| + if( pItem->fg.isTabFunc ){
|
| + sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:");
|
| + }
|
| + sqlite3TreeViewPop(pView);
|
| + }
|
| + sqlite3TreeViewPop(pView);
|
| + }
|
| + if( p->pWhere ){
|
| + sqlite3TreeViewItem(pView, "WHERE", (n--)>0);
|
| + sqlite3TreeViewExpr(pView, p->pWhere, 0);
|
| + sqlite3TreeViewPop(pView);
|
| + }
|
| + if( p->pGroupBy ){
|
| + sqlite3TreeViewExprList(pView, p->pGroupBy, (n--)>0, "GROUPBY");
|
| + }
|
| + if( p->pHaving ){
|
| + sqlite3TreeViewItem(pView, "HAVING", (n--)>0);
|
| + sqlite3TreeViewExpr(pView, p->pHaving, 0);
|
| + sqlite3TreeViewPop(pView);
|
| + }
|
| + if( p->pOrderBy ){
|
| + sqlite3TreeViewExprList(pView, p->pOrderBy, (n--)>0, "ORDERBY");
|
| + }
|
| + if( p->pLimit ){
|
| + sqlite3TreeViewItem(pView, "LIMIT", (n--)>0);
|
| + sqlite3TreeViewExpr(pView, p->pLimit, 0);
|
| + sqlite3TreeViewPop(pView);
|
| + }
|
| + if( p->pOffset ){
|
| + sqlite3TreeViewItem(pView, "OFFSET", (n--)>0);
|
| + sqlite3TreeViewExpr(pView, p->pOffset, 0);
|
| + sqlite3TreeViewPop(pView);
|
| + }
|
| + if( p->pPrior ){
|
| + const char *zOp = "UNION";
|
| + switch( p->op ){
|
| + case TK_ALL: zOp = "UNION ALL"; break;
|
| + case TK_INTERSECT: zOp = "INTERSECT"; break;
|
| + case TK_EXCEPT: zOp = "EXCEPT"; break;
|
| + }
|
| + sqlite3TreeViewItem(pView, zOp, 1);
|
| + }
|
| + p = p->pPrior;
|
| + }while( p!=0 );
|
| + sqlite3TreeViewPop(pView);
|
| +}
|
| +
|
| +/*
|
| +** Generate a human-readable explanation of an expression tree.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
|
| + const char *zBinOp = 0; /* Binary operator */
|
| + const char *zUniOp = 0; /* Unary operator */
|
| + char zFlgs[30];
|
| + pView = sqlite3TreeViewPush(pView, moreToFollow);
|
| + if( pExpr==0 ){
|
| + sqlite3TreeViewLine(pView, "nil");
|
| + sqlite3TreeViewPop(pView);
|
| + return;
|
| + }
|
| + if( pExpr->flags ){
|
| + sqlite3_snprintf(sizeof(zFlgs),zFlgs," flags=0x%x",pExpr->flags);
|
| + }else{
|
| + zFlgs[0] = 0;
|
| + }
|
| + switch( pExpr->op ){
|
| + case TK_AGG_COLUMN: {
|
| + sqlite3TreeViewLine(pView, "AGG{%d:%d}%s",
|
| + pExpr->iTable, pExpr->iColumn, zFlgs);
|
| + break;
|
| + }
|
| + case TK_COLUMN: {
|
| + if( pExpr->iTable<0 ){
|
| + /* This only happens when coding check constraints */
|
| + sqlite3TreeViewLine(pView, "COLUMN(%d)%s", pExpr->iColumn, zFlgs);
|
| + }else{
|
| + sqlite3TreeViewLine(pView, "{%d:%d}%s",
|
| + pExpr->iTable, pExpr->iColumn, zFlgs);
|
| + }
|
| + break;
|
| + }
|
| + case TK_INTEGER: {
|
| + if( pExpr->flags & EP_IntValue ){
|
| + sqlite3TreeViewLine(pView, "%d", pExpr->u.iValue);
|
| + }else{
|
| + sqlite3TreeViewLine(pView, "%s", pExpr->u.zToken);
|
| + }
|
| + break;
|
| + }
|
| +#ifndef SQLITE_OMIT_FLOATING_POINT
|
| + case TK_FLOAT: {
|
| + sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken);
|
| + break;
|
| + }
|
| +#endif
|
| + case TK_STRING: {
|
| + sqlite3TreeViewLine(pView,"%Q", pExpr->u.zToken);
|
| + break;
|
| + }
|
| + case TK_NULL: {
|
| + sqlite3TreeViewLine(pView,"NULL");
|
| + break;
|
| + }
|
| +#ifndef SQLITE_OMIT_BLOB_LITERAL
|
| + case TK_BLOB: {
|
| + sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken);
|
| + break;
|
| + }
|
| +#endif
|
| + case TK_VARIABLE: {
|
| + sqlite3TreeViewLine(pView,"VARIABLE(%s,%d)",
|
| + pExpr->u.zToken, pExpr->iColumn);
|
| + break;
|
| + }
|
| + case TK_REGISTER: {
|
| + sqlite3TreeViewLine(pView,"REGISTER(%d)", pExpr->iTable);
|
| + break;
|
| + }
|
| + case TK_ID: {
|
| + sqlite3TreeViewLine(pView,"ID \"%w\"", pExpr->u.zToken);
|
| + break;
|
| + }
|
| +#ifndef SQLITE_OMIT_CAST
|
| + case TK_CAST: {
|
| + /* Expressions of the form: CAST(pLeft AS token) */
|
| + sqlite3TreeViewLine(pView,"CAST %Q", pExpr->u.zToken);
|
| + sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
|
| + break;
|
| + }
|
| +#endif /* SQLITE_OMIT_CAST */
|
| + case TK_LT: zBinOp = "LT"; break;
|
| + case TK_LE: zBinOp = "LE"; break;
|
| + case TK_GT: zBinOp = "GT"; break;
|
| + case TK_GE: zBinOp = "GE"; break;
|
| + case TK_NE: zBinOp = "NE"; break;
|
| + case TK_EQ: zBinOp = "EQ"; break;
|
| + case TK_IS: zBinOp = "IS"; break;
|
| + case TK_ISNOT: zBinOp = "ISNOT"; break;
|
| + case TK_AND: zBinOp = "AND"; break;
|
| + case TK_OR: zBinOp = "OR"; break;
|
| + case TK_PLUS: zBinOp = "ADD"; break;
|
| + case TK_STAR: zBinOp = "MUL"; break;
|
| + case TK_MINUS: zBinOp = "SUB"; break;
|
| + case TK_REM: zBinOp = "REM"; break;
|
| + case TK_BITAND: zBinOp = "BITAND"; break;
|
| + case TK_BITOR: zBinOp = "BITOR"; break;
|
| + case TK_SLASH: zBinOp = "DIV"; break;
|
| + case TK_LSHIFT: zBinOp = "LSHIFT"; break;
|
| + case TK_RSHIFT: zBinOp = "RSHIFT"; break;
|
| + case TK_CONCAT: zBinOp = "CONCAT"; break;
|
| + case TK_DOT: zBinOp = "DOT"; break;
|
| +
|
| + case TK_UMINUS: zUniOp = "UMINUS"; break;
|
| + case TK_UPLUS: zUniOp = "UPLUS"; break;
|
| + case TK_BITNOT: zUniOp = "BITNOT"; break;
|
| + case TK_NOT: zUniOp = "NOT"; break;
|
| + case TK_ISNULL: zUniOp = "ISNULL"; break;
|
| + case TK_NOTNULL: zUniOp = "NOTNULL"; break;
|
| +
|
| + case TK_SPAN: {
|
| + sqlite3TreeViewLine(pView, "SPAN %Q", pExpr->u.zToken);
|
| + sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
|
| + break;
|
| + }
|
| +
|
| + case TK_COLLATE: {
|
| + sqlite3TreeViewLine(pView, "COLLATE %Q", pExpr->u.zToken);
|
| + sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
|
| + break;
|
| + }
|
| +
|
| + case TK_AGG_FUNCTION:
|
| + case TK_FUNCTION: {
|
| + ExprList *pFarg; /* List of function arguments */
|
| + if( ExprHasProperty(pExpr, EP_TokenOnly) ){
|
| + pFarg = 0;
|
| + }else{
|
| + pFarg = pExpr->x.pList;
|
| + }
|
| + if( pExpr->op==TK_AGG_FUNCTION ){
|
| + sqlite3TreeViewLine(pView, "AGG_FUNCTION%d %Q",
|
| + pExpr->op2, pExpr->u.zToken);
|
| + }else{
|
| + sqlite3TreeViewLine(pView, "FUNCTION %Q", pExpr->u.zToken);
|
| + }
|
| + if( pFarg ){
|
| + sqlite3TreeViewExprList(pView, pFarg, 0, 0);
|
| + }
|
| + break;
|
| + }
|
| +#ifndef SQLITE_OMIT_SUBQUERY
|
| + case TK_EXISTS: {
|
| + sqlite3TreeViewLine(pView, "EXISTS-expr");
|
| + sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0);
|
| + break;
|
| + }
|
| + case TK_SELECT: {
|
| + sqlite3TreeViewLine(pView, "SELECT-expr");
|
| + sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0);
|
| + break;
|
| + }
|
| + case TK_IN: {
|
| + sqlite3TreeViewLine(pView, "IN");
|
| + sqlite3TreeViewExpr(pView, pExpr->pLeft, 1);
|
| + if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
| + sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0);
|
| + }else{
|
| + sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0);
|
| + }
|
| + break;
|
| + }
|
| +#endif /* SQLITE_OMIT_SUBQUERY */
|
| +
|
| + /*
|
| + ** x BETWEEN y AND z
|
| + **
|
| + ** This is equivalent to
|
| + **
|
| + ** x>=y AND x<=z
|
| + **
|
| + ** X is stored in pExpr->pLeft.
|
| + ** Y is stored in pExpr->pList->a[0].pExpr.
|
| + ** Z is stored in pExpr->pList->a[1].pExpr.
|
| + */
|
| + case TK_BETWEEN: {
|
| + Expr *pX = pExpr->pLeft;
|
| + Expr *pY = pExpr->x.pList->a[0].pExpr;
|
| + Expr *pZ = pExpr->x.pList->a[1].pExpr;
|
| + sqlite3TreeViewLine(pView, "BETWEEN");
|
| + sqlite3TreeViewExpr(pView, pX, 1);
|
| + sqlite3TreeViewExpr(pView, pY, 1);
|
| + sqlite3TreeViewExpr(pView, pZ, 0);
|
| + break;
|
| + }
|
| + case TK_TRIGGER: {
|
| + /* If the opcode is TK_TRIGGER, then the expression is a reference
|
| + ** to a column in the new.* or old.* pseudo-tables available to
|
| + ** trigger programs. In this case Expr.iTable is set to 1 for the
|
| + ** new.* pseudo-table, or 0 for the old.* pseudo-table. Expr.iColumn
|
| + ** is set to the column of the pseudo-table to read, or to -1 to
|
| + ** read the rowid field.
|
| + */
|
| + sqlite3TreeViewLine(pView, "%s(%d)",
|
| + pExpr->iTable ? "NEW" : "OLD", pExpr->iColumn);
|
| + break;
|
| + }
|
| + case TK_CASE: {
|
| + sqlite3TreeViewLine(pView, "CASE");
|
| + sqlite3TreeViewExpr(pView, pExpr->pLeft, 1);
|
| + sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0);
|
| + break;
|
| + }
|
| +#ifndef SQLITE_OMIT_TRIGGER
|
| + case TK_RAISE: {
|
| + const char *zType = "unk";
|
| + switch( pExpr->affinity ){
|
| + case OE_Rollback: zType = "rollback"; break;
|
| + case OE_Abort: zType = "abort"; break;
|
| + case OE_Fail: zType = "fail"; break;
|
| + case OE_Ignore: zType = "ignore"; break;
|
| + }
|
| + sqlite3TreeViewLine(pView, "RAISE %s(%Q)", zType, pExpr->u.zToken);
|
| + break;
|
| + }
|
| +#endif
|
| + case TK_MATCH: {
|
| + sqlite3TreeViewLine(pView, "MATCH {%d:%d}%s",
|
| + pExpr->iTable, pExpr->iColumn, zFlgs);
|
| + sqlite3TreeViewExpr(pView, pExpr->pRight, 0);
|
| + break;
|
| + }
|
| + case TK_VECTOR: {
|
| + sqlite3TreeViewBareExprList(pView, pExpr->x.pList, "VECTOR");
|
| + break;
|
| + }
|
| + case TK_SELECT_COLUMN: {
|
| + sqlite3TreeViewLine(pView, "SELECT-COLUMN %d", pExpr->iColumn);
|
| + sqlite3TreeViewSelect(pView, pExpr->pLeft->x.pSelect, 0);
|
| + break;
|
| + }
|
| + default: {
|
| + sqlite3TreeViewLine(pView, "op=%d", pExpr->op);
|
| + break;
|
| + }
|
| + }
|
| + if( zBinOp ){
|
| + sqlite3TreeViewLine(pView, "%s%s", zBinOp, zFlgs);
|
| + sqlite3TreeViewExpr(pView, pExpr->pLeft, 1);
|
| + sqlite3TreeViewExpr(pView, pExpr->pRight, 0);
|
| + }else if( zUniOp ){
|
| + sqlite3TreeViewLine(pView, "%s%s", zUniOp, zFlgs);
|
| + sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
|
| + }
|
| + sqlite3TreeViewPop(pView);
|
| +}
|
| +
|
| +
|
| +/*
|
| +** Generate a human-readable explanation of an expression list.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3TreeViewBareExprList(
|
| + TreeView *pView,
|
| + const ExprList *pList,
|
| + const char *zLabel
|
| +){
|
| + if( zLabel==0 || zLabel[0]==0 ) zLabel = "LIST";
|
| + if( pList==0 ){
|
| + sqlite3TreeViewLine(pView, "%s (empty)", zLabel);
|
| + }else{
|
| + int i;
|
| + sqlite3TreeViewLine(pView, "%s", zLabel);
|
| + for(i=0; i<pList->nExpr; i++){
|
| + int j = pList->a[i].u.x.iOrderByCol;
|
| + if( j ){
|
| + sqlite3TreeViewPush(pView, 0);
|
| + sqlite3TreeViewLine(pView, "iOrderByCol=%d", j);
|
| + }
|
| + sqlite3TreeViewExpr(pView, pList->a[i].pExpr, i<pList->nExpr-1);
|
| + if( j ) sqlite3TreeViewPop(pView);
|
| + }
|
| + }
|
| +}
|
| +SQLITE_PRIVATE void sqlite3TreeViewExprList(
|
| + TreeView *pView,
|
| + const ExprList *pList,
|
| + u8 moreToFollow,
|
| + const char *zLabel
|
| +){
|
| + pView = sqlite3TreeViewPush(pView, moreToFollow);
|
| + sqlite3TreeViewBareExprList(pView, pList, zLabel);
|
| + sqlite3TreeViewPop(pView);
|
| +}
|
| +
|
| +#endif /* SQLITE_DEBUG */
|
| +
|
| +/************** End of treeview.c ********************************************/
|
| +/************** Begin file random.c ******************************************/
|
| +/*
|
| +** 2001 September 15
|
| +**
|
| +** 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 code to implement a pseudo-random number
|
| +** generator (PRNG) for SQLite.
|
| +**
|
| +** Random numbers are used by some of the database backends in order
|
| +** to generate random integer keys for tables or random filenames.
|
| +*/
|
| +/* #include "sqliteInt.h" */
|
| +
|
| +
|
| +/* All threads share a single random number generator.
|
| +** This structure is the current state of the generator.
|
| +*/
|
| +static SQLITE_WSD struct sqlite3PrngType {
|
| + unsigned char isInit; /* True if initialized */
|
| + unsigned char i, j; /* State variables */
|
| + unsigned char s[256]; /* State variables */
|
| +} sqlite3Prng;
|
| +
|
| +/*
|
| +** Return N random bytes.
|
| +*/
|
| +SQLITE_API void sqlite3_randomness(int N, void *pBuf){
|
| + unsigned char t;
|
| + unsigned char *zBuf = pBuf;
|
| +
|
| + /* The "wsdPrng" macro will resolve to the pseudo-random number generator
|
| + ** state vector. If writable static data is unsupported on the target,
|
| + ** we have to locate the state vector at run-time. In the more common
|
| + ** case where writable static data is supported, wsdPrng can refer directly
|
| + ** to the "sqlite3Prng" state vector declared above.
|
| + */
|
| +#ifdef SQLITE_OMIT_WSD
|
| + struct sqlite3PrngType *p = &GLOBAL(struct sqlite3PrngType, sqlite3Prng);
|
| +# define wsdPrng p[0]
|
| +#else
|
| +# define wsdPrng sqlite3Prng
|
| +#endif
|
| +
|
| +#if SQLITE_THREADSAFE
|
| + sqlite3_mutex *mutex;
|
| +#endif
|
| +
|
| +#ifndef SQLITE_OMIT_AUTOINIT
|
| + if( sqlite3_initialize() ) return;
|
| +#endif
|
| +
|
| +#if SQLITE_THREADSAFE
|
| + mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PRNG);
|
| +#endif
|
| +
|
| + sqlite3_mutex_enter(mutex);
|
| + if( N<=0 || pBuf==0 ){
|
| + wsdPrng.isInit = 0;
|
| + sqlite3_mutex_leave(mutex);
|
| + return;
|
| + }
|
| +
|
| + /* Initialize the state of the random number generator once,
|
| + ** the first time this routine is called. The seed value does
|
| + ** not need to contain a lot of randomness since we are not
|
| + ** trying to do secure encryption or anything like that...
|
| + **
|
| + ** Nothing in this file or anywhere else in SQLite does any kind of
|
| + ** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random
|
| + ** number generator) not as an encryption device.
|
| + */
|
| + if( !wsdPrng.isInit ){
|
| + int i;
|
| + char k[256];
|
| + wsdPrng.j = 0;
|
| + wsdPrng.i = 0;
|
| + sqlite3OsRandomness(sqlite3_vfs_find(0), 256, k);
|
| + for(i=0; i<256; i++){
|
| + wsdPrng.s[i] = (u8)i;
|
| + }
|
| + for(i=0; i<256; i++){
|
| + wsdPrng.j += wsdPrng.s[i] + k[i];
|
| + t = wsdPrng.s[wsdPrng.j];
|
| + wsdPrng.s[wsdPrng.j] = wsdPrng.s[i];
|
| + wsdPrng.s[i] = t;
|
| + }
|
| + wsdPrng.isInit = 1;
|
| + }
|
| +
|
| + assert( N>0 );
|
| + do{
|
| + wsdPrng.i++;
|
| + t = wsdPrng.s[wsdPrng.i];
|
| + wsdPrng.j += t;
|
| + wsdPrng.s[wsdPrng.i] = wsdPrng.s[wsdPrng.j];
|
| + wsdPrng.s[wsdPrng.j] = t;
|
| + t += wsdPrng.s[wsdPrng.i];
|
| + *(zBuf++) = wsdPrng.s[t];
|
| + }while( --N );
|
| + sqlite3_mutex_leave(mutex);
|
| +}
|
| +
|
| +#ifndef SQLITE_UNTESTABLE
|
| +/*
|
| +** For testing purposes, we sometimes want to preserve the state of
|
| +** PRNG and restore the PRNG to its saved state at a later time, or
|
| +** to reset the PRNG to its initial state. These routines accomplish
|
| +** those tasks.
|
| +**
|
| +** The sqlite3_test_control() interface calls these routines to
|
| +** control the PRNG.
|
| +*/
|
| +static SQLITE_WSD struct sqlite3PrngType sqlite3SavedPrng;
|
| +SQLITE_PRIVATE void sqlite3PrngSaveState(void){
|
| + memcpy(
|
| + &GLOBAL(struct sqlite3PrngType, sqlite3SavedPrng),
|
| + &GLOBAL(struct sqlite3PrngType, sqlite3Prng),
|
| + sizeof(sqlite3Prng)
|
| + );
|
| +}
|
| +SQLITE_PRIVATE void sqlite3PrngRestoreState(void){
|
| + memcpy(
|
| + &GLOBAL(struct sqlite3PrngType, sqlite3Prng),
|
| + &GLOBAL(struct sqlite3PrngType, sqlite3SavedPrng),
|
| + sizeof(sqlite3Prng)
|
| + );
|
| +}
|
| +#endif /* SQLITE_UNTESTABLE */
|
| +
|
| +/************** End of random.c **********************************************/
|
| +/************** Begin file threads.c *****************************************/
|
| +/*
|
| +** 2012 July 21
|
| +**
|
| +** 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 presents a simple cross-platform threading interface for
|
| +** use internally by SQLite.
|
| +**
|
| +** A "thread" can be created using sqlite3ThreadCreate(). This thread
|
| +** runs independently of its creator until it is joined using
|
| +** sqlite3ThreadJoin(), at which point it terminates.
|
| +**
|
| +** Threads do not have to be real. It could be that the work of the
|
| +** "thread" is done by the main thread at either the sqlite3ThreadCreate()
|
| +** or sqlite3ThreadJoin() call. This is, in fact, what happens in
|
| +** single threaded systems. Nothing in SQLite requires multiple threads.
|
| +** This interface exists so that applications that want to take advantage
|
| +** of multiple cores can do so, while also allowing applications to stay
|
| +** single-threaded if desired.
|
| +*/
|
| +/* #include "sqliteInt.h" */
|
| +#if SQLITE_OS_WIN
|
| +/* # include "os_win.h" */
|
| +#endif
|
| +
|
| +#if SQLITE_MAX_WORKER_THREADS>0
|
| +
|
| +/********************************* Unix Pthreads ****************************/
|
| +#if SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) && SQLITE_THREADSAFE>0
|
| +
|
| +#define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */
|
| +/* #include <pthread.h> */
|
| +
|
| +/* A running thread */
|
| +struct SQLiteThread {
|
| + pthread_t tid; /* Thread ID */
|
| + int done; /* Set to true when thread finishes */
|
| + void *pOut; /* Result returned by the thread */
|
| + void *(*xTask)(void*); /* The thread routine */
|
| + void *pIn; /* Argument to the thread */
|
| +};
|
| +
|
| +/* Create a new thread */
|
| +SQLITE_PRIVATE int sqlite3ThreadCreate(
|
| + SQLiteThread **ppThread, /* OUT: Write the thread object here */
|
| + void *(*xTask)(void*), /* Routine to run in a separate thread */
|
| + void *pIn /* Argument passed into xTask() */
|
| +){
|
| + SQLiteThread *p;
|
| + int rc;
|
| +
|
| + assert( ppThread!=0 );
|
| + assert( xTask!=0 );
|
| + /* This routine is never used in single-threaded mode */
|
| + assert( sqlite3GlobalConfig.bCoreMutex!=0 );
|
| +
|
| + *ppThread = 0;
|
| + p = sqlite3Malloc(sizeof(*p));
|
| + if( p==0 ) return SQLITE_NOMEM_BKPT;
|
| + memset(p, 0, sizeof(*p));
|
| + p->xTask = xTask;
|
| + p->pIn = pIn;
|
| + /* If the SQLITE_TESTCTRL_FAULT_INSTALL callback is registered to a
|
| + ** function that returns SQLITE_ERROR when passed the argument 200, that
|
| + ** forces worker threads to run sequentially and deterministically
|
| + ** for testing purposes. */
|
| + if( sqlite3FaultSim(200) ){
|
| + rc = 1;
|
| + }else{
|
| + rc = pthread_create(&p->tid, 0, xTask, pIn);
|
| + }
|
| + if( rc ){
|
| + p->done = 1;
|
| + p->pOut = xTask(pIn);
|
| + }
|
| + *ppThread = p;
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/* Get the results of the thread */
|
| +SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
|
| + int rc;
|
| +
|
| + assert( ppOut!=0 );
|
| + if( NEVER(p==0) ) return SQLITE_NOMEM_BKPT;
|
| + if( p->done ){
|
| + *ppOut = p->pOut;
|
| + rc = SQLITE_OK;
|
| + }else{
|
| + rc = pthread_join(p->tid, ppOut) ? SQLITE_ERROR : SQLITE_OK;
|
| + }
|
| + sqlite3_free(p);
|
| + return rc;
|
| +}
|
| +
|
| +#endif /* SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) */
|
| +/******************************** End Unix Pthreads *************************/
|
| +
|
| +
|
| +/********************************* Win32 Threads ****************************/
|
| +#if SQLITE_OS_WIN_THREADS
|
| +
|
| +#define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */
|
| +#include <process.h>
|
| +
|
| +/* A running thread */
|
| +struct SQLiteThread {
|
| + void *tid; /* The thread handle */
|
| + unsigned id; /* The thread identifier */
|
| + void *(*xTask)(void*); /* The routine to run as a thread */
|
| + void *pIn; /* Argument to xTask */
|
| + void *pResult; /* Result of xTask */
|
| +};
|
| +
|
| +/* Thread procedure Win32 compatibility shim */
|
| +static unsigned __stdcall sqlite3ThreadProc(
|
| + void *pArg /* IN: Pointer to the SQLiteThread structure */
|
| +){
|
| + SQLiteThread *p = (SQLiteThread *)pArg;
|
| +
|
| + assert( p!=0 );
|
| +#if 0
|
| + /*
|
| + ** This assert appears to trigger spuriously on certain
|
| + ** versions of Windows, possibly due to _beginthreadex()
|
| + ** and/or CreateThread() not fully setting their thread
|
| + ** ID parameter before starting the thread.
|
| + */
|
| + assert( p->id==GetCurrentThreadId() );
|
| +#endif
|
| + assert( p->xTask!=0 );
|
| + p->pResult = p->xTask(p->pIn);
|
| +
|
| + _endthreadex(0);
|
| + return 0; /* NOT REACHED */
|
| +}
|
| +
|
| +/* Create a new thread */
|
| +SQLITE_PRIVATE int sqlite3ThreadCreate(
|
| + SQLiteThread **ppThread, /* OUT: Write the thread object here */
|
| + void *(*xTask)(void*), /* Routine to run in a separate thread */
|
| + void *pIn /* Argument passed into xTask() */
|
| +){
|
| + SQLiteThread *p;
|
| +
|
| + assert( ppThread!=0 );
|
| + assert( xTask!=0 );
|
| + *ppThread = 0;
|
| + p = sqlite3Malloc(sizeof(*p));
|
| + if( p==0 ) return SQLITE_NOMEM_BKPT;
|
| + /* If the SQLITE_TESTCTRL_FAULT_INSTALL callback is registered to a
|
| + ** function that returns SQLITE_ERROR when passed the argument 200, that
|
| + ** forces worker threads to run sequentially and deterministically
|
| + ** (via the sqlite3FaultSim() term of the conditional) for testing
|
| + ** purposes. */
|
| + if( sqlite3GlobalConfig.bCoreMutex==0 || sqlite3FaultSim(200) ){
|
| + memset(p, 0, sizeof(*p));
|
| + }else{
|
| + p->xTask = xTask;
|
| + p->pIn = pIn;
|
| + p->tid = (void*)_beginthreadex(0, 0, sqlite3ThreadProc, p, 0, &p->id);
|
| + if( p->tid==0 ){
|
| + memset(p, 0, sizeof(*p));
|
| + }
|
| + }
|
| + if( p->xTask==0 ){
|
| + p->id = GetCurrentThreadId();
|
| + p->pResult = xTask(pIn);
|
| + }
|
| + *ppThread = p;
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +SQLITE_PRIVATE DWORD sqlite3Win32Wait(HANDLE hObject); /* os_win.c */
|
| +
|
| +/* Get the results of the thread */
|
| +SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
|
| + DWORD rc;
|
| + BOOL bRc;
|
| +
|
| + assert( ppOut!=0 );
|
| + if( NEVER(p==0) ) return SQLITE_NOMEM_BKPT;
|
| + if( p->xTask==0 ){
|
| + /* assert( p->id==GetCurrentThreadId() ); */
|
| + rc = WAIT_OBJECT_0;
|
| + assert( p->tid==0 );
|
| + }else{
|
| + assert( p->id!=0 && p->id!=GetCurrentThreadId() );
|
| + rc = sqlite3Win32Wait((HANDLE)p->tid);
|
| + assert( rc!=WAIT_IO_COMPLETION );
|
| + bRc = CloseHandle((HANDLE)p->tid);
|
| + assert( bRc );
|
| + }
|
| + if( rc==WAIT_OBJECT_0 ) *ppOut = p->pResult;
|
| + sqlite3_free(p);
|
| + return (rc==WAIT_OBJECT_0) ? SQLITE_OK : SQLITE_ERROR;
|
| +}
|
| +
|
| +#endif /* SQLITE_OS_WIN_THREADS */
|
| +/******************************** End Win32 Threads *************************/
|
| +
|
| +
|
| +/********************************* Single-Threaded **************************/
|
| +#ifndef SQLITE_THREADS_IMPLEMENTED
|
| +/*
|
| +** This implementation does not actually create a new thread. It does the
|
| +** work of the thread in the main thread, when either the thread is created
|
| +** or when it is joined
|
| +*/
|
| +
|
| +/* A running thread */
|
| +struct SQLiteThread {
|
| + void *(*xTask)(void*); /* The routine to run as a thread */
|
| + void *pIn; /* Argument to xTask */
|
| + void *pResult; /* Result of xTask */
|
| +};
|
| +
|
| +/* Create a new thread */
|
| +SQLITE_PRIVATE int sqlite3ThreadCreate(
|
| + SQLiteThread **ppThread, /* OUT: Write the thread object here */
|
| + void *(*xTask)(void*), /* Routine to run in a separate thread */
|
| + void *pIn /* Argument passed into xTask() */
|
| +){
|
| + SQLiteThread *p;
|
| +
|
| + assert( ppThread!=0 );
|
| + assert( xTask!=0 );
|
| + *ppThread = 0;
|
| + p = sqlite3Malloc(sizeof(*p));
|
| + if( p==0 ) return SQLITE_NOMEM_BKPT;
|
| + if( (SQLITE_PTR_TO_INT(p)/17)&1 ){
|
| + p->xTask = xTask;
|
| + p->pIn = pIn;
|
| + }else{
|
| + p->xTask = 0;
|
| + p->pResult = xTask(pIn);
|
| + }
|
| + *ppThread = p;
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/* Get the results of the thread */
|
| +SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
|
| +
|
| + assert( ppOut!=0 );
|
| + if( NEVER(p==0) ) return SQLITE_NOMEM_BKPT;
|
| + if( p->xTask ){
|
| + *ppOut = p->xTask(p->pIn);
|
| + }else{
|
| + *ppOut = p->pResult;
|
| + }
|
| + sqlite3_free(p);
|
| +
|
| +#if defined(SQLITE_TEST)
|
| + {
|
| + void *pTstAlloc = sqlite3Malloc(10);
|
| + if (!pTstAlloc) return SQLITE_NOMEM_BKPT;
|
| + sqlite3_free(pTstAlloc);
|
| + }
|
| +#endif
|
| +
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +#endif /* !defined(SQLITE_THREADS_IMPLEMENTED) */
|
| +/****************************** End Single-Threaded *************************/
|
| +#endif /* SQLITE_MAX_WORKER_THREADS>0 */
|
| +
|
| +/************** End of threads.c *********************************************/
|
| +/************** Begin file utf.c *********************************************/
|
| +/*
|
| +** 2004 April 13
|
| +**
|
| +** 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 routines used to translate between UTF-8,
|
| +** UTF-16, UTF-16BE, and UTF-16LE.
|
| +**
|
| +** Notes on UTF-8:
|
| +**
|
| +** Byte-0 Byte-1 Byte-2 Byte-3 Value
|
| +** 0xxxxxxx 00000000 00000000 0xxxxxxx
|
| +** 110yyyyy 10xxxxxx 00000000 00000yyy yyxxxxxx
|
| +** 1110zzzz 10yyyyyy 10xxxxxx 00000000 zzzzyyyy yyxxxxxx
|
| +** 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 000uuuuu zzzzyyyy yyxxxxxx
|
| +**
|
| +**
|
| +** Notes on UTF-16: (with wwww+1==uuuuu)
|
| +**
|
| +** Word-0 Word-1 Value
|
| +** 110110ww wwzzzzyy 110111yy yyxxxxxx 000uuuuu zzzzyyyy yyxxxxxx
|
| +** zzzzyyyy yyxxxxxx 00000000 zzzzyyyy yyxxxxxx
|
| +**
|
| +**
|
| +** BOM or Byte Order Mark:
|
| +** 0xff 0xfe little-endian utf-16 follows
|
| +** 0xfe 0xff big-endian utf-16 follows
|
| +**
|
| +*/
|
| +/* #include "sqliteInt.h" */
|
| +/* #include <assert.h> */
|
| +/* #include "vdbeInt.h" */
|
| +
|
| +#if !defined(SQLITE_AMALGAMATION) && SQLITE_BYTEORDER==0
|
| +/*
|
| +** The following constant value is used by the SQLITE_BIGENDIAN and
|
| +** SQLITE_LITTLEENDIAN macros.
|
| +*/
|
| +SQLITE_PRIVATE const int sqlite3one = 1;
|
| +#endif /* SQLITE_AMALGAMATION && SQLITE_BYTEORDER==0 */
|
| +
|
| +/*
|
| +** This lookup table is used to help decode the first byte of
|
| +** a multi-byte UTF8 character.
|
| +*/
|
| +static const unsigned char sqlite3Utf8Trans1[] = {
|
| + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
| + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
| + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
| + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
| + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
| + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
| + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
| + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
|
| +};
|
| +
|
| +
|
| +#define WRITE_UTF8(zOut, c) { \
|
| + if( c<0x00080 ){ \
|
| + *zOut++ = (u8)(c&0xFF); \
|
| + } \
|
| + else if( c<0x00800 ){ \
|
| + *zOut++ = 0xC0 + (u8)((c>>6)&0x1F); \
|
| + *zOut++ = 0x80 + (u8)(c & 0x3F); \
|
| + } \
|
| + else if( c<0x10000 ){ \
|
| + *zOut++ = 0xE0 + (u8)((c>>12)&0x0F); \
|
| + *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); \
|
| + *zOut++ = 0x80 + (u8)(c & 0x3F); \
|
| + }else{ \
|
| + *zOut++ = 0xF0 + (u8)((c>>18) & 0x07); \
|
| + *zOut++ = 0x80 + (u8)((c>>12) & 0x3F); \
|
| + *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); \
|
| + *zOut++ = 0x80 + (u8)(c & 0x3F); \
|
| + } \
|
| +}
|
| +
|
| +#define WRITE_UTF16LE(zOut, c) { \
|
| + if( c<=0xFFFF ){ \
|
| + *zOut++ = (u8)(c&0x00FF); \
|
| + *zOut++ = (u8)((c>>8)&0x00FF); \
|
| + }else{ \
|
| + *zOut++ = (u8)(((c>>10)&0x003F) + (((c-0x10000)>>10)&0x00C0)); \
|
| + *zOut++ = (u8)(0x00D8 + (((c-0x10000)>>18)&0x03)); \
|
| + *zOut++ = (u8)(c&0x00FF); \
|
| + *zOut++ = (u8)(0x00DC + ((c>>8)&0x03)); \
|
| + } \
|
| +}
|
| +
|
| +#define WRITE_UTF16BE(zOut, c) { \
|
| + if( c<=0xFFFF ){ \
|
| + *zOut++ = (u8)((c>>8)&0x00FF); \
|
| + *zOut++ = (u8)(c&0x00FF); \
|
| + }else{ \
|
| + *zOut++ = (u8)(0x00D8 + (((c-0x10000)>>18)&0x03)); \
|
| + *zOut++ = (u8)(((c>>10)&0x003F) + (((c-0x10000)>>10)&0x00C0)); \
|
| + *zOut++ = (u8)(0x00DC + ((c>>8)&0x03)); \
|
| + *zOut++ = (u8)(c&0x00FF); \
|
| + } \
|
| +}
|
| +
|
| +#define READ_UTF16LE(zIn, TERM, c){ \
|
| + c = (*zIn++); \
|
| + c += ((*zIn++)<<8); \
|
| + if( c>=0xD800 && c<0xE000 && TERM ){ \
|
| + int c2 = (*zIn++); \
|
| + c2 += ((*zIn++)<<8); \
|
| + c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \
|
| + } \
|
| +}
|
| +
|
| +#define READ_UTF16BE(zIn, TERM, c){ \
|
| + c = ((*zIn++)<<8); \
|
| + c += (*zIn++); \
|
| + if( c>=0xD800 && c<0xE000 && TERM ){ \
|
| + int c2 = ((*zIn++)<<8); \
|
| + c2 += (*zIn++); \
|
| + c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \
|
| + } \
|
| +}
|
| +
|
| +/*
|
| +** Translate a single UTF-8 character. Return the unicode value.
|
| +**
|
| +** During translation, assume that the byte that zTerm points
|
| +** is a 0x00.
|
| +**
|
| +** Write a pointer to the next unread byte back into *pzNext.
|
| +**
|
| +** Notes On Invalid UTF-8:
|
| +**
|
| +** * This routine never allows a 7-bit character (0x00 through 0x7f) to
|
| +** be encoded as a multi-byte character. Any multi-byte character that
|
| +** attempts to encode a value between 0x00 and 0x7f is rendered as 0xfffd.
|
| +**
|
| +** * This routine never allows a UTF16 surrogate value to be encoded.
|
| +** If a multi-byte character attempts to encode a value between
|
| +** 0xd800 and 0xe000 then it is rendered as 0xfffd.
|
| +**
|
| +** * Bytes in the range of 0x80 through 0xbf which occur as the first
|
| +** byte of a character are interpreted as single-byte characters
|
| +** and rendered as themselves even though they are technically
|
| +** invalid characters.
|
| +**
|
| +** * This routine accepts over-length UTF8 encodings
|
| +** for unicode values 0x80 and greater. It does not change over-length
|
| +** encodings to 0xfffd as some systems recommend.
|
| +*/
|
| +#define READ_UTF8(zIn, zTerm, c) \
|
| + c = *(zIn++); \
|
| + if( c>=0xc0 ){ \
|
| + c = sqlite3Utf8Trans1[c-0xc0]; \
|
| + while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \
|
| + c = (c<<6) + (0x3f & *(zIn++)); \
|
| + } \
|
| + if( c<0x80 \
|
| + || (c&0xFFFFF800)==0xD800 \
|
| + || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \
|
| + }
|
| +SQLITE_PRIVATE u32 sqlite3Utf8Read(
|
| + const unsigned char **pz /* Pointer to string from which to read char */
|
| +){
|
| + unsigned int c;
|
| +
|
| + /* Same as READ_UTF8() above but without the zTerm parameter.
|
| + ** For this routine, we assume the UTF8 string is always zero-terminated.
|
| + */
|
| + c = *((*pz)++);
|
| + if( c>=0xc0 ){
|
| + c = sqlite3Utf8Trans1[c-0xc0];
|
| + while( (*(*pz) & 0xc0)==0x80 ){
|
| + c = (c<<6) + (0x3f & *((*pz)++));
|
| + }
|
| + if( c<0x80
|
| + || (c&0xFFFFF800)==0xD800
|
| + || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; }
|
| + }
|
| + return c;
|
| +}
|
| +
|
| +
|
| +
|
| +
|
| +/*
|
| +** If the TRANSLATE_TRACE macro is defined, the value of each Mem is
|
| +** printed on stderr on the way into and out of sqlite3VdbeMemTranslate().
|
| +*/
|
| +/* #define TRANSLATE_TRACE 1 */
|
| +
|
| +#ifndef SQLITE_OMIT_UTF16
|
| +/*
|
| +** This routine transforms the internal text encoding used by pMem to
|
| +** desiredEnc. It is an error if the string is already of the desired
|
| +** encoding, or if *pMem does not contain a string value.
|
| +*/
|
| +SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
|
| + int len; /* Maximum length of output string in bytes */
|
| + unsigned char *zOut; /* Output buffer */
|
| + unsigned char *zIn; /* Input iterator */
|
| + unsigned char *zTerm; /* End of input */
|
| + unsigned char *z; /* Output iterator */
|
| + unsigned int c;
|
| +
|
| + assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
| + assert( pMem->flags&MEM_Str );
|
| + assert( pMem->enc!=desiredEnc );
|
| + assert( pMem->enc!=0 );
|
| + assert( pMem->n>=0 );
|
| +
|
| +#if defined(TRANSLATE_TRACE) && defined(SQLITE_DEBUG)
|
| + {
|
| + char zBuf[100];
|
| + sqlite3VdbeMemPrettyPrint(pMem, zBuf);
|
| + fprintf(stderr, "INPUT: %s\n", zBuf);
|
| + }
|
| +#endif
|
| +
|
| + /* If the translation is between UTF-16 little and big endian, then
|
| + ** all that is required is to swap the byte order. This case is handled
|
| + ** differently from the others.
|
| + */
|
| + if( pMem->enc!=SQLITE_UTF8 && desiredEnc!=SQLITE_UTF8 ){
|
| + u8 temp;
|
| + int rc;
|
| + rc = sqlite3VdbeMemMakeWriteable(pMem);
|
| + if( rc!=SQLITE_OK ){
|
| + assert( rc==SQLITE_NOMEM );
|
| + return SQLITE_NOMEM_BKPT;
|
| + }
|
| + zIn = (u8*)pMem->z;
|
| + zTerm = &zIn[pMem->n&~1];
|
| + while( zIn<zTerm ){
|
| + temp = *zIn;
|
| + *zIn = *(zIn+1);
|
| + zIn++;
|
| + *zIn++ = temp;
|
| + }
|
| + pMem->enc = desiredEnc;
|
| + goto translate_out;
|
| + }
|
| +
|
| + /* Set len to the maximum number of bytes required in the output buffer. */
|
| + if( desiredEnc==SQLITE_UTF8 ){
|
| + /* When converting from UTF-16, the maximum growth results from
|
| + ** translating a 2-byte character to a 4-byte UTF-8 character.
|
| + ** A single byte is required for the output string
|
| + ** nul-terminator.
|
| + */
|
| + pMem->n &= ~1;
|
| + len = pMem->n * 2 + 1;
|
| + }else{
|
| + /* When converting from UTF-8 to UTF-16 the maximum growth is caused
|
| + ** when a 1-byte UTF-8 character is translated into a 2-byte UTF-16
|
| + ** character. Two bytes are required in the output buffer for the
|
| + ** nul-terminator.
|
| + */
|
| + len = pMem->n * 2 + 2;
|
| + }
|
| +
|
| + /* Set zIn to point at the start of the input buffer and zTerm to point 1
|
| + ** byte past the end.
|
| + **
|
| + ** Variable zOut is set to point at the output buffer, space obtained
|
| + ** from sqlite3_malloc().
|
| + */
|
| + zIn = (u8*)pMem->z;
|
| + zTerm = &zIn[pMem->n];
|
| + zOut = sqlite3DbMallocRaw(pMem->db, len);
|
| + if( !zOut ){
|
| + return SQLITE_NOMEM_BKPT;
|
| + }
|
| + z = zOut;
|
| +
|
| + if( pMem->enc==SQLITE_UTF8 ){
|
| + if( desiredEnc==SQLITE_UTF16LE ){
|
| + /* UTF-8 -> UTF-16 Little-endian */
|
| + while( zIn<zTerm ){
|
| + READ_UTF8(zIn, zTerm, c);
|
| + WRITE_UTF16LE(z, c);
|
| + }
|
| + }else{
|
| + assert( desiredEnc==SQLITE_UTF16BE );
|
| + /* UTF-8 -> UTF-16 Big-endian */
|
| + while( zIn<zTerm ){
|
| + READ_UTF8(zIn, zTerm, c);
|
| + WRITE_UTF16BE(z, c);
|
| + }
|
| + }
|
| + pMem->n = (int)(z - zOut);
|
| + *z++ = 0;
|
| + }else{
|
| + assert( desiredEnc==SQLITE_UTF8 );
|
| + if( pMem->enc==SQLITE_UTF16LE ){
|
| + /* UTF-16 Little-endian -> UTF-8 */
|
| + while( zIn<zTerm ){
|
| + READ_UTF16LE(zIn, zIn<zTerm, c);
|
| + WRITE_UTF8(z, c);
|
| + }
|
| + }else{
|
| + /* UTF-16 Big-endian -> UTF-8 */
|
| + while( zIn<zTerm ){
|
| + READ_UTF16BE(zIn, zIn<zTerm, c);
|
| + WRITE_UTF8(z, c);
|
| + }
|
| + }
|
| + pMem->n = (int)(z - zOut);
|
| + }
|
| + *z = 0;
|
| + assert( (pMem->n+(desiredEnc==SQLITE_UTF8?1:2))<=len );
|
| +
|
| + c = pMem->flags;
|
| + sqlite3VdbeMemRelease(pMem);
|
| + pMem->flags = MEM_Str|MEM_Term|(c&(MEM_AffMask|MEM_Subtype));
|
| + pMem->enc = desiredEnc;
|
| + pMem->z = (char*)zOut;
|
| + pMem->zMalloc = pMem->z;
|
| + pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->z);
|
| +
|
| +translate_out:
|
| +#if defined(TRANSLATE_TRACE) && defined(SQLITE_DEBUG)
|
| + {
|
| + char zBuf[100];
|
| + sqlite3VdbeMemPrettyPrint(pMem, zBuf);
|
| + fprintf(stderr, "OUTPUT: %s\n", zBuf);
|
| + }
|
| +#endif
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** This routine checks for a byte-order mark at the beginning of the
|
| +** UTF-16 string stored in *pMem. If one is present, it is removed and
|
| +** the encoding of the Mem adjusted. This routine does not do any
|
| +** byte-swapping, it just sets Mem.enc appropriately.
|
| +**
|
| +** The allocation (static, dynamic etc.) and encoding of the Mem may be
|
| +** changed by this function.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3VdbeMemHandleBom(Mem *pMem){
|
| + int rc = SQLITE_OK;
|
| + u8 bom = 0;
|
| +
|
| + assert( pMem->n>=0 );
|
| + if( pMem->n>1 ){
|
| + u8 b1 = *(u8 *)pMem->z;
|
| + u8 b2 = *(((u8 *)pMem->z) + 1);
|
| + if( b1==0xFE && b2==0xFF ){
|
| + bom = SQLITE_UTF16BE;
|
| + }
|
| + if( b1==0xFF && b2==0xFE ){
|
| + bom = SQLITE_UTF16LE;
|
| + }
|
| + }
|
| +
|
| + if( bom ){
|
| + rc = sqlite3VdbeMemMakeWriteable(pMem);
|
| + if( rc==SQLITE_OK ){
|
| + pMem->n -= 2;
|
| + memmove(pMem->z, &pMem->z[2], pMem->n);
|
| + pMem->z[pMem->n] = '\0';
|
| + pMem->z[pMem->n+1] = '\0';
|
| + pMem->flags |= MEM_Term;
|
| + pMem->enc = bom;
|
| + }
|
| + }
|
| + return rc;
|
| +}
|
| +#endif /* SQLITE_OMIT_UTF16 */
|
| +
|
| +/*
|
| +** pZ is a UTF-8 encoded unicode string. If nByte is less than zero,
|
| +** return the number of unicode characters in pZ up to (but not including)
|
| +** the first 0x00 byte. If nByte is not less than zero, return the
|
| +** number of unicode characters in the first nByte of pZ (or up to
|
| +** the first 0x00, whichever comes first).
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *zIn, int nByte){
|
| + int r = 0;
|
| + const u8 *z = (const u8*)zIn;
|
| + const u8 *zTerm;
|
| + if( nByte>=0 ){
|
| + zTerm = &z[nByte];
|
| + }else{
|
| + zTerm = (const u8*)(-1);
|
| + }
|
| + assert( z<=zTerm );
|
| + while( *z!=0 && z<zTerm ){
|
| + SQLITE_SKIP_UTF8(z);
|
| + r++;
|
| + }
|
| + return r;
|
| +}
|
| +
|
| +/* This test function is not currently used by the automated test-suite.
|
| +** Hence it is only available in debug builds.
|
| +*/
|
| +#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
|
| +/*
|
| +** Translate UTF-8 to UTF-8.
|
| +**
|
| +** This has the effect of making sure that the string is well-formed
|
| +** UTF-8. Miscoded characters are removed.
|
| +**
|
| +** The translation is done in-place and aborted if the output
|
| +** overruns the input.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3Utf8To8(unsigned char *zIn){
|
| + unsigned char *zOut = zIn;
|
| + unsigned char *zStart = zIn;
|
| + u32 c;
|
| +
|
| + while( zIn[0] && zOut<=zIn ){
|
| + c = sqlite3Utf8Read((const u8**)&zIn);
|
| + if( c!=0xfffd ){
|
| + WRITE_UTF8(zOut, c);
|
| + }
|
| + }
|
| + *zOut = 0;
|
| + return (int)(zOut - zStart);
|
| +}
|
| +#endif
|
| +
|
| +#ifndef SQLITE_OMIT_UTF16
|
| +/*
|
| +** Convert a UTF-16 string in the native encoding into a UTF-8 string.
|
| +** Memory to hold the UTF-8 string is obtained from sqlite3_malloc and must
|
| +** be freed by the calling function.
|
| +**
|
| +** NULL is returned if there is an allocation error.
|
| +*/
|
| +SQLITE_PRIVATE char *sqlite3Utf16to8(sqlite3 *db, const void *z, int nByte, u8 enc){
|
| + Mem m;
|
| + memset(&m, 0, sizeof(m));
|
| + m.db = db;
|
| + sqlite3VdbeMemSetStr(&m, z, nByte, enc, SQLITE_STATIC);
|
| + sqlite3VdbeChangeEncoding(&m, SQLITE_UTF8);
|
| + if( db->mallocFailed ){
|
| + sqlite3VdbeMemRelease(&m);
|
| + m.z = 0;
|
| + }
|
| + assert( (m.flags & MEM_Term)!=0 || db->mallocFailed );
|
| + assert( (m.flags & MEM_Str)!=0 || db->mallocFailed );
|
| + assert( m.z || db->mallocFailed );
|
| + return m.z;
|
| +}
|
| +
|
| +/*
|
| +** zIn is a UTF-16 encoded unicode string at least nChar characters long.
|
| +** Return the number of bytes in the first nChar unicode characters
|
| +** in pZ. nChar must be non-negative.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *zIn, int nChar){
|
| + int c;
|
| + unsigned char const *z = zIn;
|
| + int n = 0;
|
| +
|
| + if( SQLITE_UTF16NATIVE==SQLITE_UTF16BE ){
|
| + while( n<nChar ){
|
| + READ_UTF16BE(z, 1, c);
|
| + n++;
|
| + }
|
| + }else{
|
| + while( n<nChar ){
|
| + READ_UTF16LE(z, 1, c);
|
| + n++;
|
| + }
|
| + }
|
| + return (int)(z-(unsigned char const *)zIn);
|
| +}
|
| +
|
| +#if defined(SQLITE_TEST)
|
| +/*
|
| +** This routine is called from the TCL test function "translate_selftest".
|
| +** It checks that the primitives for serializing and deserializing
|
| +** characters in each encoding are inverses of each other.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3UtfSelfTest(void){
|
| + unsigned int i, t;
|
| + unsigned char zBuf[20];
|
| + unsigned char *z;
|
| + int n;
|
| + unsigned int c;
|
| +
|
| + for(i=0; i<0x00110000; i++){
|
| + z = zBuf;
|
| + WRITE_UTF8(z, i);
|
| + n = (int)(z-zBuf);
|
| + assert( n>0 && n<=4 );
|
| + z[0] = 0;
|
| + z = zBuf;
|
| + c = sqlite3Utf8Read((const u8**)&z);
|
| + t = i;
|
| + if( i>=0xD800 && i<=0xDFFF ) t = 0xFFFD;
|
| + if( (i&0xFFFFFFFE)==0xFFFE ) t = 0xFFFD;
|
| + assert( c==t );
|
| + assert( (z-zBuf)==n );
|
| + }
|
| + for(i=0; i<0x00110000; i++){
|
| + if( i>=0xD800 && i<0xE000 ) continue;
|
| + z = zBuf;
|
| + WRITE_UTF16LE(z, i);
|
| + n = (int)(z-zBuf);
|
| + assert( n>0 && n<=4 );
|
| + z[0] = 0;
|
| + z = zBuf;
|
| + READ_UTF16LE(z, 1, c);
|
| + assert( c==i );
|
| + assert( (z-zBuf)==n );
|
| + }
|
| + for(i=0; i<0x00110000; i++){
|
| + if( i>=0xD800 && i<0xE000 ) continue;
|
| + z = zBuf;
|
| + WRITE_UTF16BE(z, i);
|
| + n = (int)(z-zBuf);
|
| + assert( n>0 && n<=4 );
|
| + z[0] = 0;
|
| + z = zBuf;
|
| + READ_UTF16BE(z, 1, c);
|
| + assert( c==i );
|
| + assert( (z-zBuf)==n );
|
| + }
|
| +}
|
| +#endif /* SQLITE_TEST */
|
| +#endif /* SQLITE_OMIT_UTF16 */
|
| +
|
| +/************** End of utf.c *************************************************/
|
| +/************** Begin file util.c ********************************************/
|
| +/*
|
| +** 2001 September 15
|
| +**
|
| +** 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.
|
| +**
|
| +*************************************************************************
|
| +** Utility functions used throughout sqlite.
|
| +**
|
| +** This file contains functions for allocating memory, comparing
|
| +** strings, and stuff like that.
|
| +**
|
| +*/
|
| +/* #include "sqliteInt.h" */
|
| +/* #include <stdarg.h> */
|
| +#if HAVE_ISNAN || SQLITE_HAVE_ISNAN
|
| +# include <math.h>
|
| +#endif
|
| +
|
| +/*
|
| +** Routine needed to support the testcase() macro.
|
| +*/
|
| +#ifdef SQLITE_COVERAGE_TEST
|
| +SQLITE_PRIVATE void sqlite3Coverage(int x){
|
| + static unsigned dummy = 0;
|
| + dummy += (unsigned)x;
|
| +}
|
| +#endif
|
| +
|
| +/*
|
| +** Give a callback to the test harness that can be used to simulate faults
|
| +** in places where it is difficult or expensive to do so purely by means
|
| +** of inputs.
|
| +**
|
| +** The intent of the integer argument is to let the fault simulator know
|
| +** which of multiple sqlite3FaultSim() calls has been hit.
|
| +**
|
| +** Return whatever integer value the test callback returns, or return
|
| +** SQLITE_OK if no test callback is installed.
|
| +*/
|
| +#ifndef SQLITE_UNTESTABLE
|
| +SQLITE_PRIVATE int sqlite3FaultSim(int iTest){
|
| + int (*xCallback)(int) = sqlite3GlobalConfig.xTestCallback;
|
| + return xCallback ? xCallback(iTest) : SQLITE_OK;
|
| +}
|
| +#endif
|
| +
|
| +#ifndef SQLITE_OMIT_FLOATING_POINT
|
| +/*
|
| +** Return true if the floating point value is Not a Number (NaN).
|
| +**
|
| +** Use the math library isnan() function if compiled with SQLITE_HAVE_ISNAN.
|
| +** Otherwise, we have our own implementation that works on most systems.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3IsNaN(double x){
|
| + int rc; /* The value return */
|
| +#if !SQLITE_HAVE_ISNAN && !HAVE_ISNAN
|
| + /*
|
| + ** Systems that support the isnan() library function should probably
|
| + ** make use of it by compiling with -DSQLITE_HAVE_ISNAN. But we have
|
| + ** found that many systems do not have a working isnan() function so
|
| + ** this implementation is provided as an alternative.
|
| + **
|
| + ** This NaN test sometimes fails if compiled on GCC with -ffast-math.
|
| + ** On the other hand, the use of -ffast-math comes with the following
|
| + ** warning:
|
| + **
|
| + ** This option [-ffast-math] should never be turned on by any
|
| + ** -O option since it can result in incorrect output for programs
|
| + ** which depend on an exact implementation of IEEE or ISO
|
| + ** rules/specifications for math functions.
|
| + **
|
| + ** Under MSVC, this NaN test may fail if compiled with a floating-
|
| + ** point precision mode other than /fp:precise. From the MSDN
|
| + ** documentation:
|
| + **
|
| + ** The compiler [with /fp:precise] will properly handle comparisons
|
| + ** involving NaN. For example, x != x evaluates to true if x is NaN
|
| + ** ...
|
| + */
|
| +#ifdef __FAST_MATH__
|
| +# error SQLite will not work correctly with the -ffast-math option of GCC.
|
| +#endif
|
| + volatile double y = x;
|
| + volatile double z = y;
|
| + rc = (y!=z);
|
| +#else /* if HAVE_ISNAN */
|
| + rc = isnan(x);
|
| +#endif /* HAVE_ISNAN */
|
| + testcase( rc );
|
| + return rc;
|
| +}
|
| +#endif /* SQLITE_OMIT_FLOATING_POINT */
|
| +
|
| +/*
|
| +** Compute a string length that is limited to what can be stored in
|
| +** lower 30 bits of a 32-bit signed integer.
|
| +**
|
| +** The value returned will never be negative. Nor will it ever be greater
|
| +** than the actual length of the string. For very long strings (greater
|
| +** than 1GiB) the value returned might be less than the true string length.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3Strlen30(const char *z){
|
| + if( z==0 ) return 0;
|
| + return 0x3fffffff & (int)strlen(z);
|
| +}
|
| +
|
| +/*
|
| +** Return the declared type of a column. Or return zDflt if the column
|
| +** has no declared type.
|
| +**
|
| +** The column type is an extra string stored after the zero-terminator on
|
| +** the column name if and only if the COLFLAG_HASTYPE flag is set.
|
| +*/
|
| +SQLITE_PRIVATE char *sqlite3ColumnType(Column *pCol, char *zDflt){
|
| + if( (pCol->colFlags & COLFLAG_HASTYPE)==0 ) return zDflt;
|
| + return pCol->zName + strlen(pCol->zName) + 1;
|
| +}
|
| +
|
| +/*
|
| +** Helper function for sqlite3Error() - called rarely. Broken out into
|
| +** a separate routine to avoid unnecessary register saves on entry to
|
| +** sqlite3Error().
|
| +*/
|
| +static SQLITE_NOINLINE void sqlite3ErrorFinish(sqlite3 *db, int err_code){
|
| + if( db->pErr ) sqlite3ValueSetNull(db->pErr);
|
| + sqlite3SystemError(db, err_code);
|
| +}
|
| +
|
| +/*
|
| +** Set the current error code to err_code and clear any prior error message.
|
| +** Also set iSysErrno (by calling sqlite3System) if the err_code indicates
|
| +** that would be appropriate.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3Error(sqlite3 *db, int err_code){
|
| + assert( db!=0 );
|
| + db->errCode = err_code;
|
| + if( err_code || db->pErr ) sqlite3ErrorFinish(db, err_code);
|
| +}
|
| +
|
| +/*
|
| +** Load the sqlite3.iSysErrno field if that is an appropriate thing
|
| +** to do based on the SQLite error code in rc.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3SystemError(sqlite3 *db, int rc){
|
| + if( rc==SQLITE_IOERR_NOMEM ) return;
|
| + rc &= 0xff;
|
| + if( rc==SQLITE_CANTOPEN || rc==SQLITE_IOERR ){
|
| + db->iSysErrno = sqlite3OsGetLastError(db->pVfs);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Set the most recent error code and error string for the sqlite
|
| +** handle "db". The error code is set to "err_code".
|
| +**
|
| +** If it is not NULL, string zFormat specifies the format of the
|
| +** error string in the style of the printf functions: The following
|
| +** format characters are allowed:
|
| +**
|
| +** %s Insert a string
|
| +** %z A string that should be freed after use
|
| +** %d Insert an integer
|
| +** %T Insert a token
|
| +** %S Insert the first element of a SrcList
|
| +**
|
| +** zFormat and any string tokens that follow it are assumed to be
|
| +** encoded in UTF-8.
|
| +**
|
| +** To clear the most recent error for sqlite handle "db", sqlite3Error
|
| +** should be called with err_code set to SQLITE_OK and zFormat set
|
| +** to NULL.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3 *db, int err_code, const char *zFormat, ...){
|
| + assert( db!=0 );
|
| + db->errCode = err_code;
|
| + sqlite3SystemError(db, err_code);
|
| + if( zFormat==0 ){
|
| + sqlite3Error(db, err_code);
|
| + }else if( db->pErr || (db->pErr = sqlite3ValueNew(db))!=0 ){
|
| + char *z;
|
| + va_list ap;
|
| + va_start(ap, zFormat);
|
| + z = sqlite3VMPrintf(db, zFormat, ap);
|
| + va_end(ap);
|
| + sqlite3ValueSetStr(db->pErr, -1, z, SQLITE_UTF8, SQLITE_DYNAMIC);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Add an error message to pParse->zErrMsg and increment pParse->nErr.
|
| +** The following formatting characters are allowed:
|
| +**
|
| +** %s Insert a string
|
| +** %z A string that should be freed after use
|
| +** %d Insert an integer
|
| +** %T Insert a token
|
| +** %S Insert the first element of a SrcList
|
| +**
|
| +** This function should be used to report any error that occurs while
|
| +** compiling an SQL statement (i.e. within sqlite3_prepare()). The
|
| +** last thing the sqlite3_prepare() function does is copy the error
|
| +** stored by this function into the database handle using sqlite3Error().
|
| +** Functions sqlite3Error() or sqlite3ErrorWithMsg() should be used
|
| +** during statement execution (sqlite3_step() etc.).
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){
|
| + char *zMsg;
|
| + va_list ap;
|
| + sqlite3 *db = pParse->db;
|
| + va_start(ap, zFormat);
|
| + zMsg = sqlite3VMPrintf(db, zFormat, ap);
|
| + va_end(ap);
|
| + if( db->suppressErr ){
|
| + sqlite3DbFree(db, zMsg);
|
| + }else{
|
| + pParse->nErr++;
|
| + sqlite3DbFree(db, pParse->zErrMsg);
|
| + pParse->zErrMsg = zMsg;
|
| + pParse->rc = SQLITE_ERROR;
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Convert an SQL-style quoted string into a normal string by removing
|
| +** the quote characters. The conversion is done in-place. If the
|
| +** input does not begin with a quote character, then this routine
|
| +** is a no-op.
|
| +**
|
| +** The input string must be zero-terminated. A new zero-terminator
|
| +** is added to the dequoted string.
|
| +**
|
| +** The return value is -1 if no dequoting occurs or the length of the
|
| +** dequoted string, exclusive of the zero terminator, if dequoting does
|
| +** occur.
|
| +**
|
| +** 2002-Feb-14: This routine is extended to remove MS-Access style
|
| +** brackets from around identifiers. For example: "[a-b-c]" becomes
|
| +** "a-b-c".
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3Dequote(char *z){
|
| + char quote;
|
| + int i, j;
|
| + if( z==0 ) return;
|
| + quote = z[0];
|
| + if( !sqlite3Isquote(quote) ) return;
|
| + if( quote=='[' ) quote = ']';
|
| + for(i=1, j=0;; i++){
|
| + assert( z[i] );
|
| + if( z[i]==quote ){
|
| + if( z[i+1]==quote ){
|
| + z[j++] = quote;
|
| + i++;
|
| + }else{
|
| + break;
|
| + }
|
| + }else{
|
| + z[j++] = z[i];
|
| + }
|
| + }
|
| + z[j] = 0;
|
| +}
|
| +
|
| +/*
|
| +** Generate a Token object from a string
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3TokenInit(Token *p, char *z){
|
| + p->z = z;
|
| + p->n = sqlite3Strlen30(z);
|
| +}
|
| +
|
| +/* Convenient short-hand */
|
| +#define UpperToLower sqlite3UpperToLower
|
| +
|
| +/*
|
| +** Some systems have stricmp(). Others have strcasecmp(). Because
|
| +** there is no consistency, we will define our own.
|
| +**
|
| +** IMPLEMENTATION-OF: R-30243-02494 The sqlite3_stricmp() and
|
| +** sqlite3_strnicmp() APIs allow applications and extensions to compare
|
| +** the contents of two buffers containing UTF-8 strings in a
|
| +** case-independent fashion, using the same definition of "case
|
| +** independence" that SQLite uses internally when comparing identifiers.
|
| +*/
|
| +SQLITE_API int sqlite3_stricmp(const char *zLeft, const char *zRight){
|
| + if( zLeft==0 ){
|
| + return zRight ? -1 : 0;
|
| + }else if( zRight==0 ){
|
| + return 1;
|
| + }
|
| + return sqlite3StrICmp(zLeft, zRight);
|
| +}
|
| +SQLITE_PRIVATE int sqlite3StrICmp(const char *zLeft, const char *zRight){
|
| + unsigned char *a, *b;
|
| + int c;
|
| + a = (unsigned char *)zLeft;
|
| + b = (unsigned char *)zRight;
|
| + for(;;){
|
| + c = (int)UpperToLower[*a] - (int)UpperToLower[*b];
|
| + if( c || *a==0 ) break;
|
| + a++;
|
| + b++;
|
| + }
|
| + return c;
|
| +}
|
| +SQLITE_API int sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){
|
| + register unsigned char *a, *b;
|
| + if( zLeft==0 ){
|
| + return zRight ? -1 : 0;
|
| + }else if( zRight==0 ){
|
| + return 1;
|
| + }
|
| + a = (unsigned char *)zLeft;
|
| + b = (unsigned char *)zRight;
|
| + while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; }
|
| + return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b];
|
| +}
|
| +
|
| +/*
|
| +** The string z[] is an text representation of a real number.
|
| +** Convert this string to a double and write it into *pResult.
|
| +**
|
| +** The string z[] is length bytes in length (bytes, not characters) and
|
| +** uses the encoding enc. The string is not necessarily zero-terminated.
|
| +**
|
| +** Return TRUE if the result is a valid real number (or integer) and FALSE
|
| +** if the string is empty or contains extraneous text. Valid numbers
|
| +** are in one of these formats:
|
| +**
|
| +** [+-]digits[E[+-]digits]
|
| +** [+-]digits.[digits][E[+-]digits]
|
| +** [+-].digits[E[+-]digits]
|
| +**
|
| +** Leading and trailing whitespace is ignored for the purpose of determining
|
| +** validity.
|
| +**
|
| +** If some prefix of the input string is a valid number, this routine
|
| +** returns FALSE but it still converts the prefix and writes the result
|
| +** into *pResult.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){
|
| +#ifndef SQLITE_OMIT_FLOATING_POINT
|
| + int incr;
|
| + const char *zEnd = z + length;
|
| + /* sign * significand * (10 ^ (esign * exponent)) */
|
| + int sign = 1; /* sign of significand */
|
| + i64 s = 0; /* significand */
|
| + int d = 0; /* adjust exponent for shifting decimal point */
|
| + int esign = 1; /* sign of exponent */
|
| + int e = 0; /* exponent */
|
| + int eValid = 1; /* True exponent is either not used or is well-formed */
|
| + double result;
|
| + int nDigits = 0;
|
| + int nonNum = 0; /* True if input contains UTF16 with high byte non-zero */
|
| +
|
| + assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
|
| + *pResult = 0.0; /* Default return value, in case of an error */
|
| +
|
| + if( enc==SQLITE_UTF8 ){
|
| + incr = 1;
|
| + }else{
|
| + int i;
|
| + incr = 2;
|
| + assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
|
| + for(i=3-enc; i<length && z[i]==0; i+=2){}
|
| + nonNum = i<length;
|
| + zEnd = &z[i^1];
|
| + z += (enc&1);
|
| + }
|
| +
|
| + /* skip leading spaces */
|
| + while( z<zEnd && sqlite3Isspace(*z) ) z+=incr;
|
| + if( z>=zEnd ) return 0;
|
| +
|
| + /* get sign of significand */
|
| + if( *z=='-' ){
|
| + sign = -1;
|
| + z+=incr;
|
| + }else if( *z=='+' ){
|
| + z+=incr;
|
| + }
|
| +
|
| + /* copy max significant digits to significand */
|
| + while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){
|
| + s = s*10 + (*z - '0');
|
| + z+=incr, nDigits++;
|
| + }
|
| +
|
| + /* skip non-significant significand digits
|
| + ** (increase exponent by d to shift decimal left) */
|
| + while( z<zEnd && sqlite3Isdigit(*z) ) z+=incr, nDigits++, d++;
|
| + if( z>=zEnd ) goto do_atof_calc;
|
| +
|
| + /* if decimal point is present */
|
| + if( *z=='.' ){
|
| + z+=incr;
|
| + /* copy digits from after decimal to significand
|
| + ** (decrease exponent by d to shift decimal right) */
|
| + while( z<zEnd && sqlite3Isdigit(*z) ){
|
| + if( s<((LARGEST_INT64-9)/10) ){
|
| + s = s*10 + (*z - '0');
|
| + d--;
|
| + }
|
| + z+=incr, nDigits++;
|
| + }
|
| + }
|
| + if( z>=zEnd ) goto do_atof_calc;
|
| +
|
| + /* if exponent is present */
|
| + if( *z=='e' || *z=='E' ){
|
| + z+=incr;
|
| + eValid = 0;
|
| +
|
| + /* This branch is needed to avoid a (harmless) buffer overread. The
|
| + ** special comment alerts the mutation tester that the correct answer
|
| + ** is obtained even if the branch is omitted */
|
| + if( z>=zEnd ) goto do_atof_calc; /*PREVENTS-HARMLESS-OVERREAD*/
|
| +
|
| + /* get sign of exponent */
|
| + if( *z=='-' ){
|
| + esign = -1;
|
| + z+=incr;
|
| + }else if( *z=='+' ){
|
| + z+=incr;
|
| + }
|
| + /* copy digits to exponent */
|
| + while( z<zEnd && sqlite3Isdigit(*z) ){
|
| + e = e<10000 ? (e*10 + (*z - '0')) : 10000;
|
| + z+=incr;
|
| + eValid = 1;
|
| + }
|
| + }
|
| +
|
| + /* skip trailing spaces */
|
| + while( z<zEnd && sqlite3Isspace(*z) ) z+=incr;
|
| +
|
| +do_atof_calc:
|
| + /* adjust exponent by d, and update sign */
|
| + e = (e*esign) + d;
|
| + if( e<0 ) {
|
| + esign = -1;
|
| + e *= -1;
|
| + } else {
|
| + esign = 1;
|
| + }
|
| +
|
| + if( s==0 ) {
|
| + /* In the IEEE 754 standard, zero is signed. */
|
| + result = sign<0 ? -(double)0 : (double)0;
|
| + } else {
|
| + /* Attempt to reduce exponent.
|
| + **
|
| + ** Branches that are not required for the correct answer but which only
|
| + ** help to obtain the correct answer faster are marked with special
|
| + ** comments, as a hint to the mutation tester.
|
| + */
|
| + while( e>0 ){ /*OPTIMIZATION-IF-TRUE*/
|
| + if( esign>0 ){
|
| + if( s>=(LARGEST_INT64/10) ) break; /*OPTIMIZATION-IF-FALSE*/
|
| + s *= 10;
|
| + }else{
|
| + if( s%10!=0 ) break; /*OPTIMIZATION-IF-FALSE*/
|
| + s /= 10;
|
| + }
|
| + e--;
|
| + }
|
| +
|
| + /* adjust the sign of significand */
|
| + s = sign<0 ? -s : s;
|
| +
|
| + if( e==0 ){ /*OPTIMIZATION-IF-TRUE*/
|
| + result = (double)s;
|
| + }else{
|
| + LONGDOUBLE_TYPE scale = 1.0;
|
| + /* attempt to handle extremely small/large numbers better */
|
| + if( e>307 ){ /*OPTIMIZATION-IF-TRUE*/
|
| + if( e<342 ){ /*OPTIMIZATION-IF-TRUE*/
|
| + while( e%308 ) { scale *= 1.0e+1; e -= 1; }
|
| + if( esign<0 ){
|
| + result = s / scale;
|
| + result /= 1.0e+308;
|
| + }else{
|
| + result = s * scale;
|
| + result *= 1.0e+308;
|
| + }
|
| + }else{ assert( e>=342 );
|
| + if( esign<0 ){
|
| + result = 0.0*s;
|
| + }else{
|
| + result = 1e308*1e308*s; /* Infinity */
|
| + }
|
| + }
|
| + }else{
|
| + /* 1.0e+22 is the largest power of 10 than can be
|
| + ** represented exactly. */
|
| + while( e%22 ) { scale *= 1.0e+1; e -= 1; }
|
| + while( e>0 ) { scale *= 1.0e+22; e -= 22; }
|
| + if( esign<0 ){
|
| + result = s / scale;
|
| + }else{
|
| + result = s * scale;
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + /* store the result */
|
| + *pResult = result;
|
| +
|
| + /* return true if number and no extra non-whitespace chracters after */
|
| + return z==zEnd && nDigits>0 && eValid && nonNum==0;
|
| +#else
|
| + return !sqlite3Atoi64(z, pResult, length, enc);
|
| +#endif /* SQLITE_OMIT_FLOATING_POINT */
|
| +}
|
| +
|
| +/*
|
| +** Compare the 19-character string zNum against the text representation
|
| +** value 2^63: 9223372036854775808. Return negative, zero, or positive
|
| +** if zNum is less than, equal to, or greater than the string.
|
| +** Note that zNum must contain exactly 19 characters.
|
| +**
|
| +** Unlike memcmp() this routine is guaranteed to return the difference
|
| +** in the values of the last digit if the only difference is in the
|
| +** last digit. So, for example,
|
| +**
|
| +** compare2pow63("9223372036854775800", 1)
|
| +**
|
| +** will return -8.
|
| +*/
|
| +static int compare2pow63(const char *zNum, int incr){
|
| + int c = 0;
|
| + int i;
|
| + /* 012345678901234567 */
|
| + const char *pow63 = "922337203685477580";
|
| + for(i=0; c==0 && i<18; i++){
|
| + c = (zNum[i*incr]-pow63[i])*10;
|
| + }
|
| + if( c==0 ){
|
| + c = zNum[18*incr] - '8';
|
| + testcase( c==(-1) );
|
| + testcase( c==0 );
|
| + testcase( c==(+1) );
|
| + }
|
| + return c;
|
| +}
|
| +
|
| +/*
|
| +** Convert zNum to a 64-bit signed integer. zNum must be decimal. This
|
| +** routine does *not* accept hexadecimal notation.
|
| +**
|
| +** If the zNum value is representable as a 64-bit twos-complement
|
| +** integer, then write that value into *pNum and return 0.
|
| +**
|
| +** If zNum is exactly 9223372036854775808, return 2. This special
|
| +** case is broken out because while 9223372036854775808 cannot be a
|
| +** signed 64-bit integer, its negative -9223372036854775808 can be.
|
| +**
|
| +** If zNum is too big for a 64-bit integer and is not
|
| +** 9223372036854775808 or if zNum contains any non-numeric text,
|
| +** then return 1.
|
| +**
|
| +** length is the number of bytes in the string (bytes, not characters).
|
| +** The string is not necessarily zero-terminated. The encoding is
|
| +** given by enc.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){
|
| + int incr;
|
| + u64 u = 0;
|
| + int neg = 0; /* assume positive */
|
| + int i;
|
| + int c = 0;
|
| + int nonNum = 0; /* True if input contains UTF16 with high byte non-zero */
|
| + const char *zStart;
|
| + const char *zEnd = zNum + length;
|
| + assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
|
| + if( enc==SQLITE_UTF8 ){
|
| + incr = 1;
|
| + }else{
|
| + incr = 2;
|
| + assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
|
| + for(i=3-enc; i<length && zNum[i]==0; i+=2){}
|
| + nonNum = i<length;
|
| + zEnd = &zNum[i^1];
|
| + zNum += (enc&1);
|
| + }
|
| + while( zNum<zEnd && sqlite3Isspace(*zNum) ) zNum+=incr;
|
| + if( zNum<zEnd ){
|
| + if( *zNum=='-' ){
|
| + neg = 1;
|
| + zNum+=incr;
|
| + }else if( *zNum=='+' ){
|
| + zNum+=incr;
|
| + }
|
| + }
|
| + zStart = zNum;
|
| + while( zNum<zEnd && zNum[0]=='0' ){ zNum+=incr; } /* Skip leading zeros. */
|
| + for(i=0; &zNum[i]<zEnd && (c=zNum[i])>='0' && c<='9'; i+=incr){
|
| + u = u*10 + c - '0';
|
| + }
|
| + if( u>LARGEST_INT64 ){
|
| + *pNum = neg ? SMALLEST_INT64 : LARGEST_INT64;
|
| + }else if( neg ){
|
| + *pNum = -(i64)u;
|
| + }else{
|
| + *pNum = (i64)u;
|
| + }
|
| + testcase( i==18 );
|
| + testcase( i==19 );
|
| + testcase( i==20 );
|
| + if( &zNum[i]<zEnd /* Extra bytes at the end */
|
| + || (i==0 && zStart==zNum) /* No digits */
|
| + || i>19*incr /* Too many digits */
|
| + || nonNum /* UTF16 with high-order bytes non-zero */
|
| + ){
|
| + /* zNum is empty or contains non-numeric text or is longer
|
| + ** than 19 digits (thus guaranteeing that it is too large) */
|
| + return 1;
|
| + }else if( i<19*incr ){
|
| + /* Less than 19 digits, so we know that it fits in 64 bits */
|
| + assert( u<=LARGEST_INT64 );
|
| + return 0;
|
| + }else{
|
| + /* zNum is a 19-digit numbers. Compare it against 9223372036854775808. */
|
| + c = compare2pow63(zNum, incr);
|
| + if( c<0 ){
|
| + /* zNum is less than 9223372036854775808 so it fits */
|
| + assert( u<=LARGEST_INT64 );
|
| + return 0;
|
| + }else if( c>0 ){
|
| + /* zNum is greater than 9223372036854775808 so it overflows */
|
| + return 1;
|
| + }else{
|
| + /* zNum is exactly 9223372036854775808. Fits if negative. The
|
| + ** special case 2 overflow if positive */
|
| + assert( u-1==LARGEST_INT64 );
|
| + return neg ? 0 : 2;
|
| + }
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Transform a UTF-8 integer literal, in either decimal or hexadecimal,
|
| +** into a 64-bit signed integer. This routine accepts hexadecimal literals,
|
| +** whereas sqlite3Atoi64() does not.
|
| +**
|
| +** Returns:
|
| +**
|
| +** 0 Successful transformation. Fits in a 64-bit signed integer.
|
| +** 1 Integer too large for a 64-bit signed integer or is malformed
|
| +** 2 Special case of 9223372036854775808
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char *z, i64 *pOut){
|
| +#ifndef SQLITE_OMIT_HEX_INTEGER
|
| + if( z[0]=='0'
|
| + && (z[1]=='x' || z[1]=='X')
|
| + ){
|
| + u64 u = 0;
|
| + int i, k;
|
| + for(i=2; z[i]=='0'; i++){}
|
| + for(k=i; sqlite3Isxdigit(z[k]); k++){
|
| + u = u*16 + sqlite3HexToInt(z[k]);
|
| + }
|
| + memcpy(pOut, &u, 8);
|
| + return (z[k]==0 && k-i<=16) ? 0 : 1;
|
| + }else
|
| +#endif /* SQLITE_OMIT_HEX_INTEGER */
|
| + {
|
| + return sqlite3Atoi64(z, pOut, sqlite3Strlen30(z), SQLITE_UTF8);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** If zNum represents an integer that will fit in 32-bits, then set
|
| +** *pValue to that integer and return true. Otherwise return false.
|
| +**
|
| +** This routine accepts both decimal and hexadecimal notation for integers.
|
| +**
|
| +** Any non-numeric characters that following zNum are ignored.
|
| +** This is different from sqlite3Atoi64() which requires the
|
| +** input number to be zero-terminated.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3GetInt32(const char *zNum, int *pValue){
|
| + sqlite_int64 v = 0;
|
| + int i, c;
|
| + int neg = 0;
|
| + if( zNum[0]=='-' ){
|
| + neg = 1;
|
| + zNum++;
|
| + }else if( zNum[0]=='+' ){
|
| + zNum++;
|
| + }
|
| +#ifndef SQLITE_OMIT_HEX_INTEGER
|
| + else if( zNum[0]=='0'
|
| + && (zNum[1]=='x' || zNum[1]=='X')
|
| + && sqlite3Isxdigit(zNum[2])
|
| + ){
|
| + u32 u = 0;
|
| + zNum += 2;
|
| + while( zNum[0]=='0' ) zNum++;
|
| + for(i=0; sqlite3Isxdigit(zNum[i]) && i<8; i++){
|
| + u = u*16 + sqlite3HexToInt(zNum[i]);
|
| + }
|
| + if( (u&0x80000000)==0 && sqlite3Isxdigit(zNum[i])==0 ){
|
| + memcpy(pValue, &u, 4);
|
| + return 1;
|
| + }else{
|
| + return 0;
|
| + }
|
| + }
|
| +#endif
|
| + while( zNum[0]=='0' ) zNum++;
|
| + for(i=0; i<11 && (c = zNum[i] - '0')>=0 && c<=9; i++){
|
| + v = v*10 + c;
|
| + }
|
| +
|
| + /* The longest decimal representation of a 32 bit integer is 10 digits:
|
| + **
|
| + ** 1234567890
|
| + ** 2^31 -> 2147483648
|
| + */
|
| + testcase( i==10 );
|
| + if( i>10 ){
|
| + return 0;
|
| + }
|
| + testcase( v-neg==2147483647 );
|
| + if( v-neg>2147483647 ){
|
| + return 0;
|
| + }
|
| + if( neg ){
|
| + v = -v;
|
| + }
|
| + *pValue = (int)v;
|
| + return 1;
|
| +}
|
| +
|
| +/*
|
| +** Return a 32-bit integer value extracted from a string. If the
|
| +** string is not an integer, just return 0.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3Atoi(const char *z){
|
| + int x = 0;
|
| + if( z ) sqlite3GetInt32(z, &x);
|
| + return x;
|
| +}
|
| +
|
| +/*
|
| +** The variable-length integer encoding is as follows:
|
| +**
|
| +** KEY:
|
| +** A = 0xxxxxxx 7 bits of data and one flag bit
|
| +** B = 1xxxxxxx 7 bits of data and one flag bit
|
| +** C = xxxxxxxx 8 bits of data
|
| +**
|
| +** 7 bits - A
|
| +** 14 bits - BA
|
| +** 21 bits - BBA
|
| +** 28 bits - BBBA
|
| +** 35 bits - BBBBA
|
| +** 42 bits - BBBBBA
|
| +** 49 bits - BBBBBBA
|
| +** 56 bits - BBBBBBBA
|
| +** 64 bits - BBBBBBBBC
|
| +*/
|
| +
|
| +/*
|
| +** Write a 64-bit variable-length integer to memory starting at p[0].
|
| +** The length of data write will be between 1 and 9 bytes. The number
|
| +** of bytes written is returned.
|
| +**
|
| +** A variable-length integer consists of the lower 7 bits of each byte
|
| +** for all bytes that have the 8th bit set and one byte with the 8th
|
| +** bit clear. Except, if we get to the 9th byte, it stores the full
|
| +** 8 bits and is the last byte.
|
| +*/
|
| +static int SQLITE_NOINLINE putVarint64(unsigned char *p, u64 v){
|
| + int i, j, n;
|
| + u8 buf[10];
|
| + if( v & (((u64)0xff000000)<<32) ){
|
| + p[8] = (u8)v;
|
| + v >>= 8;
|
| + for(i=7; i>=0; i--){
|
| + p[i] = (u8)((v & 0x7f) | 0x80);
|
| + v >>= 7;
|
| + }
|
| + return 9;
|
| + }
|
| + n = 0;
|
| + do{
|
| + buf[n++] = (u8)((v & 0x7f) | 0x80);
|
| + v >>= 7;
|
| + }while( v!=0 );
|
| + buf[0] &= 0x7f;
|
| + assert( n<=9 );
|
| + for(i=0, j=n-1; j>=0; j--, i++){
|
| + p[i] = buf[j];
|
| + }
|
| + return n;
|
| +}
|
| +SQLITE_PRIVATE int sqlite3PutVarint(unsigned char *p, u64 v){
|
| + if( v<=0x7f ){
|
| + p[0] = v&0x7f;
|
| + return 1;
|
| + }
|
| + if( v<=0x3fff ){
|
| + p[0] = ((v>>7)&0x7f)|0x80;
|
| + p[1] = v&0x7f;
|
| + return 2;
|
| + }
|
| + return putVarint64(p,v);
|
| +}
|
| +
|
| +/*
|
| +** Bitmasks used by sqlite3GetVarint(). These precomputed constants
|
| +** are defined here rather than simply putting the constant expressions
|
| +** inline in order to work around bugs in the RVT compiler.
|
| +**
|
| +** SLOT_2_0 A mask for (0x7f<<14) | 0x7f
|
| +**
|
| +** SLOT_4_2_0 A mask for (0x7f<<28) | SLOT_2_0
|
| +*/
|
| +#define SLOT_2_0 0x001fc07f
|
| +#define SLOT_4_2_0 0xf01fc07f
|
| +
|
| +
|
| +/*
|
| +** Read a 64-bit variable-length integer from memory starting at p[0].
|
| +** Return the number of bytes read. The value is stored in *v.
|
| +*/
|
| +SQLITE_PRIVATE u8 sqlite3GetVarint(const unsigned char *p, u64 *v){
|
| + u32 a,b,s;
|
| +
|
| + a = *p;
|
| + /* a: p0 (unmasked) */
|
| + if (!(a&0x80))
|
| + {
|
| + *v = a;
|
| + return 1;
|
| + }
|
| +
|
| + p++;
|
| + b = *p;
|
| + /* b: p1 (unmasked) */
|
| + if (!(b&0x80))
|
| + {
|
| + a &= 0x7f;
|
| + a = a<<7;
|
| + a |= b;
|
| + *v = a;
|
| + return 2;
|
| + }
|
| +
|
| + /* Verify that constants are precomputed correctly */
|
| + assert( SLOT_2_0 == ((0x7f<<14) | (0x7f)) );
|
| + assert( SLOT_4_2_0 == ((0xfU<<28) | (0x7f<<14) | (0x7f)) );
|
| +
|
| + p++;
|
| + a = a<<14;
|
| + a |= *p;
|
| + /* a: p0<<14 | p2 (unmasked) */
|
| + if (!(a&0x80))
|
| + {
|
| + a &= SLOT_2_0;
|
| + b &= 0x7f;
|
| + b = b<<7;
|
| + a |= b;
|
| + *v = a;
|
| + return 3;
|
| + }
|
| +
|
| + /* CSE1 from below */
|
| + a &= SLOT_2_0;
|
| + p++;
|
| + b = b<<14;
|
| + b |= *p;
|
| + /* b: p1<<14 | p3 (unmasked) */
|
| + if (!(b&0x80))
|
| + {
|
| + b &= SLOT_2_0;
|
| + /* moved CSE1 up */
|
| + /* a &= (0x7f<<14)|(0x7f); */
|
| + a = a<<7;
|
| + a |= b;
|
| + *v = a;
|
| + return 4;
|
| + }
|
| +
|
| + /* a: p0<<14 | p2 (masked) */
|
| + /* b: p1<<14 | p3 (unmasked) */
|
| + /* 1:save off p0<<21 | p1<<14 | p2<<7 | p3 (masked) */
|
| + /* moved CSE1 up */
|
| + /* a &= (0x7f<<14)|(0x7f); */
|
| + b &= SLOT_2_0;
|
| + s = a;
|
| + /* s: p0<<14 | p2 (masked) */
|
| +
|
| + p++;
|
| + a = a<<14;
|
| + a |= *p;
|
| + /* a: p0<<28 | p2<<14 | p4 (unmasked) */
|
| + if (!(a&0x80))
|
| + {
|
| + /* we can skip these cause they were (effectively) done above
|
| + ** while calculating s */
|
| + /* a &= (0x7f<<28)|(0x7f<<14)|(0x7f); */
|
| + /* b &= (0x7f<<14)|(0x7f); */
|
| + b = b<<7;
|
| + a |= b;
|
| + s = s>>18;
|
| + *v = ((u64)s)<<32 | a;
|
| + return 5;
|
| + }
|
| +
|
| + /* 2:save off p0<<21 | p1<<14 | p2<<7 | p3 (masked) */
|
| + s = s<<7;
|
| + s |= b;
|
| + /* s: p0<<21 | p1<<14 | p2<<7 | p3 (masked) */
|
| +
|
| + p++;
|
| + b = b<<14;
|
| + b |= *p;
|
| + /* b: p1<<28 | p3<<14 | p5 (unmasked) */
|
| + if (!(b&0x80))
|
| + {
|
| + /* we can skip this cause it was (effectively) done above in calc'ing s */
|
| + /* b &= (0x7f<<28)|(0x7f<<14)|(0x7f); */
|
| + a &= SLOT_2_0;
|
| + a = a<<7;
|
| + a |= b;
|
| + s = s>>18;
|
| + *v = ((u64)s)<<32 | a;
|
| + return 6;
|
| + }
|
| +
|
| + p++;
|
| + a = a<<14;
|
| + a |= *p;
|
| + /* a: p2<<28 | p4<<14 | p6 (unmasked) */
|
| + if (!(a&0x80))
|
| + {
|
| + a &= SLOT_4_2_0;
|
| + b &= SLOT_2_0;
|
| + b = b<<7;
|
| + a |= b;
|
| + s = s>>11;
|
| + *v = ((u64)s)<<32 | a;
|
| + return 7;
|
| + }
|
| +
|
| + /* CSE2 from below */
|
| + a &= SLOT_2_0;
|
| + p++;
|
| + b = b<<14;
|
| + b |= *p;
|
| + /* b: p3<<28 | p5<<14 | p7 (unmasked) */
|
| + if (!(b&0x80))
|
| + {
|
| + b &= SLOT_4_2_0;
|
| + /* moved CSE2 up */
|
| + /* a &= (0x7f<<14)|(0x7f); */
|
| + a = a<<7;
|
| + a |= b;
|
| + s = s>>4;
|
| + *v = ((u64)s)<<32 | a;
|
| + return 8;
|
| + }
|
| +
|
| + p++;
|
| + a = a<<15;
|
| + a |= *p;
|
| + /* a: p4<<29 | p6<<15 | p8 (unmasked) */
|
| +
|
| + /* moved CSE2 up */
|
| + /* a &= (0x7f<<29)|(0x7f<<15)|(0xff); */
|
| + b &= SLOT_2_0;
|
| + b = b<<8;
|
| + a |= b;
|
| +
|
| + s = s<<4;
|
| + b = p[-4];
|
| + b &= 0x7f;
|
| + b = b>>3;
|
| + s |= b;
|
| +
|
| + *v = ((u64)s)<<32 | a;
|
| +
|
| + return 9;
|
| +}
|
| +
|
| +/*
|
| +** Read a 32-bit variable-length integer from memory starting at p[0].
|
| +** Return the number of bytes read. The value is stored in *v.
|
| +**
|
| +** If the varint stored in p[0] is larger than can fit in a 32-bit unsigned
|
| +** integer, then set *v to 0xffffffff.
|
| +**
|
| +** A MACRO version, getVarint32, is provided which inlines the
|
| +** single-byte case. All code should use the MACRO version as
|
| +** this function assumes the single-byte case has already been handled.
|
| +*/
|
| +SQLITE_PRIVATE u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){
|
| + u32 a,b;
|
| +
|
| + /* The 1-byte case. Overwhelmingly the most common. Handled inline
|
| + ** by the getVarin32() macro */
|
| + a = *p;
|
| + /* a: p0 (unmasked) */
|
| +#ifndef getVarint32
|
| + if (!(a&0x80))
|
| + {
|
| + /* Values between 0 and 127 */
|
| + *v = a;
|
| + return 1;
|
| + }
|
| +#endif
|
| +
|
| + /* The 2-byte case */
|
| + p++;
|
| + b = *p;
|
| + /* b: p1 (unmasked) */
|
| + if (!(b&0x80))
|
| + {
|
| + /* Values between 128 and 16383 */
|
| + a &= 0x7f;
|
| + a = a<<7;
|
| + *v = a | b;
|
| + return 2;
|
| + }
|
| +
|
| + /* The 3-byte case */
|
| + p++;
|
| + a = a<<14;
|
| + a |= *p;
|
| + /* a: p0<<14 | p2 (unmasked) */
|
| + if (!(a&0x80))
|
| + {
|
| + /* Values between 16384 and 2097151 */
|
| + a &= (0x7f<<14)|(0x7f);
|
| + b &= 0x7f;
|
| + b = b<<7;
|
| + *v = a | b;
|
| + return 3;
|
| + }
|
| +
|
| + /* A 32-bit varint is used to store size information in btrees.
|
| + ** Objects are rarely larger than 2MiB limit of a 3-byte varint.
|
| + ** A 3-byte varint is sufficient, for example, to record the size
|
| + ** of a 1048569-byte BLOB or string.
|
| + **
|
| + ** We only unroll the first 1-, 2-, and 3- byte cases. The very
|
| + ** rare larger cases can be handled by the slower 64-bit varint
|
| + ** routine.
|
| + */
|
| +#if 1
|
| + {
|
| + u64 v64;
|
| + u8 n;
|
| +
|
| + p -= 2;
|
| + n = sqlite3GetVarint(p, &v64);
|
| + assert( n>3 && n<=9 );
|
| + if( (v64 & SQLITE_MAX_U32)!=v64 ){
|
| + *v = 0xffffffff;
|
| + }else{
|
| + *v = (u32)v64;
|
| + }
|
| + return n;
|
| + }
|
| +
|
| +#else
|
| + /* For following code (kept for historical record only) shows an
|
| + ** unrolling for the 3- and 4-byte varint cases. This code is
|
| + ** slightly faster, but it is also larger and much harder to test.
|
| + */
|
| + p++;
|
| + b = b<<14;
|
| + b |= *p;
|
| + /* b: p1<<14 | p3 (unmasked) */
|
| + if (!(b&0x80))
|
| + {
|
| + /* Values between 2097152 and 268435455 */
|
| + b &= (0x7f<<14)|(0x7f);
|
| + a &= (0x7f<<14)|(0x7f);
|
| + a = a<<7;
|
| + *v = a | b;
|
| + return 4;
|
| + }
|
| +
|
| + p++;
|
| + a = a<<14;
|
| + a |= *p;
|
| + /* a: p0<<28 | p2<<14 | p4 (unmasked) */
|
| + if (!(a&0x80))
|
| + {
|
| + /* Values between 268435456 and 34359738367 */
|
| + a &= SLOT_4_2_0;
|
| + b &= SLOT_4_2_0;
|
| + b = b<<7;
|
| + *v = a | b;
|
| + return 5;
|
| + }
|
| +
|
| + /* We can only reach this point when reading a corrupt database
|
| + ** file. In that case we are not in any hurry. Use the (relatively
|
| + ** slow) general-purpose sqlite3GetVarint() routine to extract the
|
| + ** value. */
|
| + {
|
| + u64 v64;
|
| + u8 n;
|
| +
|
| + p -= 4;
|
| + n = sqlite3GetVarint(p, &v64);
|
| + assert( n>5 && n<=9 );
|
| + *v = (u32)v64;
|
| + return n;
|
| + }
|
| +#endif
|
| +}
|
| +
|
| +/*
|
| +** Return the number of bytes that will be needed to store the given
|
| +** 64-bit integer.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3VarintLen(u64 v){
|
| + int i;
|
| + for(i=1; (v >>= 7)!=0; i++){ assert( i<10 ); }
|
| + return i;
|
| +}
|
| +
|
| +
|
| +/*
|
| +** Read or write a four-byte big-endian integer value.
|
| +*/
|
| +SQLITE_PRIVATE u32 sqlite3Get4byte(const u8 *p){
|
| +#if SQLITE_BYTEORDER==4321
|
| + u32 x;
|
| + memcpy(&x,p,4);
|
| + return x;
|
| +#elif SQLITE_BYTEORDER==1234 && (GCC_VERSION>=4003000 || CLANG_VERSION>=3000000)
|
| + u32 x;
|
| + memcpy(&x,p,4);
|
| + return __builtin_bswap32(x);
|
| +#elif SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300
|
| + u32 x;
|
| + memcpy(&x,p,4);
|
| + return _byteswap_ulong(x);
|
| +#else
|
| + testcase( p[0]&0x80 );
|
| + return ((unsigned)p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
|
| +#endif
|
| +}
|
| +SQLITE_PRIVATE void sqlite3Put4byte(unsigned char *p, u32 v){
|
| +#if SQLITE_BYTEORDER==4321
|
| + memcpy(p,&v,4);
|
| +#elif SQLITE_BYTEORDER==1234 && (GCC_VERSION>=4003000 || CLANG_VERSION>=3000000)
|
| + u32 x = __builtin_bswap32(v);
|
| + memcpy(p,&x,4);
|
| +#elif SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300
|
| + u32 x = _byteswap_ulong(v);
|
| + memcpy(p,&x,4);
|
| +#else
|
| + p[0] = (u8)(v>>24);
|
| + p[1] = (u8)(v>>16);
|
| + p[2] = (u8)(v>>8);
|
| + p[3] = (u8)v;
|
| +#endif
|
| +}
|
| +
|
| +
|
| +
|
| +/*
|
| +** Translate a single byte of Hex into an integer.
|
| +** This routine only works if h really is a valid hexadecimal
|
| +** character: 0..9a..fA..F
|
| +*/
|
| +SQLITE_PRIVATE u8 sqlite3HexToInt(int h){
|
| + assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') );
|
| +#ifdef SQLITE_ASCII
|
| + h += 9*(1&(h>>6));
|
| +#endif
|
| +#ifdef SQLITE_EBCDIC
|
| + h += 9*(1&~(h>>4));
|
| +#endif
|
| + return (u8)(h & 0xf);
|
| +}
|
| +
|
| +#if !defined(SQLITE_OMIT_BLOB_LITERAL) || defined(SQLITE_HAS_CODEC)
|
| +/*
|
| +** Convert a BLOB literal of the form "x'hhhhhh'" into its binary
|
| +** value. Return a pointer to its binary value. Space to hold the
|
| +** binary value has been obtained from malloc and must be freed by
|
| +** the calling routine.
|
| +*/
|
| +SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3 *db, const char *z, int n){
|
| + char *zBlob;
|
| + int i;
|
| +
|
| + zBlob = (char *)sqlite3DbMallocRawNN(db, n/2 + 1);
|
| + n--;
|
| + if( zBlob ){
|
| + for(i=0; i<n; i+=2){
|
| + zBlob[i/2] = (sqlite3HexToInt(z[i])<<4) | sqlite3HexToInt(z[i+1]);
|
| + }
|
| + zBlob[i/2] = 0;
|
| + }
|
| + return zBlob;
|
| +}
|
| +#endif /* !SQLITE_OMIT_BLOB_LITERAL || SQLITE_HAS_CODEC */
|
| +
|
| +/*
|
| +** Log an error that is an API call on a connection pointer that should
|
| +** not have been used. The "type" of connection pointer is given as the
|
| +** argument. The zType is a word like "NULL" or "closed" or "invalid".
|
| +*/
|
| +static void logBadConnection(const char *zType){
|
| + sqlite3_log(SQLITE_MISUSE,
|
| + "API call with %s database connection pointer",
|
| + zType
|
| + );
|
| +}
|
| +
|
| +/*
|
| +** Check to make sure we have a valid db pointer. This test is not
|
| +** foolproof but it does provide some measure of protection against
|
| +** misuse of the interface such as passing in db pointers that are
|
| +** NULL or which have been previously closed. If this routine returns
|
| +** 1 it means that the db pointer is valid and 0 if it should not be
|
| +** dereferenced for any reason. The calling function should invoke
|
| +** SQLITE_MISUSE immediately.
|
| +**
|
| +** sqlite3SafetyCheckOk() requires that the db pointer be valid for
|
| +** use. sqlite3SafetyCheckSickOrOk() allows a db pointer that failed to
|
| +** open properly and is not fit for general use but which can be
|
| +** used as an argument to sqlite3_errmsg() or sqlite3_close().
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3 *db){
|
| + u32 magic;
|
| + if( db==0 ){
|
| + logBadConnection("NULL");
|
| + return 0;
|
| + }
|
| + magic = db->magic;
|
| + if( magic!=SQLITE_MAGIC_OPEN ){
|
| + if( sqlite3SafetyCheckSickOrOk(db) ){
|
| + testcase( sqlite3GlobalConfig.xLog!=0 );
|
| + logBadConnection("unopened");
|
| + }
|
| + return 0;
|
| + }else{
|
| + return 1;
|
| + }
|
| +}
|
| +SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3 *db){
|
| + u32 magic;
|
| + magic = db->magic;
|
| + if( magic!=SQLITE_MAGIC_SICK &&
|
| + magic!=SQLITE_MAGIC_OPEN &&
|
| + magic!=SQLITE_MAGIC_BUSY ){
|
| + testcase( sqlite3GlobalConfig.xLog!=0 );
|
| + logBadConnection("invalid");
|
| + return 0;
|
| + }else{
|
| + return 1;
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Attempt to add, substract, or multiply the 64-bit signed value iB against
|
| +** the other 64-bit signed integer at *pA and store the result in *pA.
|
| +** Return 0 on success. Or if the operation would have resulted in an
|
| +** overflow, leave *pA unchanged and return 1.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3AddInt64(i64 *pA, i64 iB){
|
| +#if GCC_VERSION>=5004000 || CLANG_VERSION>=4000000
|
| + return __builtin_add_overflow(*pA, iB, pA);
|
| +#else
|
| + i64 iA = *pA;
|
| + testcase( iA==0 ); testcase( iA==1 );
|
| + testcase( iB==-1 ); testcase( iB==0 );
|
| + if( iB>=0 ){
|
| + testcase( iA>0 && LARGEST_INT64 - iA == iB );
|
| + testcase( iA>0 && LARGEST_INT64 - iA == iB - 1 );
|
| + if( iA>0 && LARGEST_INT64 - iA < iB ) return 1;
|
| + }else{
|
| + testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 1 );
|
| + testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 2 );
|
| + if( iA<0 && -(iA + LARGEST_INT64) > iB + 1 ) return 1;
|
| + }
|
| + *pA += iB;
|
| + return 0;
|
| +#endif
|
| +}
|
| +SQLITE_PRIVATE int sqlite3SubInt64(i64 *pA, i64 iB){
|
| +#if GCC_VERSION>=5004000 || CLANG_VERSION>=4000000
|
| + return __builtin_sub_overflow(*pA, iB, pA);
|
| +#else
|
| + testcase( iB==SMALLEST_INT64+1 );
|
| + if( iB==SMALLEST_INT64 ){
|
| + testcase( (*pA)==(-1) ); testcase( (*pA)==0 );
|
| + if( (*pA)>=0 ) return 1;
|
| + *pA -= iB;
|
| + return 0;
|
| + }else{
|
| + return sqlite3AddInt64(pA, -iB);
|
| + }
|
| +#endif
|
| +}
|
| +SQLITE_PRIVATE int sqlite3MulInt64(i64 *pA, i64 iB){
|
| +/* TODO(shess): Chromium Android clang generates a link error:
|
| +** undefined reference to '__mulodi4'
|
| +** UPDATE(shess): Also, apparently, 32-bit Linux clang.
|
| +*/
|
| +#if GCC_VERSION>=5004000 || \
|
| + (CLANG_VERSION>=4000000 && !defined(__ANDROID__) && \
|
| + (!defined(__linux__) || !defined(__i386__)))
|
| + return __builtin_mul_overflow(*pA, iB, pA);
|
| +#else
|
| + i64 iA = *pA;
|
| + if( iB>0 ){
|
| + if( iA>LARGEST_INT64/iB ) return 1;
|
| + if( iA<SMALLEST_INT64/iB ) return 1;
|
| + }else if( iB<0 ){
|
| + if( iA>0 ){
|
| + if( iB<SMALLEST_INT64/iA ) return 1;
|
| + }else if( iA<0 ){
|
| + if( iB==SMALLEST_INT64 ) return 1;
|
| + if( iA==SMALLEST_INT64 ) return 1;
|
| + if( -iA>LARGEST_INT64/-iB ) return 1;
|
| + }
|
| + }
|
| + *pA = iA*iB;
|
| + return 0;
|
| +#endif
|
| +}
|
| +
|
| +/*
|
| +** Compute the absolute value of a 32-bit signed integer, of possible. Or
|
| +** if the integer has a value of -2147483648, return +2147483647
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3AbsInt32(int x){
|
| + if( x>=0 ) return x;
|
| + if( x==(int)0x80000000 ) return 0x7fffffff;
|
| + return -x;
|
| +}
|
| +
|
| +#ifdef SQLITE_ENABLE_8_3_NAMES
|
| +/*
|
| +** If SQLITE_ENABLE_8_3_NAMES is set at compile-time and if the database
|
| +** filename in zBaseFilename is a URI with the "8_3_names=1" parameter and
|
| +** if filename in z[] has a suffix (a.k.a. "extension") that is longer than
|
| +** three characters, then shorten the suffix on z[] to be the last three
|
| +** characters of the original suffix.
|
| +**
|
| +** If SQLITE_ENABLE_8_3_NAMES is set to 2 at compile-time, then always
|
| +** do the suffix shortening regardless of URI parameter.
|
| +**
|
| +** Examples:
|
| +**
|
| +** test.db-journal => test.nal
|
| +** test.db-wal => test.wal
|
| +** test.db-shm => test.shm
|
| +** test.db-mj7f3319fa => test.9fa
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3FileSuffix3(const char *zBaseFilename, char *z){
|
| +#if SQLITE_ENABLE_8_3_NAMES<2
|
| + if( sqlite3_uri_boolean(zBaseFilename, "8_3_names", 0) )
|
| +#endif
|
| + {
|
| + int i, sz;
|
| + sz = sqlite3Strlen30(z);
|
| + for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){}
|
| + if( z[i]=='.' && ALWAYS(sz>i+4) ) memmove(&z[i+1], &z[sz-3], 4);
|
| + }
|
| +}
|
| +#endif
|
| +
|
| +/*
|
| +** Find (an approximate) sum of two LogEst values. This computation is
|
| +** not a simple "+" operator because LogEst is stored as a logarithmic
|
| +** value.
|
| +**
|
| +*/
|
| +SQLITE_PRIVATE LogEst sqlite3LogEstAdd(LogEst a, LogEst b){
|
| + static const unsigned char x[] = {
|
| + 10, 10, /* 0,1 */
|
| + 9, 9, /* 2,3 */
|
| + 8, 8, /* 4,5 */
|
| + 7, 7, 7, /* 6,7,8 */
|
| + 6, 6, 6, /* 9,10,11 */
|
| + 5, 5, 5, /* 12-14 */
|
| + 4, 4, 4, 4, /* 15-18 */
|
| + 3, 3, 3, 3, 3, 3, /* 19-24 */
|
| + 2, 2, 2, 2, 2, 2, 2, /* 25-31 */
|
| + };
|
| + if( a>=b ){
|
| + if( a>b+49 ) return a;
|
| + if( a>b+31 ) return a+1;
|
| + return a+x[a-b];
|
| + }else{
|
| + if( b>a+49 ) return b;
|
| + if( b>a+31 ) return b+1;
|
| + return b+x[b-a];
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Convert an integer into a LogEst. In other words, compute an
|
| +** approximation for 10*log2(x).
|
| +*/
|
| +SQLITE_PRIVATE LogEst sqlite3LogEst(u64 x){
|
| + static LogEst a[] = { 0, 2, 3, 5, 6, 7, 8, 9 };
|
| + LogEst y = 40;
|
| + if( x<8 ){
|
| + if( x<2 ) return 0;
|
| + while( x<8 ){ y -= 10; x <<= 1; }
|
| + }else{
|
| + while( x>255 ){ y += 40; x >>= 4; } /*OPTIMIZATION-IF-TRUE*/
|
| + while( x>15 ){ y += 10; x >>= 1; }
|
| + }
|
| + return a[x&7] + y - 10;
|
| +}
|
| +
|
| +#ifndef SQLITE_OMIT_VIRTUALTABLE
|
| +/*
|
| +** Convert a double into a LogEst
|
| +** In other words, compute an approximation for 10*log2(x).
|
| +*/
|
| +SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double x){
|
| + u64 a;
|
| + LogEst e;
|
| + assert( sizeof(x)==8 && sizeof(a)==8 );
|
| + if( x<=1 ) return 0;
|
| + if( x<=2000000000 ) return sqlite3LogEst((u64)x);
|
| + memcpy(&a, &x, 8);
|
| + e = (a>>52) - 1022;
|
| + return e*10;
|
| +}
|
| +#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
| +
|
| +#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \
|
| + defined(SQLITE_ENABLE_STAT3_OR_STAT4) || \
|
| + defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
|
| +/*
|
| +** Convert a LogEst into an integer.
|
| +**
|
| +** Note that this routine is only used when one or more of various
|
| +** non-standard compile-time options is enabled.
|
| +*/
|
| +SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst x){
|
| + u64 n;
|
| + n = x%10;
|
| + x /= 10;
|
| + if( n>=5 ) n -= 2;
|
| + else if( n>=1 ) n -= 1;
|
| +#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \
|
| + defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
|
| + if( x>60 ) return (u64)LARGEST_INT64;
|
| +#else
|
| + /* If only SQLITE_ENABLE_STAT3_OR_STAT4 is on, then the largest input
|
| + ** possible to this routine is 310, resulting in a maximum x of 31 */
|
| + assert( x<=60 );
|
| +#endif
|
| + return x>=3 ? (n+8)<<(x-3) : (n+8)>>(3-x);
|
| +}
|
| +#endif /* defined SCANSTAT or STAT4 or ESTIMATED_ROWS */
|
| +
|
| +/*
|
| +** Add a new name/number pair to a VList. This might require that the
|
| +** VList object be reallocated, so return the new VList. If an OOM
|
| +** error occurs, the original VList returned and the
|
| +** db->mallocFailed flag is set.
|
| +**
|
| +** A VList is really just an array of integers. To destroy a VList,
|
| +** simply pass it to sqlite3DbFree().
|
| +**
|
| +** The first integer is the number of integers allocated for the whole
|
| +** VList. The second integer is the number of integers actually used.
|
| +** Each name/number pair is encoded by subsequent groups of 3 or more
|
| +** integers.
|
| +**
|
| +** Each name/number pair starts with two integers which are the numeric
|
| +** value for the pair and the size of the name/number pair, respectively.
|
| +** The text name overlays one or more following integers. The text name
|
| +** is always zero-terminated.
|
| +**
|
| +** Conceptually:
|
| +**
|
| +** struct VList {
|
| +** int nAlloc; // Number of allocated slots
|
| +** int nUsed; // Number of used slots
|
| +** struct VListEntry {
|
| +** int iValue; // Value for this entry
|
| +** int nSlot; // Slots used by this entry
|
| +** // ... variable name goes here
|
| +** } a[0];
|
| +** }
|
| +**
|
| +** During code generation, pointers to the variable names within the
|
| +** VList are taken. When that happens, nAlloc is set to zero as an
|
| +** indication that the VList may never again be enlarged, since the
|
| +** accompanying realloc() would invalidate the pointers.
|
| +*/
|
| +SQLITE_PRIVATE VList *sqlite3VListAdd(
|
| + sqlite3 *db, /* The database connection used for malloc() */
|
| + VList *pIn, /* The input VList. Might be NULL */
|
| + const char *zName, /* Name of symbol to add */
|
| + int nName, /* Bytes of text in zName */
|
| + int iVal /* Value to associate with zName */
|
| +){
|
| + int nInt; /* number of sizeof(int) objects needed for zName */
|
| + char *z; /* Pointer to where zName will be stored */
|
| + int i; /* Index in pIn[] where zName is stored */
|
| +
|
| + nInt = nName/4 + 3;
|
| + assert( pIn==0 || pIn[0]>=3 ); /* Verify ok to add new elements */
|
| + if( pIn==0 || pIn[1]+nInt > pIn[0] ){
|
| + /* Enlarge the allocation */
|
| + int nAlloc = (pIn ? pIn[0]*2 : 10) + nInt;
|
| + VList *pOut = sqlite3DbRealloc(db, pIn, nAlloc*sizeof(int));
|
| + if( pOut==0 ) return pIn;
|
| + if( pIn==0 ) pOut[1] = 2;
|
| + pIn = pOut;
|
| + pIn[0] = nAlloc;
|
| + }
|
| + i = pIn[1];
|
| + pIn[i] = iVal;
|
| + pIn[i+1] = nInt;
|
| + z = (char*)&pIn[i+2];
|
| + pIn[1] = i+nInt;
|
| + assert( pIn[1]<=pIn[0] );
|
| + memcpy(z, zName, nName);
|
| + z[nName] = 0;
|
| + return pIn;
|
| +}
|
| +
|
| +/*
|
| +** Return a pointer to the name of a variable in the given VList that
|
| +** has the value iVal. Or return a NULL if there is no such variable in
|
| +** the list
|
| +*/
|
| +SQLITE_PRIVATE const char *sqlite3VListNumToName(VList *pIn, int iVal){
|
| + int i, mx;
|
| + if( pIn==0 ) return 0;
|
| + mx = pIn[1];
|
| + i = 2;
|
| + do{
|
| + if( pIn[i]==iVal ) return (char*)&pIn[i+2];
|
| + i += pIn[i+1];
|
| + }while( i<mx );
|
| + return 0;
|
| +}
|
| +
|
| +/*
|
| +** Return the number of the variable named zName, if it is in VList.
|
| +** or return 0 if there is no such variable.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3VListNameToNum(VList *pIn, const char *zName, int nName){
|
| + int i, mx;
|
| + if( pIn==0 ) return 0;
|
| + mx = pIn[1];
|
| + i = 2;
|
| + do{
|
| + const char *z = (const char*)&pIn[i+2];
|
| + if( strncmp(z,zName,nName)==0 && z[nName]==0 ) return pIn[i];
|
| + i += pIn[i+1];
|
| + }while( i<mx );
|
| + return 0;
|
| +}
|
| +
|
| +/************** End of util.c ************************************************/
|
| +/************** Begin file hash.c ********************************************/
|
| +/*
|
| +** 2001 September 22
|
| +**
|
| +** 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 is the implementation of generic hash-tables
|
| +** used in SQLite.
|
| +*/
|
| +/* #include "sqliteInt.h" */
|
| +/* #include <assert.h> */
|
| +
|
| +/* Turn bulk memory into a hash table object by initializing the
|
| +** fields of the Hash structure.
|
| +**
|
| +** "pNew" is a pointer to the hash table that is to be initialized.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3HashInit(Hash *pNew){
|
| + assert( pNew!=0 );
|
| + pNew->first = 0;
|
| + pNew->count = 0;
|
| + pNew->htsize = 0;
|
| + pNew->ht = 0;
|
| +}
|
| +
|
| +/* Remove all entries from a hash table. Reclaim all memory.
|
| +** Call this routine to delete a hash table or to reset a hash table
|
| +** to the empty state.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3HashClear(Hash *pH){
|
| + HashElem *elem; /* For looping over all elements of the table */
|
| +
|
| + assert( pH!=0 );
|
| + elem = pH->first;
|
| + pH->first = 0;
|
| + sqlite3_free(pH->ht);
|
| + pH->ht = 0;
|
| + pH->htsize = 0;
|
| + while( elem ){
|
| + HashElem *next_elem = elem->next;
|
| + sqlite3_free(elem);
|
| + elem = next_elem;
|
| + }
|
| + pH->count = 0;
|
| +}
|
| +
|
| +/*
|
| +** The hashing function.
|
| +*/
|
| +static unsigned int strHash(const char *z){
|
| + unsigned int h = 0;
|
| + unsigned char c;
|
| + while( (c = (unsigned char)*z++)!=0 ){ /*OPTIMIZATION-IF-TRUE*/
|
| + /* Knuth multiplicative hashing. (Sorting & Searching, p. 510).
|
| + ** 0x9e3779b1 is 2654435761 which is the closest prime number to
|
| + ** (2**32)*golden_ratio, where golden_ratio = (sqrt(5) - 1)/2. */
|
| + h += sqlite3UpperToLower[c];
|
| + h *= 0x9e3779b1;
|
| + }
|
| + return h;
|
| +}
|
| +
|
| +
|
| +/* Link pNew element into the hash table pH. If pEntry!=0 then also
|
| +** insert pNew into the pEntry hash bucket.
|
| +*/
|
| +static void insertElement(
|
| + Hash *pH, /* The complete hash table */
|
| + struct _ht *pEntry, /* The entry into which pNew is inserted */
|
| + HashElem *pNew /* The element to be inserted */
|
| +){
|
| + HashElem *pHead; /* First element already in pEntry */
|
| + if( pEntry ){
|
| + pHead = pEntry->count ? pEntry->chain : 0;
|
| + pEntry->count++;
|
| + pEntry->chain = pNew;
|
| + }else{
|
| + pHead = 0;
|
| + }
|
| + if( pHead ){
|
| + pNew->next = pHead;
|
| + pNew->prev = pHead->prev;
|
| + if( pHead->prev ){ pHead->prev->next = pNew; }
|
| + else { pH->first = pNew; }
|
| + pHead->prev = pNew;
|
| + }else{
|
| + pNew->next = pH->first;
|
| + if( pH->first ){ pH->first->prev = pNew; }
|
| + pNew->prev = 0;
|
| + pH->first = pNew;
|
| + }
|
| +}
|
| +
|
| +
|
| +/* Resize the hash table so that it cantains "new_size" buckets.
|
| +**
|
| +** The hash table might fail to resize if sqlite3_malloc() fails or
|
| +** if the new size is the same as the prior size.
|
| +** Return TRUE if the resize occurs and false if not.
|
| +*/
|
| +static int rehash(Hash *pH, unsigned int new_size){
|
| + struct _ht *new_ht; /* The new hash table */
|
| + HashElem *elem, *next_elem; /* For looping over existing elements */
|
| +
|
| +#if SQLITE_MALLOC_SOFT_LIMIT>0
|
| + if( new_size*sizeof(struct _ht)>SQLITE_MALLOC_SOFT_LIMIT ){
|
| + new_size = SQLITE_MALLOC_SOFT_LIMIT/sizeof(struct _ht);
|
| + }
|
| + if( new_size==pH->htsize ) return 0;
|
| +#endif
|
| +
|
| + /* The inability to allocates space for a larger hash table is
|
| + ** a performance hit but it is not a fatal error. So mark the
|
| + ** allocation as a benign. Use sqlite3Malloc()/memset(0) instead of
|
| + ** sqlite3MallocZero() to make the allocation, as sqlite3MallocZero()
|
| + ** only zeroes the requested number of bytes whereas this module will
|
| + ** use the actual amount of space allocated for the hash table (which
|
| + ** may be larger than the requested amount).
|
| + */
|
| + sqlite3BeginBenignMalloc();
|
| + new_ht = (struct _ht *)sqlite3Malloc( new_size*sizeof(struct _ht) );
|
| + sqlite3EndBenignMalloc();
|
| +
|
| + if( new_ht==0 ) return 0;
|
| + sqlite3_free(pH->ht);
|
| + pH->ht = new_ht;
|
| + pH->htsize = new_size = sqlite3MallocSize(new_ht)/sizeof(struct _ht);
|
| + memset(new_ht, 0, new_size*sizeof(struct _ht));
|
| + for(elem=pH->first, pH->first=0; elem; elem = next_elem){
|
| + unsigned int h = strHash(elem->pKey) % new_size;
|
| + next_elem = elem->next;
|
| + insertElement(pH, &new_ht[h], elem);
|
| + }
|
| + return 1;
|
| +}
|
| +
|
| +/* This function (for internal use only) locates an element in an
|
| +** hash table that matches the given key. The hash for this key is
|
| +** also computed and returned in the *pH parameter.
|
| +*/
|
| +static HashElem *findElementWithHash(
|
| + const Hash *pH, /* The pH to be searched */
|
| + const char *pKey, /* The key we are searching for */
|
| + unsigned int *pHash /* Write the hash value here */
|
| +){
|
| + HashElem *elem; /* Used to loop thru the element list */
|
| + int count; /* Number of elements left to test */
|
| + unsigned int h; /* The computed hash */
|
| +
|
| + if( pH->ht ){ /*OPTIMIZATION-IF-TRUE*/
|
| + struct _ht *pEntry;
|
| + h = strHash(pKey) % pH->htsize;
|
| + pEntry = &pH->ht[h];
|
| + elem = pEntry->chain;
|
| + count = pEntry->count;
|
| + }else{
|
| + h = 0;
|
| + elem = pH->first;
|
| + count = pH->count;
|
| + }
|
| + *pHash = h;
|
| + while( count-- ){
|
| + assert( elem!=0 );
|
| + if( sqlite3StrICmp(elem->pKey,pKey)==0 ){
|
| + return elem;
|
| + }
|
| + elem = elem->next;
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| +/* Remove a single entry from the hash table given a pointer to that
|
| +** element and a hash on the element's key.
|
| +*/
|
| +static void removeElementGivenHash(
|
| + Hash *pH, /* The pH containing "elem" */
|
| + HashElem* elem, /* The element to be removed from the pH */
|
| + unsigned int h /* Hash value for the element */
|
| +){
|
| + struct _ht *pEntry;
|
| + if( elem->prev ){
|
| + elem->prev->next = elem->next;
|
| + }else{
|
| + pH->first = elem->next;
|
| + }
|
| + if( elem->next ){
|
| + elem->next->prev = elem->prev;
|
| + }
|
| + if( pH->ht ){
|
| + pEntry = &pH->ht[h];
|
| + if( pEntry->chain==elem ){
|
| + pEntry->chain = elem->next;
|
| + }
|
| + pEntry->count--;
|
| + assert( pEntry->count>=0 );
|
| + }
|
| + sqlite3_free( elem );
|
| + pH->count--;
|
| + if( pH->count==0 ){
|
| + assert( pH->first==0 );
|
| + assert( pH->count==0 );
|
| + sqlite3HashClear(pH);
|
| + }
|
| +}
|
| +
|
| +/* Attempt to locate an element of the hash table pH with a key
|
| +** that matches pKey. Return the data for this element if it is
|
| +** found, or NULL if there is no match.
|
| +*/
|
| +SQLITE_PRIVATE void *sqlite3HashFind(const Hash *pH, const char *pKey){
|
| + HashElem *elem; /* The element that matches key */
|
| + unsigned int h; /* A hash on key */
|
| +
|
| + assert( pH!=0 );
|
| + assert( pKey!=0 );
|
| + elem = findElementWithHash(pH, pKey, &h);
|
| + return elem ? elem->data : 0;
|
| +}
|
| +
|
| +/* Insert an element into the hash table pH. The key is pKey
|
| +** and the data is "data".
|
| +**
|
| +** If no element exists with a matching key, then a new
|
| +** element is created and NULL is returned.
|
| +**
|
| +** If another element already exists with the same key, then the
|
| +** new data replaces the old data and the old data is returned.
|
| +** The key is not copied in this instance. If a malloc fails, then
|
| +** the new data is returned and the hash table is unchanged.
|
| +**
|
| +** If the "data" parameter to this function is NULL, then the
|
| +** element corresponding to "key" is removed from the hash table.
|
| +*/
|
| +SQLITE_PRIVATE void *sqlite3HashInsert(Hash *pH, const char *pKey, void *data){
|
| + unsigned int h; /* the hash of the key modulo hash table size */
|
| + HashElem *elem; /* Used to loop thru the element list */
|
| + HashElem *new_elem; /* New element added to the pH */
|
| +
|
| + assert( pH!=0 );
|
| + assert( pKey!=0 );
|
| + elem = findElementWithHash(pH,pKey,&h);
|
| + if( elem ){
|
| + void *old_data = elem->data;
|
| + if( data==0 ){
|
| + removeElementGivenHash(pH,elem,h);
|
| + }else{
|
| + elem->data = data;
|
| + elem->pKey = pKey;
|
| + }
|
| + return old_data;
|
| + }
|
| + if( data==0 ) return 0;
|
| + new_elem = (HashElem*)sqlite3Malloc( sizeof(HashElem) );
|
| + if( new_elem==0 ) return data;
|
| + new_elem->pKey = pKey;
|
| + new_elem->data = data;
|
| + pH->count++;
|
| + if( pH->count>=10 && pH->count > 2*pH->htsize ){
|
| + if( rehash(pH, pH->count*2) ){
|
| + assert( pH->htsize>0 );
|
| + h = strHash(pKey) % pH->htsize;
|
| + }
|
| + }
|
| + insertElement(pH, pH->ht ? &pH->ht[h] : 0, new_elem);
|
| + return 0;
|
| +}
|
| +
|
| +/************** End of hash.c ************************************************/
|
| +/************** Begin file opcodes.c *****************************************/
|
| +/* Automatically generated. Do not edit */
|
| +/* See the tool/mkopcodec.tcl script for details. */
|
| +#if !defined(SQLITE_OMIT_EXPLAIN) \
|
| + || defined(VDBE_PROFILE) \
|
| + || defined(SQLITE_DEBUG)
|
| +#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) || defined(SQLITE_DEBUG)
|
| +# define OpHelp(X) "\0" X
|
| +#else
|
| +# define OpHelp(X)
|
| +#endif
|
| +SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
|
| + static const char *const azName[] = {
|
| + /* 0 */ "Savepoint" OpHelp(""),
|
| + /* 1 */ "AutoCommit" OpHelp(""),
|
| + /* 2 */ "Transaction" OpHelp(""),
|
| + /* 3 */ "SorterNext" OpHelp(""),
|
| + /* 4 */ "PrevIfOpen" OpHelp(""),
|
| + /* 5 */ "NextIfOpen" OpHelp(""),
|
| + /* 6 */ "Prev" OpHelp(""),
|
| + /* 7 */ "Next" OpHelp(""),
|
| + /* 8 */ "Checkpoint" OpHelp(""),
|
| + /* 9 */ "JournalMode" OpHelp(""),
|
| + /* 10 */ "Vacuum" OpHelp(""),
|
| + /* 11 */ "VFilter" OpHelp("iplan=r[P3] zplan='P4'"),
|
| + /* 12 */ "VUpdate" OpHelp("data=r[P3@P2]"),
|
| + /* 13 */ "Goto" OpHelp(""),
|
| + /* 14 */ "Gosub" OpHelp(""),
|
| + /* 15 */ "InitCoroutine" OpHelp(""),
|
| + /* 16 */ "Yield" OpHelp(""),
|
| + /* 17 */ "MustBeInt" OpHelp(""),
|
| + /* 18 */ "Jump" OpHelp(""),
|
| + /* 19 */ "Not" OpHelp("r[P2]= !r[P1]"),
|
| + /* 20 */ "Once" OpHelp(""),
|
| + /* 21 */ "If" OpHelp(""),
|
| + /* 22 */ "IfNot" OpHelp(""),
|
| + /* 23 */ "SeekLT" OpHelp("key=r[P3@P4]"),
|
| + /* 24 */ "SeekLE" OpHelp("key=r[P3@P4]"),
|
| + /* 25 */ "SeekGE" OpHelp("key=r[P3@P4]"),
|
| + /* 26 */ "SeekGT" OpHelp("key=r[P3@P4]"),
|
| + /* 27 */ "Or" OpHelp("r[P3]=(r[P1] || r[P2])"),
|
| + /* 28 */ "And" OpHelp("r[P3]=(r[P1] && r[P2])"),
|
| + /* 29 */ "NoConflict" OpHelp("key=r[P3@P4]"),
|
| + /* 30 */ "NotFound" OpHelp("key=r[P3@P4]"),
|
| + /* 31 */ "Found" OpHelp("key=r[P3@P4]"),
|
| + /* 32 */ "SeekRowid" OpHelp("intkey=r[P3]"),
|
| + /* 33 */ "NotExists" OpHelp("intkey=r[P3]"),
|
| + /* 34 */ "IsNull" OpHelp("if r[P1]==NULL goto P2"),
|
| + /* 35 */ "NotNull" OpHelp("if r[P1]!=NULL goto P2"),
|
| + /* 36 */ "Ne" OpHelp("IF r[P3]!=r[P1]"),
|
| + /* 37 */ "Eq" OpHelp("IF r[P3]==r[P1]"),
|
| + /* 38 */ "Gt" OpHelp("IF r[P3]>r[P1]"),
|
| + /* 39 */ "Le" OpHelp("IF r[P3]<=r[P1]"),
|
| + /* 40 */ "Lt" OpHelp("IF r[P3]<r[P1]"),
|
| + /* 41 */ "Ge" OpHelp("IF r[P3]>=r[P1]"),
|
| + /* 42 */ "ElseNotEq" OpHelp(""),
|
| + /* 43 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"),
|
| + /* 44 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"),
|
| + /* 45 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<<r[P1]"),
|
| + /* 46 */ "ShiftRight" OpHelp("r[P3]=r[P2]>>r[P1]"),
|
| + /* 47 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"),
|
| + /* 48 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"),
|
| + /* 49 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"),
|
| + /* 50 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"),
|
| + /* 51 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"),
|
| + /* 52 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"),
|
| + /* 53 */ "Last" OpHelp(""),
|
| + /* 54 */ "BitNot" OpHelp("r[P1]= ~r[P1]"),
|
| + /* 55 */ "SorterSort" OpHelp(""),
|
| + /* 56 */ "Sort" OpHelp(""),
|
| + /* 57 */ "Rewind" OpHelp(""),
|
| + /* 58 */ "IdxLE" OpHelp("key=r[P3@P4]"),
|
| + /* 59 */ "IdxGT" OpHelp("key=r[P3@P4]"),
|
| + /* 60 */ "IdxLT" OpHelp("key=r[P3@P4]"),
|
| + /* 61 */ "IdxGE" OpHelp("key=r[P3@P4]"),
|
| + /* 62 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"),
|
| + /* 63 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"),
|
| + /* 64 */ "Program" OpHelp(""),
|
| + /* 65 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"),
|
| + /* 66 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"),
|
| + /* 67 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]--, goto P2"),
|
| + /* 68 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"),
|
| + /* 69 */ "IncrVacuum" OpHelp(""),
|
| + /* 70 */ "VNext" OpHelp(""),
|
| + /* 71 */ "Init" OpHelp("Start at P2"),
|
| + /* 72 */ "Return" OpHelp(""),
|
| + /* 73 */ "EndCoroutine" OpHelp(""),
|
| + /* 74 */ "HaltIfNull" OpHelp("if r[P3]=null halt"),
|
| + /* 75 */ "Halt" OpHelp(""),
|
| + /* 76 */ "Integer" OpHelp("r[P2]=P1"),
|
| + /* 77 */ "Int64" OpHelp("r[P2]=P4"),
|
| + /* 78 */ "String" OpHelp("r[P2]='P4' (len=P1)"),
|
| + /* 79 */ "Null" OpHelp("r[P2..P3]=NULL"),
|
| + /* 80 */ "SoftNull" OpHelp("r[P1]=NULL"),
|
| + /* 81 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"),
|
| + /* 82 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"),
|
| + /* 83 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"),
|
| + /* 84 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"),
|
| + /* 85 */ "SCopy" OpHelp("r[P2]=r[P1]"),
|
| + /* 86 */ "IntCopy" OpHelp("r[P2]=r[P1]"),
|
| + /* 87 */ "ResultRow" OpHelp("output=r[P1@P2]"),
|
| + /* 88 */ "CollSeq" OpHelp(""),
|
| + /* 89 */ "Function0" OpHelp("r[P3]=func(r[P2@P5])"),
|
| + /* 90 */ "Function" OpHelp("r[P3]=func(r[P2@P5])"),
|
| + /* 91 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"),
|
| + /* 92 */ "RealAffinity" OpHelp(""),
|
| + /* 93 */ "Cast" OpHelp("affinity(r[P1])"),
|
| + /* 94 */ "Permutation" OpHelp(""),
|
| + /* 95 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"),
|
| + /* 96 */ "Column" OpHelp("r[P3]=PX"),
|
| + /* 97 */ "String8" OpHelp("r[P2]='P4'"),
|
| + /* 98 */ "Affinity" OpHelp("affinity(r[P1@P2])"),
|
| + /* 99 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"),
|
| + /* 100 */ "Count" OpHelp("r[P2]=count()"),
|
| + /* 101 */ "ReadCookie" OpHelp(""),
|
| + /* 102 */ "SetCookie" OpHelp(""),
|
| + /* 103 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"),
|
| + /* 104 */ "OpenRead" OpHelp("root=P2 iDb=P3"),
|
| + /* 105 */ "OpenWrite" OpHelp("root=P2 iDb=P3"),
|
| + /* 106 */ "OpenAutoindex" OpHelp("nColumn=P2"),
|
| + /* 107 */ "OpenEphemeral" OpHelp("nColumn=P2"),
|
| + /* 108 */ "SorterOpen" OpHelp(""),
|
| + /* 109 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"),
|
| + /* 110 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"),
|
| + /* 111 */ "Close" OpHelp(""),
|
| + /* 112 */ "ColumnsUsed" OpHelp(""),
|
| + /* 113 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"),
|
| + /* 114 */ "NewRowid" OpHelp("r[P2]=rowid"),
|
| + /* 115 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"),
|
| + /* 116 */ "InsertInt" OpHelp("intkey=P3 data=r[P2]"),
|
| + /* 117 */ "Delete" OpHelp(""),
|
| + /* 118 */ "ResetCount" OpHelp(""),
|
| + /* 119 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"),
|
| + /* 120 */ "SorterData" OpHelp("r[P2]=data"),
|
| + /* 121 */ "RowData" OpHelp("r[P2]=data"),
|
| + /* 122 */ "Rowid" OpHelp("r[P2]=rowid"),
|
| + /* 123 */ "NullRow" OpHelp(""),
|
| + /* 124 */ "SorterInsert" OpHelp("key=r[P2]"),
|
| + /* 125 */ "IdxInsert" OpHelp("key=r[P2]"),
|
| + /* 126 */ "IdxDelete" OpHelp("key=r[P2@P3]"),
|
| + /* 127 */ "Seek" OpHelp("Move P3 to P1.rowid"),
|
| + /* 128 */ "IdxRowid" OpHelp("r[P2]=rowid"),
|
| + /* 129 */ "Destroy" OpHelp(""),
|
| + /* 130 */ "Clear" OpHelp(""),
|
| + /* 131 */ "ResetSorter" OpHelp(""),
|
| + /* 132 */ "Real" OpHelp("r[P2]=P4"),
|
| + /* 133 */ "CreateIndex" OpHelp("r[P2]=root iDb=P1"),
|
| + /* 134 */ "CreateTable" OpHelp("r[P2]=root iDb=P1"),
|
| + /* 135 */ "ParseSchema" OpHelp(""),
|
| + /* 136 */ "LoadAnalysis" OpHelp(""),
|
| + /* 137 */ "DropTable" OpHelp(""),
|
| + /* 138 */ "DropIndex" OpHelp(""),
|
| + /* 139 */ "DropTrigger" OpHelp(""),
|
| + /* 140 */ "IntegrityCk" OpHelp(""),
|
| + /* 141 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"),
|
| + /* 142 */ "Param" OpHelp(""),
|
| + /* 143 */ "FkCounter" OpHelp("fkctr[P1]+=P2"),
|
| + /* 144 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"),
|
| + /* 145 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"),
|
| + /* 146 */ "AggStep0" OpHelp("accum=r[P3] step(r[P2@P5])"),
|
| + /* 147 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"),
|
| + /* 148 */ "AggFinal" OpHelp("accum=r[P1] N=P2"),
|
| + /* 149 */ "Expire" OpHelp(""),
|
| + /* 150 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"),
|
| + /* 151 */ "VBegin" OpHelp(""),
|
| + /* 152 */ "VCreate" OpHelp(""),
|
| + /* 153 */ "VDestroy" OpHelp(""),
|
| + /* 154 */ "VOpen" OpHelp(""),
|
| + /* 155 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"),
|
| + /* 156 */ "VRename" OpHelp(""),
|
| + /* 157 */ "Pagecount" OpHelp(""),
|
| + /* 158 */ "MaxPgcnt" OpHelp(""),
|
| + /* 159 */ "CursorHint" OpHelp(""),
|
| + /* 160 */ "Noop" OpHelp(""),
|
| + /* 161 */ "Explain" OpHelp(""),
|
| + };
|
| + return azName[i];
|
| +}
|
| +#endif
|
| +
|
| +/************** End of opcodes.c *********************************************/
|
| +/************** Begin file os_unix.c *****************************************/
|
| +/*
|
| +** 2004 May 22
|
| +**
|
| +** 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 VFS implementation for unix-like operating systems
|
| +** include Linux, MacOSX, *BSD, QNX, VxWorks, AIX, HPUX, and others.
|
| +**
|
| +** There are actually several different VFS implementations in this file.
|
| +** The differences are in the way that file locking is done. The default
|
| +** implementation uses Posix Advisory Locks. Alternative implementations
|
| +** use flock(), dot-files, various proprietary locking schemas, or simply
|
| +** skip locking all together.
|
| +**
|
| +** This source file is organized into divisions where the logic for various
|
| +** subfunctions is contained within the appropriate division. PLEASE
|
| +** KEEP THE STRUCTURE OF THIS FILE INTACT. New code should be placed
|
| +** in the correct division and should be clearly labeled.
|
| +**
|
| +** The layout of divisions is as follows:
|
| +**
|
| +** * General-purpose declarations and utility functions.
|
| +** * Unique file ID logic used by VxWorks.
|
| +** * Various locking primitive implementations (all except proxy locking):
|
| +** + for Posix Advisory Locks
|
| +** + for no-op locks
|
| +** + for dot-file locks
|
| +** + for flock() locking
|
| +** + for named semaphore locks (VxWorks only)
|
| +** + for AFP filesystem locks (MacOSX only)
|
| +** * sqlite3_file methods not associated with locking.
|
| +** * Definitions of sqlite3_io_methods objects for all locking
|
| +** methods plus "finder" functions for each locking method.
|
| +** * sqlite3_vfs method implementations.
|
| +** * Locking primitives for the proxy uber-locking-method. (MacOSX only)
|
| +** * Definitions of sqlite3_vfs objects for all locking methods
|
| +** plus implementations of sqlite3_os_init() and sqlite3_os_end().
|
| +*/
|
| +/* #include "sqliteInt.h" */
|
| +#if SQLITE_OS_UNIX /* This file is used on unix only */
|
| +
|
| +/*
|
| +** There are various methods for file locking used for concurrency
|
| +** control:
|
| +**
|
| +** 1. POSIX locking (the default),
|
| +** 2. No locking,
|
| +** 3. Dot-file locking,
|
| +** 4. flock() locking,
|
| +** 5. AFP locking (OSX only),
|
| +** 6. Named POSIX semaphores (VXWorks only),
|
| +** 7. proxy locking. (OSX only)
|
| +**
|
| +** Styles 4, 5, and 7 are only available of SQLITE_ENABLE_LOCKING_STYLE
|
| +** is defined to 1. The SQLITE_ENABLE_LOCKING_STYLE also enables automatic
|
| +** selection of the appropriate locking style based on the filesystem
|
| +** where the database is located.
|
| +*/
|
| +#if !defined(SQLITE_ENABLE_LOCKING_STYLE)
|
| +# if defined(__APPLE__)
|
| +# define SQLITE_ENABLE_LOCKING_STYLE 1
|
| +# else
|
| +# define SQLITE_ENABLE_LOCKING_STYLE 0
|
| +# endif
|
| +#endif
|
| +
|
| +/* Use pread() and pwrite() if they are available */
|
| +#if defined(__APPLE__)
|
| +# define HAVE_PREAD 1
|
| +# define HAVE_PWRITE 1
|
| +#endif
|
| +#if defined(HAVE_PREAD64) && defined(HAVE_PWRITE64)
|
| +# undef USE_PREAD
|
| +# define USE_PREAD64 1
|
| +#elif defined(HAVE_PREAD) && defined(HAVE_PWRITE)
|
| +# undef USE_PREAD64
|
| +# define USE_PREAD 1
|
| +#endif
|
| +
|
| +/*
|
| +** standard include files.
|
| +*/
|
| +#include <sys/types.h>
|
| +#include <sys/stat.h>
|
| +#include <fcntl.h>
|
| +#include <unistd.h>
|
| +/* #include <time.h> */
|
| +#include <sys/time.h>
|
| +#include <errno.h>
|
| +#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
|
| +# include <sys/mman.h>
|
| +#endif
|
| +
|
| +#if SQLITE_ENABLE_LOCKING_STYLE
|
| +# include <sys/ioctl.h>
|
| +# include <sys/file.h>
|
| +# include <sys/param.h>
|
| +#endif /* SQLITE_ENABLE_LOCKING_STYLE */
|
| +
|
| +#if defined(__APPLE__) && ((__MAC_OS_X_VERSION_MIN_REQUIRED > 1050) || \
|
| + (__IPHONE_OS_VERSION_MIN_REQUIRED > 2000))
|
| +# if (!defined(TARGET_OS_EMBEDDED) || (TARGET_OS_EMBEDDED==0)) \
|
| + && (!defined(TARGET_IPHONE_SIMULATOR) || (TARGET_IPHONE_SIMULATOR==0))
|
| +# define HAVE_GETHOSTUUID 1
|
| +# else
|
| +# warning "gethostuuid() is disabled."
|
| +# endif
|
| +#endif
|
| +
|
| +
|
| +#if OS_VXWORKS
|
| +/* # include <sys/ioctl.h> */
|
| +# include <semaphore.h>
|
| +# include <limits.h>
|
| +#endif /* OS_VXWORKS */
|
| +
|
| +#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE
|
| +# include <sys/mount.h>
|
| +#endif
|
| +
|
| +#ifdef HAVE_UTIME
|
| +# include <utime.h>
|
| +#endif
|
| +
|
| +/*
|
| +** Allowed values of unixFile.fsFlags
|
| +*/
|
| +#define SQLITE_FSFLAGS_IS_MSDOS 0x1
|
| +
|
| +/*
|
| +** If we are to be thread-safe, include the pthreads header and define
|
| +** the SQLITE_UNIX_THREADS macro.
|
| +*/
|
| +#if SQLITE_THREADSAFE
|
| +/* # include <pthread.h> */
|
| +# define SQLITE_UNIX_THREADS 1
|
| +#endif
|
| +
|
| +/*
|
| +** Default permissions when creating a new file
|
| +*/
|
| +#ifndef SQLITE_DEFAULT_FILE_PERMISSIONS
|
| +# define SQLITE_DEFAULT_FILE_PERMISSIONS 0644
|
| +#endif
|
| +
|
| +/*
|
| +** Default permissions when creating auto proxy dir
|
| +*/
|
| +#ifndef SQLITE_DEFAULT_PROXYDIR_PERMISSIONS
|
| +# define SQLITE_DEFAULT_PROXYDIR_PERMISSIONS 0755
|
| +#endif
|
| +
|
| +/*
|
| +** Maximum supported path-length.
|
| +*/
|
| +#define MAX_PATHNAME 512
|
| +
|
| +/*
|
| +** Maximum supported symbolic links
|
| +*/
|
| +#define SQLITE_MAX_SYMLINKS 100
|
| +
|
| +/* Always cast the getpid() return type for compatibility with
|
| +** kernel modules in VxWorks. */
|
| +#define osGetpid(X) (pid_t)getpid()
|
| +
|
| +/*
|
| +** Only set the lastErrno if the error code is a real error and not
|
| +** a normal expected return code of SQLITE_BUSY or SQLITE_OK
|
| +*/
|
| +#define IS_LOCK_ERROR(x) ((x != SQLITE_OK) && (x != SQLITE_BUSY))
|
| +
|
| +/* Forward references */
|
| +typedef struct unixShm unixShm; /* Connection shared memory */
|
| +typedef struct unixShmNode unixShmNode; /* Shared memory instance */
|
| +typedef struct unixInodeInfo unixInodeInfo; /* An i-node */
|
| +typedef struct UnixUnusedFd UnixUnusedFd; /* An unused file descriptor */
|
| +
|
| +/*
|
| +** Sometimes, after a file handle is closed by SQLite, the file descriptor
|
| +** cannot be closed immediately. In these cases, instances of the following
|
| +** structure are used to store the file descriptor while waiting for an
|
| +** opportunity to either close or reuse it.
|
| +*/
|
| +struct UnixUnusedFd {
|
| + int fd; /* File descriptor to close */
|
| + int flags; /* Flags this file descriptor was opened with */
|
| + UnixUnusedFd *pNext; /* Next unused file descriptor on same file */
|
| +};
|
| +
|
| +/*
|
| +** The unixFile structure is subclass of sqlite3_file specific to the unix
|
| +** VFS implementations.
|
| +*/
|
| +typedef struct unixFile unixFile;
|
| +struct unixFile {
|
| + sqlite3_io_methods const *pMethod; /* Always the first entry */
|
| + sqlite3_vfs *pVfs; /* The VFS that created this unixFile */
|
| + unixInodeInfo *pInode; /* Info about locks on this inode */
|
| + int h; /* The file descriptor */
|
| + unsigned char eFileLock; /* The type of lock held on this fd */
|
| + unsigned short int ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */
|
| + int lastErrno; /* The unix errno from last I/O error */
|
| + void *lockingContext; /* Locking style specific state */
|
| + UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */
|
| + const char *zPath; /* Name of the file */
|
| + unixShm *pShm; /* Shared memory segment information */
|
| + int szChunk; /* Configured by FCNTL_CHUNK_SIZE */
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| + int nFetchOut; /* Number of outstanding xFetch refs */
|
| + sqlite3_int64 mmapSize; /* Usable size of mapping at pMapRegion */
|
| + sqlite3_int64 mmapSizeActual; /* Actual size of mapping at pMapRegion */
|
| + sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */
|
| + void *pMapRegion; /* Memory mapped region */
|
| +#endif
|
| +#ifdef __QNXNTO__
|
| + int sectorSize; /* Device sector size */
|
| + int deviceCharacteristics; /* Precomputed device characteristics */
|
| +#endif
|
| +#if SQLITE_ENABLE_LOCKING_STYLE
|
| + int openFlags; /* The flags specified at open() */
|
| +#endif
|
| +#if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__)
|
| + unsigned fsFlags; /* cached details from statfs() */
|
| +#endif
|
| +#if OS_VXWORKS
|
| + struct vxworksFileId *pId; /* Unique file ID */
|
| +#endif
|
| +#ifdef SQLITE_DEBUG
|
| + /* The next group of variables are used to track whether or not the
|
| + ** transaction counter in bytes 24-27 of database files are updated
|
| + ** whenever any part of the database changes. An assertion fault will
|
| + ** occur if a file is updated without also updating the transaction
|
| + ** counter. This test is made to avoid new problems similar to the
|
| + ** one described by ticket #3584.
|
| + */
|
| + unsigned char transCntrChng; /* True if the transaction counter changed */
|
| + unsigned char dbUpdate; /* True if any part of database file changed */
|
| + unsigned char inNormalWrite; /* True if in a normal write operation */
|
| +
|
| +#endif
|
| +
|
| +#ifdef SQLITE_TEST
|
| + /* In test mode, increase the size of this structure a bit so that
|
| + ** it is larger than the struct CrashFile defined in test6.c.
|
| + */
|
| + char aPadding[32];
|
| +#endif
|
| +};
|
| +
|
| +/* This variable holds the process id (pid) from when the xRandomness()
|
| +** method was called. If xOpen() is called from a different process id,
|
| +** indicating that a fork() has occurred, the PRNG will be reset.
|
| +*/
|
| +static pid_t randomnessPid = 0;
|
| +
|
| +/*
|
| +** Allowed values for the unixFile.ctrlFlags bitmask:
|
| +*/
|
| +#define UNIXFILE_EXCL 0x01 /* Connections from one process only */
|
| +#define UNIXFILE_RDONLY 0x02 /* Connection is read only */
|
| +#define UNIXFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */
|
| +#ifndef SQLITE_DISABLE_DIRSYNC
|
| +# define UNIXFILE_DIRSYNC 0x08 /* Directory sync needed */
|
| +#else
|
| +# define UNIXFILE_DIRSYNC 0x00
|
| +#endif
|
| +#define UNIXFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */
|
| +#define UNIXFILE_DELETE 0x20 /* Delete on close */
|
| +#define UNIXFILE_URI 0x40 /* Filename might have query parameters */
|
| +#define UNIXFILE_NOLOCK 0x80 /* Do no file locking */
|
| +
|
| +/*
|
| +** Include code that is common to all os_*.c files
|
| +*/
|
| +/************** Include os_common.h in the middle of os_unix.c ***************/
|
| +/************** Begin file os_common.h ***************************************/
|
| +/*
|
| +** 2004 May 22
|
| +**
|
| +** 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 macros and a little bit of code that is common to
|
| +** all of the platform-specific files (os_*.c) and is #included into those
|
| +** files.
|
| +**
|
| +** This file should be #included by the os_*.c files only. It is not a
|
| +** general purpose header file.
|
| +*/
|
| +#ifndef _OS_COMMON_H_
|
| +#define _OS_COMMON_H_
|
| +
|
| +/*
|
| +** At least two bugs have slipped in because we changed the MEMORY_DEBUG
|
| +** macro to SQLITE_DEBUG and some older makefiles have not yet made the
|
| +** switch. The following code should catch this problem at compile-time.
|
| +*/
|
| +#ifdef MEMORY_DEBUG
|
| +# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
|
| +#endif
|
| +
|
| +/*
|
| +** Macros for performance tracing. Normally turned off. Only works
|
| +** on i486 hardware.
|
| +*/
|
| +#ifdef SQLITE_PERFORMANCE_TRACE
|
| +
|
| +/*
|
| +** hwtime.h contains inline assembler code for implementing
|
| +** high-performance timing routines.
|
| +*/
|
| +/************** Include hwtime.h in the middle of os_common.h ****************/
|
| +/************** Begin file hwtime.h ******************************************/
|
| +/*
|
| +** 2008 May 27
|
| +**
|
| +** 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 inline asm code for retrieving "high-performance"
|
| +** counters for x86 class CPUs.
|
| +*/
|
| +#ifndef SQLITE_HWTIME_H
|
| +#define SQLITE_HWTIME_H
|
| +
|
| +/*
|
| +** The following routine only works on pentium-class (or newer) processors.
|
| +** It uses the RDTSC opcode to read the cycle count value out of the
|
| +** processor and returns that value. This can be used for high-res
|
| +** profiling.
|
| +*/
|
| +#if (defined(__GNUC__) || defined(_MSC_VER)) && \
|
| + (defined(i386) || defined(__i386__) || defined(_M_IX86))
|
| +
|
| + #if defined(__GNUC__)
|
| +
|
| + __inline__ sqlite_uint64 sqlite3Hwtime(void){
|
| + unsigned int lo, hi;
|
| + __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
|
| + return (sqlite_uint64)hi << 32 | lo;
|
| + }
|
| +
|
| + #elif defined(_MSC_VER)
|
| +
|
| + __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){
|
| + __asm {
|
| + rdtsc
|
| + ret ; return value at EDX:EAX
|
| + }
|
| + }
|
| +
|
| + #endif
|
| +
|
| +#elif (defined(__GNUC__) && defined(__x86_64__))
|
| +
|
| + __inline__ sqlite_uint64 sqlite3Hwtime(void){
|
| + unsigned long val;
|
| + __asm__ __volatile__ ("rdtsc" : "=A" (val));
|
| + return val;
|
| + }
|
| +
|
| +#elif (defined(__GNUC__) && defined(__ppc__))
|
| +
|
| + __inline__ sqlite_uint64 sqlite3Hwtime(void){
|
| + unsigned long long retval;
|
| + unsigned long junk;
|
| + __asm__ __volatile__ ("\n\
|
| + 1: mftbu %1\n\
|
| + mftb %L0\n\
|
| + mftbu %0\n\
|
| + cmpw %0,%1\n\
|
| + bne 1b"
|
| + : "=r" (retval), "=r" (junk));
|
| + return retval;
|
| + }
|
| +
|
| +#else
|
| +
|
| + #error Need implementation of sqlite3Hwtime() for your platform.
|
| +
|
| + /*
|
| + ** To compile without implementing sqlite3Hwtime() for your platform,
|
| + ** you can remove the above #error and use the following
|
| + ** stub function. You will lose timing support for many
|
| + ** of the debugging and testing utilities, but it should at
|
| + ** least compile and run.
|
| + */
|
| +SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); }
|
| +
|
| +#endif
|
| +
|
| +#endif /* !defined(SQLITE_HWTIME_H) */
|
| +
|
| +/************** End of hwtime.h **********************************************/
|
| +/************** Continuing where we left off in os_common.h ******************/
|
| +
|
| +static sqlite_uint64 g_start;
|
| +static sqlite_uint64 g_elapsed;
|
| +#define TIMER_START g_start=sqlite3Hwtime()
|
| +#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start
|
| +#define TIMER_ELAPSED g_elapsed
|
| +#else
|
| +#define TIMER_START
|
| +#define TIMER_END
|
| +#define TIMER_ELAPSED ((sqlite_uint64)0)
|
| +#endif
|
| +
|
| +/*
|
| +** If we compile with the SQLITE_TEST macro set, then the following block
|
| +** of code will give us the ability to simulate a disk I/O error. This
|
| +** is used for testing the I/O recovery logic.
|
| +*/
|
| +#if defined(SQLITE_TEST)
|
| +SQLITE_API extern int sqlite3_io_error_hit;
|
| +SQLITE_API extern int sqlite3_io_error_hardhit;
|
| +SQLITE_API extern int sqlite3_io_error_pending;
|
| +SQLITE_API extern int sqlite3_io_error_persist;
|
| +SQLITE_API extern int sqlite3_io_error_benign;
|
| +SQLITE_API extern int sqlite3_diskfull_pending;
|
| +SQLITE_API extern int sqlite3_diskfull;
|
| +#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X)
|
| +#define SimulateIOError(CODE) \
|
| + if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \
|
| + || sqlite3_io_error_pending-- == 1 ) \
|
| + { local_ioerr(); CODE; }
|
| +static void local_ioerr(){
|
| + IOTRACE(("IOERR\n"));
|
| + sqlite3_io_error_hit++;
|
| + if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++;
|
| +}
|
| +#define SimulateDiskfullError(CODE) \
|
| + if( sqlite3_diskfull_pending ){ \
|
| + if( sqlite3_diskfull_pending == 1 ){ \
|
| + local_ioerr(); \
|
| + sqlite3_diskfull = 1; \
|
| + sqlite3_io_error_hit = 1; \
|
| + CODE; \
|
| + }else{ \
|
| + sqlite3_diskfull_pending--; \
|
| + } \
|
| + }
|
| +#else
|
| +#define SimulateIOErrorBenign(X)
|
| +#define SimulateIOError(A)
|
| +#define SimulateDiskfullError(A)
|
| +#endif /* defined(SQLITE_TEST) */
|
| +
|
| +/*
|
| +** When testing, keep a count of the number of open files.
|
| +*/
|
| +#if defined(SQLITE_TEST)
|
| +SQLITE_API extern int sqlite3_open_file_count;
|
| +#define OpenCounter(X) sqlite3_open_file_count+=(X)
|
| +#else
|
| +#define OpenCounter(X)
|
| +#endif /* defined(SQLITE_TEST) */
|
| +
|
| +#endif /* !defined(_OS_COMMON_H_) */
|
| +
|
| +/************** End of os_common.h *******************************************/
|
| +/************** Continuing where we left off in os_unix.c ********************/
|
| +
|
| +/*
|
| +** 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
|
| +
|
| +/*
|
| +** The threadid macro resolves to the thread-id or to 0. Used for
|
| +** testing and debugging only.
|
| +*/
|
| +#if SQLITE_THREADSAFE
|
| +#define threadid pthread_self()
|
| +#else
|
| +#define threadid 0
|
| +#endif
|
| +
|
| +/*
|
| +** HAVE_MREMAP defaults to true on Linux and false everywhere else.
|
| +*/
|
| +#if !defined(HAVE_MREMAP)
|
| +# if defined(__linux__) && defined(_GNU_SOURCE)
|
| +# define HAVE_MREMAP 1
|
| +# else
|
| +# define HAVE_MREMAP 0
|
| +# endif
|
| +#endif
|
| +
|
| +/*
|
| +** Explicitly call the 64-bit version of lseek() on Android. Otherwise, lseek()
|
| +** is the 32-bit version, even if _FILE_OFFSET_BITS=64 is defined.
|
| +*/
|
| +#ifdef __ANDROID__
|
| +# define lseek lseek64
|
| +#endif
|
| +
|
| +/*
|
| +** Different Unix systems declare open() in different ways. Same use
|
| +** open(const char*,int,mode_t). Others use open(const char*,int,...).
|
| +** The difference is important when using a pointer to the function.
|
| +**
|
| +** The safest way to deal with the problem is to always use this wrapper
|
| +** which always has the same well-defined interface.
|
| +*/
|
| +static int posixOpen(const char *zFile, int flags, int mode){
|
| + return open(zFile, flags, mode);
|
| +}
|
| +
|
| +/* Forward reference */
|
| +static int openDirectory(const char*, int*);
|
| +static int unixGetpagesize(void);
|
| +
|
| +/*
|
| +** Many system calls are accessed through pointer-to-functions so that
|
| +** they may be overridden at runtime to facilitate fault injection during
|
| +** testing and sandboxing. The following array holds the names and pointers
|
| +** to all overrideable system calls.
|
| +*/
|
| +static struct unix_syscall {
|
| + const char *zName; /* Name of the system call */
|
| + sqlite3_syscall_ptr pCurrent; /* Current value of the system call */
|
| + sqlite3_syscall_ptr pDefault; /* Default value */
|
| +} aSyscall[] = {
|
| + { "open", (sqlite3_syscall_ptr)posixOpen, 0 },
|
| +#define osOpen ((int(*)(const char*,int,int))aSyscall[0].pCurrent)
|
| +
|
| + { "close", (sqlite3_syscall_ptr)close, 0 },
|
| +#define osClose ((int(*)(int))aSyscall[1].pCurrent)
|
| +
|
| + { "access", (sqlite3_syscall_ptr)access, 0 },
|
| +#define osAccess ((int(*)(const char*,int))aSyscall[2].pCurrent)
|
| +
|
| + { "getcwd", (sqlite3_syscall_ptr)getcwd, 0 },
|
| +#define osGetcwd ((char*(*)(char*,size_t))aSyscall[3].pCurrent)
|
| +
|
| + { "stat", (sqlite3_syscall_ptr)stat, 0 },
|
| +#define osStat ((int(*)(const char*,struct stat*))aSyscall[4].pCurrent)
|
| +
|
| +/*
|
| +** The DJGPP compiler environment looks mostly like Unix, but it
|
| +** lacks the fcntl() system call. So redefine fcntl() to be something
|
| +** that always succeeds. This means that locking does not occur under
|
| +** DJGPP. But it is DOS - what did you expect?
|
| +*/
|
| +#ifdef __DJGPP__
|
| + { "fstat", 0, 0 },
|
| +#define osFstat(a,b,c) 0
|
| +#else
|
| + { "fstat", (sqlite3_syscall_ptr)fstat, 0 },
|
| +#define osFstat ((int(*)(int,struct stat*))aSyscall[5].pCurrent)
|
| +#endif
|
| +
|
| + { "ftruncate", (sqlite3_syscall_ptr)ftruncate, 0 },
|
| +#define osFtruncate ((int(*)(int,off_t))aSyscall[6].pCurrent)
|
| +
|
| + { "fcntl", (sqlite3_syscall_ptr)fcntl, 0 },
|
| +#define osFcntl ((int(*)(int,int,...))aSyscall[7].pCurrent)
|
| +
|
| + { "read", (sqlite3_syscall_ptr)read, 0 },
|
| +#define osRead ((ssize_t(*)(int,void*,size_t))aSyscall[8].pCurrent)
|
| +
|
| +#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE
|
| + { "pread", (sqlite3_syscall_ptr)pread, 0 },
|
| +#else
|
| + { "pread", (sqlite3_syscall_ptr)0, 0 },
|
| +#endif
|
| +#define osPread ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[9].pCurrent)
|
| +
|
| +#if defined(USE_PREAD64)
|
| + { "pread64", (sqlite3_syscall_ptr)pread64, 0 },
|
| +#else
|
| + { "pread64", (sqlite3_syscall_ptr)0, 0 },
|
| +#endif
|
| +#define osPread64 ((ssize_t(*)(int,void*,size_t,off64_t))aSyscall[10].pCurrent)
|
| +
|
| + { "write", (sqlite3_syscall_ptr)write, 0 },
|
| +#define osWrite ((ssize_t(*)(int,const void*,size_t))aSyscall[11].pCurrent)
|
| +
|
| +#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE
|
| + { "pwrite", (sqlite3_syscall_ptr)pwrite, 0 },
|
| +#else
|
| + { "pwrite", (sqlite3_syscall_ptr)0, 0 },
|
| +#endif
|
| +#define osPwrite ((ssize_t(*)(int,const void*,size_t,off_t))\
|
| + aSyscall[12].pCurrent)
|
| +
|
| +#if defined(USE_PREAD64)
|
| + { "pwrite64", (sqlite3_syscall_ptr)pwrite64, 0 },
|
| +#else
|
| + { "pwrite64", (sqlite3_syscall_ptr)0, 0 },
|
| +#endif
|
| +#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off64_t))\
|
| + aSyscall[13].pCurrent)
|
| +
|
| + { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 },
|
| +#define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent)
|
| +
|
| +#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
|
| + { "fallocate", (sqlite3_syscall_ptr)posix_fallocate, 0 },
|
| +#else
|
| + { "fallocate", (sqlite3_syscall_ptr)0, 0 },
|
| +#endif
|
| +#define osFallocate ((int(*)(int,off_t,off_t))aSyscall[15].pCurrent)
|
| +
|
| + { "unlink", (sqlite3_syscall_ptr)unlink, 0 },
|
| +#define osUnlink ((int(*)(const char*))aSyscall[16].pCurrent)
|
| +
|
| + { "openDirectory", (sqlite3_syscall_ptr)openDirectory, 0 },
|
| +#define osOpenDirectory ((int(*)(const char*,int*))aSyscall[17].pCurrent)
|
| +
|
| + { "mkdir", (sqlite3_syscall_ptr)mkdir, 0 },
|
| +#define osMkdir ((int(*)(const char*,mode_t))aSyscall[18].pCurrent)
|
| +
|
| + { "rmdir", (sqlite3_syscall_ptr)rmdir, 0 },
|
| +#define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent)
|
| +
|
| +#if defined(HAVE_FCHOWN)
|
| + { "fchown", (sqlite3_syscall_ptr)fchown, 0 },
|
| +#else
|
| + { "fchown", (sqlite3_syscall_ptr)0, 0 },
|
| +#endif
|
| +#define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent)
|
| +
|
| + { "geteuid", (sqlite3_syscall_ptr)geteuid, 0 },
|
| +#define osGeteuid ((uid_t(*)(void))aSyscall[21].pCurrent)
|
| +
|
| +#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
|
| + { "mmap", (sqlite3_syscall_ptr)mmap, 0 },
|
| +#else
|
| + { "mmap", (sqlite3_syscall_ptr)0, 0 },
|
| +#endif
|
| +#define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[22].pCurrent)
|
| +
|
| +#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
|
| + { "munmap", (sqlite3_syscall_ptr)munmap, 0 },
|
| +#else
|
| + { "munmap", (sqlite3_syscall_ptr)0, 0 },
|
| +#endif
|
| +#define osMunmap ((void*(*)(void*,size_t))aSyscall[23].pCurrent)
|
| +
|
| +#if HAVE_MREMAP && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)
|
| + { "mremap", (sqlite3_syscall_ptr)mremap, 0 },
|
| +#else
|
| + { "mremap", (sqlite3_syscall_ptr)0, 0 },
|
| +#endif
|
| +#define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[24].pCurrent)
|
| +
|
| +#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
|
| + { "getpagesize", (sqlite3_syscall_ptr)unixGetpagesize, 0 },
|
| +#else
|
| + { "getpagesize", (sqlite3_syscall_ptr)0, 0 },
|
| +#endif
|
| +#define osGetpagesize ((int(*)(void))aSyscall[25].pCurrent)
|
| +
|
| +#if defined(HAVE_READLINK)
|
| + { "readlink", (sqlite3_syscall_ptr)readlink, 0 },
|
| +#else
|
| + { "readlink", (sqlite3_syscall_ptr)0, 0 },
|
| +#endif
|
| +#define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[26].pCurrent)
|
| +
|
| +#if defined(HAVE_LSTAT)
|
| + { "lstat", (sqlite3_syscall_ptr)lstat, 0 },
|
| +#else
|
| + { "lstat", (sqlite3_syscall_ptr)0, 0 },
|
| +#endif
|
| +#define osLstat ((int(*)(const char*,struct stat*))aSyscall[27].pCurrent)
|
| +
|
| +}; /* End of the overrideable system calls */
|
| +
|
| +
|
| +/*
|
| +** On some systems, calls to fchown() will trigger a message in a security
|
| +** log if they come from non-root processes. So avoid calling fchown() if
|
| +** we are not running as root.
|
| +*/
|
| +static int robustFchown(int fd, uid_t uid, gid_t gid){
|
| +#if defined(HAVE_FCHOWN)
|
| + return osGeteuid() ? 0 : osFchown(fd,uid,gid);
|
| +#else
|
| + return 0;
|
| +#endif
|
| +}
|
| +
|
| +/*
|
| +** This is the xSetSystemCall() method of sqlite3_vfs for all of the
|
| +** "unix" VFSes. Return SQLITE_OK opon successfully updating the
|
| +** system call pointer, or SQLITE_NOTFOUND if there is no configurable
|
| +** system call named zName.
|
| +*/
|
| +static int unixSetSystemCall(
|
| + sqlite3_vfs *pNotUsed, /* The VFS pointer. Not used */
|
| + const char *zName, /* Name of system call to override */
|
| + sqlite3_syscall_ptr pNewFunc /* Pointer to new system call value */
|
| +){
|
| + unsigned int i;
|
| + int rc = SQLITE_NOTFOUND;
|
| +
|
| + UNUSED_PARAMETER(pNotUsed);
|
| + if( zName==0 ){
|
| + /* If no zName is given, restore all system calls to their default
|
| + ** settings and return NULL
|
| + */
|
| + rc = SQLITE_OK;
|
| + for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){
|
| + if( aSyscall[i].pDefault ){
|
| + aSyscall[i].pCurrent = aSyscall[i].pDefault;
|
| + }
|
| + }
|
| + }else{
|
| + /* If zName is specified, operate on only the one system call
|
| + ** specified.
|
| + */
|
| + for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){
|
| + if( strcmp(zName, aSyscall[i].zName)==0 ){
|
| + if( aSyscall[i].pDefault==0 ){
|
| + aSyscall[i].pDefault = aSyscall[i].pCurrent;
|
| + }
|
| + rc = SQLITE_OK;
|
| + if( pNewFunc==0 ) pNewFunc = aSyscall[i].pDefault;
|
| + aSyscall[i].pCurrent = pNewFunc;
|
| + break;
|
| + }
|
| + }
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Return the value of a system call. Return NULL if zName is not a
|
| +** recognized system call name. NULL is also returned if the system call
|
| +** is currently undefined.
|
| +*/
|
| +static sqlite3_syscall_ptr unixGetSystemCall(
|
| + sqlite3_vfs *pNotUsed,
|
| + const char *zName
|
| +){
|
| + unsigned int i;
|
| +
|
| + UNUSED_PARAMETER(pNotUsed);
|
| + for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){
|
| + if( strcmp(zName, aSyscall[i].zName)==0 ) return aSyscall[i].pCurrent;
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| +/*
|
| +** Return the name of the first system call after zName. If zName==NULL
|
| +** then return the name of the first system call. Return NULL if zName
|
| +** is the last system call or if zName is not the name of a valid
|
| +** system call.
|
| +*/
|
| +static const char *unixNextSystemCall(sqlite3_vfs *p, const char *zName){
|
| + int i = -1;
|
| +
|
| + UNUSED_PARAMETER(p);
|
| + if( zName ){
|
| + for(i=0; i<ArraySize(aSyscall)-1; i++){
|
| + if( strcmp(zName, aSyscall[i].zName)==0 ) break;
|
| + }
|
| + }
|
| + for(i++; i<ArraySize(aSyscall); i++){
|
| + if( aSyscall[i].pCurrent!=0 ) return aSyscall[i].zName;
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| +/*
|
| +** Do not accept any file descriptor less than this value, in order to avoid
|
| +** opening database file using file descriptors that are commonly used for
|
| +** standard input, output, and error.
|
| +*/
|
| +#ifndef SQLITE_MINIMUM_FILE_DESCRIPTOR
|
| +# define SQLITE_MINIMUM_FILE_DESCRIPTOR 3
|
| +#endif
|
| +
|
| +/*
|
| +** 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 robust_open(const char *z, int f, mode_t m){
|
| + int fd;
|
| + mode_t m2 = m ? m : SQLITE_DEFAULT_FILE_PERMISSIONS;
|
| + while(1){
|
| +#if defined(O_CLOEXEC)
|
| + fd = osOpen(z,f|O_CLOEXEC,m2);
|
| +#else
|
| + fd = osOpen(z,f,m2);
|
| +#endif
|
| + if( fd<0 ){
|
| + if( errno==EINTR ) continue;
|
| + break;
|
| + }
|
| + if( fd>=SQLITE_MINIMUM_FILE_DESCRIPTOR ) break;
|
| + osClose(fd);
|
| + sqlite3_log(SQLITE_WARNING,
|
| + "attempt to open \"%s\" as file descriptor %d", z, fd);
|
| + fd = -1;
|
| + if( osOpen("/dev/null", f, m)<0 ) break;
|
| + }
|
| + if( fd>=0 ){
|
| + if( m!=0 ){
|
| + struct stat statbuf;
|
| + if( osFstat(fd, &statbuf)==0
|
| + && statbuf.st_size==0
|
| + && (statbuf.st_mode&0777)!=m
|
| + ){
|
| + osFchmod(fd, m);
|
| + }
|
| + }
|
| +#if defined(FD_CLOEXEC) && (!defined(O_CLOEXEC) || O_CLOEXEC==0)
|
| + osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
|
| +#endif
|
| + }
|
| + return fd;
|
| +}
|
| +
|
| +/*
|
| +** Helper functions to obtain and relinquish the global mutex. The
|
| +** global mutex is used to protect the unixInodeInfo and
|
| +** vxworksFileId objects used by this file, all of which may be
|
| +** shared by multiple threads.
|
| +**
|
| +** Function unixMutexHeld() is used to assert() that the global mutex
|
| +** is held when required. This function is only used as part of assert()
|
| +** statements. e.g.
|
| +**
|
| +** unixEnterMutex()
|
| +** assert( unixMutexHeld() );
|
| +** unixEnterLeave()
|
| +*/
|
| +static void unixEnterMutex(void){
|
| + sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
|
| +}
|
| +static void unixLeaveMutex(void){
|
| + sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
|
| +}
|
| +#ifdef SQLITE_DEBUG
|
| +static int unixMutexHeld(void) {
|
| + return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
|
| +}
|
| +#endif
|
| +
|
| +
|
| +#ifdef SQLITE_HAVE_OS_TRACE
|
| +/*
|
| +** Helper function for printing out trace information from debugging
|
| +** binaries. This returns the string representation of the supplied
|
| +** integer lock-type.
|
| +*/
|
| +static const char *azFileLock(int eFileLock){
|
| + switch( eFileLock ){
|
| + case NO_LOCK: return "NONE";
|
| + case SHARED_LOCK: return "SHARED";
|
| + case RESERVED_LOCK: return "RESERVED";
|
| + case PENDING_LOCK: return "PENDING";
|
| + case EXCLUSIVE_LOCK: return "EXCLUSIVE";
|
| + }
|
| + return "ERROR";
|
| +}
|
| +#endif
|
| +
|
| +#ifdef SQLITE_LOCK_TRACE
|
| +/*
|
| +** Print out information about all locking operations.
|
| +**
|
| +** This routine is used for troubleshooting locks on multithreaded
|
| +** platforms. Enable by compiling with the -DSQLITE_LOCK_TRACE
|
| +** command-line option on the compiler. This code is normally
|
| +** turned off.
|
| +*/
|
| +static int lockTrace(int fd, int op, struct flock *p){
|
| + char *zOpName, *zType;
|
| + int s;
|
| + int savedErrno;
|
| + if( op==F_GETLK ){
|
| + zOpName = "GETLK";
|
| + }else if( op==F_SETLK ){
|
| + zOpName = "SETLK";
|
| + }else{
|
| + s = osFcntl(fd, op, p);
|
| + sqlite3DebugPrintf("fcntl unknown %d %d %d\n", fd, op, s);
|
| + return s;
|
| + }
|
| + if( p->l_type==F_RDLCK ){
|
| + zType = "RDLCK";
|
| + }else if( p->l_type==F_WRLCK ){
|
| + zType = "WRLCK";
|
| + }else if( p->l_type==F_UNLCK ){
|
| + zType = "UNLCK";
|
| + }else{
|
| + assert( 0 );
|
| + }
|
| + assert( p->l_whence==SEEK_SET );
|
| + s = osFcntl(fd, op, p);
|
| + savedErrno = errno;
|
| + sqlite3DebugPrintf("fcntl %d %d %s %s %d %d %d %d\n",
|
| + threadid, fd, zOpName, zType, (int)p->l_start, (int)p->l_len,
|
| + (int)p->l_pid, s);
|
| + if( s==(-1) && op==F_SETLK && (p->l_type==F_RDLCK || p->l_type==F_WRLCK) ){
|
| + struct flock l2;
|
| + l2 = *p;
|
| + osFcntl(fd, F_GETLK, &l2);
|
| + if( l2.l_type==F_RDLCK ){
|
| + zType = "RDLCK";
|
| + }else if( l2.l_type==F_WRLCK ){
|
| + zType = "WRLCK";
|
| + }else if( l2.l_type==F_UNLCK ){
|
| + zType = "UNLCK";
|
| + }else{
|
| + assert( 0 );
|
| + }
|
| + sqlite3DebugPrintf("fcntl-failure-reason: %s %d %d %d\n",
|
| + zType, (int)l2.l_start, (int)l2.l_len, (int)l2.l_pid);
|
| + }
|
| + errno = savedErrno;
|
| + return s;
|
| +}
|
| +#undef osFcntl
|
| +#define osFcntl lockTrace
|
| +#endif /* SQLITE_LOCK_TRACE */
|
| +
|
| +/*
|
| +** Retry ftruncate() calls that fail due to EINTR
|
| +**
|
| +** All calls to ftruncate() within this file should be made through
|
| +** this wrapper. On the Android platform, bypassing the logic below
|
| +** could lead to a corrupt database.
|
| +*/
|
| +static int robust_ftruncate(int h, sqlite3_int64 sz){
|
| + int rc;
|
| +#ifdef __ANDROID__
|
| + /* On Android, ftruncate() always uses 32-bit offsets, even if
|
| + ** _FILE_OFFSET_BITS=64 is defined. This means it is unsafe to attempt to
|
| + ** truncate a file to any size larger than 2GiB. Silently ignore any
|
| + ** such attempts. */
|
| + if( sz>(sqlite3_int64)0x7FFFFFFF ){
|
| + rc = SQLITE_OK;
|
| + }else
|
| +#endif
|
| + do{ rc = osFtruncate(h,sz); }while( rc<0 && errno==EINTR );
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** This routine translates a standard POSIX errno code into something
|
| +** useful to the clients of the sqlite3 functions. Specifically, it is
|
| +** intended to translate a variety of "try again" errors into SQLITE_BUSY
|
| +** and a variety of "please close the file descriptor NOW" errors into
|
| +** SQLITE_IOERR
|
| +**
|
| +** Errors during initialization of locks, or file system support for locks,
|
| +** should handle ENOLCK, ENOTSUP, EOPNOTSUPP separately.
|
| +*/
|
| +static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) {
|
| + assert( (sqliteIOErr == SQLITE_IOERR_LOCK) ||
|
| + (sqliteIOErr == SQLITE_IOERR_UNLOCK) ||
|
| + (sqliteIOErr == SQLITE_IOERR_RDLOCK) ||
|
| + (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) );
|
| + switch (posixError) {
|
| + case EACCES:
|
| + case EAGAIN:
|
| + case ETIMEDOUT:
|
| + case EBUSY:
|
| + case EINTR:
|
| + case ENOLCK:
|
| + /* random NFS retry error, unless during file system support
|
| + * introspection, in which it actually means what it says */
|
| + return SQLITE_BUSY;
|
| +
|
| + case EPERM:
|
| + return SQLITE_PERM;
|
| +
|
| + default:
|
| + return sqliteIOErr;
|
| + }
|
| +}
|
| +
|
| +
|
| +/******************************************************************************
|
| +****************** Begin Unique File ID Utility Used By VxWorks ***************
|
| +**
|
| +** On most versions of unix, we can get a unique ID for a file by concatenating
|
| +** the device number and the inode number. But this does not work on VxWorks.
|
| +** On VxWorks, a unique file id must be based on the canonical filename.
|
| +**
|
| +** A pointer to an instance of the following structure can be used as a
|
| +** unique file ID in VxWorks. Each instance of this structure contains
|
| +** a copy of the canonical filename. There is also a reference count.
|
| +** The structure is reclaimed when the number of pointers to it drops to
|
| +** zero.
|
| +**
|
| +** There are never very many files open at one time and lookups are not
|
| +** a performance-critical path, so it is sufficient to put these
|
| +** structures on a linked list.
|
| +*/
|
| +struct vxworksFileId {
|
| + struct vxworksFileId *pNext; /* Next in a list of them all */
|
| + int nRef; /* Number of references to this one */
|
| + int nName; /* Length of the zCanonicalName[] string */
|
| + char *zCanonicalName; /* Canonical filename */
|
| +};
|
| +
|
| +#if OS_VXWORKS
|
| +/*
|
| +** All unique filenames are held on a linked list headed by this
|
| +** variable:
|
| +*/
|
| +static struct vxworksFileId *vxworksFileList = 0;
|
| +
|
| +/*
|
| +** Simplify a filename into its canonical form
|
| +** by making the following changes:
|
| +**
|
| +** * removing any trailing and duplicate /
|
| +** * convert /./ into just /
|
| +** * convert /A/../ where A is any simple name into just /
|
| +**
|
| +** Changes are made in-place. Return the new name length.
|
| +**
|
| +** The original filename is in z[0..n-1]. Return the number of
|
| +** characters in the simplified name.
|
| +*/
|
| +static int vxworksSimplifyName(char *z, int n){
|
| + int i, j;
|
| + while( n>1 && z[n-1]=='/' ){ n--; }
|
| + for(i=j=0; i<n; i++){
|
| + if( z[i]=='/' ){
|
| + if( z[i+1]=='/' ) continue;
|
| + if( z[i+1]=='.' && i+2<n && z[i+2]=='/' ){
|
| + i += 1;
|
| + continue;
|
| + }
|
| + if( z[i+1]=='.' && i+3<n && z[i+2]=='.' && z[i+3]=='/' ){
|
| + while( j>0 && z[j-1]!='/' ){ j--; }
|
| + if( j>0 ){ j--; }
|
| + i += 2;
|
| + continue;
|
| + }
|
| + }
|
| + z[j++] = z[i];
|
| + }
|
| + z[j] = 0;
|
| + return j;
|
| +}
|
| +
|
| +/*
|
| +** Find a unique file ID for the given absolute pathname. Return
|
| +** a pointer to the vxworksFileId object. This pointer is the unique
|
| +** file ID.
|
| +**
|
| +** The nRef field of the vxworksFileId object is incremented before
|
| +** the object is returned. A new vxworksFileId object is created
|
| +** and added to the global list if necessary.
|
| +**
|
| +** If a memory allocation error occurs, return NULL.
|
| +*/
|
| +static struct vxworksFileId *vxworksFindFileId(const char *zAbsoluteName){
|
| + struct vxworksFileId *pNew; /* search key and new file ID */
|
| + struct vxworksFileId *pCandidate; /* For looping over existing file IDs */
|
| + int n; /* Length of zAbsoluteName string */
|
| +
|
| + assert( zAbsoluteName[0]=='/' );
|
| + n = (int)strlen(zAbsoluteName);
|
| + pNew = sqlite3_malloc64( sizeof(*pNew) + (n+1) );
|
| + if( pNew==0 ) return 0;
|
| + pNew->zCanonicalName = (char*)&pNew[1];
|
| + memcpy(pNew->zCanonicalName, zAbsoluteName, n+1);
|
| + n = vxworksSimplifyName(pNew->zCanonicalName, n);
|
| +
|
| + /* Search for an existing entry that matching the canonical name.
|
| + ** If found, increment the reference count and return a pointer to
|
| + ** the existing file ID.
|
| + */
|
| + unixEnterMutex();
|
| + for(pCandidate=vxworksFileList; pCandidate; pCandidate=pCandidate->pNext){
|
| + if( pCandidate->nName==n
|
| + && memcmp(pCandidate->zCanonicalName, pNew->zCanonicalName, n)==0
|
| + ){
|
| + sqlite3_free(pNew);
|
| + pCandidate->nRef++;
|
| + unixLeaveMutex();
|
| + return pCandidate;
|
| + }
|
| + }
|
| +
|
| + /* No match was found. We will make a new file ID */
|
| + pNew->nRef = 1;
|
| + pNew->nName = n;
|
| + pNew->pNext = vxworksFileList;
|
| + vxworksFileList = pNew;
|
| + unixLeaveMutex();
|
| + return pNew;
|
| +}
|
| +
|
| +/*
|
| +** Decrement the reference count on a vxworksFileId object. Free
|
| +** the object when the reference count reaches zero.
|
| +*/
|
| +static void vxworksReleaseFileId(struct vxworksFileId *pId){
|
| + unixEnterMutex();
|
| + assert( pId->nRef>0 );
|
| + pId->nRef--;
|
| + if( pId->nRef==0 ){
|
| + struct vxworksFileId **pp;
|
| + for(pp=&vxworksFileList; *pp && *pp!=pId; pp = &((*pp)->pNext)){}
|
| + assert( *pp==pId );
|
| + *pp = pId->pNext;
|
| + sqlite3_free(pId);
|
| + }
|
| + unixLeaveMutex();
|
| +}
|
| +#endif /* OS_VXWORKS */
|
| +/*************** End of Unique File ID Utility Used By VxWorks ****************
|
| +******************************************************************************/
|
| +
|
| +
|
| +/******************************************************************************
|
| +*************************** Posix Advisory Locking ****************************
|
| +**
|
| +** POSIX advisory locks are broken by design. ANSI STD 1003.1 (1996)
|
| +** section 6.5.2.2 lines 483 through 490 specify that when a process
|
| +** sets or clears a lock, that operation overrides any prior locks set
|
| +** by the same process. It does not explicitly say so, but this implies
|
| +** that it overrides locks set by the same process using a different
|
| +** file descriptor. Consider this test case:
|
| +**
|
| +** int fd1 = open("./file1", O_RDWR|O_CREAT, 0644);
|
| +** int fd2 = open("./file2", O_RDWR|O_CREAT, 0644);
|
| +**
|
| +** Suppose ./file1 and ./file2 are really the same file (because
|
| +** one is a hard or symbolic link to the other) then if you set
|
| +** an exclusive lock on fd1, then try to get an exclusive lock
|
| +** on fd2, it works. I would have expected the second lock to
|
| +** fail since there was already a lock on the file due to fd1.
|
| +** But not so. Since both locks came from the same process, the
|
| +** second overrides the first, even though they were on different
|
| +** file descriptors opened on different file names.
|
| +**
|
| +** This means that we cannot use POSIX locks to synchronize file access
|
| +** among competing threads of the same process. POSIX locks will work fine
|
| +** to synchronize access for threads in separate processes, but not
|
| +** threads within the same process.
|
| +**
|
| +** To work around the problem, SQLite has to manage file locks internally
|
| +** on its own. Whenever a new database is opened, we have to find the
|
| +** specific inode of the database file (the inode is determined by the
|
| +** st_dev and st_ino fields of the stat structure that fstat() fills in)
|
| +** and check for locks already existing on that inode. When locks are
|
| +** created or removed, we have to look at our own internal record of the
|
| +** locks to see if another thread has previously set a lock on that same
|
| +** inode.
|
| +**
|
| +** (Aside: The use of inode numbers as unique IDs does not work on VxWorks.
|
| +** For VxWorks, we have to use the alternative unique ID system based on
|
| +** canonical filename and implemented in the previous division.)
|
| +**
|
| +** The sqlite3_file structure for POSIX is no longer just an integer file
|
| +** descriptor. It is now a structure that holds the integer file
|
| +** descriptor and a pointer to a structure that describes the internal
|
| +** locks on the corresponding inode. There is one locking structure
|
| +** per inode, so if the same inode is opened twice, both unixFile structures
|
| +** point to the same locking structure. The locking structure keeps
|
| +** a reference count (so we will know when to delete it) and a "cnt"
|
| +** field that tells us its internal lock status. cnt==0 means the
|
| +** file is unlocked. cnt==-1 means the file has an exclusive lock.
|
| +** cnt>0 means there are cnt shared locks on the file.
|
| +**
|
| +** Any attempt to lock or unlock a file first checks the locking
|
| +** structure. The fcntl() system call is only invoked to set a
|
| +** POSIX lock if the internal lock structure transitions between
|
| +** a locked and an unlocked state.
|
| +**
|
| +** But wait: there are yet more problems with POSIX advisory locks.
|
| +**
|
| +** If you close a file descriptor that points to a file that has locks,
|
| +** all locks on that file that are owned by the current process are
|
| +** released. To work around this problem, each unixInodeInfo object
|
| +** maintains a count of the number of pending locks on tha inode.
|
| +** When an attempt is made to close an unixFile, if there are
|
| +** other unixFile open on the same inode that are holding locks, the call
|
| +** to close() the file descriptor is deferred until all of the locks clear.
|
| +** The unixInodeInfo structure keeps a list of file descriptors that need to
|
| +** be closed and that list is walked (and cleared) when the last lock
|
| +** clears.
|
| +**
|
| +** Yet another problem: LinuxThreads do not play well with posix locks.
|
| +**
|
| +** Many older versions of linux use the LinuxThreads library which is
|
| +** not posix compliant. Under LinuxThreads, a lock created by thread
|
| +** A cannot be modified or overridden by a different thread B.
|
| +** Only thread A can modify the lock. Locking behavior is correct
|
| +** if the appliation uses the newer Native Posix Thread Library (NPTL)
|
| +** on linux - with NPTL a lock created by thread A can override locks
|
| +** in thread B. But there is no way to know at compile-time which
|
| +** threading library is being used. So there is no way to know at
|
| +** compile-time whether or not thread A can override locks on thread B.
|
| +** One has to do a run-time check to discover the behavior of the
|
| +** current process.
|
| +**
|
| +** SQLite used to support LinuxThreads. But support for LinuxThreads
|
| +** was dropped beginning with version 3.7.0. SQLite will still work with
|
| +** LinuxThreads provided that (1) there is no more than one connection
|
| +** per database file in the same process and (2) database connections
|
| +** do not move across threads.
|
| +*/
|
| +
|
| +/*
|
| +** An instance of the following structure serves as the key used
|
| +** to locate a particular unixInodeInfo object.
|
| +*/
|
| +struct unixFileId {
|
| + dev_t dev; /* Device number */
|
| +#if OS_VXWORKS
|
| + struct vxworksFileId *pId; /* Unique file ID for vxworks. */
|
| +#else
|
| + /* We are told that some versions of Android contain a bug that
|
| + ** sizes ino_t at only 32-bits instead of 64-bits. (See
|
| + ** https://android-review.googlesource.com/#/c/115351/3/dist/sqlite3.c)
|
| + ** To work around this, always allocate 64-bits for the inode number.
|
| + ** On small machines that only have 32-bit inodes, this wastes 4 bytes,
|
| + ** but that should not be a big deal. */
|
| + /* WAS: ino_t ino; */
|
| + u64 ino; /* Inode number */
|
| +#endif
|
| +};
|
| +
|
| +/*
|
| +** An instance of the following structure is allocated for each open
|
| +** inode. Or, on LinuxThreads, there is one of these structures for
|
| +** each inode opened by each thread.
|
| +**
|
| +** A single inode can have multiple file descriptors, so each unixFile
|
| +** structure contains a pointer to an instance of this object and this
|
| +** object keeps a count of the number of unixFile pointing to it.
|
| +*/
|
| +struct unixInodeInfo {
|
| + struct unixFileId fileId; /* The lookup key */
|
| + int nShared; /* Number of SHARED locks held */
|
| + unsigned char eFileLock; /* One of SHARED_LOCK, RESERVED_LOCK etc. */
|
| + unsigned char bProcessLock; /* An exclusive process lock is held */
|
| + int nRef; /* Number of pointers to this structure */
|
| + unixShmNode *pShmNode; /* Shared memory associated with this inode */
|
| + int nLock; /* Number of outstanding file locks */
|
| + UnixUnusedFd *pUnused; /* Unused file descriptors to close */
|
| + unixInodeInfo *pNext; /* List of all unixInodeInfo objects */
|
| + unixInodeInfo *pPrev; /* .... doubly linked */
|
| +#if SQLITE_ENABLE_LOCKING_STYLE
|
| + unsigned long long sharedByte; /* for AFP simulated shared lock */
|
| +#endif
|
| +#if OS_VXWORKS
|
| + sem_t *pSem; /* Named POSIX semaphore */
|
| + char aSemName[MAX_PATHNAME+2]; /* Name of that semaphore */
|
| +#endif
|
| +};
|
| +
|
| +/*
|
| +** A lists of all unixInodeInfo objects.
|
| +*/
|
| +static unixInodeInfo *inodeList = 0;
|
| +
|
| +/*
|
| +**
|
| +** This function - unixLogErrorAtLine(), is only ever called via the macro
|
| +** unixLogError().
|
| +**
|
| +** It is invoked after an error occurs in an OS function and errno has been
|
| +** set. It logs a message using sqlite3_log() containing the current value of
|
| +** errno and, if possible, the human-readable equivalent from strerror() or
|
| +** strerror_r().
|
| +**
|
| +** The first argument passed to the macro should be the error code that
|
| +** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN).
|
| +** The two subsequent arguments should be the name of the OS function that
|
| +** failed (e.g. "unlink", "open") and the associated file-system path,
|
| +** if any.
|
| +*/
|
| +#define unixLogError(a,b,c) unixLogErrorAtLine(a,b,c,__LINE__)
|
| +static int unixLogErrorAtLine(
|
| + 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_unix.c:%d: (%d) %s(%s) - %s",
|
| + iLine, iErrno, zFunc, zPath, zErr
|
| + );
|
| +
|
| + return errcode;
|
| +}
|
| +
|
| +/*
|
| +** Close a file descriptor.
|
| +**
|
| +** We assume that close() almost always works, since it is only in a
|
| +** very sick application or on a very sick platform that it might fail.
|
| +** If it does fail, simply leak the file descriptor, but do log the
|
| +** error.
|
| +**
|
| +** Note that it is not safe to retry close() after EINTR since the
|
| +** file descriptor might have already been reused by another thread.
|
| +** So we don't even try to recover from an EINTR. Just log the error
|
| +** and move on.
|
| +*/
|
| +static void robust_close(unixFile *pFile, int h, int lineno){
|
| + if( osClose(h) ){
|
| + unixLogErrorAtLine(SQLITE_IOERR_CLOSE, "close",
|
| + pFile ? pFile->zPath : 0, lineno);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Set the pFile->lastErrno. Do this in a subroutine as that provides
|
| +** a convenient place to set a breakpoint.
|
| +*/
|
| +static void storeLastErrno(unixFile *pFile, int error){
|
| + pFile->lastErrno = error;
|
| +}
|
| +
|
| +/*
|
| +** Close all file descriptors accumuated in the unixInodeInfo->pUnused list.
|
| +*/
|
| +static void closePendingFds(unixFile *pFile){
|
| + unixInodeInfo *pInode = pFile->pInode;
|
| + UnixUnusedFd *p;
|
| + UnixUnusedFd *pNext;
|
| + for(p=pInode->pUnused; p; p=pNext){
|
| + pNext = p->pNext;
|
| + robust_close(pFile, p->fd, __LINE__);
|
| + sqlite3_free(p);
|
| + }
|
| + pInode->pUnused = 0;
|
| +}
|
| +
|
| +/*
|
| +** Release a unixInodeInfo structure previously allocated by findInodeInfo().
|
| +**
|
| +** The mutex entered using the unixEnterMutex() function must be held
|
| +** when this function is called.
|
| +*/
|
| +static void releaseInodeInfo(unixFile *pFile){
|
| + unixInodeInfo *pInode = pFile->pInode;
|
| + assert( unixMutexHeld() );
|
| + if( ALWAYS(pInode) ){
|
| + pInode->nRef--;
|
| + if( pInode->nRef==0 ){
|
| + assert( pInode->pShmNode==0 );
|
| + closePendingFds(pFile);
|
| + if( pInode->pPrev ){
|
| + assert( pInode->pPrev->pNext==pInode );
|
| + pInode->pPrev->pNext = pInode->pNext;
|
| + }else{
|
| + assert( inodeList==pInode );
|
| + inodeList = pInode->pNext;
|
| + }
|
| + if( pInode->pNext ){
|
| + assert( pInode->pNext->pPrev==pInode );
|
| + pInode->pNext->pPrev = pInode->pPrev;
|
| + }
|
| + sqlite3_free(pInode);
|
| + }
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Given a file descriptor, locate the unixInodeInfo object that
|
| +** describes that file descriptor. Create a new one if necessary. The
|
| +** return value might be uninitialized if an error occurs.
|
| +**
|
| +** The mutex entered using the unixEnterMutex() function must be held
|
| +** when this function is called.
|
| +**
|
| +** Return an appropriate error code.
|
| +*/
|
| +static int findInodeInfo(
|
| + unixFile *pFile, /* Unix file with file desc used in the key */
|
| + unixInodeInfo **ppInode /* Return the unixInodeInfo object here */
|
| +){
|
| + int rc; /* System call return code */
|
| + int fd; /* The file descriptor for pFile */
|
| + struct unixFileId fileId; /* Lookup key for the unixInodeInfo */
|
| + struct stat statbuf; /* Low-level file information */
|
| + unixInodeInfo *pInode = 0; /* Candidate unixInodeInfo object */
|
| +
|
| + assert( unixMutexHeld() );
|
| +
|
| + /* Get low-level information about the file that we can used to
|
| + ** create a unique name for the file.
|
| + */
|
| + fd = pFile->h;
|
| + rc = osFstat(fd, &statbuf);
|
| + if( rc!=0 ){
|
| + storeLastErrno(pFile, errno);
|
| +#if defined(EOVERFLOW) && defined(SQLITE_DISABLE_LFS)
|
| + if( pFile->lastErrno==EOVERFLOW ) return SQLITE_NOLFS;
|
| +#endif
|
| + return SQLITE_IOERR;
|
| + }
|
| +
|
| +#ifdef __APPLE__
|
| + /* On OS X on an msdos filesystem, the inode number is reported
|
| + ** incorrectly for zero-size files. See ticket #3260. To work
|
| + ** around this problem (we consider it a bug in OS X, not SQLite)
|
| + ** we always increase the file size to 1 by writing a single byte
|
| + ** prior to accessing the inode number. The one byte written is
|
| + ** an ASCII 'S' character which also happens to be the first byte
|
| + ** in the header of every SQLite database. In this way, if there
|
| + ** is a race condition such that another thread has already populated
|
| + ** the first page of the database, no damage is done.
|
| + */
|
| + if( statbuf.st_size==0 && (pFile->fsFlags & SQLITE_FSFLAGS_IS_MSDOS)!=0 ){
|
| + do{ rc = osWrite(fd, "S", 1); }while( rc<0 && errno==EINTR );
|
| + if( rc!=1 ){
|
| + storeLastErrno(pFile, errno);
|
| + return SQLITE_IOERR;
|
| + }
|
| + rc = osFstat(fd, &statbuf);
|
| + if( rc!=0 ){
|
| + storeLastErrno(pFile, errno);
|
| + return SQLITE_IOERR;
|
| + }
|
| + }
|
| +#endif
|
| +
|
| + memset(&fileId, 0, sizeof(fileId));
|
| + fileId.dev = statbuf.st_dev;
|
| +#if OS_VXWORKS
|
| + fileId.pId = pFile->pId;
|
| +#else
|
| + fileId.ino = (u64)statbuf.st_ino;
|
| +#endif
|
| + pInode = inodeList;
|
| + while( pInode && memcmp(&fileId, &pInode->fileId, sizeof(fileId)) ){
|
| + pInode = pInode->pNext;
|
| + }
|
| + if( pInode==0 ){
|
| + pInode = sqlite3_malloc64( sizeof(*pInode) );
|
| + if( pInode==0 ){
|
| + return SQLITE_NOMEM_BKPT;
|
| + }
|
| + memset(pInode, 0, sizeof(*pInode));
|
| + memcpy(&pInode->fileId, &fileId, sizeof(fileId));
|
| + pInode->nRef = 1;
|
| + pInode->pNext = inodeList;
|
| + pInode->pPrev = 0;
|
| + if( inodeList ) inodeList->pPrev = pInode;
|
| + inodeList = pInode;
|
| + }else{
|
| + pInode->nRef++;
|
| + }
|
| + *ppInode = pInode;
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Return TRUE if pFile has been renamed or unlinked since it was first opened.
|
| +*/
|
| +static int fileHasMoved(unixFile *pFile){
|
| +#if OS_VXWORKS
|
| + return pFile->pInode!=0 && pFile->pId!=pFile->pInode->fileId.pId;
|
| +#else
|
| + struct stat buf;
|
| +
|
| + /* TODO(shess): This check doesn't work when the Chromium's WebDB code is
|
| + ** running in the sandbox.
|
| + */
|
| + return 0;
|
| +
|
| + return pFile->pInode!=0 &&
|
| + (osStat(pFile->zPath, &buf)!=0
|
| + || (u64)buf.st_ino!=pFile->pInode->fileId.ino);
|
| +#endif
|
| +}
|
| +
|
| +
|
| +/*
|
| +** Check a unixFile that is a database. Verify the following:
|
| +**
|
| +** (1) There is exactly one hard link on the file
|
| +** (2) The file is not a symbolic link
|
| +** (3) The file has not been renamed or unlinked
|
| +**
|
| +** Issue sqlite3_log(SQLITE_WARNING,...) messages if anything is not right.
|
| +*/
|
| +static void verifyDbFile(unixFile *pFile){
|
| + struct stat buf;
|
| + int rc;
|
| +
|
| + /* These verifications occurs for the main database only */
|
| + if( pFile->ctrlFlags & UNIXFILE_NOLOCK ) return;
|
| +
|
| + rc = osFstat(pFile->h, &buf);
|
| + if( rc!=0 ){
|
| + sqlite3_log(SQLITE_WARNING, "cannot fstat db file %s", pFile->zPath);
|
| + return;
|
| + }
|
| + if( buf.st_nlink==0 ){
|
| + sqlite3_log(SQLITE_WARNING, "file unlinked while open: %s", pFile->zPath);
|
| + return;
|
| + }
|
| + if( buf.st_nlink>1 ){
|
| + sqlite3_log(SQLITE_WARNING, "multiple links to file: %s", pFile->zPath);
|
| + return;
|
| + }
|
| + if( fileHasMoved(pFile) ){
|
| + sqlite3_log(SQLITE_WARNING, "file renamed while open: %s", pFile->zPath);
|
| + return;
|
| + }
|
| +}
|
| +
|
| +
|
| +/*
|
| +** 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 unixCheckReservedLock(sqlite3_file *id, int *pResOut){
|
| + int rc = SQLITE_OK;
|
| + int reserved = 0;
|
| + unixFile *pFile = (unixFile*)id;
|
| +
|
| + SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
|
| +
|
| + assert( pFile );
|
| + assert( pFile->eFileLock<=SHARED_LOCK );
|
| + unixEnterMutex(); /* Because pFile->pInode is shared across threads */
|
| +
|
| + /* Check if a thread in this process holds such a lock */
|
| + if( pFile->pInode->eFileLock>SHARED_LOCK ){
|
| + reserved = 1;
|
| + }
|
| +
|
| + /* Otherwise see if some other process holds it.
|
| + */
|
| +#ifndef __DJGPP__
|
| + if( !reserved && !pFile->pInode->bProcessLock ){
|
| + struct flock lock;
|
| + lock.l_whence = SEEK_SET;
|
| + lock.l_start = RESERVED_BYTE;
|
| + lock.l_len = 1;
|
| + lock.l_type = F_WRLCK;
|
| + if( osFcntl(pFile->h, F_GETLK, &lock) ){
|
| + rc = SQLITE_IOERR_CHECKRESERVEDLOCK;
|
| + storeLastErrno(pFile, errno);
|
| + } else if( lock.l_type!=F_UNLCK ){
|
| + reserved = 1;
|
| + }
|
| + }
|
| +#endif
|
| +
|
| + unixLeaveMutex();
|
| + OSTRACE(("TEST WR-LOCK %d %d %d (unix)\n", pFile->h, rc, reserved));
|
| +
|
| + *pResOut = reserved;
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Attempt to set a system-lock on the file pFile. The lock is
|
| +** described by pLock.
|
| +**
|
| +** If the pFile was opened read/write from unix-excl, then the only lock
|
| +** ever obtained is an exclusive lock, and it is obtained exactly once
|
| +** the first time any lock is attempted. All subsequent system locking
|
| +** operations become no-ops. Locking operations still happen internally,
|
| +** in order to coordinate access between separate database connections
|
| +** within this process, but all of that is handled in memory and the
|
| +** operating system does not participate.
|
| +**
|
| +** This function is a pass-through to fcntl(F_SETLK) if pFile is using
|
| +** any VFS other than "unix-excl" or if pFile is opened on "unix-excl"
|
| +** and is read-only.
|
| +**
|
| +** Zero is returned if the call completes successfully, or -1 if a call
|
| +** to fcntl() fails. In this case, errno is set appropriately (by fcntl()).
|
| +*/
|
| +static int unixFileLock(unixFile *pFile, struct flock *pLock){
|
| + int rc;
|
| + unixInodeInfo *pInode = pFile->pInode;
|
| + assert( unixMutexHeld() );
|
| + assert( pInode!=0 );
|
| + if( (pFile->ctrlFlags & (UNIXFILE_EXCL|UNIXFILE_RDONLY))==UNIXFILE_EXCL ){
|
| + if( pInode->bProcessLock==0 ){
|
| + struct flock lock;
|
| + assert( pInode->nLock==0 );
|
| + lock.l_whence = SEEK_SET;
|
| + lock.l_start = SHARED_FIRST;
|
| + lock.l_len = SHARED_SIZE;
|
| + lock.l_type = F_WRLCK;
|
| + rc = osFcntl(pFile->h, F_SETLK, &lock);
|
| + if( rc<0 ) return rc;
|
| + pInode->bProcessLock = 1;
|
| + pInode->nLock++;
|
| + }else{
|
| + rc = 0;
|
| + }
|
| + }else{
|
| + rc = osFcntl(pFile->h, F_SETLK, pLock);
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** 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
|
| +**
|
| +** This routine will only increase a lock. Use the sqlite3OsUnlock()
|
| +** routine to lower a locking level.
|
| +*/
|
| +static int unixLock(sqlite3_file *id, int eFileLock){
|
| + /* The following describes the implementation of the various locks and
|
| + ** lock transitions in terms of the POSIX advisory shared and exclusive
|
| + ** lock primitives (called read-locks and write-locks below, to avoid
|
| + ** confusion with SQLite lock names). The algorithms are complicated
|
| + ** slightly in order to be compatible with Windows95 systems simultaneously
|
| + ** accessing the same database file, in case that is ever required.
|
| + **
|
| + ** Symbols defined in os.h indentify the 'pending byte' and the 'reserved
|
| + ** byte', each single bytes at well known offsets, and the 'shared byte
|
| + ** range', a range of 510 bytes at a well known offset.
|
| + **
|
| + ** To obtain a SHARED lock, a read-lock is obtained on the 'pending
|
| + ** byte'. If this is successful, 'shared byte range' is read-locked
|
| + ** and the lock on the 'pending byte' released. (Legacy note: When
|
| + ** SQLite was first developed, Windows95 systems were still very common,
|
| + ** and Widnows95 lacks a shared-lock capability. So on Windows95, a
|
| + ** single randomly selected by from the 'shared byte range' is locked.
|
| + ** Windows95 is now pretty much extinct, but this work-around for the
|
| + ** lack of shared-locks on Windows95 lives on, for backwards
|
| + ** compatibility.)
|
| + **
|
| + ** A process may only obtain a RESERVED lock after it has a SHARED lock.
|
| + ** A RESERVED lock is implemented by grabbing a write-lock on the
|
| + ** 'reserved byte'.
|
| + **
|
| + ** A process may only obtain a PENDING lock after it has obtained a
|
| + ** SHARED lock. A PENDING lock is implemented by obtaining a write-lock
|
| + ** on the 'pending byte'. This ensures that no new SHARED locks can be
|
| + ** obtained, but existing SHARED locks are allowed to persist. A process
|
| + ** does not have to obtain a RESERVED lock on the way to a PENDING lock.
|
| + ** This property is used by the algorithm for rolling back a journal file
|
| + ** after a crash.
|
| + **
|
| + ** An EXCLUSIVE lock, obtained after a PENDING lock is held, is
|
| + ** implemented by obtaining a write-lock on the entire 'shared byte
|
| + ** range'. Since all other locks require a read-lock on one of the bytes
|
| + ** within this range, this ensures that no other locks are held on the
|
| + ** database.
|
| + */
|
| + int rc = SQLITE_OK;
|
| + unixFile *pFile = (unixFile*)id;
|
| + unixInodeInfo *pInode;
|
| + struct flock lock;
|
| + int tErrno = 0;
|
| +
|
| + assert( pFile );
|
| + OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (unix)\n", pFile->h,
|
| + azFileLock(eFileLock), azFileLock(pFile->eFileLock),
|
| + azFileLock(pFile->pInode->eFileLock), pFile->pInode->nShared,
|
| + osGetpid(0)));
|
| +
|
| + /* If there is already a lock of this type or more restrictive on the
|
| + ** unixFile, do nothing. Don't use the end_lock: exit path, as
|
| + ** unixEnterMutex() hasn't been called yet.
|
| + */
|
| + if( pFile->eFileLock>=eFileLock ){
|
| + OSTRACE(("LOCK %d %s ok (already held) (unix)\n", pFile->h,
|
| + azFileLock(eFileLock)));
|
| + return SQLITE_OK;
|
| + }
|
| +
|
| + /* Make sure the locking sequence is correct.
|
| + ** (1) We never move from unlocked to anything higher than shared lock.
|
| + ** (2) SQLite never explicitly requests a pendig lock.
|
| + ** (3) A shared lock is always held when a reserve lock is requested.
|
| + */
|
| + assert( pFile->eFileLock!=NO_LOCK || eFileLock==SHARED_LOCK );
|
| + assert( eFileLock!=PENDING_LOCK );
|
| + assert( eFileLock!=RESERVED_LOCK || pFile->eFileLock==SHARED_LOCK );
|
| +
|
| + /* This mutex is needed because pFile->pInode is shared across threads
|
| + */
|
| + unixEnterMutex();
|
| + pInode = pFile->pInode;
|
| +
|
| + /* If some thread using this PID has a lock via a different unixFile*
|
| + ** handle that precludes the requested lock, return BUSY.
|
| + */
|
| + if( (pFile->eFileLock!=pInode->eFileLock &&
|
| + (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK))
|
| + ){
|
| + rc = SQLITE_BUSY;
|
| + goto end_lock;
|
| + }
|
| +
|
| + /* If a SHARED lock is requested, and some thread using this PID already
|
| + ** has a SHARED or RESERVED lock, then increment reference counts and
|
| + ** return SQLITE_OK.
|
| + */
|
| + if( eFileLock==SHARED_LOCK &&
|
| + (pInode->eFileLock==SHARED_LOCK || pInode->eFileLock==RESERVED_LOCK) ){
|
| + assert( eFileLock==SHARED_LOCK );
|
| + assert( pFile->eFileLock==0 );
|
| + assert( pInode->nShared>0 );
|
| + pFile->eFileLock = SHARED_LOCK;
|
| + pInode->nShared++;
|
| + pInode->nLock++;
|
| + goto end_lock;
|
| + }
|
| +
|
| +
|
| + /* A PENDING lock is needed before acquiring a SHARED lock and before
|
| + ** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will
|
| + ** be released.
|
| + */
|
| + lock.l_len = 1L;
|
| + lock.l_whence = SEEK_SET;
|
| + if( eFileLock==SHARED_LOCK
|
| + || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLock<PENDING_LOCK)
|
| + ){
|
| + lock.l_type = (eFileLock==SHARED_LOCK?F_RDLCK:F_WRLCK);
|
| + lock.l_start = PENDING_BYTE;
|
| + if( unixFileLock(pFile, &lock) ){
|
| + tErrno = errno;
|
| + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
|
| + if( rc!=SQLITE_BUSY ){
|
| + storeLastErrno(pFile, tErrno);
|
| + }
|
| + goto end_lock;
|
| + }
|
| + }
|
| +
|
| +
|
| + /* If control gets to this point, then actually go ahead and make
|
| + ** operating system calls for the specified lock.
|
| + */
|
| + if( eFileLock==SHARED_LOCK ){
|
| + assert( pInode->nShared==0 );
|
| + assert( pInode->eFileLock==0 );
|
| + assert( rc==SQLITE_OK );
|
| +
|
| + /* Now get the read-lock */
|
| + lock.l_start = SHARED_FIRST;
|
| + lock.l_len = SHARED_SIZE;
|
| + if( unixFileLock(pFile, &lock) ){
|
| + tErrno = errno;
|
| + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
|
| + }
|
| +
|
| + /* Drop the temporary PENDING lock */
|
| + lock.l_start = PENDING_BYTE;
|
| + lock.l_len = 1L;
|
| + lock.l_type = F_UNLCK;
|
| + if( unixFileLock(pFile, &lock) && rc==SQLITE_OK ){
|
| + /* This could happen with a network mount */
|
| + tErrno = errno;
|
| + rc = SQLITE_IOERR_UNLOCK;
|
| + }
|
| +
|
| + if( rc ){
|
| + if( rc!=SQLITE_BUSY ){
|
| + storeLastErrno(pFile, tErrno);
|
| + }
|
| + goto end_lock;
|
| + }else{
|
| + pFile->eFileLock = SHARED_LOCK;
|
| + pInode->nLock++;
|
| + pInode->nShared = 1;
|
| + }
|
| + }else if( eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1 ){
|
| + /* We are trying for an exclusive lock but another thread in this
|
| + ** same process is still holding a shared lock. */
|
| + rc = SQLITE_BUSY;
|
| + }else{
|
| + /* The request was for a RESERVED or EXCLUSIVE lock. It is
|
| + ** assumed that there is a SHARED or greater lock on the file
|
| + ** already.
|
| + */
|
| + assert( 0!=pFile->eFileLock );
|
| + lock.l_type = F_WRLCK;
|
| +
|
| + assert( eFileLock==RESERVED_LOCK || eFileLock==EXCLUSIVE_LOCK );
|
| + if( eFileLock==RESERVED_LOCK ){
|
| + lock.l_start = RESERVED_BYTE;
|
| + lock.l_len = 1L;
|
| + }else{
|
| + lock.l_start = SHARED_FIRST;
|
| + lock.l_len = SHARED_SIZE;
|
| + }
|
| +
|
| + if( unixFileLock(pFile, &lock) ){
|
| + tErrno = errno;
|
| + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
|
| + if( rc!=SQLITE_BUSY ){
|
| + storeLastErrno(pFile, tErrno);
|
| + }
|
| + }
|
| + }
|
| +
|
| +
|
| +#ifdef SQLITE_DEBUG
|
| + /* Set up the transaction-counter change checking flags when
|
| + ** transitioning from a SHARED to a RESERVED lock. The change
|
| + ** from SHARED to RESERVED marks the beginning of a normal
|
| + ** write operation (not a hot journal rollback).
|
| + */
|
| + if( rc==SQLITE_OK
|
| + && pFile->eFileLock<=SHARED_LOCK
|
| + && eFileLock==RESERVED_LOCK
|
| + ){
|
| + pFile->transCntrChng = 0;
|
| + pFile->dbUpdate = 0;
|
| + pFile->inNormalWrite = 1;
|
| + }
|
| +#endif
|
| +
|
| +
|
| + if( rc==SQLITE_OK ){
|
| + pFile->eFileLock = eFileLock;
|
| + pInode->eFileLock = eFileLock;
|
| + }else if( eFileLock==EXCLUSIVE_LOCK ){
|
| + pFile->eFileLock = PENDING_LOCK;
|
| + pInode->eFileLock = PENDING_LOCK;
|
| + }
|
| +
|
| +end_lock:
|
| + unixLeaveMutex();
|
| + OSTRACE(("LOCK %d %s %s (unix)\n", pFile->h, azFileLock(eFileLock),
|
| + rc==SQLITE_OK ? "ok" : "failed"));
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Add the file descriptor used by file handle pFile to the corresponding
|
| +** pUnused list.
|
| +*/
|
| +static void setPendingFd(unixFile *pFile){
|
| + unixInodeInfo *pInode = pFile->pInode;
|
| + UnixUnusedFd *p = pFile->pUnused;
|
| + p->pNext = pInode->pUnused;
|
| + pInode->pUnused = p;
|
| + pFile->h = -1;
|
| + pFile->pUnused = 0;
|
| +}
|
| +
|
| +/*
|
| +** 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.
|
| +**
|
| +** If handleNFSUnlock is true, then on downgrading an EXCLUSIVE_LOCK to SHARED
|
| +** the byte range is divided into 2 parts and the first part is unlocked then
|
| +** set to a read lock, then the other part is simply unlocked. This works
|
| +** around a bug in BSD NFS lockd (also seen on MacOSX 10.3+) that fails to
|
| +** remove the write lock on a region when a read lock is set.
|
| +*/
|
| +static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
|
| + unixFile *pFile = (unixFile*)id;
|
| + unixInodeInfo *pInode;
|
| + struct flock lock;
|
| + int rc = SQLITE_OK;
|
| +
|
| + assert( pFile );
|
| + OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (unix)\n", pFile->h, eFileLock,
|
| + pFile->eFileLock, pFile->pInode->eFileLock, pFile->pInode->nShared,
|
| + osGetpid(0)));
|
| +
|
| + assert( eFileLock<=SHARED_LOCK );
|
| + if( pFile->eFileLock<=eFileLock ){
|
| + return SQLITE_OK;
|
| + }
|
| + unixEnterMutex();
|
| + pInode = pFile->pInode;
|
| + assert( pInode->nShared!=0 );
|
| + if( pFile->eFileLock>SHARED_LOCK ){
|
| + assert( pInode->eFileLock==pFile->eFileLock );
|
| +
|
| +#ifdef SQLITE_DEBUG
|
| + /* When reducing a lock such that other processes can start
|
| + ** reading the database file again, make sure that the
|
| + ** transaction counter was updated if any part of the database
|
| + ** file changed. If the transaction counter is not updated,
|
| + ** other connections to the same file might not realize that
|
| + ** the file has changed and hence might not know to flush their
|
| + ** cache. The use of a stale cache can lead to database corruption.
|
| + */
|
| + pFile->inNormalWrite = 0;
|
| +#endif
|
| +
|
| + /* downgrading to a shared lock on NFS involves clearing the write lock
|
| + ** before establishing the readlock - to avoid a race condition we downgrade
|
| + ** the lock in 2 blocks, so that part of the range will be covered by a
|
| + ** write lock until the rest is covered by a read lock:
|
| + ** 1: [WWWWW]
|
| + ** 2: [....W]
|
| + ** 3: [RRRRW]
|
| + ** 4: [RRRR.]
|
| + */
|
| + if( eFileLock==SHARED_LOCK ){
|
| +#if !defined(__APPLE__) || !SQLITE_ENABLE_LOCKING_STYLE
|
| + (void)handleNFSUnlock;
|
| + assert( handleNFSUnlock==0 );
|
| +#endif
|
| +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
|
| + if( handleNFSUnlock ){
|
| + int tErrno; /* Error code from system call errors */
|
| + off_t divSize = SHARED_SIZE - 1;
|
| +
|
| + lock.l_type = F_UNLCK;
|
| + lock.l_whence = SEEK_SET;
|
| + lock.l_start = SHARED_FIRST;
|
| + lock.l_len = divSize;
|
| + if( unixFileLock(pFile, &lock)==(-1) ){
|
| + tErrno = errno;
|
| + rc = SQLITE_IOERR_UNLOCK;
|
| + storeLastErrno(pFile, tErrno);
|
| + goto end_unlock;
|
| + }
|
| + lock.l_type = F_RDLCK;
|
| + lock.l_whence = SEEK_SET;
|
| + lock.l_start = SHARED_FIRST;
|
| + lock.l_len = divSize;
|
| + if( unixFileLock(pFile, &lock)==(-1) ){
|
| + tErrno = errno;
|
| + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK);
|
| + if( IS_LOCK_ERROR(rc) ){
|
| + storeLastErrno(pFile, tErrno);
|
| + }
|
| + goto end_unlock;
|
| + }
|
| + lock.l_type = F_UNLCK;
|
| + lock.l_whence = SEEK_SET;
|
| + lock.l_start = SHARED_FIRST+divSize;
|
| + lock.l_len = SHARED_SIZE-divSize;
|
| + if( unixFileLock(pFile, &lock)==(-1) ){
|
| + tErrno = errno;
|
| + rc = SQLITE_IOERR_UNLOCK;
|
| + storeLastErrno(pFile, tErrno);
|
| + goto end_unlock;
|
| + }
|
| + }else
|
| +#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */
|
| + {
|
| + lock.l_type = F_RDLCK;
|
| + lock.l_whence = SEEK_SET;
|
| + lock.l_start = SHARED_FIRST;
|
| + lock.l_len = SHARED_SIZE;
|
| + if( unixFileLock(pFile, &lock) ){
|
| + /* In theory, the call to unixFileLock() cannot fail because another
|
| + ** process is holding an incompatible lock. If it does, this
|
| + ** indicates that the other process is not following the locking
|
| + ** protocol. If this happens, return SQLITE_IOERR_RDLOCK. Returning
|
| + ** SQLITE_BUSY would confuse the upper layer (in practice it causes
|
| + ** an assert to fail). */
|
| + rc = SQLITE_IOERR_RDLOCK;
|
| + storeLastErrno(pFile, errno);
|
| + goto end_unlock;
|
| + }
|
| + }
|
| + }
|
| + lock.l_type = F_UNLCK;
|
| + lock.l_whence = SEEK_SET;
|
| + lock.l_start = PENDING_BYTE;
|
| + lock.l_len = 2L; assert( PENDING_BYTE+1==RESERVED_BYTE );
|
| + if( unixFileLock(pFile, &lock)==0 ){
|
| + pInode->eFileLock = SHARED_LOCK;
|
| + }else{
|
| + rc = SQLITE_IOERR_UNLOCK;
|
| + storeLastErrno(pFile, errno);
|
| + goto end_unlock;
|
| + }
|
| + }
|
| + if( eFileLock==NO_LOCK ){
|
| + /* Decrement the shared lock counter. Release the lock using an
|
| + ** OS call only when all threads in this same process have released
|
| + ** the lock.
|
| + */
|
| + pInode->nShared--;
|
| + if( pInode->nShared==0 ){
|
| + lock.l_type = F_UNLCK;
|
| + lock.l_whence = SEEK_SET;
|
| + lock.l_start = lock.l_len = 0L;
|
| + if( unixFileLock(pFile, &lock)==0 ){
|
| + pInode->eFileLock = NO_LOCK;
|
| + }else{
|
| + rc = SQLITE_IOERR_UNLOCK;
|
| + storeLastErrno(pFile, errno);
|
| + pInode->eFileLock = NO_LOCK;
|
| + pFile->eFileLock = NO_LOCK;
|
| + }
|
| + }
|
| +
|
| + /* Decrement the count of locks against this same file. When the
|
| + ** count reaches zero, close any other file descriptors whose close
|
| + ** was deferred because of outstanding locks.
|
| + */
|
| + pInode->nLock--;
|
| + assert( pInode->nLock>=0 );
|
| + if( pInode->nLock==0 ){
|
| + closePendingFds(pFile);
|
| + }
|
| + }
|
| +
|
| +end_unlock:
|
| + unixLeaveMutex();
|
| + if( rc==SQLITE_OK ) pFile->eFileLock = eFileLock;
|
| + 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 unixUnlock(sqlite3_file *id, int eFileLock){
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| + assert( eFileLock==SHARED_LOCK || ((unixFile *)id)->nFetchOut==0 );
|
| +#endif
|
| + return posixUnlock(id, eFileLock, 0);
|
| +}
|
| +
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| +static int unixMapfile(unixFile *pFd, i64 nByte);
|
| +static void unixUnmapfile(unixFile *pFd);
|
| +#endif
|
| +
|
| +/*
|
| +** This function performs the parts of the "close file" operation
|
| +** common to all locking schemes. It closes the directory and file
|
| +** handles, if they are valid, and sets all fields of the unixFile
|
| +** structure to 0.
|
| +**
|
| +** It is *not* necessary to hold the mutex when this routine is called,
|
| +** even on VxWorks. A mutex will be acquired on VxWorks by the
|
| +** vxworksReleaseFileId() routine.
|
| +*/
|
| +static int closeUnixFile(sqlite3_file *id){
|
| + unixFile *pFile = (unixFile*)id;
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| + unixUnmapfile(pFile);
|
| +#endif
|
| + if( pFile->h>=0 ){
|
| + robust_close(pFile, pFile->h, __LINE__);
|
| + pFile->h = -1;
|
| + }
|
| +#if OS_VXWORKS
|
| + if( pFile->pId ){
|
| + if( pFile->ctrlFlags & UNIXFILE_DELETE ){
|
| + osUnlink(pFile->pId->zCanonicalName);
|
| + }
|
| + vxworksReleaseFileId(pFile->pId);
|
| + pFile->pId = 0;
|
| + }
|
| +#endif
|
| +#ifdef SQLITE_UNLINK_AFTER_CLOSE
|
| + if( pFile->ctrlFlags & UNIXFILE_DELETE ){
|
| + osUnlink(pFile->zPath);
|
| + sqlite3_free(*(char**)&pFile->zPath);
|
| + pFile->zPath = 0;
|
| + }
|
| +#endif
|
| + OSTRACE(("CLOSE %-3d\n", pFile->h));
|
| + OpenCounter(-1);
|
| + sqlite3_free(pFile->pUnused);
|
| + memset(pFile, 0, sizeof(unixFile));
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Close a file.
|
| +*/
|
| +static int unixClose(sqlite3_file *id){
|
| + int rc = SQLITE_OK;
|
| + unixFile *pFile = (unixFile *)id;
|
| + verifyDbFile(pFile);
|
| + unixUnlock(id, NO_LOCK);
|
| + unixEnterMutex();
|
| +
|
| + /* unixFile.pInode is always valid here. Otherwise, a different close
|
| + ** routine (e.g. nolockClose()) would be called instead.
|
| + */
|
| + assert( pFile->pInode->nLock>0 || pFile->pInode->bProcessLock==0 );
|
| + if( ALWAYS(pFile->pInode) && pFile->pInode->nLock ){
|
| + /* If there are outstanding locks, do not actually close the file just
|
| + ** yet because that would clear those locks. Instead, add the file
|
| + ** descriptor to pInode->pUnused list. It will be automatically closed
|
| + ** when the last lock is cleared.
|
| + */
|
| + setPendingFd(pFile);
|
| + }
|
| + releaseInodeInfo(pFile);
|
| + rc = closeUnixFile(id);
|
| + unixLeaveMutex();
|
| + return rc;
|
| +}
|
| +
|
| +/************** End of the posix advisory lock implementation *****************
|
| +******************************************************************************/
|
| +
|
| +/******************************************************************************
|
| +****************************** No-op Locking **********************************
|
| +**
|
| +** Of the various locking implementations available, this is by far the
|
| +** simplest: locking is ignored. No attempt is made to lock the database
|
| +** file for reading or writing.
|
| +**
|
| +** This locking mode is appropriate for use on read-only databases
|
| +** (ex: databases that are burned into CD-ROM, for example.) It can
|
| +** also be used if the application employs some external mechanism to
|
| +** prevent simultaneous access of the same database by two or more
|
| +** database connections. But there is a serious risk of database
|
| +** corruption if this locking mode is used in situations where multiple
|
| +** database connections are accessing the same database file at the same
|
| +** time and one or more of those connections are writing.
|
| +*/
|
| +
|
| +static int nolockCheckReservedLock(sqlite3_file *NotUsed, int *pResOut){
|
| + UNUSED_PARAMETER(NotUsed);
|
| + *pResOut = 0;
|
| + return SQLITE_OK;
|
| +}
|
| +static int nolockLock(sqlite3_file *NotUsed, int NotUsed2){
|
| + UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
| + return SQLITE_OK;
|
| +}
|
| +static int nolockUnlock(sqlite3_file *NotUsed, int NotUsed2){
|
| + UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Close the file.
|
| +*/
|
| +static int nolockClose(sqlite3_file *id) {
|
| + return closeUnixFile(id);
|
| +}
|
| +
|
| +/******************* End of the no-op lock implementation *********************
|
| +******************************************************************************/
|
| +
|
| +/******************************************************************************
|
| +************************* Begin dot-file Locking ******************************
|
| +**
|
| +** The dotfile locking implementation uses the existence of separate lock
|
| +** files (really a directory) to control access to the database. This works
|
| +** on just about every filesystem imaginable. But there are serious downsides:
|
| +**
|
| +** (1) There is zero concurrency. A single reader blocks all other
|
| +** connections from reading or writing the database.
|
| +**
|
| +** (2) An application crash or power loss can leave stale lock files
|
| +** sitting around that need to be cleared manually.
|
| +**
|
| +** Nevertheless, a dotlock is an appropriate locking mode for use if no
|
| +** other locking strategy is available.
|
| +**
|
| +** Dotfile locking works by creating a subdirectory in the same directory as
|
| +** the database and with the same name but with a ".lock" extension added.
|
| +** The existence of a lock directory implies an EXCLUSIVE lock. All other
|
| +** lock types (SHARED, RESERVED, PENDING) are mapped into EXCLUSIVE.
|
| +*/
|
| +
|
| +/*
|
| +** The file suffix added to the data base filename in order to create the
|
| +** lock directory.
|
| +*/
|
| +#define DOTLOCK_SUFFIX ".lock"
|
| +
|
| +/*
|
| +** 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.
|
| +**
|
| +** In dotfile locking, either a lock exists or it does not. So in this
|
| +** variation of CheckReservedLock(), *pResOut is set to true if any lock
|
| +** is held on the file and false if the file is unlocked.
|
| +*/
|
| +static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) {
|
| + int rc = SQLITE_OK;
|
| + int reserved = 0;
|
| + unixFile *pFile = (unixFile*)id;
|
| +
|
| + SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
|
| +
|
| + assert( pFile );
|
| + reserved = osAccess((const char*)pFile->lockingContext, 0)==0;
|
| + OSTRACE(("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, rc, reserved));
|
| + *pResOut = reserved;
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** 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
|
| +**
|
| +** This routine will only increase a lock. Use the sqlite3OsUnlock()
|
| +** routine to lower a locking level.
|
| +**
|
| +** With dotfile locking, we really only support state (4): EXCLUSIVE.
|
| +** But we track the other locking levels internally.
|
| +*/
|
| +static int dotlockLock(sqlite3_file *id, int eFileLock) {
|
| + unixFile *pFile = (unixFile*)id;
|
| + char *zLockFile = (char *)pFile->lockingContext;
|
| + int rc = SQLITE_OK;
|
| +
|
| +
|
| + /* If we have any lock, then the lock file already exists. All we have
|
| + ** to do is adjust our internal record of the lock level.
|
| + */
|
| + if( pFile->eFileLock > NO_LOCK ){
|
| + pFile->eFileLock = eFileLock;
|
| + /* Always update the timestamp on the old file */
|
| +#ifdef HAVE_UTIME
|
| + utime(zLockFile, NULL);
|
| +#else
|
| + utimes(zLockFile, NULL);
|
| +#endif
|
| + return SQLITE_OK;
|
| + }
|
| +
|
| + /* grab an exclusive lock */
|
| + rc = osMkdir(zLockFile, 0777);
|
| + if( rc<0 ){
|
| + /* failed to open/create the lock directory */
|
| + int tErrno = errno;
|
| + if( EEXIST == tErrno ){
|
| + rc = SQLITE_BUSY;
|
| + } else {
|
| + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
|
| + if( rc!=SQLITE_BUSY ){
|
| + storeLastErrno(pFile, tErrno);
|
| + }
|
| + }
|
| + return rc;
|
| + }
|
| +
|
| + /* got it, set the type and return ok */
|
| + pFile->eFileLock = eFileLock;
|
| + 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.
|
| +**
|
| +** When the locking level reaches NO_LOCK, delete the lock file.
|
| +*/
|
| +static int dotlockUnlock(sqlite3_file *id, int eFileLock) {
|
| + unixFile *pFile = (unixFile*)id;
|
| + char *zLockFile = (char *)pFile->lockingContext;
|
| + int rc;
|
| +
|
| + assert( pFile );
|
| + OSTRACE(("UNLOCK %d %d was %d pid=%d (dotlock)\n", pFile->h, eFileLock,
|
| + pFile->eFileLock, osGetpid(0)));
|
| + assert( eFileLock<=SHARED_LOCK );
|
| +
|
| + /* no-op if possible */
|
| + if( pFile->eFileLock==eFileLock ){
|
| + return SQLITE_OK;
|
| + }
|
| +
|
| + /* To downgrade to shared, simply update our internal notion of the
|
| + ** lock state. No need to mess with the file on disk.
|
| + */
|
| + if( eFileLock==SHARED_LOCK ){
|
| + pFile->eFileLock = SHARED_LOCK;
|
| + return SQLITE_OK;
|
| + }
|
| +
|
| + /* To fully unlock the database, delete the lock file */
|
| + assert( eFileLock==NO_LOCK );
|
| + rc = osRmdir(zLockFile);
|
| + if( rc<0 ){
|
| + int tErrno = errno;
|
| + if( tErrno==ENOENT ){
|
| + rc = SQLITE_OK;
|
| + }else{
|
| + rc = SQLITE_IOERR_UNLOCK;
|
| + storeLastErrno(pFile, tErrno);
|
| + }
|
| + return rc;
|
| + }
|
| + pFile->eFileLock = NO_LOCK;
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Close a file. Make sure the lock has been released before closing.
|
| +*/
|
| +static int dotlockClose(sqlite3_file *id) {
|
| + unixFile *pFile = (unixFile*)id;
|
| + assert( id!=0 );
|
| + dotlockUnlock(id, NO_LOCK);
|
| + sqlite3_free(pFile->lockingContext);
|
| + return closeUnixFile(id);
|
| +}
|
| +/****************** End of the dot-file lock implementation *******************
|
| +******************************************************************************/
|
| +
|
| +/******************************************************************************
|
| +************************** Begin flock Locking ********************************
|
| +**
|
| +** Use the flock() system call to do file locking.
|
| +**
|
| +** flock() locking is like dot-file locking in that the various
|
| +** fine-grain locking levels supported by SQLite are collapsed into
|
| +** a single exclusive lock. In other words, SHARED, RESERVED, and
|
| +** PENDING locks are the same thing as an EXCLUSIVE lock. SQLite
|
| +** still works when you do this, but concurrency is reduced since
|
| +** only a single process can be reading the database at a time.
|
| +**
|
| +** Omit this section if SQLITE_ENABLE_LOCKING_STYLE is turned off
|
| +*/
|
| +#if SQLITE_ENABLE_LOCKING_STYLE
|
| +
|
| +/*
|
| +** Retry flock() calls that fail with EINTR
|
| +*/
|
| +#ifdef EINTR
|
| +static int robust_flock(int fd, int op){
|
| + int rc;
|
| + do{ rc = flock(fd,op); }while( rc<0 && errno==EINTR );
|
| + return rc;
|
| +}
|
| +#else
|
| +# define robust_flock(a,b) flock(a,b)
|
| +#endif
|
| +
|
| +
|
| +/*
|
| +** 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 flockCheckReservedLock(sqlite3_file *id, int *pResOut){
|
| + int rc = SQLITE_OK;
|
| + int reserved = 0;
|
| + unixFile *pFile = (unixFile*)id;
|
| +
|
| + SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
|
| +
|
| + assert( pFile );
|
| +
|
| + /* Check if a thread in this process holds such a lock */
|
| + if( pFile->eFileLock>SHARED_LOCK ){
|
| + reserved = 1;
|
| + }
|
| +
|
| + /* Otherwise see if some other process holds it. */
|
| + if( !reserved ){
|
| + /* attempt to get the lock */
|
| + int lrc = robust_flock(pFile->h, LOCK_EX | LOCK_NB);
|
| + if( !lrc ){
|
| + /* got the lock, unlock it */
|
| + lrc = robust_flock(pFile->h, LOCK_UN);
|
| + if ( lrc ) {
|
| + int tErrno = errno;
|
| + /* unlock failed with an error */
|
| + lrc = SQLITE_IOERR_UNLOCK;
|
| + storeLastErrno(pFile, tErrno);
|
| + rc = lrc;
|
| + }
|
| + } else {
|
| + int tErrno = errno;
|
| + reserved = 1;
|
| + /* someone else might have it reserved */
|
| + lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
|
| + if( IS_LOCK_ERROR(lrc) ){
|
| + storeLastErrno(pFile, tErrno);
|
| + rc = lrc;
|
| + }
|
| + }
|
| + }
|
| + OSTRACE(("TEST WR-LOCK %d %d %d (flock)\n", pFile->h, rc, reserved));
|
| +
|
| +#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS
|
| + if( (rc & SQLITE_IOERR) == SQLITE_IOERR ){
|
| + rc = SQLITE_OK;
|
| + reserved=1;
|
| + }
|
| +#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */
|
| + *pResOut = reserved;
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** 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
|
| +**
|
| +** flock() 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 flockLock(sqlite3_file *id, int eFileLock) {
|
| + int rc = SQLITE_OK;
|
| + unixFile *pFile = (unixFile*)id;
|
| +
|
| + assert( pFile );
|
| +
|
| + /* if we already have a lock, it is exclusive.
|
| + ** Just adjust level and punt on outta here. */
|
| + if (pFile->eFileLock > NO_LOCK) {
|
| + pFile->eFileLock = eFileLock;
|
| + return SQLITE_OK;
|
| + }
|
| +
|
| + /* grab an exclusive lock */
|
| +
|
| + if (robust_flock(pFile->h, LOCK_EX | LOCK_NB)) {
|
| + int tErrno = errno;
|
| + /* didn't get, must be busy */
|
| + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
|
| + if( IS_LOCK_ERROR(rc) ){
|
| + storeLastErrno(pFile, tErrno);
|
| + }
|
| + } else {
|
| + /* got it, set the type and return ok */
|
| + pFile->eFileLock = eFileLock;
|
| + }
|
| + OSTRACE(("LOCK %d %s %s (flock)\n", pFile->h, azFileLock(eFileLock),
|
| + rc==SQLITE_OK ? "ok" : "failed"));
|
| +#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS
|
| + if( (rc & SQLITE_IOERR) == SQLITE_IOERR ){
|
| + rc = SQLITE_BUSY;
|
| + }
|
| +#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */
|
| + 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 flockUnlock(sqlite3_file *id, int eFileLock) {
|
| + unixFile *pFile = (unixFile*)id;
|
| +
|
| + assert( pFile );
|
| + OSTRACE(("UNLOCK %d %d was %d pid=%d (flock)\n", pFile->h, eFileLock,
|
| + pFile->eFileLock, osGetpid(0)));
|
| + assert( eFileLock<=SHARED_LOCK );
|
| +
|
| + /* no-op if possible */
|
| + if( pFile->eFileLock==eFileLock ){
|
| + return SQLITE_OK;
|
| + }
|
| +
|
| + /* shared can just be set because we always have an exclusive */
|
| + if (eFileLock==SHARED_LOCK) {
|
| + pFile->eFileLock = eFileLock;
|
| + return SQLITE_OK;
|
| + }
|
| +
|
| + /* no, really, unlock. */
|
| + if( robust_flock(pFile->h, LOCK_UN) ){
|
| +#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS
|
| + return SQLITE_OK;
|
| +#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */
|
| + return SQLITE_IOERR_UNLOCK;
|
| + }else{
|
| + pFile->eFileLock = NO_LOCK;
|
| + return SQLITE_OK;
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Close a file.
|
| +*/
|
| +static int flockClose(sqlite3_file *id) {
|
| + assert( id!=0 );
|
| + flockUnlock(id, NO_LOCK);
|
| + return closeUnixFile(id);
|
| +}
|
| +
|
| +#endif /* SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORK */
|
| +
|
| +/******************* End of the flock lock implementation *********************
|
| +******************************************************************************/
|
| +
|
| +/******************************************************************************
|
| +************************ Begin Named Semaphore Locking ************************
|
| +**
|
| +** Named semaphore locking is only supported on VxWorks.
|
| +**
|
| +** Semaphore locking is like dot-lock and flock in that it really only
|
| +** supports EXCLUSIVE locking. Only a single process can read or write
|
| +** the database file at a time. This reduces potential concurrency, but
|
| +** makes the lock implementation much easier.
|
| +*/
|
| +#if OS_VXWORKS
|
| +
|
| +/*
|
| +** 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 semXCheckReservedLock(sqlite3_file *id, int *pResOut) {
|
| + int rc = SQLITE_OK;
|
| + int reserved = 0;
|
| + unixFile *pFile = (unixFile*)id;
|
| +
|
| + SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
|
| +
|
| + assert( pFile );
|
| +
|
| + /* Check if a thread in this process holds such a lock */
|
| + if( pFile->eFileLock>SHARED_LOCK ){
|
| + reserved = 1;
|
| + }
|
| +
|
| + /* Otherwise see if some other process holds it. */
|
| + if( !reserved ){
|
| + sem_t *pSem = pFile->pInode->pSem;
|
| +
|
| + if( sem_trywait(pSem)==-1 ){
|
| + int tErrno = errno;
|
| + if( EAGAIN != tErrno ){
|
| + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK);
|
| + storeLastErrno(pFile, tErrno);
|
| + } else {
|
| + /* someone else has the lock when we are in NO_LOCK */
|
| + reserved = (pFile->eFileLock < SHARED_LOCK);
|
| + }
|
| + }else{
|
| + /* we could have it if we want it */
|
| + sem_post(pSem);
|
| + }
|
| + }
|
| + OSTRACE(("TEST WR-LOCK %d %d %d (sem)\n", pFile->h, rc, reserved));
|
| +
|
| + *pResOut = reserved;
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** 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 semXLock(sqlite3_file *id, int eFileLock) {
|
| + unixFile *pFile = (unixFile*)id;
|
| + sem_t *pSem = pFile->pInode->pSem;
|
| + int rc = SQLITE_OK;
|
| +
|
| + /* if we already have a lock, it is exclusive.
|
| + ** Just adjust level and punt on outta here. */
|
| + if (pFile->eFileLock > NO_LOCK) {
|
| + pFile->eFileLock = eFileLock;
|
| + rc = SQLITE_OK;
|
| + goto sem_end_lock;
|
| + }
|
| +
|
| + /* lock semaphore now but bail out when already locked. */
|
| + if( sem_trywait(pSem)==-1 ){
|
| + rc = SQLITE_BUSY;
|
| + goto sem_end_lock;
|
| + }
|
| +
|
| + /* got it, set the type and return ok */
|
| + pFile->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 semXUnlock(sqlite3_file *id, int eFileLock) {
|
| + unixFile *pFile = (unixFile*)id;
|
| + sem_t *pSem = pFile->pInode->pSem;
|
| +
|
| + assert( pFile );
|
| + assert( pSem );
|
| + OSTRACE(("UNLOCK %d %d was %d pid=%d (sem)\n", pFile->h, eFileLock,
|
| + pFile->eFileLock, osGetpid(0)));
|
| + assert( eFileLock<=SHARED_LOCK );
|
| +
|
| + /* no-op if possible */
|
| + if( pFile->eFileLock==eFileLock ){
|
| + return SQLITE_OK;
|
| + }
|
| +
|
| + /* shared can just be set because we always have an exclusive */
|
| + if (eFileLock==SHARED_LOCK) {
|
| + pFile->eFileLock = eFileLock;
|
| + return SQLITE_OK;
|
| + }
|
| +
|
| + /* no, really unlock. */
|
| + if ( sem_post(pSem)==-1 ) {
|
| + int rc, tErrno = errno;
|
| + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
|
| + if( IS_LOCK_ERROR(rc) ){
|
| + storeLastErrno(pFile, tErrno);
|
| + }
|
| + return rc;
|
| + }
|
| + pFile->eFileLock = NO_LOCK;
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| + ** Close a file.
|
| + */
|
| +static int semXClose(sqlite3_file *id) {
|
| + if( id ){
|
| + unixFile *pFile = (unixFile*)id;
|
| + semXUnlock(id, NO_LOCK);
|
| + assert( pFile );
|
| + unixEnterMutex();
|
| + releaseInodeInfo(pFile);
|
| + unixLeaveMutex();
|
| + closeUnixFile(id);
|
| + }
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +#endif /* OS_VXWORKS */
|
| +/*
|
| +** Named semaphore locking is only available on VxWorks.
|
| +**
|
| +*************** End of the named semaphore lock implementation ****************
|
| +******************************************************************************/
|
| +
|
| +
|
| +/******************************************************************************
|
| +*************************** Begin AFP Locking *********************************
|
| +**
|
| +** AFP is the Apple Filing Protocol. AFP is a network filesystem found
|
| +** on Apple Macintosh computers - both OS9 and OSX.
|
| +**
|
| +** Third-party implementations of AFP are available. But this code here
|
| +** only works on OSX.
|
| +*/
|
| +
|
| +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
|
| +/*
|
| +** The afpLockingContext structure contains all afp lock specific state
|
| +*/
|
| +typedef struct afpLockingContext afpLockingContext;
|
| +struct afpLockingContext {
|
| + int reserved;
|
| + const char *dbPath; /* Name of the open file */
|
| +};
|
| +
|
| +struct ByteRangeLockPB2
|
| +{
|
| + unsigned long long offset; /* offset to first byte to lock */
|
| + unsigned long long length; /* nbr of bytes to lock */
|
| + unsigned long long retRangeStart; /* nbr of 1st byte locked if successful */
|
| + unsigned char unLockFlag; /* 1 = unlock, 0 = lock */
|
| + unsigned char startEndFlag; /* 1=rel to end of fork, 0=rel to start */
|
| + int fd; /* file desc to assoc this lock with */
|
| +};
|
| +
|
| +#define afpfsByteRangeLock2FSCTL _IOWR('z', 23, struct ByteRangeLockPB2)
|
| +
|
| +/*
|
| +** This is a utility for setting or clearing a bit-range lock on an
|
| +** AFP filesystem.
|
| +**
|
| +** Return SQLITE_OK on success, SQLITE_BUSY on failure.
|
| +*/
|
| +static int afpSetLock(
|
| + const char *path, /* Name of the file to be locked or unlocked */
|
| + unixFile *pFile, /* Open file descriptor on path */
|
| + unsigned long long offset, /* First byte to be locked */
|
| + unsigned long long length, /* Number of bytes to lock */
|
| + int setLockFlag /* True to set lock. False to clear lock */
|
| +){
|
| + struct ByteRangeLockPB2 pb;
|
| + int err;
|
| +
|
| + pb.unLockFlag = setLockFlag ? 0 : 1;
|
| + pb.startEndFlag = 0;
|
| + pb.offset = offset;
|
| + pb.length = length;
|
| + pb.fd = pFile->h;
|
| +
|
| + OSTRACE(("AFPSETLOCK [%s] for %d%s in range %llx:%llx\n",
|
| + (setLockFlag?"ON":"OFF"), pFile->h, (pb.fd==-1?"[testval-1]":""),
|
| + offset, length));
|
| + err = fsctl(path, afpfsByteRangeLock2FSCTL, &pb, 0);
|
| + if ( err==-1 ) {
|
| + int rc;
|
| + int tErrno = errno;
|
| + OSTRACE(("AFPSETLOCK failed to fsctl() '%s' %d %s\n",
|
| + path, tErrno, strerror(tErrno)));
|
| +#ifdef SQLITE_IGNORE_AFP_LOCK_ERRORS
|
| + rc = SQLITE_BUSY;
|
| +#else
|
| + rc = sqliteErrorFromPosixError(tErrno,
|
| + setLockFlag ? SQLITE_IOERR_LOCK : SQLITE_IOERR_UNLOCK);
|
| +#endif /* SQLITE_IGNORE_AFP_LOCK_ERRORS */
|
| + if( IS_LOCK_ERROR(rc) ){
|
| + storeLastErrno(pFile, tErrno);
|
| + }
|
| + return rc;
|
| + } else {
|
| + 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 afpCheckReservedLock(sqlite3_file *id, int *pResOut){
|
| + int rc = SQLITE_OK;
|
| + int reserved = 0;
|
| + unixFile *pFile = (unixFile*)id;
|
| + afpLockingContext *context;
|
| +
|
| + SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
|
| +
|
| + assert( pFile );
|
| + context = (afpLockingContext *) pFile->lockingContext;
|
| + if( context->reserved ){
|
| + *pResOut = 1;
|
| + return SQLITE_OK;
|
| + }
|
| + unixEnterMutex(); /* Because pFile->pInode is shared across threads */
|
| +
|
| + /* Check if a thread in this process holds such a lock */
|
| + if( pFile->pInode->eFileLock>SHARED_LOCK ){
|
| + reserved = 1;
|
| + }
|
| +
|
| + /* Otherwise see if some other process holds it.
|
| + */
|
| + if( !reserved ){
|
| + /* lock the RESERVED byte */
|
| + int lrc = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1,1);
|
| + if( SQLITE_OK==lrc ){
|
| + /* if we succeeded in taking the reserved lock, unlock it to restore
|
| + ** the original state */
|
| + lrc = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1, 0);
|
| + } else {
|
| + /* if we failed to get the lock then someone else must have it */
|
| + reserved = 1;
|
| + }
|
| + if( IS_LOCK_ERROR(lrc) ){
|
| + rc=lrc;
|
| + }
|
| + }
|
| +
|
| + unixLeaveMutex();
|
| + OSTRACE(("TEST WR-LOCK %d %d %d (afp)\n", pFile->h, rc, reserved));
|
| +
|
| + *pResOut = reserved;
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** 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
|
| +**
|
| +** This routine will only increase a lock. Use the sqlite3OsUnlock()
|
| +** routine to lower a locking level.
|
| +*/
|
| +static int afpLock(sqlite3_file *id, int eFileLock){
|
| + int rc = SQLITE_OK;
|
| + unixFile *pFile = (unixFile*)id;
|
| + unixInodeInfo *pInode = pFile->pInode;
|
| + afpLockingContext *context = (afpLockingContext *) pFile->lockingContext;
|
| +
|
| + assert( pFile );
|
| + OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (afp)\n", pFile->h,
|
| + azFileLock(eFileLock), azFileLock(pFile->eFileLock),
|
| + azFileLock(pInode->eFileLock), pInode->nShared , osGetpid(0)));
|
| +
|
| + /* If there is already a lock of this type or more restrictive on the
|
| + ** unixFile, do nothing. Don't use the afp_end_lock: exit path, as
|
| + ** unixEnterMutex() hasn't been called yet.
|
| + */
|
| + if( pFile->eFileLock>=eFileLock ){
|
| + OSTRACE(("LOCK %d %s ok (already held) (afp)\n", pFile->h,
|
| + azFileLock(eFileLock)));
|
| + return SQLITE_OK;
|
| + }
|
| +
|
| + /* Make sure the locking sequence is correct
|
| + ** (1) We never move from unlocked to anything higher than shared lock.
|
| + ** (2) SQLite never explicitly requests a pendig lock.
|
| + ** (3) A shared lock is always held when a reserve lock is requested.
|
| + */
|
| + assert( pFile->eFileLock!=NO_LOCK || eFileLock==SHARED_LOCK );
|
| + assert( eFileLock!=PENDING_LOCK );
|
| + assert( eFileLock!=RESERVED_LOCK || pFile->eFileLock==SHARED_LOCK );
|
| +
|
| + /* This mutex is needed because pFile->pInode is shared across threads
|
| + */
|
| + unixEnterMutex();
|
| + pInode = pFile->pInode;
|
| +
|
| + /* If some thread using this PID has a lock via a different unixFile*
|
| + ** handle that precludes the requested lock, return BUSY.
|
| + */
|
| + if( (pFile->eFileLock!=pInode->eFileLock &&
|
| + (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK))
|
| + ){
|
| + rc = SQLITE_BUSY;
|
| + goto afp_end_lock;
|
| + }
|
| +
|
| + /* If a SHARED lock is requested, and some thread using this PID already
|
| + ** has a SHARED or RESERVED lock, then increment reference counts and
|
| + ** return SQLITE_OK.
|
| + */
|
| + if( eFileLock==SHARED_LOCK &&
|
| + (pInode->eFileLock==SHARED_LOCK || pInode->eFileLock==RESERVED_LOCK) ){
|
| + assert( eFileLock==SHARED_LOCK );
|
| + assert( pFile->eFileLock==0 );
|
| + assert( pInode->nShared>0 );
|
| + pFile->eFileLock = SHARED_LOCK;
|
| + pInode->nShared++;
|
| + pInode->nLock++;
|
| + goto afp_end_lock;
|
| + }
|
| +
|
| + /* A PENDING lock is needed before acquiring a SHARED lock and before
|
| + ** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will
|
| + ** be released.
|
| + */
|
| + if( eFileLock==SHARED_LOCK
|
| + || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLock<PENDING_LOCK)
|
| + ){
|
| + int failed;
|
| + failed = afpSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 1);
|
| + if (failed) {
|
| + rc = failed;
|
| + goto afp_end_lock;
|
| + }
|
| + }
|
| +
|
| + /* If control gets to this point, then actually go ahead and make
|
| + ** operating system calls for the specified lock.
|
| + */
|
| + if( eFileLock==SHARED_LOCK ){
|
| + int lrc1, lrc2, lrc1Errno = 0;
|
| + long lk, mask;
|
| +
|
| + assert( pInode->nShared==0 );
|
| + assert( pInode->eFileLock==0 );
|
| +
|
| + mask = (sizeof(long)==8) ? LARGEST_INT64 : 0x7fffffff;
|
| + /* Now get the read-lock SHARED_LOCK */
|
| + /* note that the quality of the randomness doesn't matter that much */
|
| + lk = random();
|
| + pInode->sharedByte = (lk & mask)%(SHARED_SIZE - 1);
|
| + lrc1 = afpSetLock(context->dbPath, pFile,
|
| + SHARED_FIRST+pInode->sharedByte, 1, 1);
|
| + if( IS_LOCK_ERROR(lrc1) ){
|
| + lrc1Errno = pFile->lastErrno;
|
| + }
|
| + /* Drop the temporary PENDING lock */
|
| + lrc2 = afpSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 0);
|
| +
|
| + if( IS_LOCK_ERROR(lrc1) ) {
|
| + storeLastErrno(pFile, lrc1Errno);
|
| + rc = lrc1;
|
| + goto afp_end_lock;
|
| + } else if( IS_LOCK_ERROR(lrc2) ){
|
| + rc = lrc2;
|
| + goto afp_end_lock;
|
| + } else if( lrc1 != SQLITE_OK ) {
|
| + rc = lrc1;
|
| + } else {
|
| + pFile->eFileLock = SHARED_LOCK;
|
| + pInode->nLock++;
|
| + pInode->nShared = 1;
|
| + }
|
| + }else if( eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1 ){
|
| + /* We are trying for an exclusive lock but another thread in this
|
| + ** same process is still holding a shared lock. */
|
| + rc = SQLITE_BUSY;
|
| + }else{
|
| + /* The request was for a RESERVED or EXCLUSIVE lock. It is
|
| + ** assumed that there is a SHARED or greater lock on the file
|
| + ** already.
|
| + */
|
| + int failed = 0;
|
| + assert( 0!=pFile->eFileLock );
|
| + if (eFileLock >= RESERVED_LOCK && pFile->eFileLock < RESERVED_LOCK) {
|
| + /* Acquire a RESERVED lock */
|
| + failed = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1,1);
|
| + if( !failed ){
|
| + context->reserved = 1;
|
| + }
|
| + }
|
| + if (!failed && eFileLock == EXCLUSIVE_LOCK) {
|
| + /* Acquire an EXCLUSIVE lock */
|
| +
|
| + /* Remove the shared lock before trying the range. we'll need to
|
| + ** reestablish the shared lock if we can't get the afpUnlock
|
| + */
|
| + if( !(failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST +
|
| + pInode->sharedByte, 1, 0)) ){
|
| + int failed2 = SQLITE_OK;
|
| + /* now attemmpt to get the exclusive lock range */
|
| + failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST,
|
| + SHARED_SIZE, 1);
|
| + if( failed && (failed2 = afpSetLock(context->dbPath, pFile,
|
| + SHARED_FIRST + pInode->sharedByte, 1, 1)) ){
|
| + /* Can't reestablish the shared lock. Sqlite can't deal, this is
|
| + ** a critical I/O error
|
| + */
|
| + rc = ((failed & SQLITE_IOERR) == SQLITE_IOERR) ? failed2 :
|
| + SQLITE_IOERR_LOCK;
|
| + goto afp_end_lock;
|
| + }
|
| + }else{
|
| + rc = failed;
|
| + }
|
| + }
|
| + if( failed ){
|
| + rc = failed;
|
| + }
|
| + }
|
| +
|
| + if( rc==SQLITE_OK ){
|
| + pFile->eFileLock = eFileLock;
|
| + pInode->eFileLock = eFileLock;
|
| + }else if( eFileLock==EXCLUSIVE_LOCK ){
|
| + pFile->eFileLock = PENDING_LOCK;
|
| + pInode->eFileLock = PENDING_LOCK;
|
| + }
|
| +
|
| +afp_end_lock:
|
| + unixLeaveMutex();
|
| + OSTRACE(("LOCK %d %s %s (afp)\n", pFile->h, azFileLock(eFileLock),
|
| + rc==SQLITE_OK ? "ok" : "failed"));
|
| + 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 afpUnlock(sqlite3_file *id, int eFileLock) {
|
| + int rc = SQLITE_OK;
|
| + unixFile *pFile = (unixFile*)id;
|
| + unixInodeInfo *pInode;
|
| + afpLockingContext *context = (afpLockingContext *) pFile->lockingContext;
|
| + int skipShared = 0;
|
| +#ifdef SQLITE_TEST
|
| + int h = pFile->h;
|
| +#endif
|
| +
|
| + assert( pFile );
|
| + OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (afp)\n", pFile->h, eFileLock,
|
| + pFile->eFileLock, pFile->pInode->eFileLock, pFile->pInode->nShared,
|
| + osGetpid(0)));
|
| +
|
| + assert( eFileLock<=SHARED_LOCK );
|
| + if( pFile->eFileLock<=eFileLock ){
|
| + return SQLITE_OK;
|
| + }
|
| + unixEnterMutex();
|
| + pInode = pFile->pInode;
|
| + assert( pInode->nShared!=0 );
|
| + if( pFile->eFileLock>SHARED_LOCK ){
|
| + assert( pInode->eFileLock==pFile->eFileLock );
|
| + SimulateIOErrorBenign(1);
|
| + SimulateIOError( h=(-1) )
|
| + SimulateIOErrorBenign(0);
|
| +
|
| +#ifdef SQLITE_DEBUG
|
| + /* When reducing a lock such that other processes can start
|
| + ** reading the database file again, make sure that the
|
| + ** transaction counter was updated if any part of the database
|
| + ** file changed. If the transaction counter is not updated,
|
| + ** other connections to the same file might not realize that
|
| + ** the file has changed and hence might not know to flush their
|
| + ** cache. The use of a stale cache can lead to database corruption.
|
| + */
|
| + assert( pFile->inNormalWrite==0
|
| + || pFile->dbUpdate==0
|
| + || pFile->transCntrChng==1 );
|
| + pFile->inNormalWrite = 0;
|
| +#endif
|
| +
|
| + if( pFile->eFileLock==EXCLUSIVE_LOCK ){
|
| + rc = afpSetLock(context->dbPath, pFile, SHARED_FIRST, SHARED_SIZE, 0);
|
| + if( rc==SQLITE_OK && (eFileLock==SHARED_LOCK || pInode->nShared>1) ){
|
| + /* only re-establish the shared lock if necessary */
|
| + int sharedLockByte = SHARED_FIRST+pInode->sharedByte;
|
| + rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 1);
|
| + } else {
|
| + skipShared = 1;
|
| + }
|
| + }
|
| + if( rc==SQLITE_OK && pFile->eFileLock>=PENDING_LOCK ){
|
| + rc = afpSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 0);
|
| + }
|
| + if( rc==SQLITE_OK && pFile->eFileLock>=RESERVED_LOCK && context->reserved ){
|
| + rc = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1, 0);
|
| + if( !rc ){
|
| + context->reserved = 0;
|
| + }
|
| + }
|
| + if( rc==SQLITE_OK && (eFileLock==SHARED_LOCK || pInode->nShared>1)){
|
| + pInode->eFileLock = SHARED_LOCK;
|
| + }
|
| + }
|
| + if( rc==SQLITE_OK && eFileLock==NO_LOCK ){
|
| +
|
| + /* Decrement the shared lock counter. Release the lock using an
|
| + ** OS call only when all threads in this same process have released
|
| + ** the lock.
|
| + */
|
| + unsigned long long sharedLockByte = SHARED_FIRST+pInode->sharedByte;
|
| + pInode->nShared--;
|
| + if( pInode->nShared==0 ){
|
| + SimulateIOErrorBenign(1);
|
| + SimulateIOError( h=(-1) )
|
| + SimulateIOErrorBenign(0);
|
| + if( !skipShared ){
|
| + rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 0);
|
| + }
|
| + if( !rc ){
|
| + pInode->eFileLock = NO_LOCK;
|
| + pFile->eFileLock = NO_LOCK;
|
| + }
|
| + }
|
| + if( rc==SQLITE_OK ){
|
| + pInode->nLock--;
|
| + assert( pInode->nLock>=0 );
|
| + if( pInode->nLock==0 ){
|
| + closePendingFds(pFile);
|
| + }
|
| + }
|
| + }
|
| +
|
| + unixLeaveMutex();
|
| + if( rc==SQLITE_OK ) pFile->eFileLock = eFileLock;
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Close a file & cleanup AFP specific locking context
|
| +*/
|
| +static int afpClose(sqlite3_file *id) {
|
| + int rc = SQLITE_OK;
|
| + unixFile *pFile = (unixFile*)id;
|
| + assert( id!=0 );
|
| + afpUnlock(id, NO_LOCK);
|
| + unixEnterMutex();
|
| + if( pFile->pInode && pFile->pInode->nLock ){
|
| + /* If there are outstanding locks, do not actually close the file just
|
| + ** yet because that would clear those locks. Instead, add the file
|
| + ** descriptor to pInode->aPending. It will be automatically closed when
|
| + ** the last lock is cleared.
|
| + */
|
| + setPendingFd(pFile);
|
| + }
|
| + releaseInodeInfo(pFile);
|
| + sqlite3_free(pFile->lockingContext);
|
| + rc = closeUnixFile(id);
|
| + unixLeaveMutex();
|
| + return rc;
|
| +}
|
| +
|
| +#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */
|
| +/*
|
| +** The code above is the AFP lock implementation. The code is specific
|
| +** to MacOSX and does not work on other unix platforms. No alternative
|
| +** is available. If you don't compile for a mac, then the "unix-afp"
|
| +** VFS is not available.
|
| +**
|
| +********************* End of the AFP lock implementation **********************
|
| +******************************************************************************/
|
| +
|
| +/******************************************************************************
|
| +*************************** Begin NFS Locking ********************************/
|
| +
|
| +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
|
| +/*
|
| + ** 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 nfsUnlock(sqlite3_file *id, int eFileLock){
|
| + return posixUnlock(id, eFileLock, 1);
|
| +}
|
| +
|
| +#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */
|
| +/*
|
| +** The code above is the NFS lock implementation. The code is specific
|
| +** to MacOSX and does not work on other unix platforms. No alternative
|
| +** is available.
|
| +**
|
| +********************* End of the NFS lock implementation **********************
|
| +******************************************************************************/
|
| +
|
| +/******************************************************************************
|
| +**************** Non-locking sqlite3_file methods *****************************
|
| +**
|
| +** The next division contains implementations for all methods of the
|
| +** sqlite3_file object other than the locking methods. The locking
|
| +** methods were defined in divisions above (one locking method per
|
| +** division). Those methods that are common to all locking modes
|
| +** are gather together into this division.
|
| +*/
|
| +
|
| +/*
|
| +** Seek to the offset passed as the second argument, then read cnt
|
| +** bytes into pBuf. Return the number of bytes actually read.
|
| +**
|
| +** NB: If you define USE_PREAD or USE_PREAD64, then it might also
|
| +** be necessary to define _XOPEN_SOURCE to be 500. This varies from
|
| +** one system to another. Since SQLite does not define USE_PREAD
|
| +** in any form by default, we will not attempt to define _XOPEN_SOURCE.
|
| +** See tickets #2741 and #2681.
|
| +**
|
| +** To avoid stomping the errno value on a failed read the lastErrno value
|
| +** is set before returning.
|
| +*/
|
| +static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){
|
| + int got;
|
| + int prior = 0;
|
| +#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
|
| + i64 newOffset;
|
| +#endif
|
| + TIMER_START;
|
| + assert( cnt==(cnt&0x1ffff) );
|
| + assert( id->h>2 );
|
| + do{
|
| +#if defined(USE_PREAD)
|
| + got = osPread(id->h, pBuf, cnt, offset);
|
| + SimulateIOError( got = -1 );
|
| +#elif defined(USE_PREAD64)
|
| + got = osPread64(id->h, pBuf, cnt, offset);
|
| + SimulateIOError( got = -1 );
|
| +#else
|
| + newOffset = lseek(id->h, offset, SEEK_SET);
|
| + SimulateIOError( newOffset = -1 );
|
| + if( newOffset<0 ){
|
| + storeLastErrno((unixFile*)id, errno);
|
| + return -1;
|
| + }
|
| + got = osRead(id->h, pBuf, cnt);
|
| +#endif
|
| + if( got==cnt ) break;
|
| + if( got<0 ){
|
| + if( errno==EINTR ){ got = 1; continue; }
|
| + prior = 0;
|
| + storeLastErrno((unixFile*)id, errno);
|
| + break;
|
| + }else if( got>0 ){
|
| + cnt -= got;
|
| + offset += got;
|
| + prior += got;
|
| + pBuf = (void*)(got + (char*)pBuf);
|
| + }
|
| + }while( got>0 );
|
| + TIMER_END;
|
| + OSTRACE(("READ %-3d %5d %7lld %llu\n",
|
| + id->h, got+prior, offset-prior, TIMER_ELAPSED));
|
| + return got+prior;
|
| +}
|
| +
|
| +/*
|
| +** Read data from a file into a buffer. Return SQLITE_OK if all
|
| +** bytes were read successfully and SQLITE_IOERR if anything goes
|
| +** wrong.
|
| +*/
|
| +static int unixRead(
|
| + sqlite3_file *id,
|
| + void *pBuf,
|
| + int amt,
|
| + sqlite3_int64 offset
|
| +){
|
| + unixFile *pFile = (unixFile *)id;
|
| + int got;
|
| + assert( id );
|
| + assert( offset>=0 );
|
| + assert( amt>0 );
|
| +
|
| + /* If this is a database file (not a journal, master-journal or temp
|
| + ** file), the bytes in the locking range should never be read or written. */
|
| +#if 0
|
| + assert( pFile->pUnused==0
|
| + || offset>=PENDING_BYTE+512
|
| + || offset+amt<=PENDING_BYTE
|
| + );
|
| +#endif
|
| +
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| + /* Deal with as much of this read request as possible by transfering
|
| + ** data from the memory mapping using memcpy(). */
|
| + if( offset<pFile->mmapSize ){
|
| + if( offset+amt <= pFile->mmapSize ){
|
| + memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], amt);
|
| + return SQLITE_OK;
|
| + }else{
|
| + int nCopy = pFile->mmapSize - offset;
|
| + memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], nCopy);
|
| + pBuf = &((u8 *)pBuf)[nCopy];
|
| + amt -= nCopy;
|
| + offset += nCopy;
|
| + }
|
| + }
|
| +#endif
|
| +
|
| + got = seekAndRead(pFile, offset, pBuf, amt);
|
| + if( got==amt ){
|
| + return SQLITE_OK;
|
| + }else if( got<0 ){
|
| + /* lastErrno set by seekAndRead */
|
| + return SQLITE_IOERR_READ;
|
| + }else{
|
| + storeLastErrno(pFile, 0); /* not a system error */
|
| + /* Unread parts of the buffer must be zero-filled */
|
| + memset(&((char*)pBuf)[got], 0, amt-got);
|
| + return SQLITE_IOERR_SHORT_READ;
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Attempt to seek the file-descriptor passed as the first argument to
|
| +** absolute offset iOff, then attempt to write nBuf bytes of data from
|
| +** pBuf to it. If an error occurs, return -1 and set *piErrno. Otherwise,
|
| +** return the actual number of bytes written (which may be less than
|
| +** nBuf).
|
| +*/
|
| +static int seekAndWriteFd(
|
| + int fd, /* File descriptor to write to */
|
| + i64 iOff, /* File offset to begin writing at */
|
| + const void *pBuf, /* Copy data from this buffer to the file */
|
| + int nBuf, /* Size of buffer pBuf in bytes */
|
| + int *piErrno /* OUT: Error number if error occurs */
|
| +){
|
| + int rc = 0; /* Value returned by system call */
|
| +
|
| + assert( nBuf==(nBuf&0x1ffff) );
|
| + assert( fd>2 );
|
| + assert( piErrno!=0 );
|
| + nBuf &= 0x1ffff;
|
| + TIMER_START;
|
| +
|
| +#if defined(USE_PREAD)
|
| + do{ rc = (int)osPwrite(fd, pBuf, nBuf, iOff); }while( rc<0 && errno==EINTR );
|
| +#elif defined(USE_PREAD64)
|
| + do{ rc = (int)osPwrite64(fd, pBuf, nBuf, iOff);}while( rc<0 && errno==EINTR);
|
| +#else
|
| + do{
|
| + i64 iSeek = lseek(fd, iOff, SEEK_SET);
|
| + SimulateIOError( iSeek = -1 );
|
| + if( iSeek<0 ){
|
| + rc = -1;
|
| + break;
|
| + }
|
| + rc = osWrite(fd, pBuf, nBuf);
|
| + }while( rc<0 && errno==EINTR );
|
| +#endif
|
| +
|
| + TIMER_END;
|
| + OSTRACE(("WRITE %-3d %5d %7lld %llu\n", fd, rc, iOff, TIMER_ELAPSED));
|
| +
|
| + if( rc<0 ) *piErrno = errno;
|
| + return rc;
|
| +}
|
| +
|
| +
|
| +/*
|
| +** Seek to the offset in id->offset then read cnt bytes into pBuf.
|
| +** Return the number of bytes actually read. Update the offset.
|
| +**
|
| +** To avoid stomping the errno value on a failed write the lastErrno value
|
| +** is set before returning.
|
| +*/
|
| +static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){
|
| + return seekAndWriteFd(id->h, offset, pBuf, cnt, &id->lastErrno);
|
| +}
|
| +
|
| +
|
| +/*
|
| +** Write data from a buffer into a file. Return SQLITE_OK on success
|
| +** or some other error code on failure.
|
| +*/
|
| +static int unixWrite(
|
| + sqlite3_file *id,
|
| + const void *pBuf,
|
| + int amt,
|
| + sqlite3_int64 offset
|
| +){
|
| + unixFile *pFile = (unixFile*)id;
|
| + int wrote = 0;
|
| + assert( id );
|
| + assert( amt>0 );
|
| +
|
| + /* If this is a database file (not a journal, master-journal or temp
|
| + ** file), the bytes in the locking range should never be read or written. */
|
| +#if 0
|
| + assert( pFile->pUnused==0
|
| + || offset>=PENDING_BYTE+512
|
| + || offset+amt<=PENDING_BYTE
|
| + );
|
| +#endif
|
| +
|
| +#ifdef SQLITE_DEBUG
|
| + /* If we are doing a normal write to a database file (as opposed to
|
| + ** doing a hot-journal rollback or a write to some file other than a
|
| + ** normal database file) then record the fact that the database
|
| + ** has changed. If the transaction counter is modified, record that
|
| + ** fact too.
|
| + */
|
| + if( pFile->inNormalWrite ){
|
| + pFile->dbUpdate = 1; /* The database has been modified */
|
| + if( offset<=24 && offset+amt>=27 ){
|
| + int rc;
|
| + char oldCntr[4];
|
| + SimulateIOErrorBenign(1);
|
| + rc = seekAndRead(pFile, 24, oldCntr, 4);
|
| + SimulateIOErrorBenign(0);
|
| + if( rc!=4 || memcmp(oldCntr, &((char*)pBuf)[24-offset], 4)!=0 ){
|
| + pFile->transCntrChng = 1; /* The transaction counter has changed */
|
| + }
|
| + }
|
| + }
|
| +#endif
|
| +
|
| +#if defined(SQLITE_MMAP_READWRITE) && SQLITE_MAX_MMAP_SIZE>0
|
| + /* Deal with as much of this write request as possible by transfering
|
| + ** data from the memory mapping using memcpy(). */
|
| + if( offset<pFile->mmapSize ){
|
| + if( offset+amt <= pFile->mmapSize ){
|
| + memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, amt);
|
| + return SQLITE_OK;
|
| + }else{
|
| + int nCopy = pFile->mmapSize - offset;
|
| + memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, nCopy);
|
| + pBuf = &((u8 *)pBuf)[nCopy];
|
| + amt -= nCopy;
|
| + offset += nCopy;
|
| + }
|
| + }
|
| +#endif
|
| +
|
| + while( (wrote = seekAndWrite(pFile, offset, pBuf, amt))<amt && wrote>0 ){
|
| + amt -= wrote;
|
| + offset += wrote;
|
| + pBuf = &((char*)pBuf)[wrote];
|
| + }
|
| + SimulateIOError(( wrote=(-1), amt=1 ));
|
| + SimulateDiskfullError(( wrote=0, amt=1 ));
|
| +
|
| + if( amt>wrote ){
|
| + if( wrote<0 && pFile->lastErrno!=ENOSPC ){
|
| + /* lastErrno set by seekAndWrite */
|
| + return SQLITE_IOERR_WRITE;
|
| + }else{
|
| + storeLastErrno(pFile, 0); /* not a system error */
|
| + return SQLITE_FULL;
|
| + }
|
| + }
|
| +
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +#ifdef SQLITE_TEST
|
| +/*
|
| +** Count the number of fullsyncs and normal syncs. This is used to test
|
| +** that syncs and fullsyncs are occurring at the right times.
|
| +*/
|
| +SQLITE_API int sqlite3_sync_count = 0;
|
| +SQLITE_API int sqlite3_fullsync_count = 0;
|
| +#endif
|
| +
|
| +/*
|
| +** We do not trust systems to provide a working fdatasync(). Some do.
|
| +** Others do no. To be safe, we will stick with the (slightly slower)
|
| +** fsync(). If you know that your system does support fdatasync() correctly,
|
| +** then simply compile with -Dfdatasync=fdatasync or -DHAVE_FDATASYNC
|
| +*/
|
| +#if !defined(fdatasync) && !HAVE_FDATASYNC
|
| +# define fdatasync fsync
|
| +#endif
|
| +
|
| +/*
|
| +** Define HAVE_FULLFSYNC to 0 or 1 depending on whether or not
|
| +** the F_FULLFSYNC macro is defined. F_FULLFSYNC is currently
|
| +** only available on Mac OS X. But that could change.
|
| +*/
|
| +#ifdef F_FULLFSYNC
|
| +# define HAVE_FULLFSYNC 1
|
| +#else
|
| +# define HAVE_FULLFSYNC 0
|
| +#endif
|
| +
|
| +
|
| +/*
|
| +** The fsync() system call does not work as advertised on many
|
| +** unix systems. The following procedure is an attempt to make
|
| +** it work better.
|
| +**
|
| +** The SQLITE_NO_SYNC macro disables all fsync()s. This is useful
|
| +** for testing when we want to run through the test suite quickly.
|
| +** You are strongly advised *not* to deploy with SQLITE_NO_SYNC
|
| +** enabled, however, since with SQLITE_NO_SYNC enabled, an OS crash
|
| +** or power failure will likely corrupt the database file.
|
| +**
|
| +** SQLite sets the dataOnly flag if the size of the file is unchanged.
|
| +** The idea behind dataOnly is that it should only write the file content
|
| +** to disk, not the inode. We only set dataOnly if the file size is
|
| +** unchanged since the file size is part of the inode. However,
|
| +** Ted Ts'o tells us that fdatasync() will also write the inode if the
|
| +** file size has changed. The only real difference between fdatasync()
|
| +** and fsync(), Ted tells us, is that fdatasync() will not flush the
|
| +** inode if the mtime or owner or other inode attributes have changed.
|
| +** We only care about the file size, not the other file attributes, so
|
| +** as far as SQLite is concerned, an fdatasync() is always adequate.
|
| +** So, we always use fdatasync() if it is available, regardless of
|
| +** the value of the dataOnly flag.
|
| +*/
|
| +static int full_fsync(int fd, int fullSync, int dataOnly){
|
| + int rc;
|
| +
|
| + /* The following "ifdef/elif/else/" block has the same structure as
|
| + ** the one below. It is replicated here solely to avoid cluttering
|
| + ** up the real code with the UNUSED_PARAMETER() macros.
|
| + */
|
| +#ifdef SQLITE_NO_SYNC
|
| + UNUSED_PARAMETER(fd);
|
| + UNUSED_PARAMETER(fullSync);
|
| + UNUSED_PARAMETER(dataOnly);
|
| +#elif HAVE_FULLFSYNC
|
| + UNUSED_PARAMETER(dataOnly);
|
| +#else
|
| + UNUSED_PARAMETER(fullSync);
|
| + UNUSED_PARAMETER(dataOnly);
|
| +#endif
|
| +
|
| + /* Record the number of times that we do a normal fsync() and
|
| + ** FULLSYNC. This is used during testing to verify that this procedure
|
| + ** gets called with the correct arguments.
|
| + */
|
| +#ifdef SQLITE_TEST
|
| + if( fullSync ) sqlite3_fullsync_count++;
|
| + sqlite3_sync_count++;
|
| +#endif
|
| +
|
| + /* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a
|
| + ** no-op. But go ahead and call fstat() to validate the file
|
| + ** descriptor as we need a method to provoke a failure during
|
| + ** coverate testing.
|
| + */
|
| +#ifdef SQLITE_NO_SYNC
|
| + {
|
| + struct stat buf;
|
| + rc = osFstat(fd, &buf);
|
| + }
|
| +#elif HAVE_FULLFSYNC
|
| + if( fullSync ){
|
| + rc = osFcntl(fd, F_FULLFSYNC, 0);
|
| + }else{
|
| + rc = 1;
|
| + }
|
| + /* If the FULLFSYNC failed, fall back to attempting an fsync().
|
| + ** It shouldn't be possible for fullfsync to fail on the local
|
| + ** file system (on OSX), so failure indicates that FULLFSYNC
|
| + ** isn't supported for this file system. So, attempt an fsync
|
| + ** and (for now) ignore the overhead of a superfluous fcntl call.
|
| + ** It'd be better to detect fullfsync support once and avoid
|
| + ** the fcntl call every time sync is called.
|
| + */
|
| + if( rc ) rc = fsync(fd);
|
| +
|
| +#elif defined(__APPLE__)
|
| + /* fdatasync() on HFS+ doesn't yet flush the file size if it changed correctly
|
| + ** so currently we default to the macro that redefines fdatasync to fsync
|
| + */
|
| + rc = fsync(fd);
|
| +#else
|
| + rc = fdatasync(fd);
|
| +#if OS_VXWORKS
|
| + if( rc==-1 && errno==ENOTSUP ){
|
| + rc = fsync(fd);
|
| + }
|
| +#endif /* OS_VXWORKS */
|
| +#endif /* ifdef SQLITE_NO_SYNC elif HAVE_FULLFSYNC */
|
| +
|
| + if( OS_VXWORKS && rc!= -1 ){
|
| + rc = 0;
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Open a file descriptor to the directory containing file zFilename.
|
| +** If successful, *pFd is set to the opened file descriptor and
|
| +** SQLITE_OK is returned. If an error occurs, either SQLITE_NOMEM
|
| +** or SQLITE_CANTOPEN is returned and *pFd is set to an undefined
|
| +** value.
|
| +**
|
| +** The directory file descriptor is used for only one thing - to
|
| +** fsync() a directory to make sure file creation and deletion events
|
| +** are flushed to disk. Such fsyncs are not needed on newer
|
| +** journaling filesystems, but are required on older filesystems.
|
| +**
|
| +** This routine can be overridden using the xSetSysCall interface.
|
| +** The ability to override this routine was added in support of the
|
| +** chromium sandbox. Opening a directory is a security risk (we are
|
| +** told) so making it overrideable allows the chromium sandbox to
|
| +** replace this routine with a harmless no-op. To make this routine
|
| +** a no-op, replace it with a stub that returns SQLITE_OK but leaves
|
| +** *pFd set to a negative number.
|
| +**
|
| +** If SQLITE_OK is returned, the caller is responsible for closing
|
| +** the file descriptor *pFd using close().
|
| +*/
|
| +static int openDirectory(const char *zFilename, int *pFd){
|
| + int ii;
|
| + int fd = -1;
|
| + char zDirname[MAX_PATHNAME+1];
|
| +
|
| + sqlite3_snprintf(MAX_PATHNAME, zDirname, "%s", zFilename);
|
| + for(ii=(int)strlen(zDirname); ii>0 && zDirname[ii]!='/'; ii--);
|
| + if( ii>0 ){
|
| + zDirname[ii] = '\0';
|
| + }else{
|
| + if( zDirname[0]!='/' ) zDirname[0] = '.';
|
| + zDirname[1] = 0;
|
| + }
|
| + fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0);
|
| + if( fd>=0 ){
|
| + OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname));
|
| + }
|
| + *pFd = fd;
|
| + if( fd>=0 ) return SQLITE_OK;
|
| + return unixLogError(SQLITE_CANTOPEN_BKPT, "openDirectory", zDirname);
|
| +}
|
| +
|
| +/*
|
| +** Make sure all writes to a particular file are committed to disk.
|
| +**
|
| +** If dataOnly==0 then both the file itself and its metadata (file
|
| +** size, access time, etc) are synced. If dataOnly!=0 then only the
|
| +** file data is synced.
|
| +**
|
| +** Under Unix, also make sure that the directory entry for the file
|
| +** has been created by fsync-ing the directory that contains the file.
|
| +** If we do not do this and we encounter a power failure, the directory
|
| +** entry for the journal might not exist after we reboot. The next
|
| +** SQLite to access the file will not know that the journal exists (because
|
| +** the directory entry for the journal was never created) and the transaction
|
| +** will not roll back - possibly leading to database corruption.
|
| +*/
|
| +static int unixSync(sqlite3_file *id, int flags){
|
| + int rc;
|
| + unixFile *pFile = (unixFile*)id;
|
| +
|
| + int isDataOnly = (flags&SQLITE_SYNC_DATAONLY);
|
| + int isFullsync = (flags&0x0F)==SQLITE_SYNC_FULL;
|
| +
|
| + /* Check that one of SQLITE_SYNC_NORMAL or FULL was passed */
|
| + assert((flags&0x0F)==SQLITE_SYNC_NORMAL
|
| + || (flags&0x0F)==SQLITE_SYNC_FULL
|
| + );
|
| +
|
| + /* Unix cannot, but some systems may return SQLITE_FULL from here. This
|
| + ** line is to test that doing so does not cause any problems.
|
| + */
|
| + SimulateDiskfullError( return SQLITE_FULL );
|
| +
|
| + assert( pFile );
|
| + OSTRACE(("SYNC %-3d\n", pFile->h));
|
| + rc = full_fsync(pFile->h, isFullsync, isDataOnly);
|
| + SimulateIOError( rc=1 );
|
| + if( rc ){
|
| + storeLastErrno(pFile, errno);
|
| + return unixLogError(SQLITE_IOERR_FSYNC, "full_fsync", pFile->zPath);
|
| + }
|
| +
|
| + /* Also fsync the directory containing the file if the DIRSYNC flag
|
| + ** is set. This is a one-time occurrence. Many systems (examples: AIX)
|
| + ** are unable to fsync a directory, so ignore errors on the fsync.
|
| + */
|
| + if( pFile->ctrlFlags & UNIXFILE_DIRSYNC ){
|
| + int dirfd;
|
| + OSTRACE(("DIRSYNC %s (have_fullfsync=%d fullsync=%d)\n", pFile->zPath,
|
| + HAVE_FULLFSYNC, isFullsync));
|
| + rc = osOpenDirectory(pFile->zPath, &dirfd);
|
| + if( rc==SQLITE_OK ){
|
| + full_fsync(dirfd, 0, 0);
|
| + robust_close(pFile, dirfd, __LINE__);
|
| + }else{
|
| + assert( rc==SQLITE_CANTOPEN );
|
| + rc = SQLITE_OK;
|
| + }
|
| + pFile->ctrlFlags &= ~UNIXFILE_DIRSYNC;
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Truncate an open file to a specified size
|
| +*/
|
| +static int unixTruncate(sqlite3_file *id, i64 nByte){
|
| + unixFile *pFile = (unixFile *)id;
|
| + int rc;
|
| + assert( pFile );
|
| + SimulateIOError( return SQLITE_IOERR_TRUNCATE );
|
| +
|
| + /* If the user has configured a chunk-size for this file, truncate the
|
| + ** file so that it consists of an integer number of chunks (i.e. the
|
| + ** actual file size after the operation may be larger than the requested
|
| + ** size).
|
| + */
|
| + if( pFile->szChunk>0 ){
|
| + nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
|
| + }
|
| +
|
| + rc = robust_ftruncate(pFile->h, nByte);
|
| + if( rc ){
|
| + storeLastErrno(pFile, errno);
|
| + return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath);
|
| + }else{
|
| +#ifdef SQLITE_DEBUG
|
| + /* If we are doing a normal write to a database file (as opposed to
|
| + ** doing a hot-journal rollback or a write to some file other than a
|
| + ** normal database file) and we truncate the file to zero length,
|
| + ** that effectively updates the change counter. This might happen
|
| + ** when restoring a database using the backup API from a zero-length
|
| + ** source.
|
| + */
|
| + if( pFile->inNormalWrite && nByte==0 ){
|
| + pFile->transCntrChng = 1;
|
| + }
|
| +#endif
|
| +
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| + /* If the file was just truncated to a size smaller than the currently
|
| + ** mapped region, reduce the effective mapping size as well. SQLite will
|
| + ** use read() and write() to access data beyond this point from now on.
|
| + */
|
| + if( nByte<pFile->mmapSize ){
|
| + pFile->mmapSize = nByte;
|
| + }
|
| +#endif
|
| +
|
| + return SQLITE_OK;
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Determine the current size of a file in bytes
|
| +*/
|
| +static int unixFileSize(sqlite3_file *id, i64 *pSize){
|
| + int rc;
|
| + struct stat buf;
|
| + assert( id );
|
| + rc = osFstat(((unixFile*)id)->h, &buf);
|
| + SimulateIOError( rc=1 );
|
| + if( rc!=0 ){
|
| + storeLastErrno((unixFile*)id, errno);
|
| + 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;
|
| +}
|
| +
|
| +#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
|
| +/*
|
| +** Handler for proxy-locking file-control verbs. Defined below in the
|
| +** proxying locking division.
|
| +*/
|
| +static int proxyFileControl(sqlite3_file*,int,void*);
|
| +#endif
|
| +
|
| +/*
|
| +** This function is called to handle the SQLITE_FCNTL_SIZE_HINT
|
| +** file-control operation. Enlarge the database to nBytes in size
|
| +** (rounded up to the next chunk-size). If the database is already
|
| +** nBytes or larger, this routine is a no-op.
|
| +*/
|
| +static int fcntlSizeHint(unixFile *pFile, i64 nByte){
|
| + if( pFile->szChunk>0 ){
|
| + i64 nSize; /* Required file size */
|
| + struct stat buf; /* Used to hold return values of fstat() */
|
| +
|
| + if( osFstat(pFile->h, &buf) ){
|
| + return SQLITE_IOERR_FSTAT;
|
| + }
|
| +
|
| + nSize = ((nByte+pFile->szChunk-1) / pFile->szChunk) * pFile->szChunk;
|
| + if( nSize>(i64)buf.st_size ){
|
| +
|
| +#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
|
| + /* The code below is handling the return value of osFallocate()
|
| + ** correctly. posix_fallocate() is defined to "returns zero on success,
|
| + ** or an error number on failure". See the manpage for details. */
|
| + int err;
|
| + do{
|
| + err = osFallocate(pFile->h, buf.st_size, nSize-buf.st_size);
|
| + }while( err==EINTR );
|
| + if( err ) return SQLITE_IOERR_WRITE;
|
| +#else
|
| + /* 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 = buf.st_blksize; /* 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 = seekAndWrite(pFile, iWrite, "", 1);
|
| + if( nWrite!=1 ) return SQLITE_IOERR_WRITE;
|
| + }
|
| +#endif
|
| + }
|
| + }
|
| +
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| + if( pFile->mmapSizeMax>0 && nByte>pFile->mmapSize ){
|
| + int rc;
|
| + if( pFile->szChunk<=0 ){
|
| + if( robust_ftruncate(pFile->h, nByte) ){
|
| + storeLastErrno(pFile, errno);
|
| + return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath);
|
| + }
|
| + }
|
| +
|
| + rc = unixMapfile(pFile, nByte);
|
| + return rc;
|
| + }
|
| +#endif
|
| +
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** If *pArg is initially negative then this is a query. Set *pArg to
|
| +** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set.
|
| +**
|
| +** If *pArg is 0 or 1, then clear or set the mask bit of pFile->ctrlFlags.
|
| +*/
|
| +static void unixModeBit(unixFile *pFile, unsigned char mask, int *pArg){
|
| + if( *pArg<0 ){
|
| + *pArg = (pFile->ctrlFlags & mask)!=0;
|
| + }else if( (*pArg)==0 ){
|
| + pFile->ctrlFlags &= ~mask;
|
| + }else{
|
| + pFile->ctrlFlags |= mask;
|
| + }
|
| +}
|
| +
|
| +/* Forward declaration */
|
| +static int unixGetTempname(int nBuf, char *zBuf);
|
| +
|
| +/*
|
| +** Information and control of an open file handle.
|
| +*/
|
| +static int unixFileControl(sqlite3_file *id, int op, void *pArg){
|
| + unixFile *pFile = (unixFile*)id;
|
| + switch( op ){
|
| + case SQLITE_FCNTL_LOCKSTATE: {
|
| + *(int*)pArg = pFile->eFileLock;
|
| + return SQLITE_OK;
|
| + }
|
| + case SQLITE_FCNTL_LAST_ERRNO: {
|
| + *(int*)pArg = pFile->lastErrno;
|
| + return SQLITE_OK;
|
| + }
|
| + case SQLITE_FCNTL_CHUNK_SIZE: {
|
| + pFile->szChunk = *(int *)pArg;
|
| + return SQLITE_OK;
|
| + }
|
| + case SQLITE_FCNTL_SIZE_HINT: {
|
| + int rc;
|
| + SimulateIOErrorBenign(1);
|
| + rc = fcntlSizeHint(pFile, *(i64 *)pArg);
|
| + SimulateIOErrorBenign(0);
|
| + return rc;
|
| + }
|
| + case SQLITE_FCNTL_PERSIST_WAL: {
|
| + unixModeBit(pFile, UNIXFILE_PERSIST_WAL, (int*)pArg);
|
| + return SQLITE_OK;
|
| + }
|
| + case SQLITE_FCNTL_POWERSAFE_OVERWRITE: {
|
| + unixModeBit(pFile, UNIXFILE_PSOW, (int*)pArg);
|
| + return SQLITE_OK;
|
| + }
|
| + case SQLITE_FCNTL_VFSNAME: {
|
| + *(char**)pArg = sqlite3_mprintf("%s", pFile->pVfs->zName);
|
| + return SQLITE_OK;
|
| + }
|
| + case SQLITE_FCNTL_TEMPFILENAME: {
|
| + char *zTFile = sqlite3_malloc64( pFile->pVfs->mxPathname );
|
| + if( zTFile ){
|
| + unixGetTempname(pFile->pVfs->mxPathname, zTFile);
|
| + *(char**)pArg = zTFile;
|
| + }
|
| + return SQLITE_OK;
|
| + }
|
| + case SQLITE_FCNTL_HAS_MOVED: {
|
| + *(int*)pArg = fileHasMoved(pFile);
|
| + return SQLITE_OK;
|
| + }
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| + case SQLITE_FCNTL_MMAP_SIZE: {
|
| + i64 newLimit = *(i64*)pArg;
|
| + int rc = SQLITE_OK;
|
| + if( newLimit>sqlite3GlobalConfig.mxMmap ){
|
| + newLimit = sqlite3GlobalConfig.mxMmap;
|
| + }
|
| + *(i64*)pArg = pFile->mmapSizeMax;
|
| + if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){
|
| + pFile->mmapSizeMax = newLimit;
|
| + if( pFile->mmapSize>0 ){
|
| + unixUnmapfile(pFile);
|
| + rc = unixMapfile(pFile, -1);
|
| + }
|
| + }
|
| + return rc;
|
| + }
|
| +#endif
|
| +#ifdef SQLITE_DEBUG
|
| + /* The pager calls this method to signal that it has done
|
| + ** a rollback and that the database is therefore unchanged and
|
| + ** it hence it is OK for the transaction change counter to be
|
| + ** unchanged.
|
| + */
|
| + case SQLITE_FCNTL_DB_UNCHANGED: {
|
| + ((unixFile*)id)->dbUpdate = 0;
|
| + return SQLITE_OK;
|
| + }
|
| +#endif
|
| +#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
|
| + case SQLITE_FCNTL_SET_LOCKPROXYFILE:
|
| + case SQLITE_FCNTL_GET_LOCKPROXYFILE: {
|
| + return proxyFileControl(id,op,pArg);
|
| + }
|
| +#endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */
|
| + }
|
| + return SQLITE_NOTFOUND;
|
| +}
|
| +
|
| +/*
|
| +** Return the sector size in bytes of the underlying block device for
|
| +** the specified file. This is almost always 512 bytes, but may be
|
| +** larger for some devices.
|
| +**
|
| +** SQLite code assumes this function cannot fail. It also assumes that
|
| +** if two files are created in the same file-system directory (i.e.
|
| +** a database and its journal file) that the sector size will be the
|
| +** same for both.
|
| +*/
|
| +#ifndef __QNXNTO__
|
| +static int unixSectorSize(sqlite3_file *NotUsed){
|
| + UNUSED_PARAMETER(NotUsed);
|
| + return SQLITE_DEFAULT_SECTOR_SIZE;
|
| +}
|
| +#endif
|
| +
|
| +/*
|
| +** The following version of unixSectorSize() is optimized for QNX.
|
| +*/
|
| +#ifdef __QNXNTO__
|
| +#include <sys/dcmd_blk.h>
|
| +#include <sys/statvfs.h>
|
| +static int unixSectorSize(sqlite3_file *id){
|
| + unixFile *pFile = (unixFile*)id;
|
| + if( pFile->sectorSize == 0 ){
|
| + struct statvfs fsInfo;
|
| +
|
| + /* Set defaults for non-supported filesystems */
|
| + pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE;
|
| + pFile->deviceCharacteristics = 0;
|
| + if( fstatvfs(pFile->h, &fsInfo) == -1 ) {
|
| + return pFile->sectorSize;
|
| + }
|
| +
|
| + if( !strcmp(fsInfo.f_basetype, "tmp") ) {
|
| + pFile->sectorSize = fsInfo.f_bsize;
|
| + pFile->deviceCharacteristics =
|
| + SQLITE_IOCAP_ATOMIC4K | /* All ram filesystem writes are atomic */
|
| + SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until
|
| + ** the write succeeds */
|
| + SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind
|
| + ** so it is ordered */
|
| + 0;
|
| + }else if( strstr(fsInfo.f_basetype, "etfs") ){
|
| + pFile->sectorSize = fsInfo.f_bsize;
|
| + pFile->deviceCharacteristics =
|
| + /* etfs cluster size writes are atomic */
|
| + (pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) |
|
| + SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until
|
| + ** the write succeeds */
|
| + SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind
|
| + ** so it is ordered */
|
| + 0;
|
| + }else if( !strcmp(fsInfo.f_basetype, "qnx6") ){
|
| + pFile->sectorSize = fsInfo.f_bsize;
|
| + pFile->deviceCharacteristics =
|
| + SQLITE_IOCAP_ATOMIC | /* All filesystem writes are atomic */
|
| + SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until
|
| + ** the write succeeds */
|
| + SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind
|
| + ** so it is ordered */
|
| + 0;
|
| + }else if( !strcmp(fsInfo.f_basetype, "qnx4") ){
|
| + pFile->sectorSize = fsInfo.f_bsize;
|
| + pFile->deviceCharacteristics =
|
| + /* full bitset of atomics from max sector size and smaller */
|
| + ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 |
|
| + SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind
|
| + ** so it is ordered */
|
| + 0;
|
| + }else if( strstr(fsInfo.f_basetype, "dos") ){
|
| + pFile->sectorSize = fsInfo.f_bsize;
|
| + pFile->deviceCharacteristics =
|
| + /* full bitset of atomics from max sector size and smaller */
|
| + ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 |
|
| + SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind
|
| + ** so it is ordered */
|
| + 0;
|
| + }else{
|
| + pFile->deviceCharacteristics =
|
| + SQLITE_IOCAP_ATOMIC512 | /* blocks are atomic */
|
| + SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until
|
| + ** the write succeeds */
|
| + 0;
|
| + }
|
| + }
|
| + /* Last chance verification. If the sector size isn't a multiple of 512
|
| + ** then it isn't valid.*/
|
| + if( pFile->sectorSize % 512 != 0 ){
|
| + pFile->deviceCharacteristics = 0;
|
| + pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE;
|
| + }
|
| + return pFile->sectorSize;
|
| +}
|
| +#endif /* __QNXNTO__ */
|
| +
|
| +/*
|
| +** Return the device characteristics for the file.
|
| +**
|
| +** This VFS is set up to return SQLITE_IOCAP_POWERSAFE_OVERWRITE by default.
|
| +** However, that choice is controversial since technically the underlying
|
| +** file system does not always provide powersafe overwrites. (In other
|
| +** words, after a power-loss event, parts of the file that were never
|
| +** written might end up being altered.) However, non-PSOW behavior is very,
|
| +** very rare. And asserting PSOW makes a large reduction in the amount
|
| +** of required I/O for journaling, since a lot of padding is eliminated.
|
| +** Hence, while POWERSAFE_OVERWRITE is on by default, there is a file-control
|
| +** available to turn it off and URI query parameter available to turn it off.
|
| +*/
|
| +static int unixDeviceCharacteristics(sqlite3_file *id){
|
| + unixFile *p = (unixFile*)id;
|
| + int rc = 0;
|
| +#ifdef __QNXNTO__
|
| + if( p->sectorSize==0 ) unixSectorSize(id);
|
| + rc = p->deviceCharacteristics;
|
| +#endif
|
| + if( p->ctrlFlags & UNIXFILE_PSOW ){
|
| + rc |= SQLITE_IOCAP_POWERSAFE_OVERWRITE;
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
|
| +
|
| +/*
|
| +** Return the system page size.
|
| +**
|
| +** This function should not be called directly by other code in this file.
|
| +** Instead, it should be called via macro osGetpagesize().
|
| +*/
|
| +static int unixGetpagesize(void){
|
| +#if OS_VXWORKS
|
| + return 1024;
|
| +#elif defined(_BSD_SOURCE)
|
| + return getpagesize();
|
| +#else
|
| + return (int)sysconf(_SC_PAGESIZE);
|
| +#endif
|
| +}
|
| +
|
| +#endif /* !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 */
|
| +
|
| +#ifndef SQLITE_OMIT_WAL
|
| +
|
| +/*
|
| +** Object used to represent an shared memory buffer.
|
| +**
|
| +** When multiple threads all reference the same wal-index, each thread
|
| +** has its own unixShm object, but they all point to a single instance
|
| +** of this unixShmNode object. In other words, each wal-index is opened
|
| +** only once per process.
|
| +**
|
| +** Each unixShmNode object is connected to a single unixInodeInfo object.
|
| +** We could coalesce this object into unixInodeInfo, but that would mean
|
| +** every open file that does not use shared memory (in other words, most
|
| +** open files) would have to carry around this extra information. So
|
| +** the unixInodeInfo object contains a pointer to this unixShmNode object
|
| +** and the unixShmNode object is created only when needed.
|
| +**
|
| +** unixMutexHeld() must be true when creating or destroying
|
| +** this object or while reading or writing the following fields:
|
| +**
|
| +** nRef
|
| +**
|
| +** The following fields are read-only after the object is created:
|
| +**
|
| +** fid
|
| +** zFilename
|
| +**
|
| +** Either unixShmNode.mutex must be held or unixShmNode.nRef==0 and
|
| +** unixMutexHeld() is true when reading or writing any other field
|
| +** in this structure.
|
| +*/
|
| +struct unixShmNode {
|
| + unixInodeInfo *pInode; /* unixInodeInfo that owns this SHM node */
|
| + sqlite3_mutex *mutex; /* Mutex to access this object */
|
| + char *zFilename; /* Name of the mmapped file */
|
| + int h; /* Open file descriptor */
|
| + int szRegion; /* Size of shared-memory regions */
|
| + u16 nRegion; /* Size of array apRegion */
|
| + u8 isReadonly; /* True if read-only */
|
| + char **apRegion; /* Array of mapped shared-memory regions */
|
| + int nRef; /* Number of unixShm objects pointing to this */
|
| + unixShm *pFirst; /* All unixShm objects pointing to this */
|
| +#ifdef SQLITE_DEBUG
|
| + u8 exclMask; /* Mask of exclusive locks held */
|
| + u8 sharedMask; /* Mask of shared locks held */
|
| + u8 nextShmId; /* Next available unixShm.id value */
|
| +#endif
|
| +};
|
| +
|
| +/*
|
| +** Structure used internally by this VFS to record the state of an
|
| +** open shared memory connection.
|
| +**
|
| +** The following fields are initialized when this object is created and
|
| +** are read-only thereafter:
|
| +**
|
| +** unixShm.pFile
|
| +** unixShm.id
|
| +**
|
| +** All other fields are read/write. The unixShm.pFile->mutex must be held
|
| +** while accessing any read/write fields.
|
| +*/
|
| +struct unixShm {
|
| + unixShmNode *pShmNode; /* The underlying unixShmNode object */
|
| + unixShm *pNext; /* Next unixShm with the same unixShmNode */
|
| + u8 hasMutex; /* True if holding the unixShmNode mutex */
|
| + u8 id; /* Id of this connection within its unixShmNode */
|
| + u16 sharedMask; /* Mask of shared locks held */
|
| + u16 exclMask; /* Mask of exclusive locks held */
|
| +};
|
| +
|
| +/*
|
| +** Constants used for locking
|
| +*/
|
| +#define UNIX_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */
|
| +#define UNIX_SHM_DMS (UNIX_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */
|
| +
|
| +/*
|
| +** Apply posix advisory locks for all bytes from ofst through ofst+n-1.
|
| +**
|
| +** Locks block if the mask is exactly UNIX_SHM_C and are non-blocking
|
| +** otherwise.
|
| +*/
|
| +static int unixShmSystemLock(
|
| + unixFile *pFile, /* Open connection to the WAL file */
|
| + int lockType, /* F_UNLCK, F_RDLCK, or F_WRLCK */
|
| + int ofst, /* First byte of the locking range */
|
| + int n /* Number of bytes to lock */
|
| +){
|
| + unixShmNode *pShmNode; /* Apply locks to this open shared-memory segment */
|
| + struct flock f; /* The posix advisory locking structure */
|
| + int rc = SQLITE_OK; /* Result code form fcntl() */
|
| +
|
| + /* Access to the unixShmNode object is serialized by the caller */
|
| + pShmNode = pFile->pInode->pShmNode;
|
| + assert( sqlite3_mutex_held(pShmNode->mutex) || pShmNode->nRef==0 );
|
| +
|
| + /* Shared locks never span more than one byte */
|
| + assert( n==1 || lockType!=F_RDLCK );
|
| +
|
| + /* Locks are within range */
|
| + assert( n>=1 && n<=SQLITE_SHM_NLOCK );
|
| +
|
| + if( pShmNode->h>=0 ){
|
| + /* Initialize the locking parameters */
|
| + memset(&f, 0, sizeof(f));
|
| + f.l_type = lockType;
|
| + f.l_whence = SEEK_SET;
|
| + f.l_start = ofst;
|
| + f.l_len = n;
|
| +
|
| + rc = osFcntl(pShmNode->h, F_SETLK, &f);
|
| + rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY;
|
| + }
|
| +
|
| + /* Update the global lock state and do debug tracing */
|
| +#ifdef SQLITE_DEBUG
|
| + { u16 mask;
|
| + OSTRACE(("SHM-LOCK "));
|
| + mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<<ofst);
|
| + if( rc==SQLITE_OK ){
|
| + if( lockType==F_UNLCK ){
|
| + OSTRACE(("unlock %d ok", ofst));
|
| + pShmNode->exclMask &= ~mask;
|
| + pShmNode->sharedMask &= ~mask;
|
| + }else if( lockType==F_RDLCK ){
|
| + OSTRACE(("read-lock %d ok", ofst));
|
| + pShmNode->exclMask &= ~mask;
|
| + pShmNode->sharedMask |= mask;
|
| + }else{
|
| + assert( lockType==F_WRLCK );
|
| + OSTRACE(("write-lock %d ok", ofst));
|
| + pShmNode->exclMask |= mask;
|
| + pShmNode->sharedMask &= ~mask;
|
| + }
|
| + }else{
|
| + if( lockType==F_UNLCK ){
|
| + OSTRACE(("unlock %d failed", ofst));
|
| + }else if( lockType==F_RDLCK ){
|
| + OSTRACE(("read-lock failed"));
|
| + }else{
|
| + assert( lockType==F_WRLCK );
|
| + OSTRACE(("write-lock %d failed", ofst));
|
| + }
|
| + }
|
| + OSTRACE((" - afterwards %03x,%03x\n",
|
| + pShmNode->sharedMask, pShmNode->exclMask));
|
| + }
|
| +#endif
|
| +
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Return the minimum number of 32KB shm regions that should be mapped at
|
| +** a time, assuming that each mapping must be an integer multiple of the
|
| +** current system page-size.
|
| +**
|
| +** Usually, this is 1. The exception seems to be systems that are configured
|
| +** to use 64KB pages - in this case each mapping must cover at least two
|
| +** shm regions.
|
| +*/
|
| +static int unixShmRegionPerMap(void){
|
| + int shmsz = 32*1024; /* SHM region size */
|
| + int pgsz = osGetpagesize(); /* System page size */
|
| + assert( ((pgsz-1)&pgsz)==0 ); /* Page size must be a power of 2 */
|
| + if( pgsz<shmsz ) return 1;
|
| + return pgsz/shmsz;
|
| +}
|
| +
|
| +/*
|
| +** Purge the unixShmNodeList list of all entries with unixShmNode.nRef==0.
|
| +**
|
| +** This is not a VFS shared-memory method; it is a utility function called
|
| +** by VFS shared-memory methods.
|
| +*/
|
| +static void unixShmPurge(unixFile *pFd){
|
| + unixShmNode *p = pFd->pInode->pShmNode;
|
| + assert( unixMutexHeld() );
|
| + if( p && ALWAYS(p->nRef==0) ){
|
| + int nShmPerMap = unixShmRegionPerMap();
|
| + int i;
|
| + assert( p->pInode==pFd->pInode );
|
| + sqlite3_mutex_free(p->mutex);
|
| + for(i=0; i<p->nRegion; i+=nShmPerMap){
|
| + if( p->h>=0 ){
|
| + osMunmap(p->apRegion[i], p->szRegion);
|
| + }else{
|
| + sqlite3_free(p->apRegion[i]);
|
| + }
|
| + }
|
| + sqlite3_free(p->apRegion);
|
| + if( p->h>=0 ){
|
| + robust_close(pFd, p->h, __LINE__);
|
| + p->h = -1;
|
| + }
|
| + p->pInode->pShmNode = 0;
|
| + sqlite3_free(p);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Open a shared-memory area associated with open database file pDbFd.
|
| +** This particular implementation uses mmapped files.
|
| +**
|
| +** The file used to implement shared-memory is in the same directory
|
| +** as the open database file and has the same name as the open database
|
| +** file with the "-shm" suffix added. For example, if the database file
|
| +** is "/home/user1/config.db" then the file that is created and mmapped
|
| +** for shared memory will be called "/home/user1/config.db-shm".
|
| +**
|
| +** Another approach to is to use files in /dev/shm or /dev/tmp or an
|
| +** some other tmpfs mount. But if a file in a different directory
|
| +** from the database file is used, then differing access permissions
|
| +** or a chroot() might cause two different processes on the same
|
| +** database to end up using different files for shared memory -
|
| +** meaning that their memory would not really be shared - resulting
|
| +** in database corruption. Nevertheless, this tmpfs file usage
|
| +** can be enabled at compile-time using -DSQLITE_SHM_DIRECTORY="/dev/shm"
|
| +** or the equivalent. The use of the SQLITE_SHM_DIRECTORY compile-time
|
| +** option results in an incompatible build of SQLite; builds of SQLite
|
| +** that with differing SQLITE_SHM_DIRECTORY settings attempt to use the
|
| +** same database file at the same time, database corruption will likely
|
| +** result. The SQLITE_SHM_DIRECTORY compile-time option is considered
|
| +** "unsupported" and may go away in a future SQLite release.
|
| +**
|
| +** When opening a new shared-memory file, if no other instances of that
|
| +** file are currently open, in this process or in other processes, then
|
| +** the file must be truncated to zero length or have its header cleared.
|
| +**
|
| +** If the original database file (pDbFd) is using the "unix-excl" VFS
|
| +** that means that an exclusive lock is held on the database file and
|
| +** that no other processes are able to read or write the database. In
|
| +** that case, we do not really need shared memory. No shared memory
|
| +** file is created. The shared memory will be simulated with heap memory.
|
| +*/
|
| +static int unixOpenSharedMemory(unixFile *pDbFd){
|
| + struct unixShm *p = 0; /* The connection to be opened */
|
| + struct unixShmNode *pShmNode; /* The underlying mmapped file */
|
| + int rc; /* Result code */
|
| + unixInodeInfo *pInode; /* The inode of fd */
|
| + char *zShmFilename; /* Name of the file used for SHM */
|
| + int nShmFilename; /* Size of the SHM filename in bytes */
|
| +
|
| + /* Allocate space for the new unixShm object. */
|
| + p = sqlite3_malloc64( sizeof(*p) );
|
| + if( p==0 ) return SQLITE_NOMEM_BKPT;
|
| + memset(p, 0, sizeof(*p));
|
| + assert( pDbFd->pShm==0 );
|
| +
|
| + /* Check to see if a unixShmNode object already exists. Reuse an existing
|
| + ** one if present. Create a new one if necessary.
|
| + */
|
| + unixEnterMutex();
|
| + pInode = pDbFd->pInode;
|
| + pShmNode = pInode->pShmNode;
|
| + if( pShmNode==0 ){
|
| + struct stat sStat; /* fstat() info for database file */
|
| +#ifndef SQLITE_SHM_DIRECTORY
|
| + const char *zBasePath = pDbFd->zPath;
|
| +#endif
|
| +
|
| + /* Call fstat() to figure out the permissions on the database file. If
|
| + ** a new *-shm file is created, an attempt will be made to create it
|
| + ** with the same permissions.
|
| + */
|
| + if( osFstat(pDbFd->h, &sStat) ){
|
| + rc = SQLITE_IOERR_FSTAT;
|
| + goto shm_open_err;
|
| + }
|
| +
|
| +#ifdef SQLITE_SHM_DIRECTORY
|
| + nShmFilename = sizeof(SQLITE_SHM_DIRECTORY) + 31;
|
| +#else
|
| + nShmFilename = 6 + (int)strlen(zBasePath);
|
| +#endif
|
| + pShmNode = sqlite3_malloc64( sizeof(*pShmNode) + nShmFilename );
|
| + if( pShmNode==0 ){
|
| + rc = SQLITE_NOMEM_BKPT;
|
| + goto shm_open_err;
|
| + }
|
| + memset(pShmNode, 0, sizeof(*pShmNode)+nShmFilename);
|
| + zShmFilename = pShmNode->zFilename = (char*)&pShmNode[1];
|
| +#ifdef SQLITE_SHM_DIRECTORY
|
| + sqlite3_snprintf(nShmFilename, zShmFilename,
|
| + SQLITE_SHM_DIRECTORY "/sqlite-shm-%x-%x",
|
| + (u32)sStat.st_ino, (u32)sStat.st_dev);
|
| +#else
|
| + sqlite3_snprintf(nShmFilename, zShmFilename, "%s-shm", zBasePath);
|
| + sqlite3FileSuffix3(pDbFd->zPath, zShmFilename);
|
| +#endif
|
| + pShmNode->h = -1;
|
| + pDbFd->pInode->pShmNode = pShmNode;
|
| + pShmNode->pInode = pDbFd->pInode;
|
| + if( sqlite3GlobalConfig.bCoreMutex ){
|
| + pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
|
| + if( pShmNode->mutex==0 ){
|
| + rc = SQLITE_NOMEM_BKPT;
|
| + goto shm_open_err;
|
| + }
|
| + }
|
| +
|
| + if( pInode->bProcessLock==0 ){
|
| + int openFlags = O_RDWR | O_CREAT;
|
| + if( sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
|
| + openFlags = O_RDONLY;
|
| + pShmNode->isReadonly = 1;
|
| + }
|
| + pShmNode->h = robust_open(zShmFilename, openFlags, (sStat.st_mode&0777));
|
| + if( pShmNode->h<0 ){
|
| + rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename);
|
| + goto shm_open_err;
|
| + }
|
| +
|
| + /* If this process is running as root, make sure that the SHM file
|
| + ** is owned by the same user that owns the original database. Otherwise,
|
| + ** the original owner will not be able to connect.
|
| + */
|
| + robustFchown(pShmNode->h, sStat.st_uid, sStat.st_gid);
|
| +
|
| + /* Check to see if another process is holding the dead-man switch.
|
| + ** If not, truncate the file to zero length.
|
| + */
|
| + rc = SQLITE_OK;
|
| + if( unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){
|
| + if( robust_ftruncate(pShmNode->h, 0) ){
|
| + rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename);
|
| + }
|
| + }
|
| + if( rc==SQLITE_OK ){
|
| + rc = unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, 1);
|
| + }
|
| + if( rc ) goto shm_open_err;
|
| + }
|
| + }
|
| +
|
| + /* Make the new connection a child of the unixShmNode */
|
| + p->pShmNode = pShmNode;
|
| +#ifdef SQLITE_DEBUG
|
| + p->id = pShmNode->nextShmId++;
|
| +#endif
|
| + pShmNode->nRef++;
|
| + pDbFd->pShm = p;
|
| + unixLeaveMutex();
|
| +
|
| + /* The reference count on pShmNode has already been incremented under
|
| + ** the cover of the unixEnterMutex() mutex and the pointer from the
|
| + ** new (struct unixShm) object to the pShmNode has been set. All that is
|
| + ** left to do is to link the new object into the linked list starting
|
| + ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex
|
| + ** mutex.
|
| + */
|
| + sqlite3_mutex_enter(pShmNode->mutex);
|
| + p->pNext = pShmNode->pFirst;
|
| + pShmNode->pFirst = p;
|
| + sqlite3_mutex_leave(pShmNode->mutex);
|
| + return SQLITE_OK;
|
| +
|
| + /* Jump here on any error */
|
| +shm_open_err:
|
| + unixShmPurge(pDbFd); /* This call frees pShmNode if required */
|
| + sqlite3_free(p);
|
| + unixLeaveMutex();
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** This function is called to obtain a pointer to region iRegion of the
|
| +** shared-memory associated with the database file fd. Shared-memory regions
|
| +** are numbered starting from zero. Each shared-memory region is szRegion
|
| +** bytes in size.
|
| +**
|
| +** If an error occurs, an error code is returned and *pp is set to NULL.
|
| +**
|
| +** Otherwise, if the bExtend parameter is 0 and the requested shared-memory
|
| +** region has not been allocated (by any client, including one running in a
|
| +** separate process), then *pp is set to NULL and SQLITE_OK returned. If
|
| +** bExtend is non-zero and the requested shared-memory region has not yet
|
| +** been allocated, it is allocated by this function.
|
| +**
|
| +** If the shared-memory region has already been allocated or is allocated by
|
| +** this call as described above, then it is mapped into this processes
|
| +** address space (if it is not already), *pp is set to point to the mapped
|
| +** memory and SQLITE_OK returned.
|
| +*/
|
| +static int unixShmMap(
|
| + sqlite3_file *fd, /* Handle open on database file */
|
| + int iRegion, /* Region to retrieve */
|
| + int szRegion, /* Size of regions */
|
| + int bExtend, /* True to extend file if necessary */
|
| + void volatile **pp /* OUT: Mapped memory */
|
| +){
|
| + unixFile *pDbFd = (unixFile*)fd;
|
| + unixShm *p;
|
| + unixShmNode *pShmNode;
|
| + int rc = SQLITE_OK;
|
| + int nShmPerMap = unixShmRegionPerMap();
|
| + int nReqRegion;
|
| +
|
| + /* If the shared-memory file has not yet been opened, open it now. */
|
| + if( pDbFd->pShm==0 ){
|
| + rc = unixOpenSharedMemory(pDbFd);
|
| + if( rc!=SQLITE_OK ) return rc;
|
| + }
|
| +
|
| + p = pDbFd->pShm;
|
| + pShmNode = p->pShmNode;
|
| + sqlite3_mutex_enter(pShmNode->mutex);
|
| + assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 );
|
| + assert( pShmNode->pInode==pDbFd->pInode );
|
| + assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 );
|
| + assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 );
|
| +
|
| + /* Minimum number of regions required to be mapped. */
|
| + nReqRegion = ((iRegion+nShmPerMap) / nShmPerMap) * nShmPerMap;
|
| +
|
| + if( pShmNode->nRegion<nReqRegion ){
|
| + char **apNew; /* New apRegion[] array */
|
| + int nByte = nReqRegion*szRegion; /* Minimum required file size */
|
| + struct stat sStat; /* Used by fstat() */
|
| +
|
| + pShmNode->szRegion = szRegion;
|
| +
|
| + if( pShmNode->h>=0 ){
|
| + /* The requested region is not mapped into this processes address space.
|
| + ** Check to see if it has been allocated (i.e. if the wal-index file is
|
| + ** large enough to contain the requested region).
|
| + */
|
| + if( osFstat(pShmNode->h, &sStat) ){
|
| + rc = SQLITE_IOERR_SHMSIZE;
|
| + goto shmpage_out;
|
| + }
|
| +
|
| + if( sStat.st_size<nByte ){
|
| + /* The requested memory region does not exist. If bExtend is set to
|
| + ** false, exit early. *pp will be set to NULL and SQLITE_OK returned.
|
| + */
|
| + if( !bExtend ){
|
| + goto shmpage_out;
|
| + }
|
| +
|
| + /* Alternatively, if bExtend is true, extend the file. Do this by
|
| + ** writing a single byte to the end of each (OS) page being
|
| + ** allocated or extended. Technically, we need only write to the
|
| + ** last page in order to extend the file. But writing to all new
|
| + ** pages forces the OS to allocate them immediately, which reduces
|
| + ** the chances of SIGBUS while accessing the mapped region later on.
|
| + */
|
| + else{
|
| + static const int pgsz = 4096;
|
| + int iPg;
|
| +
|
| + /* Write to the last byte of each newly allocated or extended page */
|
| + assert( (nByte % pgsz)==0 );
|
| + for(iPg=(sStat.st_size/pgsz); iPg<(nByte/pgsz); iPg++){
|
| + int x = 0;
|
| + if( seekAndWriteFd(pShmNode->h, iPg*pgsz + pgsz-1, "", 1, &x)!=1 ){
|
| + const char *zFile = pShmNode->zFilename;
|
| + rc = unixLogError(SQLITE_IOERR_SHMSIZE, "write", zFile);
|
| + goto shmpage_out;
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + /* Map the requested memory region into this processes address space. */
|
| + apNew = (char **)sqlite3_realloc(
|
| + pShmNode->apRegion, nReqRegion*sizeof(char *)
|
| + );
|
| + if( !apNew ){
|
| + rc = SQLITE_IOERR_NOMEM_BKPT;
|
| + goto shmpage_out;
|
| + }
|
| + pShmNode->apRegion = apNew;
|
| + while( pShmNode->nRegion<nReqRegion ){
|
| + int nMap = szRegion*nShmPerMap;
|
| + int i;
|
| + void *pMem;
|
| + if( pShmNode->h>=0 ){
|
| + pMem = osMmap(0, nMap,
|
| + pShmNode->isReadonly ? PROT_READ : PROT_READ|PROT_WRITE,
|
| + MAP_SHARED, pShmNode->h, szRegion*(i64)pShmNode->nRegion
|
| + );
|
| + if( pMem==MAP_FAILED ){
|
| + rc = unixLogError(SQLITE_IOERR_SHMMAP, "mmap", pShmNode->zFilename);
|
| + goto shmpage_out;
|
| + }
|
| + }else{
|
| + pMem = sqlite3_malloc64(szRegion);
|
| + if( pMem==0 ){
|
| + rc = SQLITE_NOMEM_BKPT;
|
| + goto shmpage_out;
|
| + }
|
| + memset(pMem, 0, szRegion);
|
| + }
|
| +
|
| + for(i=0; i<nShmPerMap; i++){
|
| + pShmNode->apRegion[pShmNode->nRegion+i] = &((char*)pMem)[szRegion*i];
|
| + }
|
| + pShmNode->nRegion += nShmPerMap;
|
| + }
|
| + }
|
| +
|
| +shmpage_out:
|
| + if( pShmNode->nRegion>iRegion ){
|
| + *pp = pShmNode->apRegion[iRegion];
|
| + }else{
|
| + *pp = 0;
|
| + }
|
| + if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY;
|
| + sqlite3_mutex_leave(pShmNode->mutex);
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Change the lock state for a shared-memory segment.
|
| +**
|
| +** Note that the relationship between SHAREd and EXCLUSIVE locks is a little
|
| +** different here than in posix. In xShmLock(), one can go from unlocked
|
| +** to shared and back or from unlocked to exclusive and back. But one may
|
| +** not go from shared to exclusive or from exclusive to shared.
|
| +*/
|
| +static int unixShmLock(
|
| + sqlite3_file *fd, /* Database file holding the shared memory */
|
| + int ofst, /* First lock to acquire or release */
|
| + int n, /* Number of locks to acquire or release */
|
| + int flags /* What to do with the lock */
|
| +){
|
| + unixFile *pDbFd = (unixFile*)fd; /* Connection holding shared memory */
|
| + unixShm *p = pDbFd->pShm; /* The shared memory being locked */
|
| + unixShm *pX; /* For looping over all siblings */
|
| + unixShmNode *pShmNode = p->pShmNode; /* The underlying file iNode */
|
| + int rc = SQLITE_OK; /* Result code */
|
| + u16 mask; /* Mask of locks to take or release */
|
| +
|
| + assert( pShmNode==pDbFd->pInode->pShmNode );
|
| + assert( pShmNode->pInode==pDbFd->pInode );
|
| + assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK );
|
| + assert( n>=1 );
|
| + assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED)
|
| + || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE)
|
| + || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
|
| + || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
|
| + assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );
|
| + assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 );
|
| + assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 );
|
| +
|
| + mask = (1<<(ofst+n)) - (1<<ofst);
|
| + assert( n>1 || mask==(1<<ofst) );
|
| + sqlite3_mutex_enter(pShmNode->mutex);
|
| + if( flags & SQLITE_SHM_UNLOCK ){
|
| + u16 allMask = 0; /* Mask of locks held by siblings */
|
| +
|
| + /* See if any siblings hold this same lock */
|
| + for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
|
| + if( pX==p ) continue;
|
| + assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 );
|
| + allMask |= pX->sharedMask;
|
| + }
|
| +
|
| + /* Unlock the system-level locks */
|
| + if( (mask & allMask)==0 ){
|
| + rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n);
|
| + }else{
|
| + rc = SQLITE_OK;
|
| + }
|
| +
|
| + /* Undo the local locks */
|
| + if( rc==SQLITE_OK ){
|
| + p->exclMask &= ~mask;
|
| + p->sharedMask &= ~mask;
|
| + }
|
| + }else if( flags & SQLITE_SHM_SHARED ){
|
| + u16 allShared = 0; /* Union of locks held by connections other than "p" */
|
| +
|
| + /* Find out which shared locks are already held by sibling connections.
|
| + ** If any sibling already holds an exclusive lock, go ahead and return
|
| + ** SQLITE_BUSY.
|
| + */
|
| + for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
|
| + if( (pX->exclMask & mask)!=0 ){
|
| + rc = SQLITE_BUSY;
|
| + break;
|
| + }
|
| + allShared |= pX->sharedMask;
|
| + }
|
| +
|
| + /* Get shared locks at the system level, if necessary */
|
| + if( rc==SQLITE_OK ){
|
| + if( (allShared & mask)==0 ){
|
| + rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n);
|
| + }else{
|
| + rc = SQLITE_OK;
|
| + }
|
| + }
|
| +
|
| + /* Get the local shared locks */
|
| + if( rc==SQLITE_OK ){
|
| + p->sharedMask |= mask;
|
| + }
|
| + }else{
|
| + /* Make sure no sibling connections hold locks that will block this
|
| + ** lock. If any do, return SQLITE_BUSY right away.
|
| + */
|
| + for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
|
| + if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){
|
| + rc = SQLITE_BUSY;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + /* Get the exclusive locks at the system level. Then if successful
|
| + ** also mark the local connection as being locked.
|
| + */
|
| + if( rc==SQLITE_OK ){
|
| + rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n);
|
| + if( rc==SQLITE_OK ){
|
| + assert( (p->sharedMask & mask)==0 );
|
| + p->exclMask |= mask;
|
| + }
|
| + }
|
| + }
|
| + sqlite3_mutex_leave(pShmNode->mutex);
|
| + OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n",
|
| + p->id, osGetpid(0), p->sharedMask, p->exclMask));
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Implement a memory barrier or memory fence on shared memory.
|
| +**
|
| +** All loads and stores begun before the barrier must complete before
|
| +** any load or store begun after the barrier.
|
| +*/
|
| +static void unixShmBarrier(
|
| + sqlite3_file *fd /* Database file holding the shared memory */
|
| +){
|
| + UNUSED_PARAMETER(fd);
|
| + sqlite3MemoryBarrier(); /* compiler-defined memory barrier */
|
| + unixEnterMutex(); /* Also mutex, for redundancy */
|
| + unixLeaveMutex();
|
| +}
|
| +
|
| +/*
|
| +** Close a connection to shared-memory. Delete the underlying
|
| +** storage if deleteFlag is true.
|
| +**
|
| +** If there is no shared memory associated with the connection then this
|
| +** routine is a harmless no-op.
|
| +*/
|
| +static int unixShmUnmap(
|
| + sqlite3_file *fd, /* The underlying database file */
|
| + int deleteFlag /* Delete shared-memory if true */
|
| +){
|
| + unixShm *p; /* The connection to be closed */
|
| + unixShmNode *pShmNode; /* The underlying shared-memory file */
|
| + unixShm **pp; /* For looping over sibling connections */
|
| + unixFile *pDbFd; /* The underlying database file */
|
| +
|
| + pDbFd = (unixFile*)fd;
|
| + p = pDbFd->pShm;
|
| + if( p==0 ) return SQLITE_OK;
|
| + pShmNode = p->pShmNode;
|
| +
|
| + assert( pShmNode==pDbFd->pInode->pShmNode );
|
| + assert( pShmNode->pInode==pDbFd->pInode );
|
| +
|
| + /* Remove connection p from the set of connections associated
|
| + ** with pShmNode */
|
| + sqlite3_mutex_enter(pShmNode->mutex);
|
| + for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){}
|
| + *pp = p->pNext;
|
| +
|
| + /* Free the connection p */
|
| + sqlite3_free(p);
|
| + pDbFd->pShm = 0;
|
| + sqlite3_mutex_leave(pShmNode->mutex);
|
| +
|
| + /* If pShmNode->nRef has reached 0, then close the underlying
|
| + ** shared-memory file, too */
|
| + unixEnterMutex();
|
| + assert( pShmNode->nRef>0 );
|
| + pShmNode->nRef--;
|
| + if( pShmNode->nRef==0 ){
|
| + if( deleteFlag && pShmNode->h>=0 ){
|
| + osUnlink(pShmNode->zFilename);
|
| + }
|
| + unixShmPurge(pDbFd);
|
| + }
|
| + unixLeaveMutex();
|
| +
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +
|
| +#else
|
| +# define unixShmMap 0
|
| +# define unixShmLock 0
|
| +# define unixShmBarrier 0
|
| +# define unixShmUnmap 0
|
| +#endif /* #ifndef SQLITE_OMIT_WAL */
|
| +
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| +/*
|
| +** If it is currently memory mapped, unmap file pFd.
|
| +*/
|
| +static void unixUnmapfile(unixFile *pFd){
|
| + assert( pFd->nFetchOut==0 );
|
| + if( pFd->pMapRegion ){
|
| + osMunmap(pFd->pMapRegion, pFd->mmapSizeActual);
|
| + pFd->pMapRegion = 0;
|
| + pFd->mmapSize = 0;
|
| + pFd->mmapSizeActual = 0;
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Attempt to set the size of the memory mapping maintained by file
|
| +** descriptor pFd to nNew bytes. Any existing mapping is discarded.
|
| +**
|
| +** If successful, this function sets the following variables:
|
| +**
|
| +** unixFile.pMapRegion
|
| +** unixFile.mmapSize
|
| +** unixFile.mmapSizeActual
|
| +**
|
| +** If unsuccessful, an error message is logged via sqlite3_log() and
|
| +** the three variables above are zeroed. In this case SQLite should
|
| +** continue accessing the database using the xRead() and xWrite()
|
| +** methods.
|
| +*/
|
| +static void unixRemapfile(
|
| + unixFile *pFd, /* File descriptor object */
|
| + i64 nNew /* Required mapping size */
|
| +){
|
| + const char *zErr = "mmap";
|
| + int h = pFd->h; /* File descriptor open on db file */
|
| + u8 *pOrig = (u8 *)pFd->pMapRegion; /* Pointer to current file mapping */
|
| + i64 nOrig = pFd->mmapSizeActual; /* Size of pOrig region in bytes */
|
| + u8 *pNew = 0; /* Location of new mapping */
|
| + int flags = PROT_READ; /* Flags to pass to mmap() */
|
| +
|
| + assert( pFd->nFetchOut==0 );
|
| + assert( nNew>pFd->mmapSize );
|
| + assert( nNew<=pFd->mmapSizeMax );
|
| + assert( nNew>0 );
|
| + assert( pFd->mmapSizeActual>=pFd->mmapSize );
|
| + assert( MAP_FAILED!=0 );
|
| +
|
| +#ifdef SQLITE_MMAP_READWRITE
|
| + if( (pFd->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE;
|
| +#endif
|
| +
|
| + if( pOrig ){
|
| +#if HAVE_MREMAP
|
| + i64 nReuse = pFd->mmapSize;
|
| +#else
|
| + const int szSyspage = osGetpagesize();
|
| + i64 nReuse = (pFd->mmapSize & ~(szSyspage-1));
|
| +#endif
|
| + u8 *pReq = &pOrig[nReuse];
|
| +
|
| + /* Unmap any pages of the existing mapping that cannot be reused. */
|
| + if( nReuse!=nOrig ){
|
| + osMunmap(pReq, nOrig-nReuse);
|
| + }
|
| +
|
| +#if HAVE_MREMAP
|
| + pNew = osMremap(pOrig, nReuse, nNew, MREMAP_MAYMOVE);
|
| + zErr = "mremap";
|
| +#else
|
| + pNew = osMmap(pReq, nNew-nReuse, flags, MAP_SHARED, h, nReuse);
|
| + if( pNew!=MAP_FAILED ){
|
| + if( pNew!=pReq ){
|
| + osMunmap(pNew, nNew - nReuse);
|
| + pNew = 0;
|
| + }else{
|
| + pNew = pOrig;
|
| + }
|
| + }
|
| +#endif
|
| +
|
| + /* The attempt to extend the existing mapping failed. Free it. */
|
| + if( pNew==MAP_FAILED || pNew==0 ){
|
| + osMunmap(pOrig, nReuse);
|
| + }
|
| + }
|
| +
|
| + /* If pNew is still NULL, try to create an entirely new mapping. */
|
| + if( pNew==0 ){
|
| + pNew = osMmap(0, nNew, flags, MAP_SHARED, h, 0);
|
| + }
|
| +
|
| + if( pNew==MAP_FAILED ){
|
| + pNew = 0;
|
| + nNew = 0;
|
| + unixLogError(SQLITE_OK, zErr, pFd->zPath);
|
| +
|
| + /* If the mmap() above failed, assume that all subsequent mmap() calls
|
| + ** will probably fail too. Fall back to using xRead/xWrite exclusively
|
| + ** in this case. */
|
| + pFd->mmapSizeMax = 0;
|
| + }
|
| + pFd->pMapRegion = (void *)pNew;
|
| + pFd->mmapSize = pFd->mmapSizeActual = nNew;
|
| +}
|
| +
|
| +/*
|
| +** Memory map or remap the file opened by file-descriptor pFd (if the file
|
| +** is already mapped, the existing mapping is replaced by the new). Or, if
|
| +** there already exists a mapping for this file, and there are still
|
| +** outstanding xFetch() references to it, this function is a no-op.
|
| +**
|
| +** If parameter nByte is non-negative, then it is the requested size of
|
| +** the mapping to create. Otherwise, if nByte is less than zero, then the
|
| +** requested size is the size of the file on disk. The actual size of the
|
| +** created mapping is either the requested size or the value configured
|
| +** using SQLITE_FCNTL_MMAP_LIMIT, whichever is smaller.
|
| +**
|
| +** SQLITE_OK is returned if no error occurs (even if the mapping is not
|
| +** recreated as a result of outstanding references) or an SQLite error
|
| +** code otherwise.
|
| +*/
|
| +static int unixMapfile(unixFile *pFd, i64 nMap){
|
| + assert( nMap>=0 || pFd->nFetchOut==0 );
|
| + assert( nMap>0 || (pFd->mmapSize==0 && pFd->pMapRegion==0) );
|
| + if( pFd->nFetchOut>0 ) return SQLITE_OK;
|
| +
|
| + if( nMap<0 ){
|
| + struct stat statbuf; /* Low-level file information */
|
| + if( osFstat(pFd->h, &statbuf) ){
|
| + return SQLITE_IOERR_FSTAT;
|
| + }
|
| + nMap = statbuf.st_size;
|
| + }
|
| + if( nMap>pFd->mmapSizeMax ){
|
| + nMap = pFd->mmapSizeMax;
|
| + }
|
| +
|
| + assert( nMap>0 || (pFd->mmapSize==0 && pFd->pMapRegion==0) );
|
| + if( nMap!=pFd->mmapSize ){
|
| + unixRemapfile(pFd, nMap);
|
| + }
|
| +
|
| + return SQLITE_OK;
|
| +}
|
| +#endif /* SQLITE_MAX_MMAP_SIZE>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 unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| + unixFile *pFd = (unixFile *)fd; /* The underlying database file */
|
| +#endif
|
| + *pp = 0;
|
| +
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| + if( pFd->mmapSizeMax>0 ){
|
| + if( pFd->pMapRegion==0 ){
|
| + int rc = unixMapfile(pFd, -1);
|
| + if( rc!=SQLITE_OK ) return rc;
|
| + }
|
| + if( pFd->mmapSize >= iOff+nAmt ){
|
| + *pp = &((u8 *)pFd->pMapRegion)[iOff];
|
| + pFd->nFetchOut++;
|
| + }
|
| + }
|
| +#endif
|
| + 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 unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| + unixFile *pFd = (unixFile *)fd; /* The underlying database file */
|
| + UNUSED_PARAMETER(iOff);
|
| +
|
| + /* If p==0 (unmap the entire file) then there must be no outstanding
|
| + ** xFetch references. Or, if p!=0 (meaning it is an xFetch reference),
|
| + ** then there must be at least one outstanding. */
|
| + assert( (p==0)==(pFd->nFetchOut==0) );
|
| +
|
| + /* If p!=0, it must match the iOff value. */
|
| + assert( p==0 || p==&((u8 *)pFd->pMapRegion)[iOff] );
|
| +
|
| + if( p ){
|
| + pFd->nFetchOut--;
|
| + }else{
|
| + unixUnmapfile(pFd);
|
| + }
|
| +
|
| + assert( pFd->nFetchOut>=0 );
|
| +#else
|
| + UNUSED_PARAMETER(fd);
|
| + UNUSED_PARAMETER(p);
|
| + UNUSED_PARAMETER(iOff);
|
| +#endif
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Here ends the implementation of all sqlite3_file methods.
|
| +**
|
| +********************** End sqlite3_file Methods *******************************
|
| +******************************************************************************/
|
| +
|
| +/*
|
| +** This division contains definitions of sqlite3_io_methods objects that
|
| +** implement various file locking strategies. It also contains definitions
|
| +** of "finder" functions. A finder-function is used to locate the appropriate
|
| +** sqlite3_io_methods object for a particular database file. The pAppData
|
| +** field of the sqlite3_vfs VFS objects are initialized to be pointers to
|
| +** the correct finder-function for that VFS.
|
| +**
|
| +** Most finder functions return a pointer to a fixed sqlite3_io_methods
|
| +** object. The only interesting finder-function is autolockIoFinder, which
|
| +** looks at the filesystem type and tries to guess the best locking
|
| +** strategy from that.
|
| +**
|
| +** For finder-function F, two objects are created:
|
| +**
|
| +** (1) The real finder-function named "FImpt()".
|
| +**
|
| +** (2) A constant pointer to this function named just "F".
|
| +**
|
| +**
|
| +** A pointer to the F pointer is used as the pAppData value for VFS
|
| +** objects. We have to do this instead of letting pAppData point
|
| +** directly at the finder-function since C90 rules prevent a void*
|
| +** from be cast into a function pointer.
|
| +**
|
| +**
|
| +** Each instance of this macro generates two objects:
|
| +**
|
| +** * A constant sqlite3_io_methods object call METHOD that has locking
|
| +** methods CLOSE, LOCK, UNLOCK, CKRESLOCK.
|
| +**
|
| +** * An I/O method finder function called FINDER that returns a pointer
|
| +** to the METHOD object in the previous bullet.
|
| +*/
|
| +#define IOMETHODS(FINDER,METHOD,VERSION,CLOSE,LOCK,UNLOCK,CKLOCK,SHMMAP) \
|
| +static const sqlite3_io_methods METHOD = { \
|
| + VERSION, /* iVersion */ \
|
| + CLOSE, /* xClose */ \
|
| + unixRead, /* xRead */ \
|
| + unixWrite, /* xWrite */ \
|
| + unixTruncate, /* xTruncate */ \
|
| + unixSync, /* xSync */ \
|
| + unixFileSize, /* xFileSize */ \
|
| + LOCK, /* xLock */ \
|
| + UNLOCK, /* xUnlock */ \
|
| + CKLOCK, /* xCheckReservedLock */ \
|
| + unixFileControl, /* xFileControl */ \
|
| + unixSectorSize, /* xSectorSize */ \
|
| + unixDeviceCharacteristics, /* xDeviceCapabilities */ \
|
| + SHMMAP, /* xShmMap */ \
|
| + unixShmLock, /* xShmLock */ \
|
| + unixShmBarrier, /* xShmBarrier */ \
|
| + unixShmUnmap, /* xShmUnmap */ \
|
| + unixFetch, /* xFetch */ \
|
| + unixUnfetch, /* xUnfetch */ \
|
| +}; \
|
| +static const sqlite3_io_methods *FINDER##Impl(const char *z, unixFile *p){ \
|
| + UNUSED_PARAMETER(z); UNUSED_PARAMETER(p); \
|
| + return &METHOD; \
|
| +} \
|
| +static const sqlite3_io_methods *(*const FINDER)(const char*,unixFile *p) \
|
| + = FINDER##Impl;
|
| +
|
| +/*
|
| +** Here are all of the sqlite3_io_methods objects for each of the
|
| +** locking strategies. Functions that return pointers to these methods
|
| +** are also created.
|
| +*/
|
| +IOMETHODS(
|
| + posixIoFinder, /* Finder function name */
|
| + posixIoMethods, /* sqlite3_io_methods object name */
|
| + 3, /* shared memory and mmap are enabled */
|
| + unixClose, /* xClose method */
|
| + unixLock, /* xLock method */
|
| + unixUnlock, /* xUnlock method */
|
| + unixCheckReservedLock, /* xCheckReservedLock method */
|
| + unixShmMap /* xShmMap method */
|
| +)
|
| +IOMETHODS(
|
| + nolockIoFinder, /* Finder function name */
|
| + nolockIoMethods, /* sqlite3_io_methods object name */
|
| + 3, /* shared memory is disabled */
|
| + nolockClose, /* xClose method */
|
| + nolockLock, /* xLock method */
|
| + nolockUnlock, /* xUnlock method */
|
| + nolockCheckReservedLock, /* xCheckReservedLock method */
|
| + 0 /* xShmMap method */
|
| +)
|
| +IOMETHODS(
|
| + dotlockIoFinder, /* Finder function name */
|
| + dotlockIoMethods, /* sqlite3_io_methods object name */
|
| + 1, /* shared memory is disabled */
|
| + dotlockClose, /* xClose method */
|
| + dotlockLock, /* xLock method */
|
| + dotlockUnlock, /* xUnlock method */
|
| + dotlockCheckReservedLock, /* xCheckReservedLock method */
|
| + 0 /* xShmMap method */
|
| +)
|
| +
|
| +#if SQLITE_ENABLE_LOCKING_STYLE
|
| +IOMETHODS(
|
| + flockIoFinder, /* Finder function name */
|
| + flockIoMethods, /* sqlite3_io_methods object name */
|
| + 1, /* shared memory is disabled */
|
| + flockClose, /* xClose method */
|
| + flockLock, /* xLock method */
|
| + flockUnlock, /* xUnlock method */
|
| + flockCheckReservedLock, /* xCheckReservedLock method */
|
| + 0 /* xShmMap method */
|
| +)
|
| +#endif
|
| +
|
| +#if OS_VXWORKS
|
| +IOMETHODS(
|
| + semIoFinder, /* Finder function name */
|
| + semIoMethods, /* sqlite3_io_methods object name */
|
| + 1, /* shared memory is disabled */
|
| + semXClose, /* xClose method */
|
| + semXLock, /* xLock method */
|
| + semXUnlock, /* xUnlock method */
|
| + semXCheckReservedLock, /* xCheckReservedLock method */
|
| + 0 /* xShmMap method */
|
| +)
|
| +#endif
|
| +
|
| +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
|
| +IOMETHODS(
|
| + afpIoFinder, /* Finder function name */
|
| + afpIoMethods, /* sqlite3_io_methods object name */
|
| + 1, /* shared memory is disabled */
|
| + afpClose, /* xClose method */
|
| + afpLock, /* xLock method */
|
| + afpUnlock, /* xUnlock method */
|
| + afpCheckReservedLock, /* xCheckReservedLock method */
|
| + 0 /* xShmMap method */
|
| +)
|
| +#endif
|
| +
|
| +/*
|
| +** The proxy locking method is a "super-method" in the sense that it
|
| +** opens secondary file descriptors for the conch and lock files and
|
| +** it uses proxy, dot-file, AFP, and flock() locking methods on those
|
| +** secondary files. For this reason, the division that implements
|
| +** proxy locking is located much further down in the file. But we need
|
| +** to go ahead and define the sqlite3_io_methods and finder function
|
| +** for proxy locking here. So we forward declare the I/O methods.
|
| +*/
|
| +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
|
| +static int proxyClose(sqlite3_file*);
|
| +static int proxyLock(sqlite3_file*, int);
|
| +static int proxyUnlock(sqlite3_file*, int);
|
| +static int proxyCheckReservedLock(sqlite3_file*, int*);
|
| +IOMETHODS(
|
| + proxyIoFinder, /* Finder function name */
|
| + proxyIoMethods, /* sqlite3_io_methods object name */
|
| + 1, /* shared memory is disabled */
|
| + proxyClose, /* xClose method */
|
| + proxyLock, /* xLock method */
|
| + proxyUnlock, /* xUnlock method */
|
| + proxyCheckReservedLock, /* xCheckReservedLock method */
|
| + 0 /* xShmMap method */
|
| +)
|
| +#endif
|
| +
|
| +/* nfs lockd on OSX 10.3+ doesn't clear write locks when a read lock is set */
|
| +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
|
| +IOMETHODS(
|
| + nfsIoFinder, /* Finder function name */
|
| + nfsIoMethods, /* sqlite3_io_methods object name */
|
| + 1, /* shared memory is disabled */
|
| + unixClose, /* xClose method */
|
| + unixLock, /* xLock method */
|
| + nfsUnlock, /* xUnlock method */
|
| + unixCheckReservedLock, /* xCheckReservedLock method */
|
| + 0 /* xShmMap method */
|
| +)
|
| +#endif
|
| +
|
| +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
|
| +/*
|
| +** This "finder" function attempts to determine the best locking strategy
|
| +** for the database file "filePath". It then returns the sqlite3_io_methods
|
| +** object that implements that strategy.
|
| +**
|
| +** This is for MacOSX only.
|
| +*/
|
| +static const sqlite3_io_methods *autolockIoFinderImpl(
|
| + const char *filePath, /* name of the database file */
|
| + unixFile *pNew /* open file object for the database file */
|
| +){
|
| + static const struct Mapping {
|
| + const char *zFilesystem; /* Filesystem type name */
|
| + const sqlite3_io_methods *pMethods; /* Appropriate locking method */
|
| + } aMap[] = {
|
| + { "hfs", &posixIoMethods },
|
| + { "ufs", &posixIoMethods },
|
| + { "afpfs", &afpIoMethods },
|
| + { "smbfs", &afpIoMethods },
|
| + { "webdav", &nolockIoMethods },
|
| + { 0, 0 }
|
| + };
|
| + int i;
|
| + struct statfs fsInfo;
|
| + struct flock lockInfo;
|
| +
|
| + if( !filePath ){
|
| + /* If filePath==NULL that means we are dealing with a transient file
|
| + ** that does not need to be locked. */
|
| + return &nolockIoMethods;
|
| + }
|
| + if( statfs(filePath, &fsInfo) != -1 ){
|
| + if( fsInfo.f_flags & MNT_RDONLY ){
|
| + return &nolockIoMethods;
|
| + }
|
| + for(i=0; aMap[i].zFilesystem; i++){
|
| + if( strcmp(fsInfo.f_fstypename, aMap[i].zFilesystem)==0 ){
|
| + return aMap[i].pMethods;
|
| + }
|
| + }
|
| + }
|
| +
|
| + /* Default case. Handles, amongst others, "nfs".
|
| + ** Test byte-range lock using fcntl(). If the call succeeds,
|
| + ** assume that the file-system supports POSIX style locks.
|
| + */
|
| + lockInfo.l_len = 1;
|
| + lockInfo.l_start = 0;
|
| + lockInfo.l_whence = SEEK_SET;
|
| + lockInfo.l_type = F_RDLCK;
|
| + if( osFcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) {
|
| + if( strcmp(fsInfo.f_fstypename, "nfs")==0 ){
|
| + return &nfsIoMethods;
|
| + } else {
|
| + return &posixIoMethods;
|
| + }
|
| + }else{
|
| + return &dotlockIoMethods;
|
| + }
|
| +}
|
| +static const sqlite3_io_methods
|
| + *(*const autolockIoFinder)(const char*,unixFile*) = autolockIoFinderImpl;
|
| +
|
| +#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */
|
| +
|
| +#if OS_VXWORKS
|
| +/*
|
| +** This "finder" function for VxWorks checks to see if posix advisory
|
| +** locking works. If it does, then that is what is used. If it does not
|
| +** work, then fallback to named semaphore locking.
|
| +*/
|
| +static const sqlite3_io_methods *vxworksIoFinderImpl(
|
| + const char *filePath, /* name of the database file */
|
| + unixFile *pNew /* the open file object */
|
| +){
|
| + struct flock lockInfo;
|
| +
|
| + if( !filePath ){
|
| + /* If filePath==NULL that means we are dealing with a transient file
|
| + ** that does not need to be locked. */
|
| + return &nolockIoMethods;
|
| + }
|
| +
|
| + /* Test if fcntl() is supported and use POSIX style locks.
|
| + ** Otherwise fall back to the named semaphore method.
|
| + */
|
| + lockInfo.l_len = 1;
|
| + lockInfo.l_start = 0;
|
| + lockInfo.l_whence = SEEK_SET;
|
| + lockInfo.l_type = F_RDLCK;
|
| + if( osFcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) {
|
| + return &posixIoMethods;
|
| + }else{
|
| + return &semIoMethods;
|
| + }
|
| +}
|
| +static const sqlite3_io_methods
|
| + *(*const vxworksIoFinder)(const char*,unixFile*) = vxworksIoFinderImpl;
|
| +
|
| +#endif /* OS_VXWORKS */
|
| +
|
| +/*
|
| +** An abstract type for a pointer to an IO method finder function:
|
| +*/
|
| +typedef const sqlite3_io_methods *(*finder_type)(const char*,unixFile*);
|
| +
|
| +
|
| +/****************************************************************************
|
| +**************************** sqlite3_vfs methods ****************************
|
| +**
|
| +** This division contains the implementation of methods on the
|
| +** sqlite3_vfs object.
|
| +*/
|
| +
|
| +/*
|
| +** Initialize the contents of the unixFile structure pointed to by pId.
|
| +*/
|
| +static int fillInUnixFile(
|
| + sqlite3_vfs *pVfs, /* Pointer to vfs object */
|
| + int h, /* Open file descriptor of file being opened */
|
| + sqlite3_file *pId, /* Write to the unixFile structure here */
|
| + const char *zFilename, /* Name of the file being opened */
|
| + int ctrlFlags /* Zero or more UNIXFILE_* values */
|
| +){
|
| + const sqlite3_io_methods *pLockingStyle;
|
| + unixFile *pNew = (unixFile *)pId;
|
| + int rc = SQLITE_OK;
|
| +
|
| + assert( pNew->pInode==NULL );
|
| +
|
| + /* Usually the path zFilename should not be a relative pathname. The
|
| + ** exception is when opening the proxy "conch" file in builds that
|
| + ** include the special Apple locking styles.
|
| + */
|
| +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
|
| + assert( zFilename==0 || zFilename[0]=='/'
|
| + || pVfs->pAppData==(void*)&autolockIoFinder );
|
| +#else
|
| + assert( zFilename==0 || zFilename[0]=='/' );
|
| +#endif
|
| +
|
| + /* No locking occurs in temporary files */
|
| + assert( zFilename!=0 || (ctrlFlags & UNIXFILE_NOLOCK)!=0 );
|
| +
|
| + OSTRACE(("OPEN %-3d %s\n", h, zFilename));
|
| + pNew->h = h;
|
| + pNew->pVfs = pVfs;
|
| + pNew->zPath = zFilename;
|
| + pNew->ctrlFlags = (u8)ctrlFlags;
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| + pNew->mmapSizeMax = sqlite3GlobalConfig.szMmap;
|
| +#endif
|
| + if( sqlite3_uri_boolean(((ctrlFlags & UNIXFILE_URI) ? zFilename : 0),
|
| + "psow", SQLITE_POWERSAFE_OVERWRITE) ){
|
| + pNew->ctrlFlags |= UNIXFILE_PSOW;
|
| + }
|
| + if( strcmp(pVfs->zName,"unix-excl")==0 ){
|
| + pNew->ctrlFlags |= UNIXFILE_EXCL;
|
| + }
|
| +
|
| +#if OS_VXWORKS
|
| + pNew->pId = vxworksFindFileId(zFilename);
|
| + if( pNew->pId==0 ){
|
| + ctrlFlags |= UNIXFILE_NOLOCK;
|
| + rc = SQLITE_NOMEM_BKPT;
|
| + }
|
| +#endif
|
| +
|
| + if( ctrlFlags & UNIXFILE_NOLOCK ){
|
| + pLockingStyle = &nolockIoMethods;
|
| + }else{
|
| + pLockingStyle = (**(finder_type*)pVfs->pAppData)(zFilename, pNew);
|
| +#if SQLITE_ENABLE_LOCKING_STYLE
|
| + /* Cache zFilename in the locking context (AFP and dotlock override) for
|
| + ** proxyLock activation is possible (remote proxy is based on db name)
|
| + ** zFilename remains valid until file is closed, to support */
|
| + pNew->lockingContext = (void*)zFilename;
|
| +#endif
|
| + }
|
| +
|
| + if( pLockingStyle == &posixIoMethods
|
| +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
|
| + || pLockingStyle == &nfsIoMethods
|
| +#endif
|
| + ){
|
| + unixEnterMutex();
|
| + rc = findInodeInfo(pNew, &pNew->pInode);
|
| + if( rc!=SQLITE_OK ){
|
| + /* If an error occurred in findInodeInfo(), close the file descriptor
|
| + ** immediately, before releasing the mutex. findInodeInfo() may fail
|
| + ** in two scenarios:
|
| + **
|
| + ** (a) A call to fstat() failed.
|
| + ** (b) A malloc failed.
|
| + **
|
| + ** Scenario (b) may only occur if the process is holding no other
|
| + ** file descriptors open on the same file. If there were other file
|
| + ** descriptors on this file, then no malloc would be required by
|
| + ** findInodeInfo(). If this is the case, it is quite safe to close
|
| + ** handle h - as it is guaranteed that no posix locks will be released
|
| + ** by doing so.
|
| + **
|
| + ** If scenario (a) caused the error then things are not so safe. The
|
| + ** implicit assumption here is that if fstat() fails, things are in
|
| + ** such bad shape that dropping a lock or two doesn't matter much.
|
| + */
|
| + robust_close(pNew, h, __LINE__);
|
| + h = -1;
|
| + }
|
| + unixLeaveMutex();
|
| + }
|
| +
|
| +#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
|
| + else if( pLockingStyle == &afpIoMethods ){
|
| + /* AFP locking uses the file path so it needs to be included in
|
| + ** the afpLockingContext.
|
| + */
|
| + afpLockingContext *pCtx;
|
| + pNew->lockingContext = pCtx = sqlite3_malloc64( sizeof(*pCtx) );
|
| + if( pCtx==0 ){
|
| + rc = SQLITE_NOMEM_BKPT;
|
| + }else{
|
| + /* NB: zFilename exists and remains valid until the file is closed
|
| + ** according to requirement F11141. So we do not need to make a
|
| + ** copy of the filename. */
|
| + pCtx->dbPath = zFilename;
|
| + pCtx->reserved = 0;
|
| + srandomdev();
|
| + unixEnterMutex();
|
| + rc = findInodeInfo(pNew, &pNew->pInode);
|
| + if( rc!=SQLITE_OK ){
|
| + sqlite3_free(pNew->lockingContext);
|
| + robust_close(pNew, h, __LINE__);
|
| + h = -1;
|
| + }
|
| + unixLeaveMutex();
|
| + }
|
| + }
|
| +#endif
|
| +
|
| + else if( pLockingStyle == &dotlockIoMethods ){
|
| + /* Dotfile locking uses the file path so it needs to be included in
|
| + ** the dotlockLockingContext
|
| + */
|
| + char *zLockFile;
|
| + int nFilename;
|
| + assert( zFilename!=0 );
|
| + nFilename = (int)strlen(zFilename) + 6;
|
| + zLockFile = (char *)sqlite3_malloc64(nFilename);
|
| + if( zLockFile==0 ){
|
| + rc = SQLITE_NOMEM_BKPT;
|
| + }else{
|
| + sqlite3_snprintf(nFilename, zLockFile, "%s" DOTLOCK_SUFFIX, zFilename);
|
| + }
|
| + pNew->lockingContext = zLockFile;
|
| + }
|
| +
|
| +#if OS_VXWORKS
|
| + else if( pLockingStyle == &semIoMethods ){
|
| + /* Named semaphore locking uses the file path so it needs to be
|
| + ** included in the semLockingContext
|
| + */
|
| + unixEnterMutex();
|
| + rc = findInodeInfo(pNew, &pNew->pInode);
|
| + if( (rc==SQLITE_OK) && (pNew->pInode->pSem==NULL) ){
|
| + char *zSemName = pNew->pInode->aSemName;
|
| + int n;
|
| + sqlite3_snprintf(MAX_PATHNAME, zSemName, "/%s.sem",
|
| + pNew->pId->zCanonicalName);
|
| + for( n=1; zSemName[n]; n++ )
|
| + if( zSemName[n]=='/' ) zSemName[n] = '_';
|
| + pNew->pInode->pSem = sem_open(zSemName, O_CREAT, 0666, 1);
|
| + if( pNew->pInode->pSem == SEM_FAILED ){
|
| + rc = SQLITE_NOMEM_BKPT;
|
| + pNew->pInode->aSemName[0] = '\0';
|
| + }
|
| + }
|
| + unixLeaveMutex();
|
| + }
|
| +#endif
|
| +
|
| + storeLastErrno(pNew, 0);
|
| +#if OS_VXWORKS
|
| + if( rc!=SQLITE_OK ){
|
| + if( h>=0 ) robust_close(pNew, h, __LINE__);
|
| + h = -1;
|
| + osUnlink(zFilename);
|
| + pNew->ctrlFlags |= UNIXFILE_DELETE;
|
| + }
|
| +#endif
|
| + if( rc!=SQLITE_OK ){
|
| + if( h>=0 ) robust_close(pNew, h, __LINE__);
|
| + }else{
|
| + pNew->pMethod = pLockingStyle;
|
| + OpenCounter(+1);
|
| + verifyDbFile(pNew);
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Return the name of a directory in which to put temporary files.
|
| +** If no suitable temporary file directory can be found, return NULL.
|
| +*/
|
| +static const char *unixTempFileDir(void){
|
| + static const char *azDirs[] = {
|
| + 0,
|
| + 0,
|
| + "/var/tmp",
|
| + "/usr/tmp",
|
| + "/tmp",
|
| + "."
|
| + };
|
| + unsigned int i = 0;
|
| + struct stat buf;
|
| + const char *zDir = sqlite3_temp_directory;
|
| +
|
| + if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR");
|
| + if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR");
|
| + while(1){
|
| + if( zDir!=0
|
| + && osStat(zDir, &buf)==0
|
| + && S_ISDIR(buf.st_mode)
|
| + && osAccess(zDir, 03)==0
|
| + ){
|
| + return zDir;
|
| + }
|
| + if( i>=sizeof(azDirs)/sizeof(azDirs[0]) ) break;
|
| + zDir = azDirs[i++];
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| +/*
|
| +** 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 unixGetTempname(int nBuf, char *zBuf){
|
| + const char *zDir;
|
| + int iLimit = 0;
|
| +
|
| + /* It's odd to simulate an io-error here, but really this is just
|
| + ** using the io-error infrastructure to test that SQLite handles this
|
| + ** function failing.
|
| + */
|
| + zBuf[0] = 0;
|
| + SimulateIOError( return SQLITE_IOERR );
|
| +
|
| + zDir = unixTempFileDir();
|
| + if( zDir==0 ) return SQLITE_IOERR_GETTEMPPATH;
|
| + do{
|
| + u64 r;
|
| + sqlite3_randomness(sizeof(r), &r);
|
| + assert( nBuf>2 );
|
| + zBuf[nBuf-2] = 0;
|
| + sqlite3_snprintf(nBuf, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX"%llx%c",
|
| + zDir, r, 0);
|
| + if( zBuf[nBuf-2]!=0 || (iLimit++)>10 ) return SQLITE_ERROR;
|
| + }while( osAccess(zBuf,0)==0 );
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
|
| +/*
|
| +** Routine to transform a unixFile into a proxy-locking unixFile.
|
| +** Implementation in the proxy-lock division, but used by unixOpen()
|
| +** if SQLITE_PREFER_PROXY_LOCKING is defined.
|
| +*/
|
| +static int proxyTransformUnixFile(unixFile*, const char*);
|
| +#endif
|
| +
|
| +/*
|
| +** Search for an unused file descriptor that was opened on the database
|
| +** file (not a journal or master-journal file) identified by pathname
|
| +** zPath with SQLITE_OPEN_XXX flags matching those passed as the second
|
| +** argument to this function.
|
| +**
|
| +** Such a file descriptor may exist if a database connection was closed
|
| +** but the associated file descriptor could not be closed because some
|
| +** other file descriptor open on the same file is holding a file-lock.
|
| +** Refer to comments in the unixClose() function and the lengthy comment
|
| +** describing "Posix Advisory Locking" at the start of this file for
|
| +** further details. Also, ticket #4018.
|
| +**
|
| +** If a suitable file descriptor is found, then it is returned. If no
|
| +** such file descriptor is located, -1 is returned.
|
| +*/
|
| +static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
|
| + UnixUnusedFd *pUnused = 0;
|
| +
|
| + /* Do not search for an unused file descriptor on vxworks. Not because
|
| + ** vxworks would not benefit from the change (it might, we're not sure),
|
| + ** but because no way to test it is currently available. It is better
|
| + ** not to risk breaking vxworks support for the sake of such an obscure
|
| + ** feature. */
|
| +#if !OS_VXWORKS
|
| + struct stat sStat; /* Results of stat() call */
|
| +
|
| + /* A stat() call may fail for various reasons. If this happens, it is
|
| + ** almost certain that an open() call on the same path will also fail.
|
| + ** For this reason, if an error occurs in the stat() call here, it is
|
| + ** ignored and -1 is returned. The caller will try to open a new file
|
| + ** descriptor on the same path, fail, and return an error to SQLite.
|
| + **
|
| + ** Even if a subsequent open() call does succeed, the consequences of
|
| + ** not searching for a reusable file descriptor are not dire. */
|
| + if( 0==osStat(zPath, &sStat) ){
|
| + unixInodeInfo *pInode;
|
| +
|
| + unixEnterMutex();
|
| + pInode = inodeList;
|
| + while( pInode && (pInode->fileId.dev!=sStat.st_dev
|
| + || pInode->fileId.ino!=(u64)sStat.st_ino) ){
|
| + pInode = pInode->pNext;
|
| + }
|
| + if( pInode ){
|
| + UnixUnusedFd **pp;
|
| + for(pp=&pInode->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext));
|
| + pUnused = *pp;
|
| + if( pUnused ){
|
| + *pp = pUnused->pNext;
|
| + }
|
| + }
|
| + unixLeaveMutex();
|
| + }
|
| +#endif /* if !OS_VXWORKS */
|
| + return pUnused;
|
| +}
|
| +
|
| +/*
|
| +** Find the mode, uid and gid of file zFile.
|
| +*/
|
| +static int getFileMode(
|
| + const char *zFile, /* File name */
|
| + mode_t *pMode, /* OUT: Permissions of zFile */
|
| + uid_t *pUid, /* OUT: uid of zFile. */
|
| + gid_t *pGid /* OUT: gid of zFile. */
|
| +){
|
| + struct stat sStat; /* Output of stat() on database file */
|
| + int rc = SQLITE_OK;
|
| + if( 0==osStat(zFile, &sStat) ){
|
| + *pMode = sStat.st_mode & 0777;
|
| + *pUid = sStat.st_uid;
|
| + *pGid = sStat.st_gid;
|
| + }else{
|
| + rc = SQLITE_IOERR_FSTAT;
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** This function is called by unixOpen() to determine the unix permissions
|
| +** to create new files with. If no error occurs, then SQLITE_OK is returned
|
| +** and a value suitable for passing as the third argument to open(2) is
|
| +** written to *pMode. If an IO error occurs, an SQLite error code is
|
| +** returned and the value of *pMode is not modified.
|
| +**
|
| +** In most cases, this routine sets *pMode to 0, which will become
|
| +** an indication to robust_open() to create the file using
|
| +** SQLITE_DEFAULT_FILE_PERMISSIONS adjusted by the umask.
|
| +** But if the file being opened is a WAL or regular journal file, then
|
| +** this function queries the file-system for the permissions on the
|
| +** corresponding database file and sets *pMode to this value. Whenever
|
| +** possible, WAL and journal files are created using the same permissions
|
| +** as the associated database file.
|
| +**
|
| +** If the SQLITE_ENABLE_8_3_NAMES option is enabled, then the
|
| +** original filename is unavailable. But 8_3_NAMES is only used for
|
| +** FAT filesystems and permissions do not matter there, so just use
|
| +** the default permissions.
|
| +*/
|
| +static int findCreateFileMode(
|
| + const char *zPath, /* Path of file (possibly) being created */
|
| + int flags, /* Flags passed as 4th argument to xOpen() */
|
| + mode_t *pMode, /* OUT: Permissions to open file with */
|
| + uid_t *pUid, /* OUT: uid to set on the file */
|
| + gid_t *pGid /* OUT: gid to set on the file */
|
| +){
|
| + int rc = SQLITE_OK; /* Return Code */
|
| + *pMode = 0;
|
| + *pUid = 0;
|
| + *pGid = 0;
|
| + if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
|
| + char zDb[MAX_PATHNAME+1]; /* Database file path */
|
| + int nDb; /* Number of valid bytes in zDb */
|
| +
|
| + /* zPath is a path to a WAL or journal file. The following block derives
|
| + ** the path to the associated database file from zPath. This block handles
|
| + ** the following naming conventions:
|
| + **
|
| + ** "<path to db>-journal"
|
| + ** "<path to db>-wal"
|
| + ** "<path to db>-journalNN"
|
| + ** "<path to db>-walNN"
|
| + **
|
| + ** where NN is a decimal number. The NN naming schemes are
|
| + ** used by the test_multiplex.c module.
|
| + */
|
| + nDb = sqlite3Strlen30(zPath) - 1;
|
| + while( zPath[nDb]!='-' ){
|
| +#ifndef SQLITE_ENABLE_8_3_NAMES
|
| + /* In the normal case (8+3 filenames disabled) the journal filename
|
| + ** is guaranteed to contain a '-' character. */
|
| + assert( nDb>0 );
|
| + assert( sqlite3Isalnum(zPath[nDb]) );
|
| +#else
|
| + /* If 8+3 names are possible, then the journal file might not contain
|
| + ** a '-' character. So check for that case and return early. */
|
| + if( nDb==0 || zPath[nDb]=='.' ) return SQLITE_OK;
|
| +#endif
|
| + nDb--;
|
| + }
|
| + memcpy(zDb, zPath, nDb);
|
| + zDb[nDb] = '\0';
|
| +
|
| + rc = getFileMode(zDb, pMode, pUid, pGid);
|
| + }else if( flags & SQLITE_OPEN_DELETEONCLOSE ){
|
| + *pMode = 0600;
|
| + }else if( flags & SQLITE_OPEN_URI ){
|
| + /* If this is a main database file and the file was opened using a URI
|
| + ** filename, check for the "modeof" parameter. If present, interpret
|
| + ** its value as a filename and try to copy the mode, uid and gid from
|
| + ** that file. */
|
| + const char *z = sqlite3_uri_parameter(zPath, "modeof");
|
| + if( z ){
|
| + rc = getFileMode(z, pMode, pUid, pGid);
|
| + }
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Initialize |unixFile| internals of |file| on behalf of chromiumOpen() in
|
| +** WebDatabase SQLiteFileSystemPosix.cpp. Function is a subset of unixOpen(),
|
| +** each duplicated piece is marked by "Duplicated in" comment in unixOpen().
|
| +*/
|
| +CHROMIUM_SQLITE_API
|
| +int chromium_sqlite3_fill_in_unix_sqlite3_file(sqlite3_vfs* pVfs,
|
| + int fd,
|
| + sqlite3_file* pFile,
|
| + const char* zPath,
|
| + int noLock,
|
| + int flags) {
|
| + unixFile *p = (unixFile *)pFile;
|
| + const int eType = flags&0xFFFFFF00; /* Type of file to open */
|
| + const int ctrlFlags = (noLock ? UNIXFILE_NOLOCK : 0);
|
| + int rc;
|
| +
|
| + memset(p, 0, sizeof(unixFile));
|
| +
|
| + /* osStat() will not work in the sandbox, so findReusableFd() will always
|
| + ** fail, so directly include the failure-case setup then initialize pUnused.
|
| + */
|
| + if( eType==SQLITE_OPEN_MAIN_DB ){
|
| + p->pUnused = sqlite3_malloc(sizeof(*p->pUnused));
|
| + if (!p->pUnused) {
|
| + return SQLITE_NOMEM_BKPT;
|
| + }
|
| + p->pUnused->fd = fd;
|
| + p->pUnused->flags = flags;
|
| + }
|
| +
|
| + rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags);
|
| + if( rc!=SQLITE_OK ){
|
| + sqlite3_free(p->pUnused);
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Open the file zPath.
|
| +**
|
| +** Previously, the SQLite OS layer used three functions in place of this
|
| +** one:
|
| +**
|
| +** sqlite3OsOpenReadWrite();
|
| +** sqlite3OsOpenReadOnly();
|
| +** sqlite3OsOpenExclusive();
|
| +**
|
| +** These calls correspond to the following combinations of flags:
|
| +**
|
| +** ReadWrite() -> (READWRITE | CREATE)
|
| +** ReadOnly() -> (READONLY)
|
| +** OpenExclusive() -> (READWRITE | CREATE | EXCLUSIVE)
|
| +**
|
| +** The old OpenExclusive() accepted a boolean argument - "delFlag". If
|
| +** true, the file was configured to be automatically deleted when the
|
| +** file handle closed. To achieve the same effect using this new
|
| +** interface, add the DELETEONCLOSE flag to those specified above for
|
| +** OpenExclusive().
|
| +*/
|
| +static int unixOpen(
|
| + sqlite3_vfs *pVfs, /* The VFS for which this is the xOpen method */
|
| + const char *zPath, /* Pathname of file to be opened */
|
| + sqlite3_file *pFile, /* The file descriptor to be filled in */
|
| + int flags, /* Input flags to control the opening */
|
| + int *pOutFlags /* Output flags returned to SQLite core */
|
| +){
|
| + unixFile *p = (unixFile *)pFile;
|
| + int fd = -1; /* File descriptor returned by open() */
|
| + int openFlags = 0; /* Flags to pass to open() */
|
| + int eType = flags&0xFFFFFF00; /* Type of file to open */
|
| + int noLock; /* True to omit locking primitives */
|
| + int rc = SQLITE_OK; /* Function Return Code */
|
| + int ctrlFlags = 0; /* UNIXFILE_* flags */
|
| +
|
| + 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 SQLITE_ENABLE_LOCKING_STYLE
|
| + int isAutoProxy = (flags & SQLITE_OPEN_AUTOPROXY);
|
| +#endif
|
| +#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE
|
| + struct statfs fsInfo;
|
| +#endif
|
| +
|
| + /* If creating a master or main-file journal, this function will open
|
| + ** a file-descriptor on the directory too. The first time unixSync()
|
| + ** is called the directory file descriptor will be fsync()ed and close()d.
|
| + */
|
| + int syncDir = (isCreate && (
|
| + eType==SQLITE_OPEN_MASTER_JOURNAL
|
| + || eType==SQLITE_OPEN_MAIN_JOURNAL
|
| + || eType==SQLITE_OPEN_WAL
|
| + ));
|
| +
|
| + /* 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[MAX_PATHNAME+2];
|
| + const char *zName = zPath;
|
| +
|
| + /* 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 && zName) || eType!=SQLITE_OPEN_MAIN_DB );
|
| + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL );
|
| + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL );
|
| + assert( (!isDelete && zName) || 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
|
| + );
|
| +
|
| + /* Detect a pid change and reset the PRNG. There is a race condition
|
| + ** here such that two or more threads all trying to open databases at
|
| + ** the same instant might all reset the PRNG. But multiple resets
|
| + ** are harmless.
|
| + */
|
| + if( randomnessPid!=osGetpid(0) ){
|
| + randomnessPid = osGetpid(0);
|
| + sqlite3_randomness(0,0);
|
| + }
|
| +
|
| + /* Duplicated in chromium_sqlite3_fill_in_unix_sqlite3_file(). */
|
| + memset(p, 0, sizeof(unixFile));
|
| +
|
| + if( eType==SQLITE_OPEN_MAIN_DB ){
|
| + UnixUnusedFd *pUnused;
|
| + pUnused = findReusableFd(zName, flags);
|
| + if( pUnused ){
|
| + fd = pUnused->fd;
|
| + }else{
|
| + /* Duplicated in chromium_sqlite3_fill_in_unix_sqlite3_file(). */
|
| + pUnused = sqlite3_malloc64(sizeof(*pUnused));
|
| + if( !pUnused ){
|
| + return SQLITE_NOMEM_BKPT;
|
| + }
|
| + }
|
| + p->pUnused = pUnused;
|
| +
|
| + /* Database filenames are double-zero terminated if they are not
|
| + ** URIs with parameters. Hence, they can always be passed into
|
| + ** sqlite3_uri_parameter(). */
|
| + assert( (flags & SQLITE_OPEN_URI) || zName[strlen(zName)+1]==0 );
|
| +
|
| + }else if( !zName ){
|
| + /* If zName is NULL, the upper layer is requesting a temp file. */
|
| + assert(isDelete && !syncDir);
|
| + rc = unixGetTempname(pVfs->mxPathname, zTmpname);
|
| + if( rc!=SQLITE_OK ){
|
| + return rc;
|
| + }
|
| + zName = zTmpname;
|
| +
|
| + /* Generated temporary filenames are always double-zero terminated
|
| + ** for use by sqlite3_uri_parameter(). */
|
| + assert( zName[strlen(zName)+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);
|
| +
|
| + if( fd<0 ){
|
| + mode_t openMode; /* Permissions to create file with */
|
| + uid_t uid; /* Userid for the file */
|
| + gid_t gid; /* Groupid for the file */
|
| + rc = findCreateFileMode(zName, flags, &openMode, &uid, &gid);
|
| + if( rc!=SQLITE_OK ){
|
| + assert( !p->pUnused );
|
| + assert( eType==SQLITE_OPEN_WAL || eType==SQLITE_OPEN_MAIN_JOURNAL );
|
| + return rc;
|
| + }
|
| + fd = robust_open(zName, openFlags, openMode);
|
| + OSTRACE(("OPENX %-3d %s 0%o\n", fd, zName, openFlags));
|
| + assert( !isExclusive || (openFlags & O_CREAT)!=0 );
|
| + if( fd<0 && errno!=EISDIR && isReadWrite ){
|
| + /* 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 = robust_open(zName, openFlags, openMode);
|
| + }
|
| + if( fd<0 ){
|
| + rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zName);
|
| + goto open_finished;
|
| + }
|
| +
|
| + /* If this process is running as root and if creating a new rollback
|
| + ** journal or WAL file, set the ownership of the journal or WAL to be
|
| + ** the same as the original database.
|
| + */
|
| + if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
|
| + robustFchown(fd, uid, gid);
|
| + }
|
| + }
|
| + assert( fd>=0 );
|
| + if( pOutFlags ){
|
| + *pOutFlags = flags;
|
| + }
|
| +
|
| + if( p->pUnused ){
|
| + /* Duplicated in chromium_sqlite3_fill_in_unix_sqlite3_file(). */
|
| + p->pUnused->fd = fd;
|
| + p->pUnused->flags = flags;
|
| + }
|
| +
|
| + if( isDelete ){
|
| +#if OS_VXWORKS
|
| + zPath = zName;
|
| +#elif defined(SQLITE_UNLINK_AFTER_CLOSE)
|
| + zPath = sqlite3_mprintf("%s", zName);
|
| + if( zPath==0 ){
|
| + robust_close(p, fd, __LINE__);
|
| + return SQLITE_NOMEM_BKPT;
|
| + }
|
| +#else
|
| + osUnlink(zName);
|
| +#endif
|
| + }
|
| +#if SQLITE_ENABLE_LOCKING_STYLE
|
| + else{
|
| + p->openFlags = openFlags;
|
| + }
|
| +#endif
|
| +
|
| +#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE
|
| + if( fstatfs(fd, &fsInfo) == -1 ){
|
| + storeLastErrno(p, errno);
|
| + robust_close(p, fd, __LINE__);
|
| + return SQLITE_IOERR_ACCESS;
|
| + }
|
| + if (0 == strncmp("msdos", fsInfo.f_fstypename, 5)) {
|
| + ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS;
|
| + }
|
| + if (0 == strncmp("exfat", fsInfo.f_fstypename, 5)) {
|
| + ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS;
|
| + }
|
| +#endif
|
| +
|
| + /* Set up appropriate ctrlFlags */
|
| + if( isDelete ) ctrlFlags |= UNIXFILE_DELETE;
|
| + if( isReadonly ) ctrlFlags |= UNIXFILE_RDONLY;
|
| + noLock = eType!=SQLITE_OPEN_MAIN_DB;
|
| + if( noLock ) ctrlFlags |= UNIXFILE_NOLOCK;
|
| + if( syncDir ) ctrlFlags |= UNIXFILE_DIRSYNC;
|
| + if( flags & SQLITE_OPEN_URI ) ctrlFlags |= UNIXFILE_URI;
|
| +
|
| +#if SQLITE_ENABLE_LOCKING_STYLE
|
| +#if SQLITE_PREFER_PROXY_LOCKING
|
| + isAutoProxy = 1;
|
| +#endif
|
| + if( isAutoProxy && (zPath!=NULL) && (!noLock) && pVfs->xOpen ){
|
| + char *envforce = getenv("SQLITE_FORCE_PROXY_LOCKING");
|
| + int useProxy = 0;
|
| +
|
| + /* SQLITE_FORCE_PROXY_LOCKING==1 means force always use proxy, 0 means
|
| + ** never use proxy, NULL means use proxy for non-local files only. */
|
| + if( envforce!=NULL ){
|
| + useProxy = atoi(envforce)>0;
|
| + }else{
|
| + useProxy = !(fsInfo.f_flags&MNT_LOCAL);
|
| + }
|
| + if( useProxy ){
|
| + rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags);
|
| + if( rc==SQLITE_OK ){
|
| + rc = proxyTransformUnixFile((unixFile*)pFile, ":auto:");
|
| + if( rc!=SQLITE_OK ){
|
| + /* Use unixClose to clean up the resources added in fillInUnixFile
|
| + ** and clear all the structure's references. Specifically,
|
| + ** pFile->pMethods will be NULL so sqlite3OsClose will be a no-op
|
| + */
|
| + unixClose(pFile);
|
| + return rc;
|
| + }
|
| + }
|
| + goto open_finished;
|
| + }
|
| + }
|
| +#endif
|
| +
|
| + /* Duplicated in chromium_sqlite3_fill_in_unix_sqlite3_file(). */
|
| + rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags);
|
| +
|
| +open_finished:
|
| + if( rc!=SQLITE_OK ){
|
| + /* Duplicated in chromium_sqlite3_fill_in_unix_sqlite3_file(). */
|
| + sqlite3_free(p->pUnused);
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +
|
| +/*
|
| +** Delete the file at zPath. If the dirSync argument is true, fsync()
|
| +** the directory after deleting the file.
|
| +*/
|
| +static int unixDelete(
|
| + sqlite3_vfs *NotUsed, /* VFS containing this as the xDelete method */
|
| + const char *zPath, /* Name of file to be deleted */
|
| + int dirSync /* If true, fsync() directory after deleting file */
|
| +){
|
| + int rc = SQLITE_OK;
|
| + UNUSED_PARAMETER(NotUsed);
|
| + SimulateIOError(return SQLITE_IOERR_DELETE);
|
| + if( osUnlink(zPath)==(-1) ){
|
| + if( errno==ENOENT
|
| +#if OS_VXWORKS
|
| + || osAccess(zPath,0)!=0
|
| +#endif
|
| + ){
|
| + rc = SQLITE_IOERR_DELETE_NOENT;
|
| + }else{
|
| + rc = unixLogError(SQLITE_IOERR_DELETE, "unlink", zPath);
|
| + }
|
| + return rc;
|
| + }
|
| +#ifndef SQLITE_DISABLE_DIRSYNC
|
| + if( (dirSync & 1)!=0 ){
|
| + int fd;
|
| + rc = osOpenDirectory(zPath, &fd);
|
| + if( rc==SQLITE_OK ){
|
| + if( full_fsync(fd,0,0) ){
|
| + rc = unixLogError(SQLITE_IOERR_DIR_FSYNC, "fsync", zPath);
|
| + }
|
| + robust_close(0, fd, __LINE__);
|
| + }else{
|
| + assert( rc==SQLITE_CANTOPEN );
|
| + rc = SQLITE_OK;
|
| + }
|
| + }
|
| +#endif
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Test the existence of or access permissions of file zPath. The
|
| +** test performed depends on the value of flags:
|
| +**
|
| +** SQLITE_ACCESS_EXISTS: Return 1 if the file exists
|
| +** SQLITE_ACCESS_READWRITE: Return 1 if the file is read and writable.
|
| +** SQLITE_ACCESS_READONLY: Return 1 if the file is readable.
|
| +**
|
| +** Otherwise return 0.
|
| +*/
|
| +static int unixAccess(
|
| + sqlite3_vfs *NotUsed, /* The VFS containing this xAccess method */
|
| + const char *zPath, /* Path of the file to examine */
|
| + int flags, /* What do we want to learn about the zPath file? */
|
| + int *pResOut /* Write result boolean here */
|
| +){
|
| + UNUSED_PARAMETER(NotUsed);
|
| + SimulateIOError( return SQLITE_IOERR_ACCESS; );
|
| + assert( pResOut!=0 );
|
| +
|
| + /* The spec says there are three possible values for flags. But only
|
| + ** two of them are actually used */
|
| + assert( flags==SQLITE_ACCESS_EXISTS || flags==SQLITE_ACCESS_READWRITE );
|
| +
|
| + if( flags==SQLITE_ACCESS_EXISTS ){
|
| + struct stat buf;
|
| + *pResOut = (0==osStat(zPath, &buf) && buf.st_size>0);
|
| + }else{
|
| + *pResOut = osAccess(zPath, W_OK|R_OK)==0;
|
| + }
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +**
|
| +*/
|
| +static int mkFullPathname(
|
| + const char *zPath, /* Input path */
|
| + char *zOut, /* Output buffer */
|
| + int nOut /* Allocated size of buffer zOut */
|
| +){
|
| + int nPath = sqlite3Strlen30(zPath);
|
| + int iOff = 0;
|
| + if( zPath[0]!='/' ){
|
| + if( osGetcwd(zOut, nOut-2)==0 ){
|
| + return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath);
|
| + }
|
| + iOff = sqlite3Strlen30(zOut);
|
| + zOut[iOff++] = '/';
|
| + }
|
| + if( (iOff+nPath+1)>nOut ){
|
| + /* SQLite assumes that xFullPathname() nul-terminates the output buffer
|
| + ** even if it returns an error. */
|
| + zOut[iOff] = '\0';
|
| + return SQLITE_CANTOPEN_BKPT;
|
| + }
|
| + sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath);
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Turn a relative pathname into a full pathname. The relative path
|
| +** is stored as a nul-terminated string in the buffer pointed to by
|
| +** zPath.
|
| +**
|
| +** zOut points to a buffer of at least sqlite3_vfs.mxPathname bytes
|
| +** (in this case, MAX_PATHNAME bytes). The full-path is written to
|
| +** this buffer before returning.
|
| +*/
|
| +static int unixFullPathname(
|
| + sqlite3_vfs *pVfs, /* Pointer to vfs object */
|
| + const char *zPath, /* Possibly relative input path */
|
| + int nOut, /* Size of output buffer in bytes */
|
| + char *zOut /* Output buffer */
|
| +){
|
| +#if !defined(HAVE_READLINK) || !defined(HAVE_LSTAT)
|
| + return mkFullPathname(zPath, zOut, nOut);
|
| +#else
|
| + int rc = SQLITE_OK;
|
| + int nByte;
|
| + int nLink = 1; /* Number of symbolic links followed so far */
|
| + const char *zIn = zPath; /* Input path for each iteration of loop */
|
| + char *zDel = 0;
|
| +
|
| + assert( pVfs->mxPathname==MAX_PATHNAME );
|
| + UNUSED_PARAMETER(pVfs);
|
| +
|
| + /* It's odd to simulate an io-error here, but really this is just
|
| + ** using the io-error infrastructure to test that SQLite handles this
|
| + ** function failing. This function could fail if, for example, the
|
| + ** current working directory has been unlinked.
|
| + */
|
| + SimulateIOError( return SQLITE_ERROR );
|
| +
|
| + do {
|
| +
|
| + /* Call stat() on path zIn. Set bLink to true if the path is a symbolic
|
| + ** link, or false otherwise. */
|
| + int bLink = 0;
|
| + struct stat buf;
|
| + if( osLstat(zIn, &buf)!=0 ){
|
| + if( errno!=ENOENT ){
|
| + rc = unixLogError(SQLITE_CANTOPEN_BKPT, "lstat", zIn);
|
| + }
|
| + }else{
|
| + bLink = S_ISLNK(buf.st_mode);
|
| + }
|
| +
|
| + if( bLink ){
|
| + if( zDel==0 ){
|
| + zDel = sqlite3_malloc(nOut);
|
| + if( zDel==0 ) rc = SQLITE_NOMEM_BKPT;
|
| + }else if( ++nLink>SQLITE_MAX_SYMLINKS ){
|
| + rc = SQLITE_CANTOPEN_BKPT;
|
| + }
|
| +
|
| + if( rc==SQLITE_OK ){
|
| + nByte = osReadlink(zIn, zDel, nOut-1);
|
| + if( nByte<0 ){
|
| + rc = unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zIn);
|
| + }else{
|
| + if( zDel[0]!='/' ){
|
| + int n;
|
| + for(n = sqlite3Strlen30(zIn); n>0 && zIn[n-1]!='/'; n--);
|
| + if( nByte+n+1>nOut ){
|
| + rc = SQLITE_CANTOPEN_BKPT;
|
| + }else{
|
| + memmove(&zDel[n], zDel, nByte+1);
|
| + memcpy(zDel, zIn, n);
|
| + nByte += n;
|
| + }
|
| + }
|
| + zDel[nByte] = '\0';
|
| + }
|
| + }
|
| +
|
| + zIn = zDel;
|
| + }
|
| +
|
| + assert( rc!=SQLITE_OK || zIn!=zOut || zIn[0]=='/' );
|
| + if( rc==SQLITE_OK && zIn!=zOut ){
|
| + rc = mkFullPathname(zIn, zOut, nOut);
|
| + }
|
| + if( bLink==0 ) break;
|
| + zIn = zOut;
|
| + }while( rc==SQLITE_OK );
|
| +
|
| + sqlite3_free(zDel);
|
| + return rc;
|
| +#endif /* HAVE_READLINK && HAVE_LSTAT */
|
| +}
|
| +
|
| +
|
| +#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
| +/*
|
| +** Interfaces for opening a shared library, finding entry points
|
| +** within the shared library, and closing the shared library.
|
| +*/
|
| +#include <dlfcn.h>
|
| +static void *unixDlOpen(sqlite3_vfs *NotUsed, const char *zFilename){
|
| + UNUSED_PARAMETER(NotUsed);
|
| + return dlopen(zFilename, RTLD_NOW | RTLD_GLOBAL);
|
| +}
|
| +
|
| +/*
|
| +** SQLite calls this function immediately after a call to unixDlSym() or
|
| +** unixDlOpen() fails (returns a null pointer). If a more detailed error
|
| +** message is available, it is written to zBufOut. If no error message
|
| +** is available, zBufOut is left unmodified and SQLite uses a default
|
| +** error message.
|
| +*/
|
| +static void unixDlError(sqlite3_vfs *NotUsed, int nBuf, char *zBufOut){
|
| + const char *zErr;
|
| + UNUSED_PARAMETER(NotUsed);
|
| + unixEnterMutex();
|
| + zErr = dlerror();
|
| + if( zErr ){
|
| + sqlite3_snprintf(nBuf, zBufOut, "%s", zErr);
|
| + }
|
| + unixLeaveMutex();
|
| +}
|
| +static void (*unixDlSym(sqlite3_vfs *NotUsed, void *p, const char*zSym))(void){
|
| + /*
|
| + ** GCC with -pedantic-errors says that C90 does not allow a void* to be
|
| + ** cast into a pointer to a function. And yet the library dlsym() routine
|
| + ** returns a void* which is really a pointer to a function. So how do we
|
| + ** use dlsym() with -pedantic-errors?
|
| + **
|
| + ** Variable x below is defined to be a pointer to a function taking
|
| + ** parameters void* and const char* and returning a pointer to a function.
|
| + ** We initialize x by assigning it a pointer to the dlsym() function.
|
| + ** (That assignment requires a cast.) Then we call the function that
|
| + ** x points to.
|
| + **
|
| + ** This work-around is unlikely to work correctly on any system where
|
| + ** you really cannot cast a function pointer into void*. But then, on the
|
| + ** other hand, dlsym() will not work on such a system either, so we have
|
| + ** not really lost anything.
|
| + */
|
| + void (*(*x)(void*,const char*))(void);
|
| + UNUSED_PARAMETER(NotUsed);
|
| + x = (void(*(*)(void*,const char*))(void))dlsym;
|
| + return (*x)(p, zSym);
|
| +}
|
| +static void unixDlClose(sqlite3_vfs *NotUsed, void *pHandle){
|
| + UNUSED_PARAMETER(NotUsed);
|
| + dlclose(pHandle);
|
| +}
|
| +#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */
|
| + #define unixDlOpen 0
|
| + #define unixDlError 0
|
| + #define unixDlSym 0
|
| + #define unixDlClose 0
|
| +#endif
|
| +
|
| +/*
|
| +** Write nBuf bytes of random data to the supplied buffer zBuf.
|
| +*/
|
| +static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){
|
| + UNUSED_PARAMETER(NotUsed);
|
| + assert((size_t)nBuf>=(sizeof(time_t)+sizeof(int)));
|
| +
|
| + /* We have to initialize zBuf to prevent valgrind from reporting
|
| + ** errors. The reports issued by valgrind are incorrect - we would
|
| + ** prefer that the randomness be increased by making use of the
|
| + ** uninitialized space in zBuf - but valgrind errors tend to worry
|
| + ** some users. Rather than argue, it seems easier just to initialize
|
| + ** the whole array and silence valgrind, even if that means less randomness
|
| + ** in the random seed.
|
| + **
|
| + ** When testing, initializing zBuf[] to zero is all we do. That means
|
| + ** that we always use the same random number sequence. This makes the
|
| + ** tests repeatable.
|
| + */
|
| + memset(zBuf, 0, nBuf);
|
| + randomnessPid = osGetpid(0);
|
| +#if !defined(SQLITE_TEST) && !defined(SQLITE_OMIT_RANDOMNESS)
|
| + {
|
| + int fd, got;
|
| + fd = robust_open("/dev/urandom", O_RDONLY, 0);
|
| + if( fd<0 ){
|
| + time_t t;
|
| + time(&t);
|
| + memcpy(zBuf, &t, sizeof(t));
|
| + memcpy(&zBuf[sizeof(t)], &randomnessPid, sizeof(randomnessPid));
|
| + assert( sizeof(t)+sizeof(randomnessPid)<=(size_t)nBuf );
|
| + nBuf = sizeof(t) + sizeof(randomnessPid);
|
| + }else{
|
| + do{ got = osRead(fd, zBuf, nBuf); }while( got<0 && errno==EINTR );
|
| + robust_close(0, fd, __LINE__);
|
| + }
|
| + }
|
| +#endif
|
| + return nBuf;
|
| +}
|
| +
|
| +
|
| +/*
|
| +** Sleep for a little while. Return the amount of time slept.
|
| +** The argument is the number of microseconds we want to sleep.
|
| +** The return value is the number of microseconds of sleep actually
|
| +** requested from the underlying operating system, a number which
|
| +** might be greater than or equal to the argument, but not less
|
| +** than the argument.
|
| +*/
|
| +static int unixSleep(sqlite3_vfs *NotUsed, int microseconds){
|
| +#if OS_VXWORKS
|
| + struct timespec sp;
|
| +
|
| + sp.tv_sec = microseconds / 1000000;
|
| + sp.tv_nsec = (microseconds % 1000000) * 1000;
|
| + nanosleep(&sp, NULL);
|
| + UNUSED_PARAMETER(NotUsed);
|
| + return microseconds;
|
| +#elif defined(HAVE_USLEEP) && HAVE_USLEEP
|
| + usleep(microseconds);
|
| + UNUSED_PARAMETER(NotUsed);
|
| + return microseconds;
|
| +#else
|
| + int seconds = (microseconds+999999)/1000000;
|
| + sleep(seconds);
|
| + UNUSED_PARAMETER(NotUsed);
|
| + return seconds*1000000;
|
| +#endif
|
| +}
|
| +
|
| +/*
|
| +** The following variable, if set to a non-zero value, is interpreted as
|
| +** the number of seconds since 1970 and is used to set the result of
|
| +** sqlite3OsCurrentTime() during testing.
|
| +*/
|
| +#ifdef SQLITE_TEST
|
| +SQLITE_API int sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */
|
| +#endif
|
| +
|
| +/*
|
| +** Find the current time (in Universal Coordinated Time). Write into *piNow
|
| +** the current time and date as a Julian Day number times 86_400_000. In
|
| +** other words, write into *piNow the number of milliseconds since the Julian
|
| +** epoch of noon in Greenwich on November 24, 4714 B.C according to the
|
| +** proleptic Gregorian calendar.
|
| +**
|
| +** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date
|
| +** cannot be found.
|
| +*/
|
| +static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){
|
| + static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000;
|
| + int rc = SQLITE_OK;
|
| +#if defined(NO_GETTOD)
|
| + time_t t;
|
| + time(&t);
|
| + *piNow = ((sqlite3_int64)t)*1000 + unixEpoch;
|
| +#elif OS_VXWORKS
|
| + struct timespec sNow;
|
| + clock_gettime(CLOCK_REALTIME, &sNow);
|
| + *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_nsec/1000000;
|
| +#else
|
| + struct timeval sNow;
|
| + (void)gettimeofday(&sNow, 0); /* Cannot fail given valid arguments */
|
| + *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000;
|
| +#endif
|
| +
|
| +#ifdef SQLITE_TEST
|
| + if( sqlite3_current_time ){
|
| + *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch;
|
| + }
|
| +#endif
|
| + UNUSED_PARAMETER(NotUsed);
|
| + return rc;
|
| +}
|
| +
|
| +#ifndef SQLITE_OMIT_DEPRECATED
|
| +/*
|
| +** Find the current time (in Universal Coordinated Time). Write the
|
| +** current time and date as a Julian Day number into *prNow and
|
| +** return 0. Return 1 if the time and date cannot be found.
|
| +*/
|
| +static int unixCurrentTime(sqlite3_vfs *NotUsed, double *prNow){
|
| + sqlite3_int64 i = 0;
|
| + int rc;
|
| + UNUSED_PARAMETER(NotUsed);
|
| + rc = unixCurrentTimeInt64(0, &i);
|
| + *prNow = i/86400000.0;
|
| + return rc;
|
| +}
|
| +#else
|
| +# define unixCurrentTime 0
|
| +#endif
|
| +
|
| +/*
|
| +** The xGetLastError() method is designed to return a better
|
| +** low-level error message when operating-system problems come up
|
| +** during SQLite operation. Only the integer return code is currently
|
| +** used.
|
| +*/
|
| +static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
|
| + UNUSED_PARAMETER(NotUsed);
|
| + UNUSED_PARAMETER(NotUsed2);
|
| + UNUSED_PARAMETER(NotUsed3);
|
| + return errno;
|
| +}
|
| +
|
| +
|
| +/*
|
| +************************ End of sqlite3_vfs methods ***************************
|
| +******************************************************************************/
|
| +
|
| +/******************************************************************************
|
| +************************** Begin Proxy Locking ********************************
|
| +**
|
| +** Proxy locking is a "uber-locking-method" in this sense: It uses the
|
| +** other locking methods on secondary lock files. Proxy locking is a
|
| +** meta-layer over top of the primitive locking implemented above. For
|
| +** this reason, the division that implements of proxy locking is deferred
|
| +** until late in the file (here) after all of the other I/O methods have
|
| +** been defined - so that the primitive locking methods are available
|
| +** as services to help with the implementation of proxy locking.
|
| +**
|
| +****
|
| +**
|
| +** The default locking schemes in SQLite use byte-range locks on the
|
| +** database file to coordinate safe, concurrent access by multiple readers
|
| +** and writers [http://sqlite.org/lockingv3.html]. The five file locking
|
| +** states (UNLOCKED, PENDING, SHARED, RESERVED, EXCLUSIVE) are implemented
|
| +** as POSIX read & write locks over fixed set of locations (via fsctl),
|
| +** on AFP and SMB only exclusive byte-range locks are available via fsctl
|
| +** with _IOWR('z', 23, struct ByteRangeLockPB2) to track the same 5 states.
|
| +** To simulate a F_RDLCK on the shared range, on AFP a randomly selected
|
| +** address in the shared range is taken for a SHARED lock, the entire
|
| +** shared range is taken for an EXCLUSIVE lock):
|
| +**
|
| +** PENDING_BYTE 0x40000000
|
| +** RESERVED_BYTE 0x40000001
|
| +** SHARED_RANGE 0x40000002 -> 0x40000200
|
| +**
|
| +** This works well on the local file system, but shows a nearly 100x
|
| +** slowdown in read performance on AFP because the AFP client disables
|
| +** the read cache when byte-range locks are present. Enabling the read
|
| +** cache exposes a cache coherency problem that is present on all OS X
|
| +** supported network file systems. NFS and AFP both observe the
|
| +** close-to-open semantics for ensuring cache coherency
|
| +** [http://nfs.sourceforge.net/#faq_a8], which does not effectively
|
| +** address the requirements for concurrent database access by multiple
|
| +** readers and writers
|
| +** [http://www.nabble.com/SQLite-on-NFS-cache-coherency-td15655701.html].
|
| +**
|
| +** To address the performance and cache coherency issues, proxy file locking
|
| +** changes the way database access is controlled by limiting access to a
|
| +** single host at a time and moving file locks off of the database file
|
| +** and onto a proxy file on the local file system.
|
| +**
|
| +**
|
| +** Using proxy locks
|
| +** -----------------
|
| +**
|
| +** C APIs
|
| +**
|
| +** sqlite3_file_control(db, dbname, SQLITE_FCNTL_SET_LOCKPROXYFILE,
|
| +** <proxy_path> | ":auto:");
|
| +** sqlite3_file_control(db, dbname, SQLITE_FCNTL_GET_LOCKPROXYFILE,
|
| +** &<proxy_path>);
|
| +**
|
| +**
|
| +** SQL pragmas
|
| +**
|
| +** PRAGMA [database.]lock_proxy_file=<proxy_path> | :auto:
|
| +** PRAGMA [database.]lock_proxy_file
|
| +**
|
| +** Specifying ":auto:" means that if there is a conch file with a matching
|
| +** host ID in it, the proxy path in the conch file will be used, otherwise
|
| +** a proxy path based on the user's temp dir
|
| +** (via confstr(_CS_DARWIN_USER_TEMP_DIR,...)) will be used and the
|
| +** actual proxy file name is generated from the name and path of the
|
| +** database file. For example:
|
| +**
|
| +** For database path "/Users/me/foo.db"
|
| +** The lock path will be "<tmpdir>/sqliteplocks/_Users_me_foo.db:auto:")
|
| +**
|
| +** Once a lock proxy is configured for a database connection, it can not
|
| +** be removed, however it may be switched to a different proxy path via
|
| +** the above APIs (assuming the conch file is not being held by another
|
| +** connection or process).
|
| +**
|
| +**
|
| +** How proxy locking works
|
| +** -----------------------
|
| +**
|
| +** Proxy file locking relies primarily on two new supporting files:
|
| +**
|
| +** * conch file to limit access to the database file to a single host
|
| +** at a time
|
| +**
|
| +** * proxy file to act as a proxy for the advisory locks normally
|
| +** taken on the database
|
| +**
|
| +** The conch file - to use a proxy file, sqlite must first "hold the conch"
|
| +** by taking an sqlite-style shared lock on the conch file, reading the
|
| +** contents and comparing the host's unique host ID (see below) and lock
|
| +** proxy path against the values stored in the conch. The conch file is
|
| +** stored in the same directory as the database file and the file name
|
| +** is patterned after the database file name as ".<databasename>-conch".
|
| +** If the conch file does not exist, or its contents do not match the
|
| +** host ID and/or proxy path, then the lock is escalated to an exclusive
|
| +** lock and the conch file contents is updated with the host ID and proxy
|
| +** path and the lock is downgraded to a shared lock again. If the conch
|
| +** is held by another process (with a shared lock), the exclusive lock
|
| +** will fail and SQLITE_BUSY is returned.
|
| +**
|
| +** The proxy file - a single-byte file used for all advisory file locks
|
| +** normally taken on the database file. This allows for safe sharing
|
| +** of the database file for multiple readers and writers on the same
|
| +** host (the conch ensures that they all use the same local lock file).
|
| +**
|
| +** Requesting the lock proxy does not immediately take the conch, it is
|
| +** only taken when the first request to lock database file is made.
|
| +** This matches the semantics of the traditional locking behavior, where
|
| +** opening a connection to a database file does not take a lock on it.
|
| +** The shared lock and an open file descriptor are maintained until
|
| +** the connection to the database is closed.
|
| +**
|
| +** The proxy file and the lock file are never deleted so they only need
|
| +** to be created the first time they are used.
|
| +**
|
| +** Configuration options
|
| +** ---------------------
|
| +**
|
| +** SQLITE_PREFER_PROXY_LOCKING
|
| +**
|
| +** Database files accessed on non-local file systems are
|
| +** automatically configured for proxy locking, lock files are
|
| +** named automatically using the same logic as
|
| +** PRAGMA lock_proxy_file=":auto:"
|
| +**
|
| +** SQLITE_PROXY_DEBUG
|
| +**
|
| +** Enables the logging of error messages during host id file
|
| +** retrieval and creation
|
| +**
|
| +** LOCKPROXYDIR
|
| +**
|
| +** Overrides the default directory used for lock proxy files that
|
| +** are named automatically via the ":auto:" setting
|
| +**
|
| +** SQLITE_DEFAULT_PROXYDIR_PERMISSIONS
|
| +**
|
| +** Permissions to use when creating a directory for storing the
|
| +** lock proxy files, only used when LOCKPROXYDIR is not set.
|
| +**
|
| +**
|
| +** As mentioned above, when compiled with SQLITE_PREFER_PROXY_LOCKING,
|
| +** setting the environment variable SQLITE_FORCE_PROXY_LOCKING to 1 will
|
| +** force proxy locking to be used for every database file opened, and 0
|
| +** will force automatic proxy locking to be disabled for all database
|
| +** files (explicitly calling the SQLITE_FCNTL_SET_LOCKPROXYFILE pragma or
|
| +** sqlite_file_control API is not affected by SQLITE_FORCE_PROXY_LOCKING).
|
| +*/
|
| +
|
| +/*
|
| +** Proxy locking is only available on MacOSX
|
| +*/
|
| +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
|
| +
|
| +/*
|
| +** The proxyLockingContext has the path and file structures for the remote
|
| +** and local proxy files in it
|
| +*/
|
| +typedef struct proxyLockingContext proxyLockingContext;
|
| +struct proxyLockingContext {
|
| + unixFile *conchFile; /* Open conch file */
|
| + char *conchFilePath; /* Name of the conch file */
|
| + unixFile *lockProxy; /* Open proxy lock file */
|
| + char *lockProxyPath; /* Name of the proxy lock file */
|
| + char *dbPath; /* Name of the open file */
|
| + int conchHeld; /* 1 if the conch is held, -1 if lockless */
|
| + int nFails; /* Number of conch taking failures */
|
| + void *oldLockingContext; /* Original lockingcontext to restore on close */
|
| + sqlite3_io_methods const *pOldMethod; /* Original I/O methods for close */
|
| +};
|
| +
|
| +/*
|
| +** The proxy lock file path for the database at dbPath is written into lPath,
|
| +** which must point to valid, writable memory large enough for a maxLen length
|
| +** file path.
|
| +*/
|
| +static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){
|
| + int len;
|
| + int dbLen;
|
| + int i;
|
| +
|
| +#ifdef LOCKPROXYDIR
|
| + len = strlcpy(lPath, LOCKPROXYDIR, maxLen);
|
| +#else
|
| +# ifdef _CS_DARWIN_USER_TEMP_DIR
|
| + {
|
| + if( !confstr(_CS_DARWIN_USER_TEMP_DIR, lPath, maxLen) ){
|
| + OSTRACE(("GETLOCKPATH failed %s errno=%d pid=%d\n",
|
| + lPath, errno, osGetpid(0)));
|
| + return SQLITE_IOERR_LOCK;
|
| + }
|
| + len = strlcat(lPath, "sqliteplocks", maxLen);
|
| + }
|
| +# else
|
| + len = strlcpy(lPath, "/tmp/", maxLen);
|
| +# endif
|
| +#endif
|
| +
|
| + if( lPath[len-1]!='/' ){
|
| + len = strlcat(lPath, "/", maxLen);
|
| + }
|
| +
|
| + /* transform the db path to a unique cache name */
|
| + dbLen = (int)strlen(dbPath);
|
| + for( i=0; i<dbLen && (i+len+7)<(int)maxLen; i++){
|
| + char c = dbPath[i];
|
| + lPath[i+len] = (c=='/')?'_':c;
|
| + }
|
| + lPath[i+len]='\0';
|
| + strlcat(lPath, ":auto:", maxLen);
|
| + OSTRACE(("GETLOCKPATH proxy lock path=%s pid=%d\n", lPath, osGetpid(0)));
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| + ** Creates the lock file and any missing directories in lockPath
|
| + */
|
| +static int proxyCreateLockPath(const char *lockPath){
|
| + int i, len;
|
| + char buf[MAXPATHLEN];
|
| + int start = 0;
|
| +
|
| + assert(lockPath!=NULL);
|
| + /* try to create all the intermediate directories */
|
| + len = (int)strlen(lockPath);
|
| + buf[0] = lockPath[0];
|
| + for( i=1; i<len; i++ ){
|
| + if( lockPath[i] == '/' && (i - start > 0) ){
|
| + /* only mkdir if leaf dir != "." or "/" or ".." */
|
| + if( i-start>2 || (i-start==1 && buf[start] != '.' && buf[start] != '/')
|
| + || (i-start==2 && buf[start] != '.' && buf[start+1] != '.') ){
|
| + buf[i]='\0';
|
| + if( osMkdir(buf, SQLITE_DEFAULT_PROXYDIR_PERMISSIONS) ){
|
| + int err=errno;
|
| + if( err!=EEXIST ) {
|
| + OSTRACE(("CREATELOCKPATH FAILED creating %s, "
|
| + "'%s' proxy lock path=%s pid=%d\n",
|
| + buf, strerror(err), lockPath, osGetpid(0)));
|
| + return err;
|
| + }
|
| + }
|
| + }
|
| + start=i+1;
|
| + }
|
| + buf[i] = lockPath[i];
|
| + }
|
| + OSTRACE(("CREATELOCKPATH proxy lock path=%s pid=%d\n",lockPath,osGetpid(0)));
|
| + return 0;
|
| +}
|
| +
|
| +/*
|
| +** Create a new VFS file descriptor (stored in memory obtained from
|
| +** sqlite3_malloc) and open the file named "path" in the file descriptor.
|
| +**
|
| +** The caller is responsible not only for closing the file descriptor
|
| +** but also for freeing the memory associated with the file descriptor.
|
| +*/
|
| +static int proxyCreateUnixFile(
|
| + const char *path, /* path for the new unixFile */
|
| + unixFile **ppFile, /* unixFile created and returned by ref */
|
| + int islockfile /* if non zero missing dirs will be created */
|
| +) {
|
| + int fd = -1;
|
| + unixFile *pNew;
|
| + int rc = SQLITE_OK;
|
| + int openFlags = O_RDWR | O_CREAT;
|
| + sqlite3_vfs dummyVfs;
|
| + int terrno = 0;
|
| + UnixUnusedFd *pUnused = NULL;
|
| +
|
| + /* 1. first try to open/create the file
|
| + ** 2. if that fails, and this is a lock file (not-conch), try creating
|
| + ** the parent directories and then try again.
|
| + ** 3. if that fails, try to open the file read-only
|
| + ** otherwise return BUSY (if lock file) or CANTOPEN for the conch file
|
| + */
|
| + pUnused = findReusableFd(path, openFlags);
|
| + if( pUnused ){
|
| + fd = pUnused->fd;
|
| + }else{
|
| + pUnused = sqlite3_malloc64(sizeof(*pUnused));
|
| + if( !pUnused ){
|
| + return SQLITE_NOMEM_BKPT;
|
| + }
|
| + }
|
| + if( fd<0 ){
|
| + fd = robust_open(path, openFlags, 0);
|
| + terrno = errno;
|
| + if( fd<0 && errno==ENOENT && islockfile ){
|
| + if( proxyCreateLockPath(path) == SQLITE_OK ){
|
| + fd = robust_open(path, openFlags, 0);
|
| + }
|
| + }
|
| + }
|
| + if( fd<0 ){
|
| + openFlags = O_RDONLY;
|
| + fd = robust_open(path, openFlags, 0);
|
| + terrno = errno;
|
| + }
|
| + if( fd<0 ){
|
| + if( islockfile ){
|
| + return SQLITE_BUSY;
|
| + }
|
| + switch (terrno) {
|
| + case EACCES:
|
| + return SQLITE_PERM;
|
| + case EIO:
|
| + return SQLITE_IOERR_LOCK; /* even though it is the conch */
|
| + default:
|
| + return SQLITE_CANTOPEN_BKPT;
|
| + }
|
| + }
|
| +
|
| + pNew = (unixFile *)sqlite3_malloc64(sizeof(*pNew));
|
| + if( pNew==NULL ){
|
| + rc = SQLITE_NOMEM_BKPT;
|
| + goto end_create_proxy;
|
| + }
|
| + memset(pNew, 0, sizeof(unixFile));
|
| + pNew->openFlags = openFlags;
|
| + memset(&dummyVfs, 0, sizeof(dummyVfs));
|
| + dummyVfs.pAppData = (void*)&autolockIoFinder;
|
| + dummyVfs.zName = "dummy";
|
| + pUnused->fd = fd;
|
| + pUnused->flags = openFlags;
|
| + pNew->pUnused = pUnused;
|
| +
|
| + rc = fillInUnixFile(&dummyVfs, fd, (sqlite3_file*)pNew, path, 0);
|
| + if( rc==SQLITE_OK ){
|
| + *ppFile = pNew;
|
| + return SQLITE_OK;
|
| + }
|
| +end_create_proxy:
|
| + robust_close(pNew, fd, __LINE__);
|
| + sqlite3_free(pNew);
|
| + sqlite3_free(pUnused);
|
| + return rc;
|
| +}
|
| +
|
| +#ifdef SQLITE_TEST
|
| +/* simulate multiple hosts by creating unique hostid file paths */
|
| +SQLITE_API int sqlite3_hostid_num = 0;
|
| +#endif
|
| +
|
| +#define PROXY_HOSTIDLEN 16 /* conch file host id length */
|
| +
|
| +#ifdef HAVE_GETHOSTUUID
|
| +/* Not always defined in the headers as it ought to be */
|
| +extern int gethostuuid(uuid_t id, const struct timespec *wait);
|
| +#endif
|
| +
|
| +/* get the host ID via gethostuuid(), pHostID must point to PROXY_HOSTIDLEN
|
| +** bytes of writable memory.
|
| +*/
|
| +static int proxyGetHostID(unsigned char *pHostID, int *pError){
|
| + assert(PROXY_HOSTIDLEN == sizeof(uuid_t));
|
| + memset(pHostID, 0, PROXY_HOSTIDLEN);
|
| +#ifdef HAVE_GETHOSTUUID
|
| + {
|
| + struct timespec timeout = {1, 0}; /* 1 sec timeout */
|
| + if( gethostuuid(pHostID, &timeout) ){
|
| + int err = errno;
|
| + if( pError ){
|
| + *pError = err;
|
| + }
|
| + return SQLITE_IOERR;
|
| + }
|
| + }
|
| +#else
|
| + UNUSED_PARAMETER(pError);
|
| +#endif
|
| +#ifdef SQLITE_TEST
|
| + /* simulate multiple hosts by creating unique hostid file paths */
|
| + if( sqlite3_hostid_num != 0){
|
| + pHostID[0] = (char)(pHostID[0] + (char)(sqlite3_hostid_num & 0xFF));
|
| + }
|
| +#endif
|
| +
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/* The conch file contains the header, host id and lock file path
|
| + */
|
| +#define PROXY_CONCHVERSION 2 /* 1-byte header, 16-byte host id, path */
|
| +#define PROXY_HEADERLEN 1 /* conch file header length */
|
| +#define PROXY_PATHINDEX (PROXY_HEADERLEN+PROXY_HOSTIDLEN)
|
| +#define PROXY_MAXCONCHLEN (PROXY_HEADERLEN+PROXY_HOSTIDLEN+MAXPATHLEN)
|
| +
|
| +/*
|
| +** Takes an open conch file, copies the contents to a new path and then moves
|
| +** it back. The newly created file's file descriptor is assigned to the
|
| +** conch file structure and finally the original conch file descriptor is
|
| +** closed. Returns zero if successful.
|
| +*/
|
| +static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){
|
| + proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
|
| + unixFile *conchFile = pCtx->conchFile;
|
| + char tPath[MAXPATHLEN];
|
| + char buf[PROXY_MAXCONCHLEN];
|
| + char *cPath = pCtx->conchFilePath;
|
| + size_t readLen = 0;
|
| + size_t pathLen = 0;
|
| + char errmsg[64] = "";
|
| + int fd = -1;
|
| + int rc = -1;
|
| + UNUSED_PARAMETER(myHostID);
|
| +
|
| + /* create a new path by replace the trailing '-conch' with '-break' */
|
| + pathLen = strlcpy(tPath, cPath, MAXPATHLEN);
|
| + if( pathLen>MAXPATHLEN || pathLen<6 ||
|
| + (strlcpy(&tPath[pathLen-5], "break", 6) != 5) ){
|
| + sqlite3_snprintf(sizeof(errmsg),errmsg,"path error (len %d)",(int)pathLen);
|
| + goto end_breaklock;
|
| + }
|
| + /* read the conch content */
|
| + readLen = osPread(conchFile->h, buf, PROXY_MAXCONCHLEN, 0);
|
| + if( readLen<PROXY_PATHINDEX ){
|
| + sqlite3_snprintf(sizeof(errmsg),errmsg,"read error (len %d)",(int)readLen);
|
| + goto end_breaklock;
|
| + }
|
| + /* write it out to the temporary break file */
|
| + fd = robust_open(tPath, (O_RDWR|O_CREAT|O_EXCL), 0);
|
| + if( fd<0 ){
|
| + sqlite3_snprintf(sizeof(errmsg), errmsg, "create failed (%d)", errno);
|
| + goto end_breaklock;
|
| + }
|
| + if( osPwrite(fd, buf, readLen, 0) != (ssize_t)readLen ){
|
| + sqlite3_snprintf(sizeof(errmsg), errmsg, "write failed (%d)", errno);
|
| + goto end_breaklock;
|
| + }
|
| + if( rename(tPath, cPath) ){
|
| + sqlite3_snprintf(sizeof(errmsg), errmsg, "rename failed (%d)", errno);
|
| + goto end_breaklock;
|
| + }
|
| + rc = 0;
|
| + fprintf(stderr, "broke stale lock on %s\n", cPath);
|
| + robust_close(pFile, conchFile->h, __LINE__);
|
| + conchFile->h = fd;
|
| + conchFile->openFlags = O_RDWR | O_CREAT;
|
| +
|
| +end_breaklock:
|
| + if( rc ){
|
| + if( fd>=0 ){
|
| + osUnlink(tPath);
|
| + robust_close(pFile, fd, __LINE__);
|
| + }
|
| + fprintf(stderr, "failed to break stale lock on %s, %s\n", cPath, errmsg);
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +/* Take the requested lock on the conch file and break a stale lock if the
|
| +** host id matches.
|
| +*/
|
| +static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){
|
| + proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
|
| + unixFile *conchFile = pCtx->conchFile;
|
| + int rc = SQLITE_OK;
|
| + int nTries = 0;
|
| + struct timespec conchModTime;
|
| +
|
| + memset(&conchModTime, 0, sizeof(conchModTime));
|
| + do {
|
| + rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, lockType);
|
| + nTries ++;
|
| + if( rc==SQLITE_BUSY ){
|
| + /* If the lock failed (busy):
|
| + * 1st try: get the mod time of the conch, wait 0.5s and try again.
|
| + * 2nd try: fail if the mod time changed or host id is different, wait
|
| + * 10 sec and try again
|
| + * 3rd try: break the lock unless the mod time has changed.
|
| + */
|
| + struct stat buf;
|
| + if( osFstat(conchFile->h, &buf) ){
|
| + storeLastErrno(pFile, errno);
|
| + return SQLITE_IOERR_LOCK;
|
| + }
|
| +
|
| + if( nTries==1 ){
|
| + conchModTime = buf.st_mtimespec;
|
| + usleep(500000); /* wait 0.5 sec and try the lock again*/
|
| + continue;
|
| + }
|
| +
|
| + assert( nTries>1 );
|
| + if( conchModTime.tv_sec != buf.st_mtimespec.tv_sec ||
|
| + conchModTime.tv_nsec != buf.st_mtimespec.tv_nsec ){
|
| + return SQLITE_BUSY;
|
| + }
|
| +
|
| + if( nTries==2 ){
|
| + char tBuf[PROXY_MAXCONCHLEN];
|
| + int len = osPread(conchFile->h, tBuf, PROXY_MAXCONCHLEN, 0);
|
| + if( len<0 ){
|
| + storeLastErrno(pFile, errno);
|
| + return SQLITE_IOERR_LOCK;
|
| + }
|
| + if( len>PROXY_PATHINDEX && tBuf[0]==(char)PROXY_CONCHVERSION){
|
| + /* don't break the lock if the host id doesn't match */
|
| + if( 0!=memcmp(&tBuf[PROXY_HEADERLEN], myHostID, PROXY_HOSTIDLEN) ){
|
| + return SQLITE_BUSY;
|
| + }
|
| + }else{
|
| + /* don't break the lock on short read or a version mismatch */
|
| + return SQLITE_BUSY;
|
| + }
|
| + usleep(10000000); /* wait 10 sec and try the lock again */
|
| + continue;
|
| + }
|
| +
|
| + assert( nTries==3 );
|
| + if( 0==proxyBreakConchLock(pFile, myHostID) ){
|
| + rc = SQLITE_OK;
|
| + if( lockType==EXCLUSIVE_LOCK ){
|
| + rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, SHARED_LOCK);
|
| + }
|
| + if( !rc ){
|
| + rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, lockType);
|
| + }
|
| + }
|
| + }
|
| + } while( rc==SQLITE_BUSY && nTries<3 );
|
| +
|
| + return rc;
|
| +}
|
| +
|
| +/* Takes the conch by taking a shared lock and read the contents conch, if
|
| +** lockPath is non-NULL, the host ID and lock file path must match. A NULL
|
| +** lockPath means that the lockPath in the conch file will be used if the
|
| +** host IDs match, or a new lock path will be generated automatically
|
| +** and written to the conch file.
|
| +*/
|
| +static int proxyTakeConch(unixFile *pFile){
|
| + proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
|
| +
|
| + if( pCtx->conchHeld!=0 ){
|
| + return SQLITE_OK;
|
| + }else{
|
| + unixFile *conchFile = pCtx->conchFile;
|
| + uuid_t myHostID;
|
| + int pError = 0;
|
| + char readBuf[PROXY_MAXCONCHLEN];
|
| + char lockPath[MAXPATHLEN];
|
| + char *tempLockPath = NULL;
|
| + int rc = SQLITE_OK;
|
| + int createConch = 0;
|
| + int hostIdMatch = 0;
|
| + int readLen = 0;
|
| + int tryOldLockPath = 0;
|
| + int forceNewLockPath = 0;
|
| +
|
| + OSTRACE(("TAKECONCH %d for %s pid=%d\n", conchFile->h,
|
| + (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"),
|
| + osGetpid(0)));
|
| +
|
| + rc = proxyGetHostID(myHostID, &pError);
|
| + if( (rc&0xff)==SQLITE_IOERR ){
|
| + storeLastErrno(pFile, pError);
|
| + goto end_takeconch;
|
| + }
|
| + rc = proxyConchLock(pFile, myHostID, SHARED_LOCK);
|
| + if( rc!=SQLITE_OK ){
|
| + goto end_takeconch;
|
| + }
|
| + /* read the existing conch file */
|
| + readLen = seekAndRead((unixFile*)conchFile, 0, readBuf, PROXY_MAXCONCHLEN);
|
| + if( readLen<0 ){
|
| + /* I/O error: lastErrno set by seekAndRead */
|
| + storeLastErrno(pFile, conchFile->lastErrno);
|
| + rc = SQLITE_IOERR_READ;
|
| + goto end_takeconch;
|
| + }else if( readLen<=(PROXY_HEADERLEN+PROXY_HOSTIDLEN) ||
|
| + readBuf[0]!=(char)PROXY_CONCHVERSION ){
|
| + /* a short read or version format mismatch means we need to create a new
|
| + ** conch file.
|
| + */
|
| + createConch = 1;
|
| + }
|
| + /* if the host id matches and the lock path already exists in the conch
|
| + ** we'll try to use the path there, if we can't open that path, we'll
|
| + ** retry with a new auto-generated path
|
| + */
|
| + do { /* in case we need to try again for an :auto: named lock file */
|
| +
|
| + if( !createConch && !forceNewLockPath ){
|
| + hostIdMatch = !memcmp(&readBuf[PROXY_HEADERLEN], myHostID,
|
| + PROXY_HOSTIDLEN);
|
| + /* if the conch has data compare the contents */
|
| + if( !pCtx->lockProxyPath ){
|
| + /* for auto-named local lock file, just check the host ID and we'll
|
| + ** use the local lock file path that's already in there
|
| + */
|
| + if( hostIdMatch ){
|
| + size_t pathLen = (readLen - PROXY_PATHINDEX);
|
| +
|
| + if( pathLen>=MAXPATHLEN ){
|
| + pathLen=MAXPATHLEN-1;
|
| + }
|
| + memcpy(lockPath, &readBuf[PROXY_PATHINDEX], pathLen);
|
| + lockPath[pathLen] = 0;
|
| + tempLockPath = lockPath;
|
| + tryOldLockPath = 1;
|
| + /* create a copy of the lock path if the conch is taken */
|
| + goto end_takeconch;
|
| + }
|
| + }else if( hostIdMatch
|
| + && !strncmp(pCtx->lockProxyPath, &readBuf[PROXY_PATHINDEX],
|
| + readLen-PROXY_PATHINDEX)
|
| + ){
|
| + /* conch host and lock path match */
|
| + goto end_takeconch;
|
| + }
|
| + }
|
| +
|
| + /* if the conch isn't writable and doesn't match, we can't take it */
|
| + if( (conchFile->openFlags&O_RDWR) == 0 ){
|
| + rc = SQLITE_BUSY;
|
| + goto end_takeconch;
|
| + }
|
| +
|
| + /* either the conch didn't match or we need to create a new one */
|
| + if( !pCtx->lockProxyPath ){
|
| + proxyGetLockPath(pCtx->dbPath, lockPath, MAXPATHLEN);
|
| + tempLockPath = lockPath;
|
| + /* create a copy of the lock path _only_ if the conch is taken */
|
| + }
|
| +
|
| + /* update conch with host and path (this will fail if other process
|
| + ** has a shared lock already), if the host id matches, use the big
|
| + ** stick.
|
| + */
|
| + futimes(conchFile->h, NULL);
|
| + if( hostIdMatch && !createConch ){
|
| + if( conchFile->pInode && conchFile->pInode->nShared>1 ){
|
| + /* We are trying for an exclusive lock but another thread in this
|
| + ** same process is still holding a shared lock. */
|
| + rc = SQLITE_BUSY;
|
| + } else {
|
| + rc = proxyConchLock(pFile, myHostID, EXCLUSIVE_LOCK);
|
| + }
|
| + }else{
|
| + rc = proxyConchLock(pFile, myHostID, EXCLUSIVE_LOCK);
|
| + }
|
| + if( rc==SQLITE_OK ){
|
| + char writeBuffer[PROXY_MAXCONCHLEN];
|
| + int writeSize = 0;
|
| +
|
| + writeBuffer[0] = (char)PROXY_CONCHVERSION;
|
| + memcpy(&writeBuffer[PROXY_HEADERLEN], myHostID, PROXY_HOSTIDLEN);
|
| + if( pCtx->lockProxyPath!=NULL ){
|
| + strlcpy(&writeBuffer[PROXY_PATHINDEX], pCtx->lockProxyPath,
|
| + MAXPATHLEN);
|
| + }else{
|
| + strlcpy(&writeBuffer[PROXY_PATHINDEX], tempLockPath, MAXPATHLEN);
|
| + }
|
| + writeSize = PROXY_PATHINDEX + strlen(&writeBuffer[PROXY_PATHINDEX]);
|
| + robust_ftruncate(conchFile->h, writeSize);
|
| + rc = unixWrite((sqlite3_file *)conchFile, writeBuffer, writeSize, 0);
|
| + full_fsync(conchFile->h,0,0);
|
| + /* If we created a new conch file (not just updated the contents of a
|
| + ** valid conch file), try to match the permissions of the database
|
| + */
|
| + if( rc==SQLITE_OK && createConch ){
|
| + struct stat buf;
|
| + int err = osFstat(pFile->h, &buf);
|
| + if( err==0 ){
|
| + mode_t cmode = buf.st_mode&(S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP |
|
| + S_IROTH|S_IWOTH);
|
| + /* try to match the database file R/W permissions, ignore failure */
|
| +#ifndef SQLITE_PROXY_DEBUG
|
| + osFchmod(conchFile->h, cmode);
|
| +#else
|
| + do{
|
| + rc = osFchmod(conchFile->h, cmode);
|
| + }while( rc==(-1) && errno==EINTR );
|
| + if( rc!=0 ){
|
| + int code = errno;
|
| + fprintf(stderr, "fchmod %o FAILED with %d %s\n",
|
| + cmode, code, strerror(code));
|
| + } else {
|
| + fprintf(stderr, "fchmod %o SUCCEDED\n",cmode);
|
| + }
|
| + }else{
|
| + int code = errno;
|
| + fprintf(stderr, "STAT FAILED[%d] with %d %s\n",
|
| + err, code, strerror(code));
|
| +#endif
|
| + }
|
| + }
|
| + }
|
| + conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, SHARED_LOCK);
|
| +
|
| + end_takeconch:
|
| + OSTRACE(("TRANSPROXY: CLOSE %d\n", pFile->h));
|
| + if( rc==SQLITE_OK && pFile->openFlags ){
|
| + int fd;
|
| + if( pFile->h>=0 ){
|
| + robust_close(pFile, pFile->h, __LINE__);
|
| + }
|
| + pFile->h = -1;
|
| + fd = robust_open(pCtx->dbPath, pFile->openFlags, 0);
|
| + OSTRACE(("TRANSPROXY: OPEN %d\n", fd));
|
| + if( fd>=0 ){
|
| + pFile->h = fd;
|
| + }else{
|
| + rc=SQLITE_CANTOPEN_BKPT; /* SQLITE_BUSY? proxyTakeConch called
|
| + during locking */
|
| + }
|
| + }
|
| + if( rc==SQLITE_OK && !pCtx->lockProxy ){
|
| + char *path = tempLockPath ? tempLockPath : pCtx->lockProxyPath;
|
| + rc = proxyCreateUnixFile(path, &pCtx->lockProxy, 1);
|
| + if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM && tryOldLockPath ){
|
| + /* we couldn't create the proxy lock file with the old lock file path
|
| + ** so try again via auto-naming
|
| + */
|
| + forceNewLockPath = 1;
|
| + tryOldLockPath = 0;
|
| + continue; /* go back to the do {} while start point, try again */
|
| + }
|
| + }
|
| + if( rc==SQLITE_OK ){
|
| + /* Need to make a copy of path if we extracted the value
|
| + ** from the conch file or the path was allocated on the stack
|
| + */
|
| + if( tempLockPath ){
|
| + pCtx->lockProxyPath = sqlite3DbStrDup(0, tempLockPath);
|
| + if( !pCtx->lockProxyPath ){
|
| + rc = SQLITE_NOMEM_BKPT;
|
| + }
|
| + }
|
| + }
|
| + if( rc==SQLITE_OK ){
|
| + pCtx->conchHeld = 1;
|
| +
|
| + if( pCtx->lockProxy->pMethod == &afpIoMethods ){
|
| + afpLockingContext *afpCtx;
|
| + afpCtx = (afpLockingContext *)pCtx->lockProxy->lockingContext;
|
| + afpCtx->dbPath = pCtx->lockProxyPath;
|
| + }
|
| + } else {
|
| + conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, NO_LOCK);
|
| + }
|
| + OSTRACE(("TAKECONCH %d %s\n", conchFile->h,
|
| + rc==SQLITE_OK?"ok":"failed"));
|
| + return rc;
|
| + } while (1); /* in case we need to retry the :auto: lock file -
|
| + ** we should never get here except via the 'continue' call. */
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** If pFile holds a lock on a conch file, then release that lock.
|
| +*/
|
| +static int proxyReleaseConch(unixFile *pFile){
|
| + int rc = SQLITE_OK; /* Subroutine return code */
|
| + proxyLockingContext *pCtx; /* The locking context for the proxy lock */
|
| + unixFile *conchFile; /* Name of the conch file */
|
| +
|
| + pCtx = (proxyLockingContext *)pFile->lockingContext;
|
| + conchFile = pCtx->conchFile;
|
| + OSTRACE(("RELEASECONCH %d for %s pid=%d\n", conchFile->h,
|
| + (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"),
|
| + osGetpid(0)));
|
| + if( pCtx->conchHeld>0 ){
|
| + rc = conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, NO_LOCK);
|
| + }
|
| + pCtx->conchHeld = 0;
|
| + OSTRACE(("RELEASECONCH %d %s\n", conchFile->h,
|
| + (rc==SQLITE_OK ? "ok" : "failed")));
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Given the name of a database file, compute the name of its conch file.
|
| +** Store the conch filename in memory obtained from sqlite3_malloc64().
|
| +** Make *pConchPath point to the new name. Return SQLITE_OK on success
|
| +** or SQLITE_NOMEM if unable to obtain memory.
|
| +**
|
| +** The caller is responsible for ensuring that the allocated memory
|
| +** space is eventually freed.
|
| +**
|
| +** *pConchPath is set to NULL if a memory allocation error occurs.
|
| +*/
|
| +static int proxyCreateConchPathname(char *dbPath, char **pConchPath){
|
| + int i; /* Loop counter */
|
| + int len = (int)strlen(dbPath); /* Length of database filename - dbPath */
|
| + char *conchPath; /* buffer in which to construct conch name */
|
| +
|
| + /* Allocate space for the conch filename and initialize the name to
|
| + ** the name of the original database file. */
|
| + *pConchPath = conchPath = (char *)sqlite3_malloc64(len + 8);
|
| + if( conchPath==0 ){
|
| + return SQLITE_NOMEM_BKPT;
|
| + }
|
| + memcpy(conchPath, dbPath, len+1);
|
| +
|
| + /* now insert a "." before the last / character */
|
| + for( i=(len-1); i>=0; i-- ){
|
| + if( conchPath[i]=='/' ){
|
| + i++;
|
| + break;
|
| + }
|
| + }
|
| + conchPath[i]='.';
|
| + while ( i<len ){
|
| + conchPath[i+1]=dbPath[i];
|
| + i++;
|
| + }
|
| +
|
| + /* append the "-conch" suffix to the file */
|
| + memcpy(&conchPath[i+1], "-conch", 7);
|
| + assert( (int)strlen(conchPath) == len+7 );
|
| +
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +
|
| +/* Takes a fully configured proxy locking-style unix file and switches
|
| +** the local lock file path
|
| +*/
|
| +static int switchLockProxyPath(unixFile *pFile, const char *path) {
|
| + proxyLockingContext *pCtx = (proxyLockingContext*)pFile->lockingContext;
|
| + char *oldPath = pCtx->lockProxyPath;
|
| + int rc = SQLITE_OK;
|
| +
|
| + if( pFile->eFileLock!=NO_LOCK ){
|
| + return SQLITE_BUSY;
|
| + }
|
| +
|
| + /* nothing to do if the path is NULL, :auto: or matches the existing path */
|
| + if( !path || path[0]=='\0' || !strcmp(path, ":auto:") ||
|
| + (oldPath && !strncmp(oldPath, path, MAXPATHLEN)) ){
|
| + return SQLITE_OK;
|
| + }else{
|
| + unixFile *lockProxy = pCtx->lockProxy;
|
| + pCtx->lockProxy=NULL;
|
| + pCtx->conchHeld = 0;
|
| + if( lockProxy!=NULL ){
|
| + rc=lockProxy->pMethod->xClose((sqlite3_file *)lockProxy);
|
| + if( rc ) return rc;
|
| + sqlite3_free(lockProxy);
|
| + }
|
| + sqlite3_free(oldPath);
|
| + pCtx->lockProxyPath = sqlite3DbStrDup(0, path);
|
| + }
|
| +
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** pFile is a file that has been opened by a prior xOpen call. dbPath
|
| +** is a string buffer at least MAXPATHLEN+1 characters in size.
|
| +**
|
| +** This routine find the filename associated with pFile and writes it
|
| +** int dbPath.
|
| +*/
|
| +static int proxyGetDbPathForUnixFile(unixFile *pFile, char *dbPath){
|
| +#if defined(__APPLE__)
|
| + if( pFile->pMethod == &afpIoMethods ){
|
| + /* afp style keeps a reference to the db path in the filePath field
|
| + ** of the struct */
|
| + assert( (int)strlen((char*)pFile->lockingContext)<=MAXPATHLEN );
|
| + strlcpy(dbPath, ((afpLockingContext *)pFile->lockingContext)->dbPath,
|
| + MAXPATHLEN);
|
| + } else
|
| +#endif
|
| + if( pFile->pMethod == &dotlockIoMethods ){
|
| + /* dot lock style uses the locking context to store the dot lock
|
| + ** file path */
|
| + int len = strlen((char *)pFile->lockingContext) - strlen(DOTLOCK_SUFFIX);
|
| + memcpy(dbPath, (char *)pFile->lockingContext, len + 1);
|
| + }else{
|
| + /* all other styles use the locking context to store the db file path */
|
| + assert( strlen((char*)pFile->lockingContext)<=MAXPATHLEN );
|
| + strlcpy(dbPath, (char *)pFile->lockingContext, MAXPATHLEN);
|
| + }
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Takes an already filled in unix file and alters it so all file locking
|
| +** will be performed on the local proxy lock file. The following fields
|
| +** are preserved in the locking context so that they can be restored and
|
| +** the unix structure properly cleaned up at close time:
|
| +** ->lockingContext
|
| +** ->pMethod
|
| +*/
|
| +static int proxyTransformUnixFile(unixFile *pFile, const char *path) {
|
| + proxyLockingContext *pCtx;
|
| + char dbPath[MAXPATHLEN+1]; /* Name of the database file */
|
| + char *lockPath=NULL;
|
| + int rc = SQLITE_OK;
|
| +
|
| + if( pFile->eFileLock!=NO_LOCK ){
|
| + return SQLITE_BUSY;
|
| + }
|
| + proxyGetDbPathForUnixFile(pFile, dbPath);
|
| + if( !path || path[0]=='\0' || !strcmp(path, ":auto:") ){
|
| + lockPath=NULL;
|
| + }else{
|
| + lockPath=(char *)path;
|
| + }
|
| +
|
| + OSTRACE(("TRANSPROXY %d for %s pid=%d\n", pFile->h,
|
| + (lockPath ? lockPath : ":auto:"), osGetpid(0)));
|
| +
|
| + pCtx = sqlite3_malloc64( sizeof(*pCtx) );
|
| + if( pCtx==0 ){
|
| + return SQLITE_NOMEM_BKPT;
|
| + }
|
| + memset(pCtx, 0, sizeof(*pCtx));
|
| +
|
| + rc = proxyCreateConchPathname(dbPath, &pCtx->conchFilePath);
|
| + if( rc==SQLITE_OK ){
|
| + rc = proxyCreateUnixFile(pCtx->conchFilePath, &pCtx->conchFile, 0);
|
| + if( rc==SQLITE_CANTOPEN && ((pFile->openFlags&O_RDWR) == 0) ){
|
| + /* if (a) the open flags are not O_RDWR, (b) the conch isn't there, and
|
| + ** (c) the file system is read-only, then enable no-locking access.
|
| + ** Ugh, since O_RDONLY==0x0000 we test for !O_RDWR since unixOpen asserts
|
| + ** that openFlags will have only one of O_RDONLY or O_RDWR.
|
| + */
|
| + struct statfs fsInfo;
|
| + struct stat conchInfo;
|
| + int goLockless = 0;
|
| +
|
| + if( osStat(pCtx->conchFilePath, &conchInfo) == -1 ) {
|
| + int err = errno;
|
| + if( (err==ENOENT) && (statfs(dbPath, &fsInfo) != -1) ){
|
| + goLockless = (fsInfo.f_flags&MNT_RDONLY) == MNT_RDONLY;
|
| + }
|
| + }
|
| + if( goLockless ){
|
| + pCtx->conchHeld = -1; /* read only FS/ lockless */
|
| + rc = SQLITE_OK;
|
| + }
|
| + }
|
| + }
|
| + if( rc==SQLITE_OK && lockPath ){
|
| + pCtx->lockProxyPath = sqlite3DbStrDup(0, lockPath);
|
| + }
|
| +
|
| + if( rc==SQLITE_OK ){
|
| + pCtx->dbPath = sqlite3DbStrDup(0, dbPath);
|
| + if( pCtx->dbPath==NULL ){
|
| + rc = SQLITE_NOMEM_BKPT;
|
| + }
|
| + }
|
| + if( rc==SQLITE_OK ){
|
| + /* all memory is allocated, proxys are created and assigned,
|
| + ** switch the locking context and pMethod then return.
|
| + */
|
| + pCtx->oldLockingContext = pFile->lockingContext;
|
| + pFile->lockingContext = pCtx;
|
| + pCtx->pOldMethod = pFile->pMethod;
|
| + pFile->pMethod = &proxyIoMethods;
|
| + }else{
|
| + if( pCtx->conchFile ){
|
| + pCtx->conchFile->pMethod->xClose((sqlite3_file *)pCtx->conchFile);
|
| + sqlite3_free(pCtx->conchFile);
|
| + }
|
| + sqlite3DbFree(0, pCtx->lockProxyPath);
|
| + sqlite3_free(pCtx->conchFilePath);
|
| + sqlite3_free(pCtx);
|
| + }
|
| + OSTRACE(("TRANSPROXY %d %s\n", pFile->h,
|
| + (rc==SQLITE_OK ? "ok" : "failed")));
|
| + return rc;
|
| +}
|
| +
|
| +
|
| +/*
|
| +** This routine handles sqlite3_file_control() calls that are specific
|
| +** to proxy locking.
|
| +*/
|
| +static int proxyFileControl(sqlite3_file *id, int op, void *pArg){
|
| + switch( op ){
|
| + case SQLITE_FCNTL_GET_LOCKPROXYFILE: {
|
| + unixFile *pFile = (unixFile*)id;
|
| + if( pFile->pMethod == &proxyIoMethods ){
|
| + proxyLockingContext *pCtx = (proxyLockingContext*)pFile->lockingContext;
|
| + proxyTakeConch(pFile);
|
| + if( pCtx->lockProxyPath ){
|
| + *(const char **)pArg = pCtx->lockProxyPath;
|
| + }else{
|
| + *(const char **)pArg = ":auto: (not held)";
|
| + }
|
| + } else {
|
| + *(const char **)pArg = NULL;
|
| + }
|
| + return SQLITE_OK;
|
| + }
|
| + case SQLITE_FCNTL_SET_LOCKPROXYFILE: {
|
| + unixFile *pFile = (unixFile*)id;
|
| + int rc = SQLITE_OK;
|
| + int isProxyStyle = (pFile->pMethod == &proxyIoMethods);
|
| + if( pArg==NULL || (const char *)pArg==0 ){
|
| + if( isProxyStyle ){
|
| + /* turn off proxy locking - not supported. If support is added for
|
| + ** switching proxy locking mode off then it will need to fail if
|
| + ** the journal mode is WAL mode.
|
| + */
|
| + rc = SQLITE_ERROR /*SQLITE_PROTOCOL? SQLITE_MISUSE?*/;
|
| + }else{
|
| + /* turn off proxy locking - already off - NOOP */
|
| + rc = SQLITE_OK;
|
| + }
|
| + }else{
|
| + const char *proxyPath = (const char *)pArg;
|
| + if( isProxyStyle ){
|
| + proxyLockingContext *pCtx =
|
| + (proxyLockingContext*)pFile->lockingContext;
|
| + if( !strcmp(pArg, ":auto:")
|
| + || (pCtx->lockProxyPath &&
|
| + !strncmp(pCtx->lockProxyPath, proxyPath, MAXPATHLEN))
|
| + ){
|
| + rc = SQLITE_OK;
|
| + }else{
|
| + rc = switchLockProxyPath(pFile, proxyPath);
|
| + }
|
| + }else{
|
| + /* turn on proxy file locking */
|
| + rc = proxyTransformUnixFile(pFile, proxyPath);
|
| + }
|
| + }
|
| + return rc;
|
| + }
|
| + default: {
|
| + assert( 0 ); /* The call assures that only valid opcodes are sent */
|
| + }
|
| + }
|
| + /*NOTREACHED*/
|
| + return SQLITE_ERROR;
|
| +}
|
| +
|
| +/*
|
| +** Within this division (the proxying locking implementation) the procedures
|
| +** above this point are all utilities. The lock-related methods of the
|
| +** proxy-locking sqlite3_io_method object follow.
|
| +*/
|
| +
|
| +
|
| +/*
|
| +** 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 proxyCheckReservedLock(sqlite3_file *id, int *pResOut) {
|
| + unixFile *pFile = (unixFile*)id;
|
| + int rc = proxyTakeConch(pFile);
|
| + if( rc==SQLITE_OK ){
|
| + proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
|
| + if( pCtx->conchHeld>0 ){
|
| + unixFile *proxy = pCtx->lockProxy;
|
| + return proxy->pMethod->xCheckReservedLock((sqlite3_file*)proxy, pResOut);
|
| + }else{ /* conchHeld < 0 is lockless */
|
| + pResOut=0;
|
| + }
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** 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
|
| +**
|
| +** This routine will only increase a lock. Use the sqlite3OsUnlock()
|
| +** routine to lower a locking level.
|
| +*/
|
| +static int proxyLock(sqlite3_file *id, int eFileLock) {
|
| + unixFile *pFile = (unixFile*)id;
|
| + int rc = proxyTakeConch(pFile);
|
| + if( rc==SQLITE_OK ){
|
| + proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
|
| + if( pCtx->conchHeld>0 ){
|
| + unixFile *proxy = pCtx->lockProxy;
|
| + rc = proxy->pMethod->xLock((sqlite3_file*)proxy, eFileLock);
|
| + pFile->eFileLock = proxy->eFileLock;
|
| + }else{
|
| + /* conchHeld < 0 is lockless */
|
| + }
|
| + }
|
| + 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 proxyUnlock(sqlite3_file *id, int eFileLock) {
|
| + unixFile *pFile = (unixFile*)id;
|
| + int rc = proxyTakeConch(pFile);
|
| + if( rc==SQLITE_OK ){
|
| + proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
|
| + if( pCtx->conchHeld>0 ){
|
| + unixFile *proxy = pCtx->lockProxy;
|
| + rc = proxy->pMethod->xUnlock((sqlite3_file*)proxy, eFileLock);
|
| + pFile->eFileLock = proxy->eFileLock;
|
| + }else{
|
| + /* conchHeld < 0 is lockless */
|
| + }
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Close a file that uses proxy locks.
|
| +*/
|
| +static int proxyClose(sqlite3_file *id) {
|
| + if( ALWAYS(id) ){
|
| + unixFile *pFile = (unixFile*)id;
|
| + proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
|
| + unixFile *lockProxy = pCtx->lockProxy;
|
| + unixFile *conchFile = pCtx->conchFile;
|
| + int rc = SQLITE_OK;
|
| +
|
| + if( lockProxy ){
|
| + rc = lockProxy->pMethod->xUnlock((sqlite3_file*)lockProxy, NO_LOCK);
|
| + if( rc ) return rc;
|
| + rc = lockProxy->pMethod->xClose((sqlite3_file*)lockProxy);
|
| + if( rc ) return rc;
|
| + sqlite3_free(lockProxy);
|
| + pCtx->lockProxy = 0;
|
| + }
|
| + if( conchFile ){
|
| + if( pCtx->conchHeld ){
|
| + rc = proxyReleaseConch(pFile);
|
| + if( rc ) return rc;
|
| + }
|
| + rc = conchFile->pMethod->xClose((sqlite3_file*)conchFile);
|
| + if( rc ) return rc;
|
| + sqlite3_free(conchFile);
|
| + }
|
| + sqlite3DbFree(0, pCtx->lockProxyPath);
|
| + sqlite3_free(pCtx->conchFilePath);
|
| + sqlite3DbFree(0, pCtx->dbPath);
|
| + /* restore the original locking context and pMethod then close it */
|
| + pFile->lockingContext = pCtx->oldLockingContext;
|
| + pFile->pMethod = pCtx->pOldMethod;
|
| + sqlite3_free(pCtx);
|
| + return pFile->pMethod->xClose(id);
|
| + }
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +
|
| +
|
| +#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */
|
| +/*
|
| +** The proxy locking style is intended for use with AFP filesystems.
|
| +** And since AFP is only supported on MacOSX, the proxy locking is also
|
| +** restricted to MacOSX.
|
| +**
|
| +**
|
| +******************* End of the proxy lock implementation **********************
|
| +******************************************************************************/
|
| +
|
| +/*
|
| +** Initialize the operating system interface.
|
| +**
|
| +** This routine registers all VFS implementations for unix-like operating
|
| +** systems. This routine, and the sqlite3_os_end() routine that follows,
|
| +** should be the only routines in this file that are visible from other
|
| +** files.
|
| +**
|
| +** This routine is called once during SQLite initialization and by a
|
| +** single thread. The memory allocation and mutex subsystems have not
|
| +** necessarily been initialized when this routine is called, and so they
|
| +** should not be used.
|
| +*/
|
| +SQLITE_API int sqlite3_os_init(void){
|
| + /*
|
| + ** The following macro defines an initializer for an sqlite3_vfs object.
|
| + ** The name of the VFS is NAME. The pAppData is a pointer to a pointer
|
| + ** to the "finder" function. (pAppData is a pointer to a pointer because
|
| + ** silly C90 rules prohibit a void* from being cast to a function pointer
|
| + ** and so we have to go through the intermediate pointer to avoid problems
|
| + ** when compiling with -pedantic-errors on GCC.)
|
| + **
|
| + ** The FINDER parameter to this macro is the name of the pointer to the
|
| + ** finder-function. The finder-function returns a pointer to the
|
| + ** sqlite_io_methods object that implements the desired locking
|
| + ** behaviors. See the division above that contains the IOMETHODS
|
| + ** macro for addition information on finder-functions.
|
| + **
|
| + ** Most finders simply return a pointer to a fixed sqlite3_io_methods
|
| + ** object. But the "autolockIoFinder" available on MacOSX does a little
|
| + ** more than that; it looks at the filesystem type that hosts the
|
| + ** database file and tries to choose an locking method appropriate for
|
| + ** that filesystem time.
|
| + */
|
| + #define UNIXVFS(VFSNAME, FINDER) { \
|
| + 3, /* iVersion */ \
|
| + sizeof(unixFile), /* szOsFile */ \
|
| + MAX_PATHNAME, /* mxPathname */ \
|
| + 0, /* pNext */ \
|
| + VFSNAME, /* zName */ \
|
| + (void*)&FINDER, /* pAppData */ \
|
| + unixOpen, /* xOpen */ \
|
| + unixDelete, /* xDelete */ \
|
| + unixAccess, /* xAccess */ \
|
| + unixFullPathname, /* xFullPathname */ \
|
| + unixDlOpen, /* xDlOpen */ \
|
| + unixDlError, /* xDlError */ \
|
| + unixDlSym, /* xDlSym */ \
|
| + unixDlClose, /* xDlClose */ \
|
| + unixRandomness, /* xRandomness */ \
|
| + unixSleep, /* xSleep */ \
|
| + unixCurrentTime, /* xCurrentTime */ \
|
| + unixGetLastError, /* xGetLastError */ \
|
| + unixCurrentTimeInt64, /* xCurrentTimeInt64 */ \
|
| + unixSetSystemCall, /* xSetSystemCall */ \
|
| + unixGetSystemCall, /* xGetSystemCall */ \
|
| + unixNextSystemCall, /* xNextSystemCall */ \
|
| + }
|
| +
|
| + /*
|
| + ** All default VFSes for unix are contained in the following array.
|
| + **
|
| + ** Note that the sqlite3_vfs.pNext field of the VFS object is modified
|
| + ** by the SQLite core when the VFS is registered. So the following
|
| + ** array cannot be const.
|
| + */
|
| + static sqlite3_vfs aVfs[] = {
|
| +#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
|
| + UNIXVFS("unix", autolockIoFinder ),
|
| +#elif OS_VXWORKS
|
| + UNIXVFS("unix", vxworksIoFinder ),
|
| +#else
|
| + UNIXVFS("unix", posixIoFinder ),
|
| +#endif
|
| + UNIXVFS("unix-none", nolockIoFinder ),
|
| + UNIXVFS("unix-dotfile", dotlockIoFinder ),
|
| + UNIXVFS("unix-excl", posixIoFinder ),
|
| +#if OS_VXWORKS
|
| + UNIXVFS("unix-namedsem", semIoFinder ),
|
| +#endif
|
| +#if SQLITE_ENABLE_LOCKING_STYLE || OS_VXWORKS
|
| + UNIXVFS("unix-posix", posixIoFinder ),
|
| +#endif
|
| +#if SQLITE_ENABLE_LOCKING_STYLE
|
| + UNIXVFS("unix-flock", flockIoFinder ),
|
| +#endif
|
| +#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
|
| + UNIXVFS("unix-afp", afpIoFinder ),
|
| + UNIXVFS("unix-nfs", nfsIoFinder ),
|
| + UNIXVFS("unix-proxy", proxyIoFinder ),
|
| +#endif
|
| + };
|
| + unsigned int i; /* Loop counter */
|
| +
|
| + /* Double-check that the aSyscall[] array has been constructed
|
| + ** correctly. See ticket [bb3a86e890c8e96ab] */
|
| + assert( ArraySize(aSyscall)==28 );
|
| +
|
| + /* Register all VFSes defined in the aVfs[] array */
|
| + for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
|
| + sqlite3_vfs_register(&aVfs[i], i==0);
|
| + }
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Shutdown the operating system interface.
|
| +**
|
| +** Some operating systems might need to do some cleanup in this routine,
|
| +** to release dynamically allocated objects. But not on unix.
|
| +** This routine is a no-op for unix.
|
| +*/
|
| +SQLITE_API int sqlite3_os_end(void){
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +#endif /* SQLITE_OS_UNIX */
|
| +
|
| +/************** End of os_unix.c *********************************************/
|
| +/************** Begin file os_win.c ******************************************/
|
| +/*
|
| +** 2004 May 22
|
| +**
|
| +** 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 code that is specific to Windows.
|
| +*/
|
| +/* #include "sqliteInt.h" */
|
| +#if SQLITE_OS_WIN /* This file is used for Windows only */
|
| +
|
| +/*
|
| +** Include code that is common to all os_*.c files
|
| +*/
|
| +/************** Include os_common.h in the middle of os_win.c ****************/
|
| +/************** Begin file os_common.h ***************************************/
|
| +/*
|
| +** 2004 May 22
|
| +**
|
| +** 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 macros and a little bit of code that is common to
|
| +** all of the platform-specific files (os_*.c) and is #included into those
|
| +** files.
|
| +**
|
| +** This file should be #included by the os_*.c files only. It is not a
|
| +** general purpose header file.
|
| +*/
|
| +#ifndef _OS_COMMON_H_
|
| +#define _OS_COMMON_H_
|
| +
|
| +/*
|
| +** At least two bugs have slipped in because we changed the MEMORY_DEBUG
|
| +** macro to SQLITE_DEBUG and some older makefiles have not yet made the
|
| +** switch. The following code should catch this problem at compile-time.
|
| +*/
|
| +#ifdef MEMORY_DEBUG
|
| +# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
|
| +#endif
|
| +
|
| +/*
|
| +** Macros for performance tracing. Normally turned off. Only works
|
| +** on i486 hardware.
|
| +*/
|
| +#ifdef SQLITE_PERFORMANCE_TRACE
|
| +
|
| +/*
|
| +** hwtime.h contains inline assembler code for implementing
|
| +** high-performance timing routines.
|
| +*/
|
| +/************** Include hwtime.h in the middle of os_common.h ****************/
|
| +/************** Begin file hwtime.h ******************************************/
|
| +/*
|
| +** 2008 May 27
|
| +**
|
| +** 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 inline asm code for retrieving "high-performance"
|
| +** counters for x86 class CPUs.
|
| +*/
|
| +#ifndef SQLITE_HWTIME_H
|
| +#define SQLITE_HWTIME_H
|
| +
|
| +/*
|
| +** The following routine only works on pentium-class (or newer) processors.
|
| +** It uses the RDTSC opcode to read the cycle count value out of the
|
| +** processor and returns that value. This can be used for high-res
|
| +** profiling.
|
| +*/
|
| +#if (defined(__GNUC__) || defined(_MSC_VER)) && \
|
| + (defined(i386) || defined(__i386__) || defined(_M_IX86))
|
| +
|
| + #if defined(__GNUC__)
|
| +
|
| + __inline__ sqlite_uint64 sqlite3Hwtime(void){
|
| + unsigned int lo, hi;
|
| + __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
|
| + return (sqlite_uint64)hi << 32 | lo;
|
| + }
|
| +
|
| + #elif defined(_MSC_VER)
|
| +
|
| + __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){
|
| + __asm {
|
| + rdtsc
|
| + ret ; return value at EDX:EAX
|
| + }
|
| + }
|
| +
|
| + #endif
|
| +
|
| +#elif (defined(__GNUC__) && defined(__x86_64__))
|
| +
|
| + __inline__ sqlite_uint64 sqlite3Hwtime(void){
|
| + unsigned long val;
|
| + __asm__ __volatile__ ("rdtsc" : "=A" (val));
|
| + return val;
|
| + }
|
| +
|
| +#elif (defined(__GNUC__) && defined(__ppc__))
|
| +
|
| + __inline__ sqlite_uint64 sqlite3Hwtime(void){
|
| + unsigned long long retval;
|
| + unsigned long junk;
|
| + __asm__ __volatile__ ("\n\
|
| + 1: mftbu %1\n\
|
| + mftb %L0\n\
|
| + mftbu %0\n\
|
| + cmpw %0,%1\n\
|
| + bne 1b"
|
| + : "=r" (retval), "=r" (junk));
|
| + return retval;
|
| + }
|
| +
|
| +#else
|
| +
|
| + #error Need implementation of sqlite3Hwtime() for your platform.
|
| +
|
| + /*
|
| + ** To compile without implementing sqlite3Hwtime() for your platform,
|
| + ** you can remove the above #error and use the following
|
| + ** stub function. You will lose timing support for many
|
| + ** of the debugging and testing utilities, but it should at
|
| + ** least compile and run.
|
| + */
|
| +SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); }
|
| +
|
| +#endif
|
| +
|
| +#endif /* !defined(SQLITE_HWTIME_H) */
|
| +
|
| +/************** End of hwtime.h **********************************************/
|
| +/************** Continuing where we left off in os_common.h ******************/
|
| +
|
| +static sqlite_uint64 g_start;
|
| +static sqlite_uint64 g_elapsed;
|
| +#define TIMER_START g_start=sqlite3Hwtime()
|
| +#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start
|
| +#define TIMER_ELAPSED g_elapsed
|
| +#else
|
| +#define TIMER_START
|
| +#define TIMER_END
|
| +#define TIMER_ELAPSED ((sqlite_uint64)0)
|
| +#endif
|
| +
|
| +/*
|
| +** If we compile with the SQLITE_TEST macro set, then the following block
|
| +** of code will give us the ability to simulate a disk I/O error. This
|
| +** is used for testing the I/O recovery logic.
|
| +*/
|
| +#if defined(SQLITE_TEST)
|
| +SQLITE_API extern int sqlite3_io_error_hit;
|
| +SQLITE_API extern int sqlite3_io_error_hardhit;
|
| +SQLITE_API extern int sqlite3_io_error_pending;
|
| +SQLITE_API extern int sqlite3_io_error_persist;
|
| +SQLITE_API extern int sqlite3_io_error_benign;
|
| +SQLITE_API extern int sqlite3_diskfull_pending;
|
| +SQLITE_API extern int sqlite3_diskfull;
|
| +#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X)
|
| +#define SimulateIOError(CODE) \
|
| + if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \
|
| + || sqlite3_io_error_pending-- == 1 ) \
|
| + { local_ioerr(); CODE; }
|
| +static void local_ioerr(){
|
| + IOTRACE(("IOERR\n"));
|
| + sqlite3_io_error_hit++;
|
| + if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++;
|
| +}
|
| +#define SimulateDiskfullError(CODE) \
|
| + if( sqlite3_diskfull_pending ){ \
|
| + if( sqlite3_diskfull_pending == 1 ){ \
|
| + local_ioerr(); \
|
| + sqlite3_diskfull = 1; \
|
| + sqlite3_io_error_hit = 1; \
|
| + CODE; \
|
| + }else{ \
|
| + sqlite3_diskfull_pending--; \
|
| + } \
|
| + }
|
| +#else
|
| +#define SimulateIOErrorBenign(X)
|
| +#define SimulateIOError(A)
|
| +#define SimulateDiskfullError(A)
|
| +#endif /* defined(SQLITE_TEST) */
|
| +
|
| +/*
|
| +** When testing, keep a count of the number of open files.
|
| +*/
|
| +#if defined(SQLITE_TEST)
|
| +SQLITE_API extern int sqlite3_open_file_count;
|
| +#define OpenCounter(X) sqlite3_open_file_count+=(X)
|
| +#else
|
| +#define OpenCounter(X)
|
| +#endif /* defined(SQLITE_TEST) */
|
| +
|
| +#endif /* !defined(_OS_COMMON_H_) */
|
| +
|
| +/************** End of os_common.h *******************************************/
|
| +/************** Continuing where we left off in os_win.c *********************/
|
| +
|
| +/*
|
| +** Include the header file for the Windows VFS.
|
| +*/
|
| +/* #include "os_win.h" */
|
| +
|
| +/*
|
| +** Compiling and using WAL mode requires several APIs that are only
|
| +** available in Windows platforms based on the NT kernel.
|
| +*/
|
| +#if !SQLITE_OS_WINNT && !defined(SQLITE_OMIT_WAL)
|
| +# error "WAL mode requires support from the Windows NT kernel, compile\
|
| + with SQLITE_OMIT_WAL."
|
| +#endif
|
| +
|
| +#if !SQLITE_OS_WINNT && SQLITE_MAX_MMAP_SIZE>0
|
| +# error "Memory mapped files require support from the Windows NT kernel,\
|
| + compile with SQLITE_MAX_MMAP_SIZE=0."
|
| +#endif
|
| +
|
| +/*
|
| +** Are most of the Win32 ANSI APIs available (i.e. with certain exceptions
|
| +** based on the sub-platform)?
|
| +*/
|
| +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(SQLITE_WIN32_NO_ANSI)
|
| +# define SQLITE_WIN32_HAS_ANSI
|
| +#endif
|
| +
|
| +/*
|
| +** Are most of the Win32 Unicode APIs available (i.e. with certain exceptions
|
| +** based on the sub-platform)?
|
| +*/
|
| +#if (SQLITE_OS_WINCE || SQLITE_OS_WINNT || SQLITE_OS_WINRT) && \
|
| + !defined(SQLITE_WIN32_NO_WIDE)
|
| +# define SQLITE_WIN32_HAS_WIDE
|
| +#endif
|
| +
|
| +/*
|
| +** Make sure at least one set of Win32 APIs is available.
|
| +*/
|
| +#if !defined(SQLITE_WIN32_HAS_ANSI) && !defined(SQLITE_WIN32_HAS_WIDE)
|
| +# error "At least one of SQLITE_WIN32_HAS_ANSI and SQLITE_WIN32_HAS_WIDE\
|
| + must be defined."
|
| +#endif
|
| +
|
| +/*
|
| +** Define the required Windows SDK version constants if they are not
|
| +** already available.
|
| +*/
|
| +#ifndef NTDDI_WIN8
|
| +# define NTDDI_WIN8 0x06020000
|
| +#endif
|
| +
|
| +#ifndef NTDDI_WINBLUE
|
| +# define NTDDI_WINBLUE 0x06030000
|
| +#endif
|
| +
|
| +#ifndef NTDDI_WINTHRESHOLD
|
| +# define NTDDI_WINTHRESHOLD 0x06040000
|
| +#endif
|
| +
|
| +/*
|
| +** Check to see if the GetVersionEx[AW] functions are deprecated on the
|
| +** target system. GetVersionEx was first deprecated in Win8.1.
|
| +*/
|
| +#ifndef SQLITE_WIN32_GETVERSIONEX
|
| +# if defined(NTDDI_VERSION) && NTDDI_VERSION >= NTDDI_WINBLUE
|
| +# define SQLITE_WIN32_GETVERSIONEX 0 /* GetVersionEx() is deprecated */
|
| +# else
|
| +# define SQLITE_WIN32_GETVERSIONEX 1 /* GetVersionEx() is current */
|
| +# endif
|
| +#endif
|
| +
|
| +/*
|
| +** Check to see if the CreateFileMappingA function is supported on the
|
| +** target system. It is unavailable when using "mincore.lib" on Win10.
|
| +** When compiling for Windows 10, always assume "mincore.lib" is in use.
|
| +*/
|
| +#ifndef SQLITE_WIN32_CREATEFILEMAPPINGA
|
| +# if defined(NTDDI_VERSION) && NTDDI_VERSION >= NTDDI_WINTHRESHOLD
|
| +# define SQLITE_WIN32_CREATEFILEMAPPINGA 0
|
| +# else
|
| +# define SQLITE_WIN32_CREATEFILEMAPPINGA 1
|
| +# endif
|
| +#endif
|
| +
|
| +/*
|
| +** This constant should already be defined (in the "WinDef.h" SDK file).
|
| +*/
|
| +#ifndef MAX_PATH
|
| +# define MAX_PATH (260)
|
| +#endif
|
| +
|
| +/*
|
| +** Maximum pathname length (in chars) for Win32. This should normally be
|
| +** MAX_PATH.
|
| +*/
|
| +#ifndef SQLITE_WIN32_MAX_PATH_CHARS
|
| +# define SQLITE_WIN32_MAX_PATH_CHARS (MAX_PATH)
|
| +#endif
|
| +
|
| +/*
|
| +** This constant should already be defined (in the "WinNT.h" SDK file).
|
| +*/
|
| +#ifndef UNICODE_STRING_MAX_CHARS
|
| +# define UNICODE_STRING_MAX_CHARS (32767)
|
| +#endif
|
| +
|
| +/*
|
| +** Maximum pathname length (in chars) for WinNT. This should normally be
|
| +** UNICODE_STRING_MAX_CHARS.
|
| +*/
|
| +#ifndef SQLITE_WINNT_MAX_PATH_CHARS
|
| +# define SQLITE_WINNT_MAX_PATH_CHARS (UNICODE_STRING_MAX_CHARS)
|
| +#endif
|
| +
|
| +/*
|
| +** Maximum pathname length (in bytes) for Win32. The MAX_PATH macro is in
|
| +** characters, so we allocate 4 bytes per character assuming worst-case of
|
| +** 4-bytes-per-character for UTF8.
|
| +*/
|
| +#ifndef SQLITE_WIN32_MAX_PATH_BYTES
|
| +# define SQLITE_WIN32_MAX_PATH_BYTES (SQLITE_WIN32_MAX_PATH_CHARS*4)
|
| +#endif
|
| +
|
| +/*
|
| +** Maximum pathname length (in bytes) for WinNT. This should normally be
|
| +** UNICODE_STRING_MAX_CHARS * sizeof(WCHAR).
|
| +*/
|
| +#ifndef SQLITE_WINNT_MAX_PATH_BYTES
|
| +# define SQLITE_WINNT_MAX_PATH_BYTES \
|
| + (sizeof(WCHAR) * SQLITE_WINNT_MAX_PATH_CHARS)
|
| +#endif
|
| +
|
| +/*
|
| +** Maximum error message length (in chars) for WinRT.
|
| +*/
|
| +#ifndef SQLITE_WIN32_MAX_ERRMSG_CHARS
|
| +# define SQLITE_WIN32_MAX_ERRMSG_CHARS (1024)
|
| +#endif
|
| +
|
| +/*
|
| +** Returns non-zero if the character should be treated as a directory
|
| +** separator.
|
| +*/
|
| +#ifndef winIsDirSep
|
| +# define winIsDirSep(a) (((a) == '/') || ((a) == '\\'))
|
| +#endif
|
| +
|
| +/*
|
| +** This macro is used when a local variable is set to a value that is
|
| +** [sometimes] not used by the code (e.g. via conditional compilation).
|
| +*/
|
| +#ifndef UNUSED_VARIABLE_VALUE
|
| +# define UNUSED_VARIABLE_VALUE(x) (void)(x)
|
| +#endif
|
| +
|
| +/*
|
| +** Returns the character that should be used as the directory separator.
|
| +*/
|
| +#ifndef winGetDirSep
|
| +# define winGetDirSep() '\\'
|
| +#endif
|
| +
|
| +/*
|
| +** Do we need to manually define the Win32 file mapping APIs for use with WAL
|
| +** mode or memory mapped files (e.g. these APIs are available in the Windows
|
| +** CE SDK; however, they are not present in the header file)?
|
| +*/
|
| +#if SQLITE_WIN32_FILEMAPPING_API && \
|
| + (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)
|
| +/*
|
| +** Two of the file mapping APIs are different under WinRT. Figure out which
|
| +** set we need.
|
| +*/
|
| +#if SQLITE_OS_WINRT
|
| +WINBASEAPI HANDLE WINAPI CreateFileMappingFromApp(HANDLE, \
|
| + LPSECURITY_ATTRIBUTES, ULONG, ULONG64, LPCWSTR);
|
| +
|
| +WINBASEAPI LPVOID WINAPI MapViewOfFileFromApp(HANDLE, ULONG, ULONG64, SIZE_T);
|
| +#else
|
| +#if defined(SQLITE_WIN32_HAS_ANSI)
|
| +WINBASEAPI HANDLE WINAPI CreateFileMappingA(HANDLE, LPSECURITY_ATTRIBUTES, \
|
| + DWORD, DWORD, DWORD, LPCSTR);
|
| +#endif /* defined(SQLITE_WIN32_HAS_ANSI) */
|
| +
|
| +#if defined(SQLITE_WIN32_HAS_WIDE)
|
| +WINBASEAPI HANDLE WINAPI CreateFileMappingW(HANDLE, LPSECURITY_ATTRIBUTES, \
|
| + DWORD, DWORD, DWORD, LPCWSTR);
|
| +#endif /* defined(SQLITE_WIN32_HAS_WIDE) */
|
| +
|
| +WINBASEAPI LPVOID WINAPI MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, SIZE_T);
|
| +#endif /* SQLITE_OS_WINRT */
|
| +
|
| +/*
|
| +** These file mapping APIs are common to both Win32 and WinRT.
|
| +*/
|
| +
|
| +WINBASEAPI BOOL WINAPI FlushViewOfFile(LPCVOID, SIZE_T);
|
| +WINBASEAPI BOOL WINAPI UnmapViewOfFile(LPCVOID);
|
| +#endif /* SQLITE_WIN32_FILEMAPPING_API */
|
| +
|
| +/*
|
| +** Some Microsoft compilers lack this definition.
|
| +*/
|
| +#ifndef INVALID_FILE_ATTRIBUTES
|
| +# define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
|
| +#endif
|
| +
|
| +#ifndef FILE_FLAG_MASK
|
| +# define FILE_FLAG_MASK (0xFF3C0000)
|
| +#endif
|
| +
|
| +#ifndef FILE_ATTRIBUTE_MASK
|
| +# define FILE_ATTRIBUTE_MASK (0x0003FFF7)
|
| +#endif
|
| +
|
| +#ifndef SQLITE_OMIT_WAL
|
| +/* Forward references to structures used for WAL */
|
| +typedef struct winShm winShm; /* A connection to shared-memory */
|
| +typedef struct winShmNode winShmNode; /* A region of shared-memory */
|
| +#endif
|
| +
|
| +/*
|
| +** WinCE lacks native support for file locking so we have to fake it
|
| +** with some code of our own.
|
| +*/
|
| +#if SQLITE_OS_WINCE
|
| +typedef struct winceLock {
|
| + int nReaders; /* Number of reader locks obtained */
|
| + BOOL bPending; /* Indicates a pending lock has been obtained */
|
| + BOOL bReserved; /* Indicates a reserved lock has been obtained */
|
| + BOOL bExclusive; /* Indicates an exclusive lock has been obtained */
|
| +} winceLock;
|
| +#endif
|
| +
|
| +/*
|
| +** The winFile structure is a subclass of sqlite3_file* specific to the win32
|
| +** portability layer.
|
| +*/
|
| +typedef struct winFile winFile;
|
| +struct winFile {
|
| + const sqlite3_io_methods *pMethod; /*** Must be first ***/
|
| + sqlite3_vfs *pVfs; /* The VFS used to open this file */
|
| + HANDLE h; /* Handle for accessing the file */
|
| + u8 locktype; /* Type of lock currently held on this file */
|
| + short sharedLockByte; /* Randomly chosen byte used as a shared lock */
|
| + u8 ctrlFlags; /* Flags. See WINFILE_* below */
|
| + DWORD lastErrno; /* The Windows errno from the last I/O error */
|
| +#ifndef SQLITE_OMIT_WAL
|
| + winShm *pShm; /* Instance of shared memory on this file */
|
| +#endif
|
| + const char *zPath; /* Full pathname of this file */
|
| + int szChunk; /* Chunk size configured by FCNTL_CHUNK_SIZE */
|
| +#if SQLITE_OS_WINCE
|
| + LPWSTR zDeleteOnClose; /* Name of file to delete when closing */
|
| + HANDLE hMutex; /* Mutex used to control access to shared lock */
|
| + HANDLE hShared; /* Shared memory segment used for locking */
|
| + winceLock local; /* Locks obtained by this instance of winFile */
|
| + winceLock *shared; /* Global shared lock memory for the file */
|
| +#endif
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| + int nFetchOut; /* Number of outstanding xFetch references */
|
| + HANDLE hMap; /* Handle for accessing memory mapping */
|
| + void *pMapRegion; /* Area memory mapped */
|
| + sqlite3_int64 mmapSize; /* Usable size of mapped region */
|
| + sqlite3_int64 mmapSizeActual; /* Actual size of mapped region */
|
| + sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */
|
| +#endif
|
| +};
|
| +
|
| +/*
|
| +** The winVfsAppData structure is used for the pAppData member for all of the
|
| +** Win32 VFS variants.
|
| +*/
|
| +typedef struct winVfsAppData winVfsAppData;
|
| +struct winVfsAppData {
|
| + const sqlite3_io_methods *pMethod; /* The file I/O methods to use. */
|
| + void *pAppData; /* The extra pAppData, if any. */
|
| + BOOL bNoLock; /* Non-zero if locking is disabled. */
|
| +};
|
| +
|
| +/*
|
| +** Allowed values for winFile.ctrlFlags
|
| +*/
|
| +#define WINFILE_RDONLY 0x02 /* Connection is read only */
|
| +#define WINFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */
|
| +#define WINFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */
|
| +
|
| +/*
|
| + * The size of the buffer used by sqlite3_win32_write_debug().
|
| + */
|
| +#ifndef SQLITE_WIN32_DBG_BUF_SIZE
|
| +# define SQLITE_WIN32_DBG_BUF_SIZE ((int)(4096-sizeof(DWORD)))
|
| +#endif
|
| +
|
| +/*
|
| + * The value used with sqlite3_win32_set_directory() to specify that
|
| + * the data directory should be changed.
|
| + */
|
| +#ifndef SQLITE_WIN32_DATA_DIRECTORY_TYPE
|
| +# define SQLITE_WIN32_DATA_DIRECTORY_TYPE (1)
|
| +#endif
|
| +
|
| +/*
|
| + * The value used with sqlite3_win32_set_directory() to specify that
|
| + * the temporary directory should be changed.
|
| + */
|
| +#ifndef SQLITE_WIN32_TEMP_DIRECTORY_TYPE
|
| +# define SQLITE_WIN32_TEMP_DIRECTORY_TYPE (2)
|
| +#endif
|
| +
|
| +/*
|
| + * If compiled with SQLITE_WIN32_MALLOC on Windows, we will use the
|
| + * various Win32 API heap functions instead of our own.
|
| + */
|
| +#ifdef SQLITE_WIN32_MALLOC
|
| +
|
| +/*
|
| + * If this is non-zero, an isolated heap will be created by the native Win32
|
| + * allocator subsystem; otherwise, the default process heap will be used. This
|
| + * setting has no effect when compiling for WinRT. By default, this is enabled
|
| + * and an isolated heap will be created to store all allocated data.
|
| + *
|
| + ******************************************************************************
|
| + * WARNING: It is important to note that when this setting is non-zero and the
|
| + * winMemShutdown function is called (e.g. by the sqlite3_shutdown
|
| + * function), all data that was allocated using the isolated heap will
|
| + * be freed immediately and any attempt to access any of that freed
|
| + * data will almost certainly result in an immediate access violation.
|
| + ******************************************************************************
|
| + */
|
| +#ifndef SQLITE_WIN32_HEAP_CREATE
|
| +# define SQLITE_WIN32_HEAP_CREATE (TRUE)
|
| +#endif
|
| +
|
| +/*
|
| + * This is cache size used in the calculation of the initial size of the
|
| + * Win32-specific heap. It cannot be negative.
|
| + */
|
| +#ifndef SQLITE_WIN32_CACHE_SIZE
|
| +# if SQLITE_DEFAULT_CACHE_SIZE>=0
|
| +# define SQLITE_WIN32_CACHE_SIZE (SQLITE_DEFAULT_CACHE_SIZE)
|
| +# else
|
| +# define SQLITE_WIN32_CACHE_SIZE (-(SQLITE_DEFAULT_CACHE_SIZE))
|
| +# endif
|
| +#endif
|
| +
|
| +/*
|
| + * The initial size of the Win32-specific heap. This value may be zero.
|
| + */
|
| +#ifndef SQLITE_WIN32_HEAP_INIT_SIZE
|
| +# define SQLITE_WIN32_HEAP_INIT_SIZE ((SQLITE_WIN32_CACHE_SIZE) * \
|
| + (SQLITE_DEFAULT_PAGE_SIZE) + 4194304)
|
| +#endif
|
| +
|
| +/*
|
| + * The maximum size of the Win32-specific heap. This value may be zero.
|
| + */
|
| +#ifndef SQLITE_WIN32_HEAP_MAX_SIZE
|
| +# define SQLITE_WIN32_HEAP_MAX_SIZE (0)
|
| +#endif
|
| +
|
| +/*
|
| + * The extra flags to use in calls to the Win32 heap APIs. This value may be
|
| + * zero for the default behavior.
|
| + */
|
| +#ifndef SQLITE_WIN32_HEAP_FLAGS
|
| +# define SQLITE_WIN32_HEAP_FLAGS (0)
|
| +#endif
|
| +
|
| +
|
| +/*
|
| +** The winMemData structure stores information required by the Win32-specific
|
| +** sqlite3_mem_methods implementation.
|
| +*/
|
| +typedef struct winMemData winMemData;
|
| +struct winMemData {
|
| +#ifndef NDEBUG
|
| + u32 magic1; /* Magic number to detect structure corruption. */
|
| +#endif
|
| + HANDLE hHeap; /* The handle to our heap. */
|
| + BOOL bOwned; /* Do we own the heap (i.e. destroy it on shutdown)? */
|
| +#ifndef NDEBUG
|
| + u32 magic2; /* Magic number to detect structure corruption. */
|
| +#endif
|
| +};
|
| +
|
| +#ifndef NDEBUG
|
| +#define WINMEM_MAGIC1 0x42b2830b
|
| +#define WINMEM_MAGIC2 0xbd4d7cf4
|
| +#endif
|
| +
|
| +static struct winMemData win_mem_data = {
|
| +#ifndef NDEBUG
|
| + WINMEM_MAGIC1,
|
| +#endif
|
| + NULL, FALSE
|
| +#ifndef NDEBUG
|
| + ,WINMEM_MAGIC2
|
| +#endif
|
| +};
|
| +
|
| +#ifndef NDEBUG
|
| +#define winMemAssertMagic1() assert( win_mem_data.magic1==WINMEM_MAGIC1 )
|
| +#define winMemAssertMagic2() assert( win_mem_data.magic2==WINMEM_MAGIC2 )
|
| +#define winMemAssertMagic() winMemAssertMagic1(); winMemAssertMagic2();
|
| +#else
|
| +#define winMemAssertMagic()
|
| +#endif
|
| +
|
| +#define winMemGetDataPtr() &win_mem_data
|
| +#define winMemGetHeap() win_mem_data.hHeap
|
| +#define winMemGetOwned() win_mem_data.bOwned
|
| +
|
| +static void *winMemMalloc(int nBytes);
|
| +static void winMemFree(void *pPrior);
|
| +static void *winMemRealloc(void *pPrior, int nBytes);
|
| +static int winMemSize(void *p);
|
| +static int winMemRoundup(int n);
|
| +static int winMemInit(void *pAppData);
|
| +static void winMemShutdown(void *pAppData);
|
| +
|
| +SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetWin32(void);
|
| +#endif /* SQLITE_WIN32_MALLOC */
|
| +
|
| +/*
|
| +** The following variable is (normally) set once and never changes
|
| +** thereafter. It records whether the operating system is Win9x
|
| +** or WinNT.
|
| +**
|
| +** 0: Operating system unknown.
|
| +** 1: Operating system is Win9x.
|
| +** 2: Operating system is WinNT.
|
| +**
|
| +** In order to facilitate testing on a WinNT system, the test fixture
|
| +** can manually set this value to 1 to emulate Win98 behavior.
|
| +*/
|
| +#ifdef SQLITE_TEST
|
| +SQLITE_API LONG SQLITE_WIN32_VOLATILE sqlite3_os_type = 0;
|
| +#else
|
| +static LONG SQLITE_WIN32_VOLATILE sqlite3_os_type = 0;
|
| +#endif
|
| +
|
| +#ifndef SYSCALL
|
| +# define SYSCALL sqlite3_syscall_ptr
|
| +#endif
|
| +
|
| +/*
|
| +** This function is not available on Windows CE or WinRT.
|
| + */
|
| +
|
| +#if SQLITE_OS_WINCE || SQLITE_OS_WINRT
|
| +# define osAreFileApisANSI() 1
|
| +#endif
|
| +
|
| +/*
|
| +** Many system calls are accessed through pointer-to-functions so that
|
| +** they may be overridden at runtime to facilitate fault injection during
|
| +** testing and sandboxing. The following array holds the names and pointers
|
| +** to all overrideable system calls.
|
| +*/
|
| +static struct win_syscall {
|
| + const char *zName; /* Name of the system call */
|
| + sqlite3_syscall_ptr pCurrent; /* Current value of the system call */
|
| + sqlite3_syscall_ptr pDefault; /* Default value */
|
| +} aSyscall[] = {
|
| +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
|
| + { "AreFileApisANSI", (SYSCALL)AreFileApisANSI, 0 },
|
| +#else
|
| + { "AreFileApisANSI", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#ifndef osAreFileApisANSI
|
| +#define osAreFileApisANSI ((BOOL(WINAPI*)(VOID))aSyscall[0].pCurrent)
|
| +#endif
|
| +
|
| +#if SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE)
|
| + { "CharLowerW", (SYSCALL)CharLowerW, 0 },
|
| +#else
|
| + { "CharLowerW", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osCharLowerW ((LPWSTR(WINAPI*)(LPWSTR))aSyscall[1].pCurrent)
|
| +
|
| +#if SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE)
|
| + { "CharUpperW", (SYSCALL)CharUpperW, 0 },
|
| +#else
|
| + { "CharUpperW", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osCharUpperW ((LPWSTR(WINAPI*)(LPWSTR))aSyscall[2].pCurrent)
|
| +
|
| + { "CloseHandle", (SYSCALL)CloseHandle, 0 },
|
| +
|
| +#define osCloseHandle ((BOOL(WINAPI*)(HANDLE))aSyscall[3].pCurrent)
|
| +
|
| +#if defined(SQLITE_WIN32_HAS_ANSI)
|
| + { "CreateFileA", (SYSCALL)CreateFileA, 0 },
|
| +#else
|
| + { "CreateFileA", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osCreateFileA ((HANDLE(WINAPI*)(LPCSTR,DWORD,DWORD, \
|
| + LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[4].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
|
| + { "CreateFileW", (SYSCALL)CreateFileW, 0 },
|
| +#else
|
| + { "CreateFileW", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osCreateFileW ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD, \
|
| + LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[5].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_ANSI) && \
|
| + (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) && \
|
| + SQLITE_WIN32_CREATEFILEMAPPINGA
|
| + { "CreateFileMappingA", (SYSCALL)CreateFileMappingA, 0 },
|
| +#else
|
| + { "CreateFileMappingA", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osCreateFileMappingA ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \
|
| + DWORD,DWORD,DWORD,LPCSTR))aSyscall[6].pCurrent)
|
| +
|
| +#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \
|
| + (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0))
|
| + { "CreateFileMappingW", (SYSCALL)CreateFileMappingW, 0 },
|
| +#else
|
| + { "CreateFileMappingW", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osCreateFileMappingW ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \
|
| + DWORD,DWORD,DWORD,LPCWSTR))aSyscall[7].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
|
| + { "CreateMutexW", (SYSCALL)CreateMutexW, 0 },
|
| +#else
|
| + { "CreateMutexW", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osCreateMutexW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,BOOL, \
|
| + LPCWSTR))aSyscall[8].pCurrent)
|
| +
|
| +#if defined(SQLITE_WIN32_HAS_ANSI)
|
| + { "DeleteFileA", (SYSCALL)DeleteFileA, 0 },
|
| +#else
|
| + { "DeleteFileA", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osDeleteFileA ((BOOL(WINAPI*)(LPCSTR))aSyscall[9].pCurrent)
|
| +
|
| +#if defined(SQLITE_WIN32_HAS_WIDE)
|
| + { "DeleteFileW", (SYSCALL)DeleteFileW, 0 },
|
| +#else
|
| + { "DeleteFileW", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osDeleteFileW ((BOOL(WINAPI*)(LPCWSTR))aSyscall[10].pCurrent)
|
| +
|
| +#if SQLITE_OS_WINCE
|
| + { "FileTimeToLocalFileTime", (SYSCALL)FileTimeToLocalFileTime, 0 },
|
| +#else
|
| + { "FileTimeToLocalFileTime", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osFileTimeToLocalFileTime ((BOOL(WINAPI*)(CONST FILETIME*, \
|
| + LPFILETIME))aSyscall[11].pCurrent)
|
| +
|
| +#if SQLITE_OS_WINCE
|
| + { "FileTimeToSystemTime", (SYSCALL)FileTimeToSystemTime, 0 },
|
| +#else
|
| + { "FileTimeToSystemTime", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osFileTimeToSystemTime ((BOOL(WINAPI*)(CONST FILETIME*, \
|
| + LPSYSTEMTIME))aSyscall[12].pCurrent)
|
| +
|
| + { "FlushFileBuffers", (SYSCALL)FlushFileBuffers, 0 },
|
| +
|
| +#define osFlushFileBuffers ((BOOL(WINAPI*)(HANDLE))aSyscall[13].pCurrent)
|
| +
|
| +#if defined(SQLITE_WIN32_HAS_ANSI)
|
| + { "FormatMessageA", (SYSCALL)FormatMessageA, 0 },
|
| +#else
|
| + { "FormatMessageA", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osFormatMessageA ((DWORD(WINAPI*)(DWORD,LPCVOID,DWORD,DWORD,LPSTR, \
|
| + DWORD,va_list*))aSyscall[14].pCurrent)
|
| +
|
| +#if defined(SQLITE_WIN32_HAS_WIDE)
|
| + { "FormatMessageW", (SYSCALL)FormatMessageW, 0 },
|
| +#else
|
| + { "FormatMessageW", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osFormatMessageW ((DWORD(WINAPI*)(DWORD,LPCVOID,DWORD,DWORD,LPWSTR, \
|
| + DWORD,va_list*))aSyscall[15].pCurrent)
|
| +
|
| +#if !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
| + { "FreeLibrary", (SYSCALL)FreeLibrary, 0 },
|
| +#else
|
| + { "FreeLibrary", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osFreeLibrary ((BOOL(WINAPI*)(HMODULE))aSyscall[16].pCurrent)
|
| +
|
| + { "GetCurrentProcessId", (SYSCALL)GetCurrentProcessId, 0 },
|
| +
|
| +#define osGetCurrentProcessId ((DWORD(WINAPI*)(VOID))aSyscall[17].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_ANSI)
|
| + { "GetDiskFreeSpaceA", (SYSCALL)GetDiskFreeSpaceA, 0 },
|
| +#else
|
| + { "GetDiskFreeSpaceA", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osGetDiskFreeSpaceA ((BOOL(WINAPI*)(LPCSTR,LPDWORD,LPDWORD,LPDWORD, \
|
| + LPDWORD))aSyscall[18].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
|
| + { "GetDiskFreeSpaceW", (SYSCALL)GetDiskFreeSpaceW, 0 },
|
| +#else
|
| + { "GetDiskFreeSpaceW", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osGetDiskFreeSpaceW ((BOOL(WINAPI*)(LPCWSTR,LPDWORD,LPDWORD,LPDWORD, \
|
| + LPDWORD))aSyscall[19].pCurrent)
|
| +
|
| +#if defined(SQLITE_WIN32_HAS_ANSI)
|
| + { "GetFileAttributesA", (SYSCALL)GetFileAttributesA, 0 },
|
| +#else
|
| + { "GetFileAttributesA", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osGetFileAttributesA ((DWORD(WINAPI*)(LPCSTR))aSyscall[20].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
|
| + { "GetFileAttributesW", (SYSCALL)GetFileAttributesW, 0 },
|
| +#else
|
| + { "GetFileAttributesW", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osGetFileAttributesW ((DWORD(WINAPI*)(LPCWSTR))aSyscall[21].pCurrent)
|
| +
|
| +#if defined(SQLITE_WIN32_HAS_WIDE)
|
| + { "GetFileAttributesExW", (SYSCALL)GetFileAttributesExW, 0 },
|
| +#else
|
| + { "GetFileAttributesExW", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osGetFileAttributesExW ((BOOL(WINAPI*)(LPCWSTR,GET_FILEEX_INFO_LEVELS, \
|
| + LPVOID))aSyscall[22].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINRT
|
| + { "GetFileSize", (SYSCALL)GetFileSize, 0 },
|
| +#else
|
| + { "GetFileSize", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osGetFileSize ((DWORD(WINAPI*)(HANDLE,LPDWORD))aSyscall[23].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_ANSI)
|
| + { "GetFullPathNameA", (SYSCALL)GetFullPathNameA, 0 },
|
| +#else
|
| + { "GetFullPathNameA", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osGetFullPathNameA ((DWORD(WINAPI*)(LPCSTR,DWORD,LPSTR, \
|
| + LPSTR*))aSyscall[24].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
|
| + { "GetFullPathNameW", (SYSCALL)GetFullPathNameW, 0 },
|
| +#else
|
| + { "GetFullPathNameW", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osGetFullPathNameW ((DWORD(WINAPI*)(LPCWSTR,DWORD,LPWSTR, \
|
| + LPWSTR*))aSyscall[25].pCurrent)
|
| +
|
| + { "GetLastError", (SYSCALL)GetLastError, 0 },
|
| +
|
| +#define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[26].pCurrent)
|
| +
|
| +#if !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
| +#if SQLITE_OS_WINCE
|
| + /* The GetProcAddressA() routine is only available on Windows CE. */
|
| + { "GetProcAddressA", (SYSCALL)GetProcAddressA, 0 },
|
| +#else
|
| + /* All other Windows platforms expect GetProcAddress() to take
|
| + ** an ANSI string regardless of the _UNICODE setting */
|
| + { "GetProcAddressA", (SYSCALL)GetProcAddress, 0 },
|
| +#endif
|
| +#else
|
| + { "GetProcAddressA", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osGetProcAddressA ((FARPROC(WINAPI*)(HMODULE, \
|
| + LPCSTR))aSyscall[27].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINRT
|
| + { "GetSystemInfo", (SYSCALL)GetSystemInfo, 0 },
|
| +#else
|
| + { "GetSystemInfo", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osGetSystemInfo ((VOID(WINAPI*)(LPSYSTEM_INFO))aSyscall[28].pCurrent)
|
| +
|
| + { "GetSystemTime", (SYSCALL)GetSystemTime, 0 },
|
| +
|
| +#define osGetSystemTime ((VOID(WINAPI*)(LPSYSTEMTIME))aSyscall[29].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINCE
|
| + { "GetSystemTimeAsFileTime", (SYSCALL)GetSystemTimeAsFileTime, 0 },
|
| +#else
|
| + { "GetSystemTimeAsFileTime", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osGetSystemTimeAsFileTime ((VOID(WINAPI*)( \
|
| + LPFILETIME))aSyscall[30].pCurrent)
|
| +
|
| +#if defined(SQLITE_WIN32_HAS_ANSI)
|
| + { "GetTempPathA", (SYSCALL)GetTempPathA, 0 },
|
| +#else
|
| + { "GetTempPathA", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osGetTempPathA ((DWORD(WINAPI*)(DWORD,LPSTR))aSyscall[31].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
|
| + { "GetTempPathW", (SYSCALL)GetTempPathW, 0 },
|
| +#else
|
| + { "GetTempPathW", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osGetTempPathW ((DWORD(WINAPI*)(DWORD,LPWSTR))aSyscall[32].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINRT
|
| + { "GetTickCount", (SYSCALL)GetTickCount, 0 },
|
| +#else
|
| + { "GetTickCount", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osGetTickCount ((DWORD(WINAPI*)(VOID))aSyscall[33].pCurrent)
|
| +
|
| +#if defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_GETVERSIONEX
|
| + { "GetVersionExA", (SYSCALL)GetVersionExA, 0 },
|
| +#else
|
| + { "GetVersionExA", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osGetVersionExA ((BOOL(WINAPI*)( \
|
| + LPOSVERSIONINFOA))aSyscall[34].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \
|
| + SQLITE_WIN32_GETVERSIONEX
|
| + { "GetVersionExW", (SYSCALL)GetVersionExW, 0 },
|
| +#else
|
| + { "GetVersionExW", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osGetVersionExW ((BOOL(WINAPI*)( \
|
| + LPOSVERSIONINFOW))aSyscall[35].pCurrent)
|
| +
|
| + { "HeapAlloc", (SYSCALL)HeapAlloc, 0 },
|
| +
|
| +#define osHeapAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD, \
|
| + SIZE_T))aSyscall[36].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINRT
|
| + { "HeapCreate", (SYSCALL)HeapCreate, 0 },
|
| +#else
|
| + { "HeapCreate", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osHeapCreate ((HANDLE(WINAPI*)(DWORD,SIZE_T, \
|
| + SIZE_T))aSyscall[37].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINRT
|
| + { "HeapDestroy", (SYSCALL)HeapDestroy, 0 },
|
| +#else
|
| + { "HeapDestroy", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osHeapDestroy ((BOOL(WINAPI*)(HANDLE))aSyscall[38].pCurrent)
|
| +
|
| + { "HeapFree", (SYSCALL)HeapFree, 0 },
|
| +
|
| +#define osHeapFree ((BOOL(WINAPI*)(HANDLE,DWORD,LPVOID))aSyscall[39].pCurrent)
|
| +
|
| + { "HeapReAlloc", (SYSCALL)HeapReAlloc, 0 },
|
| +
|
| +#define osHeapReAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD,LPVOID, \
|
| + SIZE_T))aSyscall[40].pCurrent)
|
| +
|
| + { "HeapSize", (SYSCALL)HeapSize, 0 },
|
| +
|
| +#define osHeapSize ((SIZE_T(WINAPI*)(HANDLE,DWORD, \
|
| + LPCVOID))aSyscall[41].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINRT
|
| + { "HeapValidate", (SYSCALL)HeapValidate, 0 },
|
| +#else
|
| + { "HeapValidate", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osHeapValidate ((BOOL(WINAPI*)(HANDLE,DWORD, \
|
| + LPCVOID))aSyscall[42].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
|
| + { "HeapCompact", (SYSCALL)HeapCompact, 0 },
|
| +#else
|
| + { "HeapCompact", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osHeapCompact ((UINT(WINAPI*)(HANDLE,DWORD))aSyscall[43].pCurrent)
|
| +
|
| +#if defined(SQLITE_WIN32_HAS_ANSI) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
| + { "LoadLibraryA", (SYSCALL)LoadLibraryA, 0 },
|
| +#else
|
| + { "LoadLibraryA", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osLoadLibraryA ((HMODULE(WINAPI*)(LPCSTR))aSyscall[44].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \
|
| + !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
| + { "LoadLibraryW", (SYSCALL)LoadLibraryW, 0 },
|
| +#else
|
| + { "LoadLibraryW", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osLoadLibraryW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[45].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINRT
|
| + { "LocalFree", (SYSCALL)LocalFree, 0 },
|
| +#else
|
| + { "LocalFree", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osLocalFree ((HLOCAL(WINAPI*)(HLOCAL))aSyscall[46].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
|
| + { "LockFile", (SYSCALL)LockFile, 0 },
|
| +#else
|
| + { "LockFile", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#ifndef osLockFile
|
| +#define osLockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
|
| + DWORD))aSyscall[47].pCurrent)
|
| +#endif
|
| +
|
| +#if !SQLITE_OS_WINCE
|
| + { "LockFileEx", (SYSCALL)LockFileEx, 0 },
|
| +#else
|
| + { "LockFileEx", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#ifndef osLockFileEx
|
| +#define osLockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD,DWORD, \
|
| + LPOVERLAPPED))aSyscall[48].pCurrent)
|
| +#endif
|
| +
|
| +#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && \
|
| + (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0))
|
| + { "MapViewOfFile", (SYSCALL)MapViewOfFile, 0 },
|
| +#else
|
| + { "MapViewOfFile", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osMapViewOfFile ((LPVOID(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
|
| + SIZE_T))aSyscall[49].pCurrent)
|
| +
|
| + { "MultiByteToWideChar", (SYSCALL)MultiByteToWideChar, 0 },
|
| +
|
| +#define osMultiByteToWideChar ((int(WINAPI*)(UINT,DWORD,LPCSTR,int,LPWSTR, \
|
| + int))aSyscall[50].pCurrent)
|
| +
|
| + { "QueryPerformanceCounter", (SYSCALL)QueryPerformanceCounter, 0 },
|
| +
|
| +#define osQueryPerformanceCounter ((BOOL(WINAPI*)( \
|
| + LARGE_INTEGER*))aSyscall[51].pCurrent)
|
| +
|
| + { "ReadFile", (SYSCALL)ReadFile, 0 },
|
| +
|
| +#define osReadFile ((BOOL(WINAPI*)(HANDLE,LPVOID,DWORD,LPDWORD, \
|
| + LPOVERLAPPED))aSyscall[52].pCurrent)
|
| +
|
| + { "SetEndOfFile", (SYSCALL)SetEndOfFile, 0 },
|
| +
|
| +#define osSetEndOfFile ((BOOL(WINAPI*)(HANDLE))aSyscall[53].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINRT
|
| + { "SetFilePointer", (SYSCALL)SetFilePointer, 0 },
|
| +#else
|
| + { "SetFilePointer", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osSetFilePointer ((DWORD(WINAPI*)(HANDLE,LONG,PLONG, \
|
| + DWORD))aSyscall[54].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINRT
|
| + { "Sleep", (SYSCALL)Sleep, 0 },
|
| +#else
|
| + { "Sleep", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[55].pCurrent)
|
| +
|
| + { "SystemTimeToFileTime", (SYSCALL)SystemTimeToFileTime, 0 },
|
| +
|
| +#define osSystemTimeToFileTime ((BOOL(WINAPI*)(CONST SYSTEMTIME*, \
|
| + LPFILETIME))aSyscall[56].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
|
| + { "UnlockFile", (SYSCALL)UnlockFile, 0 },
|
| +#else
|
| + { "UnlockFile", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#ifndef osUnlockFile
|
| +#define osUnlockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
|
| + DWORD))aSyscall[57].pCurrent)
|
| +#endif
|
| +
|
| +#if !SQLITE_OS_WINCE
|
| + { "UnlockFileEx", (SYSCALL)UnlockFileEx, 0 },
|
| +#else
|
| + { "UnlockFileEx", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osUnlockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
|
| + LPOVERLAPPED))aSyscall[58].pCurrent)
|
| +
|
| +#if SQLITE_OS_WINCE || !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
|
| + { "UnmapViewOfFile", (SYSCALL)UnmapViewOfFile, 0 },
|
| +#else
|
| + { "UnmapViewOfFile", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osUnmapViewOfFile ((BOOL(WINAPI*)(LPCVOID))aSyscall[59].pCurrent)
|
| +
|
| + { "WideCharToMultiByte", (SYSCALL)WideCharToMultiByte, 0 },
|
| +
|
| +#define osWideCharToMultiByte ((int(WINAPI*)(UINT,DWORD,LPCWSTR,int,LPSTR,int, \
|
| + LPCSTR,LPBOOL))aSyscall[60].pCurrent)
|
| +
|
| + { "WriteFile", (SYSCALL)WriteFile, 0 },
|
| +
|
| +#define osWriteFile ((BOOL(WINAPI*)(HANDLE,LPCVOID,DWORD,LPDWORD, \
|
| + LPOVERLAPPED))aSyscall[61].pCurrent)
|
| +
|
| +#if SQLITE_OS_WINRT
|
| + { "CreateEventExW", (SYSCALL)CreateEventExW, 0 },
|
| +#else
|
| + { "CreateEventExW", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \
|
| + DWORD,DWORD))aSyscall[62].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINRT
|
| + { "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 },
|
| +#else
|
| + { "WaitForSingleObject", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \
|
| + DWORD))aSyscall[63].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINCE
|
| + { "WaitForSingleObjectEx", (SYSCALL)WaitForSingleObjectEx, 0 },
|
| +#else
|
| + { "WaitForSingleObjectEx", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \
|
| + BOOL))aSyscall[64].pCurrent)
|
| +
|
| +#if SQLITE_OS_WINRT
|
| + { "SetFilePointerEx", (SYSCALL)SetFilePointerEx, 0 },
|
| +#else
|
| + { "SetFilePointerEx", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osSetFilePointerEx ((BOOL(WINAPI*)(HANDLE,LARGE_INTEGER, \
|
| + PLARGE_INTEGER,DWORD))aSyscall[65].pCurrent)
|
| +
|
| +#if SQLITE_OS_WINRT
|
| + { "GetFileInformationByHandleEx", (SYSCALL)GetFileInformationByHandleEx, 0 },
|
| +#else
|
| + { "GetFileInformationByHandleEx", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osGetFileInformationByHandleEx ((BOOL(WINAPI*)(HANDLE, \
|
| + FILE_INFO_BY_HANDLE_CLASS,LPVOID,DWORD))aSyscall[66].pCurrent)
|
| +
|
| +#if SQLITE_OS_WINRT && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)
|
| + { "MapViewOfFileFromApp", (SYSCALL)MapViewOfFileFromApp, 0 },
|
| +#else
|
| + { "MapViewOfFileFromApp", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osMapViewOfFileFromApp ((LPVOID(WINAPI*)(HANDLE,ULONG,ULONG64, \
|
| + SIZE_T))aSyscall[67].pCurrent)
|
| +
|
| +#if SQLITE_OS_WINRT
|
| + { "CreateFile2", (SYSCALL)CreateFile2, 0 },
|
| +#else
|
| + { "CreateFile2", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osCreateFile2 ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD,DWORD, \
|
| + LPCREATEFILE2_EXTENDED_PARAMETERS))aSyscall[68].pCurrent)
|
| +
|
| +#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
| + { "LoadPackagedLibrary", (SYSCALL)LoadPackagedLibrary, 0 },
|
| +#else
|
| + { "LoadPackagedLibrary", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osLoadPackagedLibrary ((HMODULE(WINAPI*)(LPCWSTR, \
|
| + DWORD))aSyscall[69].pCurrent)
|
| +
|
| +#if SQLITE_OS_WINRT
|
| + { "GetTickCount64", (SYSCALL)GetTickCount64, 0 },
|
| +#else
|
| + { "GetTickCount64", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osGetTickCount64 ((ULONGLONG(WINAPI*)(VOID))aSyscall[70].pCurrent)
|
| +
|
| +#if SQLITE_OS_WINRT
|
| + { "GetNativeSystemInfo", (SYSCALL)GetNativeSystemInfo, 0 },
|
| +#else
|
| + { "GetNativeSystemInfo", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osGetNativeSystemInfo ((VOID(WINAPI*)( \
|
| + LPSYSTEM_INFO))aSyscall[71].pCurrent)
|
| +
|
| +#if defined(SQLITE_WIN32_HAS_ANSI)
|
| + { "OutputDebugStringA", (SYSCALL)OutputDebugStringA, 0 },
|
| +#else
|
| + { "OutputDebugStringA", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[72].pCurrent)
|
| +
|
| +#if defined(SQLITE_WIN32_HAS_WIDE)
|
| + { "OutputDebugStringW", (SYSCALL)OutputDebugStringW, 0 },
|
| +#else
|
| + { "OutputDebugStringW", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[73].pCurrent)
|
| +
|
| + { "GetProcessHeap", (SYSCALL)GetProcessHeap, 0 },
|
| +
|
| +#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[74].pCurrent)
|
| +
|
| +#if SQLITE_OS_WINRT && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)
|
| + { "CreateFileMappingFromApp", (SYSCALL)CreateFileMappingFromApp, 0 },
|
| +#else
|
| + { "CreateFileMappingFromApp", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osCreateFileMappingFromApp ((HANDLE(WINAPI*)(HANDLE, \
|
| + LPSECURITY_ATTRIBUTES,ULONG,ULONG64,LPCWSTR))aSyscall[75].pCurrent)
|
| +
|
| +/*
|
| +** NOTE: On some sub-platforms, the InterlockedCompareExchange "function"
|
| +** is really just a macro that uses a compiler intrinsic (e.g. x64).
|
| +** So do not try to make this is into a redefinable interface.
|
| +*/
|
| +#if defined(InterlockedCompareExchange)
|
| + { "InterlockedCompareExchange", (SYSCALL)0, 0 },
|
| +
|
| +#define osInterlockedCompareExchange InterlockedCompareExchange
|
| +#else
|
| + { "InterlockedCompareExchange", (SYSCALL)InterlockedCompareExchange, 0 },
|
| +
|
| +#define osInterlockedCompareExchange ((LONG(WINAPI*)(LONG \
|
| + SQLITE_WIN32_VOLATILE*, LONG,LONG))aSyscall[76].pCurrent)
|
| +#endif /* defined(InterlockedCompareExchange) */
|
| +
|
| +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID
|
| + { "UuidCreate", (SYSCALL)UuidCreate, 0 },
|
| +#else
|
| + { "UuidCreate", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osUuidCreate ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[77].pCurrent)
|
| +
|
| +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID
|
| + { "UuidCreateSequential", (SYSCALL)UuidCreateSequential, 0 },
|
| +#else
|
| + { "UuidCreateSequential", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osUuidCreateSequential \
|
| + ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[78].pCurrent)
|
| +
|
| +#if !defined(SQLITE_NO_SYNC) && SQLITE_MAX_MMAP_SIZE>0
|
| + { "FlushViewOfFile", (SYSCALL)FlushViewOfFile, 0 },
|
| +#else
|
| + { "FlushViewOfFile", (SYSCALL)0, 0 },
|
| +#endif
|
| +
|
| +#define osFlushViewOfFile \
|
| + ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[79].pCurrent)
|
| +
|
| +}; /* End of the overrideable system calls */
|
| +
|
| +/*
|
| +** This is the xSetSystemCall() method of sqlite3_vfs for all of the
|
| +** "win32" VFSes. Return SQLITE_OK opon successfully updating the
|
| +** system call pointer, or SQLITE_NOTFOUND if there is no configurable
|
| +** system call named zName.
|
| +*/
|
| +static int winSetSystemCall(
|
| + sqlite3_vfs *pNotUsed, /* The VFS pointer. Not used */
|
| + const char *zName, /* Name of system call to override */
|
| + sqlite3_syscall_ptr pNewFunc /* Pointer to new system call value */
|
| +){
|
| + unsigned int i;
|
| + int rc = SQLITE_NOTFOUND;
|
| +
|
| + UNUSED_PARAMETER(pNotUsed);
|
| + if( zName==0 ){
|
| + /* If no zName is given, restore all system calls to their default
|
| + ** settings and return NULL
|
| + */
|
| + rc = SQLITE_OK;
|
| + for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){
|
| + if( aSyscall[i].pDefault ){
|
| + aSyscall[i].pCurrent = aSyscall[i].pDefault;
|
| + }
|
| + }
|
| + }else{
|
| + /* If zName is specified, operate on only the one system call
|
| + ** specified.
|
| + */
|
| + for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){
|
| + if( strcmp(zName, aSyscall[i].zName)==0 ){
|
| + if( aSyscall[i].pDefault==0 ){
|
| + aSyscall[i].pDefault = aSyscall[i].pCurrent;
|
| + }
|
| + rc = SQLITE_OK;
|
| + if( pNewFunc==0 ) pNewFunc = aSyscall[i].pDefault;
|
| + aSyscall[i].pCurrent = pNewFunc;
|
| + break;
|
| + }
|
| + }
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Return the value of a system call. Return NULL if zName is not a
|
| +** recognized system call name. NULL is also returned if the system call
|
| +** is currently undefined.
|
| +*/
|
| +static sqlite3_syscall_ptr winGetSystemCall(
|
| + sqlite3_vfs *pNotUsed,
|
| + const char *zName
|
| +){
|
| + unsigned int i;
|
| +
|
| + UNUSED_PARAMETER(pNotUsed);
|
| + for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){
|
| + if( strcmp(zName, aSyscall[i].zName)==0 ) return aSyscall[i].pCurrent;
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| +/*
|
| +** Return the name of the first system call after zName. If zName==NULL
|
| +** then return the name of the first system call. Return NULL if zName
|
| +** is the last system call or if zName is not the name of a valid
|
| +** system call.
|
| +*/
|
| +static const char *winNextSystemCall(sqlite3_vfs *p, const char *zName){
|
| + int i = -1;
|
| +
|
| + UNUSED_PARAMETER(p);
|
| + if( zName ){
|
| + for(i=0; i<ArraySize(aSyscall)-1; i++){
|
| + if( strcmp(zName, aSyscall[i].zName)==0 ) break;
|
| + }
|
| + }
|
| + for(i++; i<ArraySize(aSyscall); i++){
|
| + if( aSyscall[i].pCurrent!=0 ) return aSyscall[i].zName;
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| +#ifdef SQLITE_WIN32_MALLOC
|
| +/*
|
| +** If a Win32 native heap has been configured, this function will attempt to
|
| +** compact it. Upon success, SQLITE_OK will be returned. Upon failure, one
|
| +** of SQLITE_NOMEM, SQLITE_ERROR, or SQLITE_NOTFOUND will be returned. The
|
| +** "pnLargest" argument, if non-zero, will be used to return the size of the
|
| +** largest committed free block in the heap, in bytes.
|
| +*/
|
| +SQLITE_API int sqlite3_win32_compact_heap(LPUINT pnLargest){
|
| + int rc = SQLITE_OK;
|
| + UINT nLargest = 0;
|
| + HANDLE hHeap;
|
| +
|
| + winMemAssertMagic();
|
| + hHeap = winMemGetHeap();
|
| + assert( hHeap!=0 );
|
| + assert( hHeap!=INVALID_HANDLE_VALUE );
|
| +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
|
| + assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
|
| +#endif
|
| +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
|
| + if( (nLargest=osHeapCompact(hHeap, SQLITE_WIN32_HEAP_FLAGS))==0 ){
|
| + DWORD lastErrno = osGetLastError();
|
| + if( lastErrno==NO_ERROR ){
|
| + sqlite3_log(SQLITE_NOMEM, "failed to HeapCompact (no space), heap=%p",
|
| + (void*)hHeap);
|
| + rc = SQLITE_NOMEM_BKPT;
|
| + }else{
|
| + sqlite3_log(SQLITE_ERROR, "failed to HeapCompact (%lu), heap=%p",
|
| + osGetLastError(), (void*)hHeap);
|
| + rc = SQLITE_ERROR;
|
| + }
|
| + }
|
| +#else
|
| + sqlite3_log(SQLITE_NOTFOUND, "failed to HeapCompact, heap=%p",
|
| + (void*)hHeap);
|
| + rc = SQLITE_NOTFOUND;
|
| +#endif
|
| + if( pnLargest ) *pnLargest = nLargest;
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** If a Win32 native heap has been configured, this function will attempt to
|
| +** destroy and recreate it. If the Win32 native heap is not isolated and/or
|
| +** the sqlite3_memory_used() function does not return zero, SQLITE_BUSY will
|
| +** be returned and no changes will be made to the Win32 native heap.
|
| +*/
|
| +SQLITE_API int sqlite3_win32_reset_heap(){
|
| + int rc;
|
| + MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */
|
| + MUTEX_LOGIC( sqlite3_mutex *pMem; ) /* The memsys static mutex */
|
| + MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
|
| + MUTEX_LOGIC( pMem = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); )
|
| + sqlite3_mutex_enter(pMaster);
|
| + sqlite3_mutex_enter(pMem);
|
| + winMemAssertMagic();
|
| + if( winMemGetHeap()!=NULL && winMemGetOwned() && sqlite3_memory_used()==0 ){
|
| + /*
|
| + ** At this point, there should be no outstanding memory allocations on
|
| + ** the heap. Also, since both the master and memsys locks are currently
|
| + ** being held by us, no other function (i.e. from another thread) should
|
| + ** be able to even access the heap. Attempt to destroy and recreate our
|
| + ** isolated Win32 native heap now.
|
| + */
|
| + assert( winMemGetHeap()!=NULL );
|
| + assert( winMemGetOwned() );
|
| + assert( sqlite3_memory_used()==0 );
|
| + winMemShutdown(winMemGetDataPtr());
|
| + assert( winMemGetHeap()==NULL );
|
| + assert( !winMemGetOwned() );
|
| + assert( sqlite3_memory_used()==0 );
|
| + rc = winMemInit(winMemGetDataPtr());
|
| + assert( rc!=SQLITE_OK || winMemGetHeap()!=NULL );
|
| + assert( rc!=SQLITE_OK || winMemGetOwned() );
|
| + assert( rc!=SQLITE_OK || sqlite3_memory_used()==0 );
|
| + }else{
|
| + /*
|
| + ** The Win32 native heap cannot be modified because it may be in use.
|
| + */
|
| + rc = SQLITE_BUSY;
|
| + }
|
| + sqlite3_mutex_leave(pMem);
|
| + sqlite3_mutex_leave(pMaster);
|
| + return rc;
|
| +}
|
| +#endif /* SQLITE_WIN32_MALLOC */
|
| +
|
| +/*
|
| +** This function outputs the specified (ANSI) string to the Win32 debugger
|
| +** (if available).
|
| +*/
|
| +
|
| +SQLITE_API void sqlite3_win32_write_debug(const char *zBuf, int nBuf){
|
| + char zDbgBuf[SQLITE_WIN32_DBG_BUF_SIZE];
|
| + int nMin = MIN(nBuf, (SQLITE_WIN32_DBG_BUF_SIZE - 1)); /* may be negative. */
|
| + if( nMin<-1 ) nMin = -1; /* all negative values become -1. */
|
| + assert( nMin==-1 || nMin==0 || nMin<SQLITE_WIN32_DBG_BUF_SIZE );
|
| +#ifdef SQLITE_ENABLE_API_ARMOR
|
| + if( !zBuf ){
|
| + (void)SQLITE_MISUSE_BKPT;
|
| + return;
|
| + }
|
| +#endif
|
| +#if defined(SQLITE_WIN32_HAS_ANSI)
|
| + if( nMin>0 ){
|
| + memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE);
|
| + memcpy(zDbgBuf, zBuf, nMin);
|
| + osOutputDebugStringA(zDbgBuf);
|
| + }else{
|
| + osOutputDebugStringA(zBuf);
|
| + }
|
| +#elif defined(SQLITE_WIN32_HAS_WIDE)
|
| + memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE);
|
| + if ( osMultiByteToWideChar(
|
| + osAreFileApisANSI() ? CP_ACP : CP_OEMCP, 0, zBuf,
|
| + nMin, (LPWSTR)zDbgBuf, SQLITE_WIN32_DBG_BUF_SIZE/sizeof(WCHAR))<=0 ){
|
| + return;
|
| + }
|
| + osOutputDebugStringW((LPCWSTR)zDbgBuf);
|
| +#else
|
| + if( nMin>0 ){
|
| + memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE);
|
| + memcpy(zDbgBuf, zBuf, nMin);
|
| + fprintf(stderr, "%s", zDbgBuf);
|
| + }else{
|
| + fprintf(stderr, "%s", zBuf);
|
| + }
|
| +#endif
|
| +}
|
| +
|
| +/*
|
| +** The following routine suspends the current thread for at least ms
|
| +** milliseconds. This is equivalent to the Win32 Sleep() interface.
|
| +*/
|
| +#if SQLITE_OS_WINRT
|
| +static HANDLE sleepObj = NULL;
|
| +#endif
|
| +
|
| +SQLITE_API void sqlite3_win32_sleep(DWORD milliseconds){
|
| +#if SQLITE_OS_WINRT
|
| + if ( sleepObj==NULL ){
|
| + sleepObj = osCreateEventExW(NULL, NULL, CREATE_EVENT_MANUAL_RESET,
|
| + SYNCHRONIZE);
|
| + }
|
| + assert( sleepObj!=NULL );
|
| + osWaitForSingleObjectEx(sleepObj, milliseconds, FALSE);
|
| +#else
|
| + osSleep(milliseconds);
|
| +#endif
|
| +}
|
| +
|
| +#if SQLITE_MAX_WORKER_THREADS>0 && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \
|
| + SQLITE_THREADSAFE>0
|
| +SQLITE_PRIVATE DWORD sqlite3Win32Wait(HANDLE hObject){
|
| + DWORD rc;
|
| + while( (rc = osWaitForSingleObjectEx(hObject, INFINITE,
|
| + TRUE))==WAIT_IO_COMPLETION ){}
|
| + return rc;
|
| +}
|
| +#endif
|
| +
|
| +/*
|
| +** Return true (non-zero) if we are running under WinNT, Win2K, WinXP,
|
| +** or WinCE. Return false (zero) for Win95, Win98, or WinME.
|
| +**
|
| +** Here is an interesting observation: Win95, Win98, and WinME lack
|
| +** the LockFileEx() API. But we can still statically link against that
|
| +** API as long as we don't call it when running Win95/98/ME. A call to
|
| +** this routine is used to determine if the host is Win95/98/ME or
|
| +** WinNT/2K/XP so that we will know whether or not we can safely call
|
| +** the LockFileEx() API.
|
| +*/
|
| +
|
| +#if !SQLITE_WIN32_GETVERSIONEX
|
| +# define osIsNT() (1)
|
| +#elif SQLITE_OS_WINCE || SQLITE_OS_WINRT || !defined(SQLITE_WIN32_HAS_ANSI)
|
| +# define osIsNT() (1)
|
| +#elif !defined(SQLITE_WIN32_HAS_WIDE)
|
| +# define osIsNT() (0)
|
| +#else
|
| +# define osIsNT() ((sqlite3_os_type==2) || sqlite3_win32_is_nt())
|
| +#endif
|
| +
|
| +/*
|
| +** This function determines if the machine is running a version of Windows
|
| +** based on the NT kernel.
|
| +*/
|
| +SQLITE_API int sqlite3_win32_is_nt(void){
|
| +#if SQLITE_OS_WINRT
|
| + /*
|
| + ** NOTE: The WinRT sub-platform is always assumed to be based on the NT
|
| + ** kernel.
|
| + */
|
| + return 1;
|
| +#elif SQLITE_WIN32_GETVERSIONEX
|
| + if( osInterlockedCompareExchange(&sqlite3_os_type, 0, 0)==0 ){
|
| +#if defined(SQLITE_WIN32_HAS_ANSI)
|
| + OSVERSIONINFOA sInfo;
|
| + sInfo.dwOSVersionInfoSize = sizeof(sInfo);
|
| + osGetVersionExA(&sInfo);
|
| + osInterlockedCompareExchange(&sqlite3_os_type,
|
| + (sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) ? 2 : 1, 0);
|
| +#elif defined(SQLITE_WIN32_HAS_WIDE)
|
| + OSVERSIONINFOW sInfo;
|
| + sInfo.dwOSVersionInfoSize = sizeof(sInfo);
|
| + osGetVersionExW(&sInfo);
|
| + osInterlockedCompareExchange(&sqlite3_os_type,
|
| + (sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) ? 2 : 1, 0);
|
| +#endif
|
| + }
|
| + return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2;
|
| +#elif SQLITE_TEST
|
| + return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2;
|
| +#else
|
| + /*
|
| + ** NOTE: All sub-platforms where the GetVersionEx[AW] functions are
|
| + ** deprecated are always assumed to be based on the NT kernel.
|
| + */
|
| + return 1;
|
| +#endif
|
| +}
|
| +
|
| +#ifdef SQLITE_WIN32_MALLOC
|
| +/*
|
| +** Allocate nBytes of memory.
|
| +*/
|
| +static void *winMemMalloc(int nBytes){
|
| + HANDLE hHeap;
|
| + void *p;
|
| +
|
| + winMemAssertMagic();
|
| + hHeap = winMemGetHeap();
|
| + assert( hHeap!=0 );
|
| + assert( hHeap!=INVALID_HANDLE_VALUE );
|
| +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
|
| + assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
|
| +#endif
|
| + assert( nBytes>=0 );
|
| + p = osHeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes);
|
| + if( !p ){
|
| + sqlite3_log(SQLITE_NOMEM, "failed to HeapAlloc %u bytes (%lu), heap=%p",
|
| + nBytes, osGetLastError(), (void*)hHeap);
|
| + }
|
| + return p;
|
| +}
|
| +
|
| +/*
|
| +** Free memory.
|
| +*/
|
| +static void winMemFree(void *pPrior){
|
| + HANDLE hHeap;
|
| +
|
| + winMemAssertMagic();
|
| + hHeap = winMemGetHeap();
|
| + assert( hHeap!=0 );
|
| + assert( hHeap!=INVALID_HANDLE_VALUE );
|
| +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
|
| + assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) );
|
| +#endif
|
| + if( !pPrior ) return; /* Passing NULL to HeapFree is undefined. */
|
| + if( !osHeapFree(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ){
|
| + sqlite3_log(SQLITE_NOMEM, "failed to HeapFree block %p (%lu), heap=%p",
|
| + pPrior, osGetLastError(), (void*)hHeap);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Change the size of an existing memory allocation
|
| +*/
|
| +static void *winMemRealloc(void *pPrior, int nBytes){
|
| + HANDLE hHeap;
|
| + void *p;
|
| +
|
| + winMemAssertMagic();
|
| + hHeap = winMemGetHeap();
|
| + assert( hHeap!=0 );
|
| + assert( hHeap!=INVALID_HANDLE_VALUE );
|
| +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
|
| + assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) );
|
| +#endif
|
| + assert( nBytes>=0 );
|
| + if( !pPrior ){
|
| + p = osHeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes);
|
| + }else{
|
| + p = osHeapReAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior, (SIZE_T)nBytes);
|
| + }
|
| + if( !p ){
|
| + sqlite3_log(SQLITE_NOMEM, "failed to %s %u bytes (%lu), heap=%p",
|
| + pPrior ? "HeapReAlloc" : "HeapAlloc", nBytes, osGetLastError(),
|
| + (void*)hHeap);
|
| + }
|
| + return p;
|
| +}
|
| +
|
| +/*
|
| +** Return the size of an outstanding allocation, in bytes.
|
| +*/
|
| +static int winMemSize(void *p){
|
| + HANDLE hHeap;
|
| + SIZE_T n;
|
| +
|
| + winMemAssertMagic();
|
| + hHeap = winMemGetHeap();
|
| + assert( hHeap!=0 );
|
| + assert( hHeap!=INVALID_HANDLE_VALUE );
|
| +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
|
| + assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, p) );
|
| +#endif
|
| + if( !p ) return 0;
|
| + n = osHeapSize(hHeap, SQLITE_WIN32_HEAP_FLAGS, p);
|
| + if( n==(SIZE_T)-1 ){
|
| + sqlite3_log(SQLITE_NOMEM, "failed to HeapSize block %p (%lu), heap=%p",
|
| + p, osGetLastError(), (void*)hHeap);
|
| + return 0;
|
| + }
|
| + return (int)n;
|
| +}
|
| +
|
| +/*
|
| +** Round up a request size to the next valid allocation size.
|
| +*/
|
| +static int winMemRoundup(int n){
|
| + return n;
|
| +}
|
| +
|
| +/*
|
| +** Initialize this module.
|
| +*/
|
| +static int winMemInit(void *pAppData){
|
| + winMemData *pWinMemData = (winMemData *)pAppData;
|
| +
|
| + if( !pWinMemData ) return SQLITE_ERROR;
|
| + assert( pWinMemData->magic1==WINMEM_MAGIC1 );
|
| + assert( pWinMemData->magic2==WINMEM_MAGIC2 );
|
| +
|
| +#if !SQLITE_OS_WINRT && SQLITE_WIN32_HEAP_CREATE
|
| + if( !pWinMemData->hHeap ){
|
| + DWORD dwInitialSize = SQLITE_WIN32_HEAP_INIT_SIZE;
|
| + DWORD dwMaximumSize = (DWORD)sqlite3GlobalConfig.nHeap;
|
| + if( dwMaximumSize==0 ){
|
| + dwMaximumSize = SQLITE_WIN32_HEAP_MAX_SIZE;
|
| + }else if( dwInitialSize>dwMaximumSize ){
|
| + dwInitialSize = dwMaximumSize;
|
| + }
|
| + pWinMemData->hHeap = osHeapCreate(SQLITE_WIN32_HEAP_FLAGS,
|
| + dwInitialSize, dwMaximumSize);
|
| + if( !pWinMemData->hHeap ){
|
| + sqlite3_log(SQLITE_NOMEM,
|
| + "failed to HeapCreate (%lu), flags=%u, initSize=%lu, maxSize=%lu",
|
| + osGetLastError(), SQLITE_WIN32_HEAP_FLAGS, dwInitialSize,
|
| + dwMaximumSize);
|
| + return SQLITE_NOMEM_BKPT;
|
| + }
|
| + pWinMemData->bOwned = TRUE;
|
| + assert( pWinMemData->bOwned );
|
| + }
|
| +#else
|
| + pWinMemData->hHeap = osGetProcessHeap();
|
| + if( !pWinMemData->hHeap ){
|
| + sqlite3_log(SQLITE_NOMEM,
|
| + "failed to GetProcessHeap (%lu)", osGetLastError());
|
| + return SQLITE_NOMEM_BKPT;
|
| + }
|
| + pWinMemData->bOwned = FALSE;
|
| + assert( !pWinMemData->bOwned );
|
| +#endif
|
| + assert( pWinMemData->hHeap!=0 );
|
| + assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE );
|
| +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
|
| + assert( osHeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
|
| +#endif
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Deinitialize this module.
|
| +*/
|
| +static void winMemShutdown(void *pAppData){
|
| + winMemData *pWinMemData = (winMemData *)pAppData;
|
| +
|
| + if( !pWinMemData ) return;
|
| + assert( pWinMemData->magic1==WINMEM_MAGIC1 );
|
| + assert( pWinMemData->magic2==WINMEM_MAGIC2 );
|
| +
|
| + if( pWinMemData->hHeap ){
|
| + assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE );
|
| +#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
|
| + assert( osHeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
|
| +#endif
|
| + if( pWinMemData->bOwned ){
|
| + if( !osHeapDestroy(pWinMemData->hHeap) ){
|
| + sqlite3_log(SQLITE_NOMEM, "failed to HeapDestroy (%lu), heap=%p",
|
| + osGetLastError(), (void*)pWinMemData->hHeap);
|
| + }
|
| + pWinMemData->bOwned = FALSE;
|
| + }
|
| + pWinMemData->hHeap = NULL;
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Populate the low-level memory allocation function pointers in
|
| +** sqlite3GlobalConfig.m with pointers to the routines in this file. The
|
| +** arguments specify the block of memory to manage.
|
| +**
|
| +** This routine is only called by sqlite3_config(), and therefore
|
| +** is not required to be threadsafe (it is not).
|
| +*/
|
| +SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetWin32(void){
|
| + static const sqlite3_mem_methods winMemMethods = {
|
| + winMemMalloc,
|
| + winMemFree,
|
| + winMemRealloc,
|
| + winMemSize,
|
| + winMemRoundup,
|
| + winMemInit,
|
| + winMemShutdown,
|
| + &win_mem_data
|
| + };
|
| + return &winMemMethods;
|
| +}
|
| +
|
| +SQLITE_PRIVATE void sqlite3MemSetDefault(void){
|
| + sqlite3_config(SQLITE_CONFIG_MALLOC, sqlite3MemGetWin32());
|
| +}
|
| +#endif /* SQLITE_WIN32_MALLOC */
|
| +
|
| +/*
|
| +** Convert a UTF-8 string to Microsoft Unicode.
|
| +**
|
| +** Space to hold the returned string is obtained from sqlite3_malloc().
|
| +*/
|
| +static LPWSTR winUtf8ToUnicode(const char *zText){
|
| + int nChar;
|
| + LPWSTR zWideText;
|
| +
|
| + nChar = osMultiByteToWideChar(CP_UTF8, 0, zText, -1, NULL, 0);
|
| + if( nChar==0 ){
|
| + return 0;
|
| + }
|
| + zWideText = sqlite3MallocZero( nChar*sizeof(WCHAR) );
|
| + if( zWideText==0 ){
|
| + return 0;
|
| + }
|
| + nChar = osMultiByteToWideChar(CP_UTF8, 0, zText, -1, zWideText,
|
| + nChar);
|
| + if( nChar==0 ){
|
| + sqlite3_free(zWideText);
|
| + zWideText = 0;
|
| + }
|
| + return zWideText;
|
| +}
|
| +
|
| +/*
|
| +** Convert a Microsoft Unicode string to UTF-8.
|
| +**
|
| +** Space to hold the returned string is obtained from sqlite3_malloc().
|
| +*/
|
| +static char *winUnicodeToUtf8(LPCWSTR zWideText){
|
| + int nByte;
|
| + char *zText;
|
| +
|
| + nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideText, -1, 0, 0, 0, 0);
|
| + if( nByte == 0 ){
|
| + return 0;
|
| + }
|
| + zText = sqlite3MallocZero( nByte );
|
| + if( zText==0 ){
|
| + return 0;
|
| + }
|
| + nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideText, -1, zText, nByte,
|
| + 0, 0);
|
| + if( nByte == 0 ){
|
| + sqlite3_free(zText);
|
| + zText = 0;
|
| + }
|
| + return zText;
|
| +}
|
| +
|
| +/*
|
| +** Convert an ANSI string to Microsoft Unicode, using the ANSI or OEM
|
| +** code page.
|
| +**
|
| +** Space to hold the returned string is obtained from sqlite3_malloc().
|
| +*/
|
| +static LPWSTR winMbcsToUnicode(const char *zText, int useAnsi){
|
| + int nByte;
|
| + LPWSTR zMbcsText;
|
| + int codepage = useAnsi ? CP_ACP : CP_OEMCP;
|
| +
|
| + nByte = osMultiByteToWideChar(codepage, 0, zText, -1, NULL,
|
| + 0)*sizeof(WCHAR);
|
| + if( nByte==0 ){
|
| + return 0;
|
| + }
|
| + zMbcsText = sqlite3MallocZero( nByte*sizeof(WCHAR) );
|
| + if( zMbcsText==0 ){
|
| + return 0;
|
| + }
|
| + nByte = osMultiByteToWideChar(codepage, 0, zText, -1, zMbcsText,
|
| + nByte);
|
| + if( nByte==0 ){
|
| + sqlite3_free(zMbcsText);
|
| + zMbcsText = 0;
|
| + }
|
| + return zMbcsText;
|
| +}
|
| +
|
| +/*
|
| +** Convert a Microsoft Unicode string to a multi-byte character string,
|
| +** using the ANSI or OEM code page.
|
| +**
|
| +** Space to hold the returned string is obtained from sqlite3_malloc().
|
| +*/
|
| +static char *winUnicodeToMbcs(LPCWSTR zWideText, int useAnsi){
|
| + int nByte;
|
| + char *zText;
|
| + int codepage = useAnsi ? CP_ACP : CP_OEMCP;
|
| +
|
| + nByte = osWideCharToMultiByte(codepage, 0, zWideText, -1, 0, 0, 0, 0);
|
| + if( nByte == 0 ){
|
| + return 0;
|
| + }
|
| + zText = sqlite3MallocZero( nByte );
|
| + if( zText==0 ){
|
| + return 0;
|
| + }
|
| + nByte = osWideCharToMultiByte(codepage, 0, zWideText, -1, zText,
|
| + nByte, 0, 0);
|
| + if( nByte == 0 ){
|
| + sqlite3_free(zText);
|
| + zText = 0;
|
| + }
|
| + return zText;
|
| +}
|
| +
|
| +/*
|
| +** Convert a multi-byte character string to UTF-8.
|
| +**
|
| +** Space to hold the returned string is obtained from sqlite3_malloc().
|
| +*/
|
| +static char *winMbcsToUtf8(const char *zText, int useAnsi){
|
| + char *zTextUtf8;
|
| + LPWSTR zTmpWide;
|
| +
|
| + zTmpWide = winMbcsToUnicode(zText, useAnsi);
|
| + if( zTmpWide==0 ){
|
| + return 0;
|
| + }
|
| + zTextUtf8 = winUnicodeToUtf8(zTmpWide);
|
| + sqlite3_free(zTmpWide);
|
| + return zTextUtf8;
|
| +}
|
| +
|
| +/*
|
| +** Convert a UTF-8 string to a multi-byte character string.
|
| +**
|
| +** Space to hold the returned string is obtained from sqlite3_malloc().
|
| +*/
|
| +static char *winUtf8ToMbcs(const char *zText, int useAnsi){
|
| + char *zTextMbcs;
|
| + LPWSTR zTmpWide;
|
| +
|
| + zTmpWide = winUtf8ToUnicode(zText);
|
| + if( zTmpWide==0 ){
|
| + return 0;
|
| + }
|
| + zTextMbcs = winUnicodeToMbcs(zTmpWide, useAnsi);
|
| + sqlite3_free(zTmpWide);
|
| + return zTextMbcs;
|
| +}
|
| +
|
| +/*
|
| +** This is a public wrapper for the winUtf8ToUnicode() function.
|
| +*/
|
| +SQLITE_API LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText){
|
| +#ifdef SQLITE_ENABLE_API_ARMOR
|
| + if( !zText ){
|
| + (void)SQLITE_MISUSE_BKPT;
|
| + return 0;
|
| + }
|
| +#endif
|
| +#ifndef SQLITE_OMIT_AUTOINIT
|
| + if( sqlite3_initialize() ) return 0;
|
| +#endif
|
| + return winUtf8ToUnicode(zText);
|
| +}
|
| +
|
| +/*
|
| +** This is a public wrapper for the winUnicodeToUtf8() function.
|
| +*/
|
| +SQLITE_API char *sqlite3_win32_unicode_to_utf8(LPCWSTR zWideText){
|
| +#ifdef SQLITE_ENABLE_API_ARMOR
|
| + if( !zWideText ){
|
| + (void)SQLITE_MISUSE_BKPT;
|
| + return 0;
|
| + }
|
| +#endif
|
| +#ifndef SQLITE_OMIT_AUTOINIT
|
| + if( sqlite3_initialize() ) return 0;
|
| +#endif
|
| + return winUnicodeToUtf8(zWideText);
|
| +}
|
| +
|
| +/*
|
| +** This is a public wrapper for the winMbcsToUtf8() function.
|
| +*/
|
| +SQLITE_API char *sqlite3_win32_mbcs_to_utf8(const char *zText){
|
| +#ifdef SQLITE_ENABLE_API_ARMOR
|
| + if( !zText ){
|
| + (void)SQLITE_MISUSE_BKPT;
|
| + return 0;
|
| + }
|
| +#endif
|
| +#ifndef SQLITE_OMIT_AUTOINIT
|
| + if( sqlite3_initialize() ) return 0;
|
| +#endif
|
| + return winMbcsToUtf8(zText, osAreFileApisANSI());
|
| +}
|
| +
|
| +/*
|
| +** This is a public wrapper for the winMbcsToUtf8() function.
|
| +*/
|
| +SQLITE_API char *sqlite3_win32_mbcs_to_utf8_v2(const char *zText, int useAnsi){
|
| +#ifdef SQLITE_ENABLE_API_ARMOR
|
| + if( !zText ){
|
| + (void)SQLITE_MISUSE_BKPT;
|
| + return 0;
|
| + }
|
| +#endif
|
| +#ifndef SQLITE_OMIT_AUTOINIT
|
| + if( sqlite3_initialize() ) return 0;
|
| +#endif
|
| + return winMbcsToUtf8(zText, useAnsi);
|
| +}
|
| +
|
| +/*
|
| +** This is a public wrapper for the winUtf8ToMbcs() function.
|
| +*/
|
| +SQLITE_API char *sqlite3_win32_utf8_to_mbcs(const char *zText){
|
| +#ifdef SQLITE_ENABLE_API_ARMOR
|
| + if( !zText ){
|
| + (void)SQLITE_MISUSE_BKPT;
|
| + return 0;
|
| + }
|
| +#endif
|
| +#ifndef SQLITE_OMIT_AUTOINIT
|
| + if( sqlite3_initialize() ) return 0;
|
| +#endif
|
| + return winUtf8ToMbcs(zText, osAreFileApisANSI());
|
| +}
|
| +
|
| +/*
|
| +** This is a public wrapper for the winUtf8ToMbcs() function.
|
| +*/
|
| +SQLITE_API char *sqlite3_win32_utf8_to_mbcs_v2(const char *zText, int useAnsi){
|
| +#ifdef SQLITE_ENABLE_API_ARMOR
|
| + if( !zText ){
|
| + (void)SQLITE_MISUSE_BKPT;
|
| + return 0;
|
| + }
|
| +#endif
|
| +#ifndef SQLITE_OMIT_AUTOINIT
|
| + if( sqlite3_initialize() ) return 0;
|
| +#endif
|
| + return winUtf8ToMbcs(zText, useAnsi);
|
| +}
|
| +
|
| +/*
|
| +** This function sets the data directory or the temporary directory based on
|
| +** the provided arguments. The type argument must be 1 in order to set the
|
| +** data directory or 2 in order to set the temporary directory. The zValue
|
| +** argument is the name of the directory to use. The return value will be
|
| +** SQLITE_OK if successful.
|
| +*/
|
| +SQLITE_API int sqlite3_win32_set_directory(DWORD type, LPCWSTR zValue){
|
| + char **ppDirectory = 0;
|
| +#ifndef SQLITE_OMIT_AUTOINIT
|
| + int rc = sqlite3_initialize();
|
| + if( rc ) return rc;
|
| +#endif
|
| + if( type==SQLITE_WIN32_DATA_DIRECTORY_TYPE ){
|
| + ppDirectory = &sqlite3_data_directory;
|
| + }else if( type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE ){
|
| + ppDirectory = &sqlite3_temp_directory;
|
| + }
|
| + assert( !ppDirectory || type==SQLITE_WIN32_DATA_DIRECTORY_TYPE
|
| + || type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE
|
| + );
|
| + assert( !ppDirectory || sqlite3MemdebugHasType(*ppDirectory, MEMTYPE_HEAP) );
|
| + if( ppDirectory ){
|
| + char *zValueUtf8 = 0;
|
| + if( zValue && zValue[0] ){
|
| + zValueUtf8 = winUnicodeToUtf8(zValue);
|
| + if ( zValueUtf8==0 ){
|
| + return SQLITE_NOMEM_BKPT;
|
| + }
|
| + }
|
| + sqlite3_free(*ppDirectory);
|
| + *ppDirectory = zValueUtf8;
|
| + return SQLITE_OK;
|
| + }
|
| + return SQLITE_ERROR;
|
| +}
|
| +
|
| +/*
|
| +** The return value of winGetLastErrorMsg
|
| +** is zero if the error message fits in the buffer, or non-zero
|
| +** otherwise (if the message was truncated).
|
| +*/
|
| +static int winGetLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){
|
| + /* FormatMessage returns 0 on failure. Otherwise it
|
| + ** returns the number of TCHARs written to the output
|
| + ** buffer, excluding the terminating null char.
|
| + */
|
| + DWORD dwLen = 0;
|
| + char *zOut = 0;
|
| +
|
| + if( osIsNT() ){
|
| +#if SQLITE_OS_WINRT
|
| + WCHAR zTempWide[SQLITE_WIN32_MAX_ERRMSG_CHARS+1];
|
| + dwLen = osFormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
|
| + FORMAT_MESSAGE_IGNORE_INSERTS,
|
| + NULL,
|
| + lastErrno,
|
| + 0,
|
| + zTempWide,
|
| + SQLITE_WIN32_MAX_ERRMSG_CHARS,
|
| + 0);
|
| +#else
|
| + LPWSTR zTempWide = NULL;
|
| + dwLen = osFormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
| + FORMAT_MESSAGE_FROM_SYSTEM |
|
| + FORMAT_MESSAGE_IGNORE_INSERTS,
|
| + NULL,
|
| + lastErrno,
|
| + 0,
|
| + (LPWSTR) &zTempWide,
|
| + 0,
|
| + 0);
|
| +#endif
|
| + if( dwLen > 0 ){
|
| + /* allocate a buffer and convert to UTF8 */
|
| + sqlite3BeginBenignMalloc();
|
| + zOut = winUnicodeToUtf8(zTempWide);
|
| + sqlite3EndBenignMalloc();
|
| +#if !SQLITE_OS_WINRT
|
| + /* free the system buffer allocated by FormatMessage */
|
| + osLocalFree(zTempWide);
|
| +#endif
|
| + }
|
| + }
|
| +#ifdef SQLITE_WIN32_HAS_ANSI
|
| + else{
|
| + char *zTemp = NULL;
|
| + dwLen = osFormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
| + FORMAT_MESSAGE_FROM_SYSTEM |
|
| + FORMAT_MESSAGE_IGNORE_INSERTS,
|
| + NULL,
|
| + lastErrno,
|
| + 0,
|
| + (LPSTR) &zTemp,
|
| + 0,
|
| + 0);
|
| + if( dwLen > 0 ){
|
| + /* allocate a buffer and convert to UTF8 */
|
| + sqlite3BeginBenignMalloc();
|
| + zOut = winMbcsToUtf8(zTemp, osAreFileApisANSI());
|
| + sqlite3EndBenignMalloc();
|
| + /* free the system buffer allocated by FormatMessage */
|
| + osLocalFree(zTemp);
|
| + }
|
| + }
|
| +#endif
|
| + if( 0 == dwLen ){
|
| + sqlite3_snprintf(nBuf, zBuf, "OsError 0x%lx (%lu)", lastErrno, lastErrno);
|
| + }else{
|
| + /* copy a maximum of nBuf chars to output buffer */
|
| + sqlite3_snprintf(nBuf, zBuf, "%s", zOut);
|
| + /* free the UTF8 buffer */
|
| + sqlite3_free(zOut);
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| +/*
|
| +**
|
| +** This function - winLogErrorAtLine() - is only ever called via the macro
|
| +** winLogError().
|
| +**
|
| +** This routine is invoked after an error occurs in an OS function.
|
| +** It logs a message using sqlite3_log() containing the current value of
|
| +** error code and, if possible, the human-readable equivalent from
|
| +** FormatMessage.
|
| +**
|
| +** The first argument passed to the macro should be the error code that
|
| +** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN).
|
| +** The two subsequent arguments should be the name of the OS function that
|
| +** failed and the associated file-system path, if any.
|
| +*/
|
| +#define winLogError(a,b,c,d) winLogErrorAtLine(a,b,c,d,__LINE__)
|
| +static int winLogErrorAtLine(
|
| + int errcode, /* SQLite error code */
|
| + DWORD lastErrno, /* Win32 last error */
|
| + 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 zMsg[500]; /* Human readable error text */
|
| + int i; /* Loop counter */
|
| +
|
| + zMsg[0] = 0;
|
| + winGetLastErrorMsg(lastErrno, sizeof(zMsg), zMsg);
|
| + assert( errcode!=SQLITE_OK );
|
| + if( zPath==0 ) zPath = "";
|
| + for(i=0; zMsg[i] && zMsg[i]!='\r' && zMsg[i]!='\n'; i++){}
|
| + zMsg[i] = 0;
|
| + sqlite3_log(errcode,
|
| + "os_win.c:%d: (%lu) %s(%s) - %s",
|
| + iLine, lastErrno, zFunc, zPath, zMsg
|
| + );
|
| +
|
| + return errcode;
|
| +}
|
| +
|
| +/*
|
| +** The number of times that a ReadFile(), WriteFile(), and DeleteFile()
|
| +** will be retried following a locking error - probably caused by
|
| +** antivirus software. Also the initial delay before the first retry.
|
| +** The delay increases linearly with each retry.
|
| +*/
|
| +#ifndef SQLITE_WIN32_IOERR_RETRY
|
| +# define SQLITE_WIN32_IOERR_RETRY 10
|
| +#endif
|
| +#ifndef SQLITE_WIN32_IOERR_RETRY_DELAY
|
| +# define SQLITE_WIN32_IOERR_RETRY_DELAY 25
|
| +#endif
|
| +static int winIoerrRetry = SQLITE_WIN32_IOERR_RETRY;
|
| +static int winIoerrRetryDelay = SQLITE_WIN32_IOERR_RETRY_DELAY;
|
| +
|
| +/*
|
| +** The "winIoerrCanRetry1" macro is used to determine if a particular I/O
|
| +** error code obtained via GetLastError() is eligible to be retried. It
|
| +** must accept the error code DWORD as its only argument and should return
|
| +** non-zero if the error code is transient in nature and the operation
|
| +** responsible for generating the original error might succeed upon being
|
| +** retried. The argument to this macro should be a variable.
|
| +**
|
| +** Additionally, a macro named "winIoerrCanRetry2" may be defined. If it
|
| +** is defined, it will be consulted only when the macro "winIoerrCanRetry1"
|
| +** returns zero. The "winIoerrCanRetry2" macro is completely optional and
|
| +** may be used to include additional error codes in the set that should
|
| +** result in the failing I/O operation being retried by the caller. If
|
| +** defined, the "winIoerrCanRetry2" macro must exhibit external semantics
|
| +** identical to those of the "winIoerrCanRetry1" macro.
|
| +*/
|
| +#if !defined(winIoerrCanRetry1)
|
| +#define winIoerrCanRetry1(a) (((a)==ERROR_ACCESS_DENIED) || \
|
| + ((a)==ERROR_SHARING_VIOLATION) || \
|
| + ((a)==ERROR_LOCK_VIOLATION) || \
|
| + ((a)==ERROR_DEV_NOT_EXIST) || \
|
| + ((a)==ERROR_NETNAME_DELETED) || \
|
| + ((a)==ERROR_SEM_TIMEOUT) || \
|
| + ((a)==ERROR_NETWORK_UNREACHABLE))
|
| +#endif
|
| +
|
| +/*
|
| +** If a ReadFile() or WriteFile() error occurs, invoke this routine
|
| +** to see if it should be retried. Return TRUE to retry. Return FALSE
|
| +** to give up with an error.
|
| +*/
|
| +static int winRetryIoerr(int *pnRetry, DWORD *pError){
|
| + DWORD e = osGetLastError();
|
| + if( *pnRetry>=winIoerrRetry ){
|
| + if( pError ){
|
| + *pError = e;
|
| + }
|
| + return 0;
|
| + }
|
| + if( winIoerrCanRetry1(e) ){
|
| + sqlite3_win32_sleep(winIoerrRetryDelay*(1+*pnRetry));
|
| + ++*pnRetry;
|
| + return 1;
|
| + }
|
| +#if defined(winIoerrCanRetry2)
|
| + else if( winIoerrCanRetry2(e) ){
|
| + sqlite3_win32_sleep(winIoerrRetryDelay*(1+*pnRetry));
|
| + ++*pnRetry;
|
| + return 1;
|
| + }
|
| +#endif
|
| + if( pError ){
|
| + *pError = e;
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| +/*
|
| +** Log a I/O error retry episode.
|
| +*/
|
| +static void winLogIoerr(int nRetry, int lineno){
|
| + if( nRetry ){
|
| + sqlite3_log(SQLITE_NOTICE,
|
| + "delayed %dms for lock/sharing conflict at line %d",
|
| + winIoerrRetryDelay*nRetry*(nRetry+1)/2, lineno
|
| + );
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** This #if does not rely on the SQLITE_OS_WINCE define because the
|
| +** corresponding section in "date.c" cannot use it.
|
| +*/
|
| +#if !defined(SQLITE_OMIT_LOCALTIME) && defined(_WIN32_WCE) && \
|
| + (!defined(SQLITE_MSVC_LOCALTIME_API) || !SQLITE_MSVC_LOCALTIME_API)
|
| +/*
|
| +** The MSVC CRT on Windows CE may not have a localtime() function.
|
| +** So define a substitute.
|
| +*/
|
| +/* # include <time.h> */
|
| +struct tm *__cdecl localtime(const time_t *t)
|
| +{
|
| + static struct tm y;
|
| + FILETIME uTm, lTm;
|
| + SYSTEMTIME pTm;
|
| + sqlite3_int64 t64;
|
| + t64 = *t;
|
| + t64 = (t64 + 11644473600)*10000000;
|
| + uTm.dwLowDateTime = (DWORD)(t64 & 0xFFFFFFFF);
|
| + uTm.dwHighDateTime= (DWORD)(t64 >> 32);
|
| + osFileTimeToLocalFileTime(&uTm,&lTm);
|
| + osFileTimeToSystemTime(&lTm,&pTm);
|
| + y.tm_year = pTm.wYear - 1900;
|
| + y.tm_mon = pTm.wMonth - 1;
|
| + y.tm_wday = pTm.wDayOfWeek;
|
| + y.tm_mday = pTm.wDay;
|
| + y.tm_hour = pTm.wHour;
|
| + y.tm_min = pTm.wMinute;
|
| + y.tm_sec = pTm.wSecond;
|
| + return &y;
|
| +}
|
| +#endif
|
| +
|
| +#if SQLITE_OS_WINCE
|
| +/*************************************************************************
|
| +** This section contains code for WinCE only.
|
| +*/
|
| +#define HANDLE_TO_WINFILE(a) (winFile*)&((char*)a)[-(int)offsetof(winFile,h)]
|
| +
|
| +/*
|
| +** Acquire a lock on the handle h
|
| +*/
|
| +static void winceMutexAcquire(HANDLE h){
|
| + DWORD dwErr;
|
| + do {
|
| + dwErr = osWaitForSingleObject(h, INFINITE);
|
| + } while (dwErr != WAIT_OBJECT_0 && dwErr != WAIT_ABANDONED);
|
| +}
|
| +/*
|
| +** Release a lock acquired by winceMutexAcquire()
|
| +*/
|
| +#define winceMutexRelease(h) ReleaseMutex(h)
|
| +
|
| +/*
|
| +** Create the mutex and shared memory used for locking in the file
|
| +** descriptor pFile
|
| +*/
|
| +static int winceCreateLock(const char *zFilename, winFile *pFile){
|
| + LPWSTR zTok;
|
| + LPWSTR zName;
|
| + DWORD lastErrno;
|
| + BOOL bLogged = FALSE;
|
| + BOOL bInit = TRUE;
|
| +
|
| + zName = winUtf8ToUnicode(zFilename);
|
| + if( zName==0 ){
|
| + /* out of memory */
|
| + return SQLITE_IOERR_NOMEM_BKPT;
|
| + }
|
| +
|
| + /* Initialize the local lockdata */
|
| + memset(&pFile->local, 0, sizeof(pFile->local));
|
| +
|
| + /* Replace the backslashes from the filename and lowercase it
|
| + ** to derive a mutex name. */
|
| + zTok = osCharLowerW(zName);
|
| + for (;*zTok;zTok++){
|
| + if (*zTok == '\\') *zTok = '_';
|
| + }
|
| +
|
| + /* Create/open the named mutex */
|
| + pFile->hMutex = osCreateMutexW(NULL, FALSE, zName);
|
| + if (!pFile->hMutex){
|
| + pFile->lastErrno = osGetLastError();
|
| + sqlite3_free(zName);
|
| + return winLogError(SQLITE_IOERR, pFile->lastErrno,
|
| + "winceCreateLock1", zFilename);
|
| + }
|
| +
|
| + /* Acquire the mutex before continuing */
|
| + winceMutexAcquire(pFile->hMutex);
|
| +
|
| + /* Since the names of named mutexes, semaphores, file mappings etc are
|
| + ** case-sensitive, take advantage of that by uppercasing the mutex name
|
| + ** and using that as the shared filemapping name.
|
| + */
|
| + osCharUpperW(zName);
|
| + pFile->hShared = osCreateFileMappingW(INVALID_HANDLE_VALUE, NULL,
|
| + PAGE_READWRITE, 0, sizeof(winceLock),
|
| + zName);
|
| +
|
| + /* Set a flag that indicates we're the first to create the memory so it
|
| + ** must be zero-initialized */
|
| + lastErrno = osGetLastError();
|
| + if (lastErrno == ERROR_ALREADY_EXISTS){
|
| + bInit = FALSE;
|
| + }
|
| +
|
| + sqlite3_free(zName);
|
| +
|
| + /* If we succeeded in making the shared memory handle, map it. */
|
| + if( pFile->hShared ){
|
| + pFile->shared = (winceLock*)osMapViewOfFile(pFile->hShared,
|
| + FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, sizeof(winceLock));
|
| + /* If mapping failed, close the shared memory handle and erase it */
|
| + if( !pFile->shared ){
|
| + pFile->lastErrno = osGetLastError();
|
| + winLogError(SQLITE_IOERR, pFile->lastErrno,
|
| + "winceCreateLock2", zFilename);
|
| + bLogged = TRUE;
|
| + osCloseHandle(pFile->hShared);
|
| + pFile->hShared = NULL;
|
| + }
|
| + }
|
| +
|
| + /* If shared memory could not be created, then close the mutex and fail */
|
| + if( pFile->hShared==NULL ){
|
| + if( !bLogged ){
|
| + pFile->lastErrno = lastErrno;
|
| + winLogError(SQLITE_IOERR, pFile->lastErrno,
|
| + "winceCreateLock3", zFilename);
|
| + bLogged = TRUE;
|
| + }
|
| + winceMutexRelease(pFile->hMutex);
|
| + osCloseHandle(pFile->hMutex);
|
| + pFile->hMutex = NULL;
|
| + return SQLITE_IOERR;
|
| + }
|
| +
|
| + /* Initialize the shared memory if we're supposed to */
|
| + if( bInit ){
|
| + memset(pFile->shared, 0, sizeof(winceLock));
|
| + }
|
| +
|
| + winceMutexRelease(pFile->hMutex);
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Destroy the part of winFile that deals with wince locks
|
| +*/
|
| +static void winceDestroyLock(winFile *pFile){
|
| + if (pFile->hMutex){
|
| + /* Acquire the mutex */
|
| + winceMutexAcquire(pFile->hMutex);
|
| +
|
| + /* The following blocks should probably assert in debug mode, but they
|
| + are to cleanup in case any locks remained open */
|
| + if (pFile->local.nReaders){
|
| + pFile->shared->nReaders --;
|
| + }
|
| + if (pFile->local.bReserved){
|
| + pFile->shared->bReserved = FALSE;
|
| + }
|
| + if (pFile->local.bPending){
|
| + pFile->shared->bPending = FALSE;
|
| + }
|
| + if (pFile->local.bExclusive){
|
| + pFile->shared->bExclusive = FALSE;
|
| + }
|
| +
|
| + /* De-reference and close our copy of the shared memory handle */
|
| + osUnmapViewOfFile(pFile->shared);
|
| + osCloseHandle(pFile->hShared);
|
| +
|
| + /* Done with the mutex */
|
| + winceMutexRelease(pFile->hMutex);
|
| + osCloseHandle(pFile->hMutex);
|
| + pFile->hMutex = NULL;
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** An implementation of the LockFile() API of Windows for CE
|
| +*/
|
| +static BOOL winceLockFile(
|
| + LPHANDLE phFile,
|
| + DWORD dwFileOffsetLow,
|
| + DWORD dwFileOffsetHigh,
|
| + DWORD nNumberOfBytesToLockLow,
|
| + DWORD nNumberOfBytesToLockHigh
|
| +){
|
| + winFile *pFile = HANDLE_TO_WINFILE(phFile);
|
| + BOOL bReturn = FALSE;
|
| +
|
| + UNUSED_PARAMETER(dwFileOffsetHigh);
|
| + UNUSED_PARAMETER(nNumberOfBytesToLockHigh);
|
| +
|
| + if (!pFile->hMutex) return TRUE;
|
| + winceMutexAcquire(pFile->hMutex);
|
| +
|
| + /* Wanting an exclusive lock? */
|
| + if (dwFileOffsetLow == (DWORD)SHARED_FIRST
|
| + && nNumberOfBytesToLockLow == (DWORD)SHARED_SIZE){
|
| + if (pFile->shared->nReaders == 0 && pFile->shared->bExclusive == 0){
|
| + pFile->shared->bExclusive = TRUE;
|
| + pFile->local.bExclusive = TRUE;
|
| + bReturn = TRUE;
|
| + }
|
| + }
|
| +
|
| + /* Want a read-only lock? */
|
| + else if (dwFileOffsetLow == (DWORD)SHARED_FIRST &&
|
| + nNumberOfBytesToLockLow == 1){
|
| + if (pFile->shared->bExclusive == 0){
|
| + pFile->local.nReaders ++;
|
| + if (pFile->local.nReaders == 1){
|
| + pFile->shared->nReaders ++;
|
| + }
|
| + bReturn = TRUE;
|
| + }
|
| + }
|
| +
|
| + /* Want a pending lock? */
|
| + else if (dwFileOffsetLow == (DWORD)PENDING_BYTE
|
| + && nNumberOfBytesToLockLow == 1){
|
| + /* If no pending lock has been acquired, then acquire it */
|
| + if (pFile->shared->bPending == 0) {
|
| + pFile->shared->bPending = TRUE;
|
| + pFile->local.bPending = TRUE;
|
| + bReturn = TRUE;
|
| + }
|
| + }
|
| +
|
| + /* Want a reserved lock? */
|
| + else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE
|
| + && nNumberOfBytesToLockLow == 1){
|
| + if (pFile->shared->bReserved == 0) {
|
| + pFile->shared->bReserved = TRUE;
|
| + pFile->local.bReserved = TRUE;
|
| + bReturn = TRUE;
|
| + }
|
| + }
|
| +
|
| + winceMutexRelease(pFile->hMutex);
|
| + return bReturn;
|
| +}
|
| +
|
| +/*
|
| +** An implementation of the UnlockFile API of Windows for CE
|
| +*/
|
| +static BOOL winceUnlockFile(
|
| + LPHANDLE phFile,
|
| + DWORD dwFileOffsetLow,
|
| + DWORD dwFileOffsetHigh,
|
| + DWORD nNumberOfBytesToUnlockLow,
|
| + DWORD nNumberOfBytesToUnlockHigh
|
| +){
|
| + winFile *pFile = HANDLE_TO_WINFILE(phFile);
|
| + BOOL bReturn = FALSE;
|
| +
|
| + UNUSED_PARAMETER(dwFileOffsetHigh);
|
| + UNUSED_PARAMETER(nNumberOfBytesToUnlockHigh);
|
| +
|
| + if (!pFile->hMutex) return TRUE;
|
| + winceMutexAcquire(pFile->hMutex);
|
| +
|
| + /* Releasing a reader lock or an exclusive lock */
|
| + if (dwFileOffsetLow == (DWORD)SHARED_FIRST){
|
| + /* Did we have an exclusive lock? */
|
| + if (pFile->local.bExclusive){
|
| + assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE);
|
| + pFile->local.bExclusive = FALSE;
|
| + pFile->shared->bExclusive = FALSE;
|
| + bReturn = TRUE;
|
| + }
|
| +
|
| + /* Did we just have a reader lock? */
|
| + else if (pFile->local.nReaders){
|
| + assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE
|
| + || nNumberOfBytesToUnlockLow == 1);
|
| + pFile->local.nReaders --;
|
| + if (pFile->local.nReaders == 0)
|
| + {
|
| + pFile->shared->nReaders --;
|
| + }
|
| + bReturn = TRUE;
|
| + }
|
| + }
|
| +
|
| + /* Releasing a pending lock */
|
| + else if (dwFileOffsetLow == (DWORD)PENDING_BYTE
|
| + && nNumberOfBytesToUnlockLow == 1){
|
| + if (pFile->local.bPending){
|
| + pFile->local.bPending = FALSE;
|
| + pFile->shared->bPending = FALSE;
|
| + bReturn = TRUE;
|
| + }
|
| + }
|
| + /* Releasing a reserved lock */
|
| + else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE
|
| + && nNumberOfBytesToUnlockLow == 1){
|
| + if (pFile->local.bReserved) {
|
| + pFile->local.bReserved = FALSE;
|
| + pFile->shared->bReserved = FALSE;
|
| + bReturn = TRUE;
|
| + }
|
| + }
|
| +
|
| + winceMutexRelease(pFile->hMutex);
|
| + return bReturn;
|
| +}
|
| +/*
|
| +** End of the special code for wince
|
| +*****************************************************************************/
|
| +#endif /* SQLITE_OS_WINCE */
|
| +
|
| +/*
|
| +** Lock a file region.
|
| +*/
|
| +static BOOL winLockFile(
|
| + LPHANDLE phFile,
|
| + DWORD flags,
|
| + DWORD offsetLow,
|
| + DWORD offsetHigh,
|
| + DWORD numBytesLow,
|
| + DWORD numBytesHigh
|
| +){
|
| +#if SQLITE_OS_WINCE
|
| + /*
|
| + ** NOTE: Windows CE is handled differently here due its lack of the Win32
|
| + ** API LockFile.
|
| + */
|
| + return winceLockFile(phFile, offsetLow, offsetHigh,
|
| + numBytesLow, numBytesHigh);
|
| +#else
|
| + if( osIsNT() ){
|
| + OVERLAPPED ovlp;
|
| + memset(&ovlp, 0, sizeof(OVERLAPPED));
|
| + ovlp.Offset = offsetLow;
|
| + ovlp.OffsetHigh = offsetHigh;
|
| + return osLockFileEx(*phFile, flags, 0, numBytesLow, numBytesHigh, &ovlp);
|
| + }else{
|
| + return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
|
| + numBytesHigh);
|
| + }
|
| +#endif
|
| +}
|
| +
|
| +/*
|
| +** Unlock a file region.
|
| + */
|
| +static BOOL winUnlockFile(
|
| + LPHANDLE phFile,
|
| + DWORD offsetLow,
|
| + DWORD offsetHigh,
|
| + DWORD numBytesLow,
|
| + DWORD numBytesHigh
|
| +){
|
| +#if SQLITE_OS_WINCE
|
| + /*
|
| + ** NOTE: Windows CE is handled differently here due its lack of the Win32
|
| + ** API UnlockFile.
|
| + */
|
| + return winceUnlockFile(phFile, offsetLow, offsetHigh,
|
| + numBytesLow, numBytesHigh);
|
| +#else
|
| + if( osIsNT() ){
|
| + OVERLAPPED ovlp;
|
| + memset(&ovlp, 0, sizeof(OVERLAPPED));
|
| + ovlp.Offset = offsetLow;
|
| + ovlp.OffsetHigh = offsetHigh;
|
| + return osUnlockFileEx(*phFile, 0, numBytesLow, numBytesHigh, &ovlp);
|
| + }else{
|
| + return osUnlockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
|
| + numBytesHigh);
|
| + }
|
| +#endif
|
| +}
|
| +
|
| +/*****************************************************************************
|
| +** The next group of routines implement the I/O methods specified
|
| +** by the sqlite3_io_methods object.
|
| +******************************************************************************/
|
| +
|
| +/*
|
| +** Some Microsoft compilers lack this definition.
|
| +*/
|
| +#ifndef INVALID_SET_FILE_POINTER
|
| +# define INVALID_SET_FILE_POINTER ((DWORD)-1)
|
| +#endif
|
| +
|
| +/*
|
| +** Move the current position of the file handle passed as the first
|
| +** argument to offset iOffset within the file. If successful, return 0.
|
| +** Otherwise, set pFile->lastErrno and return non-zero.
|
| +*/
|
| +static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){
|
| +#if !SQLITE_OS_WINRT
|
| + LONG upperBits; /* Most sig. 32 bits of new offset */
|
| + LONG lowerBits; /* Least sig. 32 bits of new offset */
|
| + DWORD dwRet; /* Value returned by SetFilePointer() */
|
| + DWORD lastErrno; /* Value returned by GetLastError() */
|
| +
|
| + OSTRACE(("SEEK file=%p, offset=%lld\n", pFile->h, iOffset));
|
| +
|
| + upperBits = (LONG)((iOffset>>32) & 0x7fffffff);
|
| + lowerBits = (LONG)(iOffset & 0xffffffff);
|
| +
|
| + /* API oddity: If successful, SetFilePointer() returns a dword
|
| + ** containing the lower 32-bits of the new file-offset. Or, if it fails,
|
| + ** it returns INVALID_SET_FILE_POINTER. However according to MSDN,
|
| + ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine
|
| + ** whether an error has actually occurred, it is also necessary to call
|
| + ** GetLastError().
|
| + */
|
| + dwRet = osSetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
|
| +
|
| + if( (dwRet==INVALID_SET_FILE_POINTER
|
| + && ((lastErrno = osGetLastError())!=NO_ERROR)) ){
|
| + pFile->lastErrno = lastErrno;
|
| + winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno,
|
| + "winSeekFile", pFile->zPath);
|
| + OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h));
|
| + return 1;
|
| + }
|
| +
|
| + OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h));
|
| + return 0;
|
| +#else
|
| + /*
|
| + ** Same as above, except that this implementation works for WinRT.
|
| + */
|
| +
|
| + LARGE_INTEGER x; /* The new offset */
|
| + BOOL bRet; /* Value returned by SetFilePointerEx() */
|
| +
|
| + x.QuadPart = iOffset;
|
| + bRet = osSetFilePointerEx(pFile->h, x, 0, FILE_BEGIN);
|
| +
|
| + if(!bRet){
|
| + pFile->lastErrno = osGetLastError();
|
| + winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno,
|
| + "winSeekFile", pFile->zPath);
|
| + OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h));
|
| + return 1;
|
| + }
|
| +
|
| + OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h));
|
| + return 0;
|
| +#endif
|
| +}
|
| +
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| +/* Forward references to VFS helper methods used for memory mapped files */
|
| +static int winMapfile(winFile*, sqlite3_int64);
|
| +static int winUnmapfile(winFile*);
|
| +#endif
|
| +
|
| +/*
|
| +** Close a file.
|
| +**
|
| +** It is reported that an attempt to close a handle might sometimes
|
| +** fail. This is a very unreasonable result, but Windows is notorious
|
| +** for being unreasonable so I do not doubt that it might happen. If
|
| +** the close fails, we pause for 100 milliseconds and try again. As
|
| +** many as MX_CLOSE_ATTEMPT attempts to close the handle are made before
|
| +** giving up and returning an error.
|
| +*/
|
| +#define MX_CLOSE_ATTEMPT 3
|
| +static int winClose(sqlite3_file *id){
|
| + int rc, cnt = 0;
|
| + winFile *pFile = (winFile*)id;
|
| +
|
| + assert( id!=0 );
|
| +#ifndef SQLITE_OMIT_WAL
|
| + assert( pFile->pShm==0 );
|
| +#endif
|
| + assert( pFile->h!=NULL && pFile->h!=INVALID_HANDLE_VALUE );
|
| + OSTRACE(("CLOSE pid=%lu, pFile=%p, file=%p\n",
|
| + osGetCurrentProcessId(), pFile, pFile->h));
|
| +
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| + winUnmapfile(pFile);
|
| +#endif
|
| +
|
| + do{
|
| + rc = osCloseHandle(pFile->h);
|
| + /* SimulateIOError( rc=0; cnt=MX_CLOSE_ATTEMPT; ); */
|
| + }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (sqlite3_win32_sleep(100), 1) );
|
| +#if SQLITE_OS_WINCE
|
| +#define WINCE_DELETION_ATTEMPTS 3
|
| + {
|
| + winVfsAppData *pAppData = (winVfsAppData*)pFile->pVfs->pAppData;
|
| + if( pAppData==NULL || !pAppData->bNoLock ){
|
| + winceDestroyLock(pFile);
|
| + }
|
| + }
|
| + if( pFile->zDeleteOnClose ){
|
| + int cnt = 0;
|
| + while(
|
| + osDeleteFileW(pFile->zDeleteOnClose)==0
|
| + && osGetFileAttributesW(pFile->zDeleteOnClose)!=0xffffffff
|
| + && cnt++ < WINCE_DELETION_ATTEMPTS
|
| + ){
|
| + sqlite3_win32_sleep(100); /* Wait a little before trying again */
|
| + }
|
| + sqlite3_free(pFile->zDeleteOnClose);
|
| + }
|
| +#endif
|
| + if( rc ){
|
| + pFile->h = NULL;
|
| + }
|
| + OpenCounter(-1);
|
| + OSTRACE(("CLOSE pid=%lu, pFile=%p, file=%p, rc=%s\n",
|
| + osGetCurrentProcessId(), pFile, pFile->h, rc ? "ok" : "failed"));
|
| + return rc ? SQLITE_OK
|
| + : winLogError(SQLITE_IOERR_CLOSE, osGetLastError(),
|
| + "winClose", pFile->zPath);
|
| +}
|
| +
|
| +/*
|
| +** Read data from a file into a buffer. Return SQLITE_OK if all
|
| +** bytes were read successfully and SQLITE_IOERR if anything goes
|
| +** wrong.
|
| +*/
|
| +static int winRead(
|
| + sqlite3_file *id, /* File to read from */
|
| + void *pBuf, /* Write content into this buffer */
|
| + int amt, /* Number of bytes to read */
|
| + sqlite3_int64 offset /* Begin reading at this offset */
|
| +){
|
| +#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED)
|
| + OVERLAPPED overlapped; /* The offset for ReadFile. */
|
| +#endif
|
| + winFile *pFile = (winFile*)id; /* file handle */
|
| + DWORD nRead; /* Number of bytes actually read from file */
|
| + int nRetry = 0; /* Number of retrys */
|
| +
|
| + assert( id!=0 );
|
| + assert( amt>0 );
|
| + assert( offset>=0 );
|
| + SimulateIOError(return SQLITE_IOERR_READ);
|
| + OSTRACE(("READ pid=%lu, pFile=%p, file=%p, buffer=%p, amount=%d, "
|
| + "offset=%lld, lock=%d\n", osGetCurrentProcessId(), pFile,
|
| + pFile->h, pBuf, amt, offset, pFile->locktype));
|
| +
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| + /* Deal with as much of this read request as possible by transfering
|
| + ** data from the memory mapping using memcpy(). */
|
| + if( offset<pFile->mmapSize ){
|
| + if( offset+amt <= pFile->mmapSize ){
|
| + memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], amt);
|
| + OSTRACE(("READ-MMAP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n",
|
| + osGetCurrentProcessId(), pFile, pFile->h));
|
| + return SQLITE_OK;
|
| + }else{
|
| + int nCopy = (int)(pFile->mmapSize - offset);
|
| + memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], nCopy);
|
| + pBuf = &((u8 *)pBuf)[nCopy];
|
| + amt -= nCopy;
|
| + offset += nCopy;
|
| + }
|
| + }
|
| +#endif
|
| +
|
| +#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED)
|
| + if( winSeekFile(pFile, offset) ){
|
| + OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_FULL\n",
|
| + osGetCurrentProcessId(), pFile, pFile->h));
|
| + return SQLITE_FULL;
|
| + }
|
| + while( !osReadFile(pFile->h, pBuf, amt, &nRead, 0) ){
|
| +#else
|
| + memset(&overlapped, 0, sizeof(OVERLAPPED));
|
| + overlapped.Offset = (LONG)(offset & 0xffffffff);
|
| + overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff);
|
| + while( !osReadFile(pFile->h, pBuf, amt, &nRead, &overlapped) &&
|
| + osGetLastError()!=ERROR_HANDLE_EOF ){
|
| +#endif
|
| + DWORD lastErrno;
|
| + if( winRetryIoerr(&nRetry, &lastErrno) ) continue;
|
| + pFile->lastErrno = lastErrno;
|
| + OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_READ\n",
|
| + osGetCurrentProcessId(), pFile, pFile->h));
|
| + return winLogError(SQLITE_IOERR_READ, pFile->lastErrno,
|
| + "winRead", pFile->zPath);
|
| + }
|
| + winLogIoerr(nRetry, __LINE__);
|
| + if( nRead<(DWORD)amt ){
|
| + /* Unread parts of the buffer must be zero-filled */
|
| + memset(&((char*)pBuf)[nRead], 0, amt-nRead);
|
| + OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_SHORT_READ\n",
|
| + osGetCurrentProcessId(), pFile, pFile->h));
|
| + return SQLITE_IOERR_SHORT_READ;
|
| + }
|
| +
|
| + OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n",
|
| + osGetCurrentProcessId(), pFile, pFile->h));
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Write data from a buffer into a file. Return SQLITE_OK on success
|
| +** or some other error code on failure.
|
| +*/
|
| +static int winWrite(
|
| + sqlite3_file *id, /* File to write into */
|
| + const void *pBuf, /* The bytes to be written */
|
| + int amt, /* Number of bytes to write */
|
| + sqlite3_int64 offset /* Offset into the file to begin writing at */
|
| +){
|
| + int rc = 0; /* True if error has occurred, else false */
|
| + winFile *pFile = (winFile*)id; /* File handle */
|
| + int nRetry = 0; /* Number of retries */
|
| +
|
| + assert( amt>0 );
|
| + assert( pFile );
|
| + SimulateIOError(return SQLITE_IOERR_WRITE);
|
| + SimulateDiskfullError(return SQLITE_FULL);
|
| +
|
| + OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, buffer=%p, amount=%d, "
|
| + "offset=%lld, lock=%d\n", osGetCurrentProcessId(), pFile,
|
| + pFile->h, pBuf, amt, offset, pFile->locktype));
|
| +
|
| +#if defined(SQLITE_MMAP_READWRITE) && SQLITE_MAX_MMAP_SIZE>0
|
| + /* Deal with as much of this write request as possible by transfering
|
| + ** data from the memory mapping using memcpy(). */
|
| + if( offset<pFile->mmapSize ){
|
| + if( offset+amt <= pFile->mmapSize ){
|
| + memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, amt);
|
| + OSTRACE(("WRITE-MMAP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n",
|
| + osGetCurrentProcessId(), pFile, pFile->h));
|
| + return SQLITE_OK;
|
| + }else{
|
| + int nCopy = (int)(pFile->mmapSize - offset);
|
| + memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, nCopy);
|
| + pBuf = &((u8 *)pBuf)[nCopy];
|
| + amt -= nCopy;
|
| + offset += nCopy;
|
| + }
|
| + }
|
| +#endif
|
| +
|
| +#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED)
|
| + rc = winSeekFile(pFile, offset);
|
| + if( rc==0 ){
|
| +#else
|
| + {
|
| +#endif
|
| +#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED)
|
| + OVERLAPPED overlapped; /* The offset for WriteFile. */
|
| +#endif
|
| + u8 *aRem = (u8 *)pBuf; /* Data yet to be written */
|
| + int nRem = amt; /* Number of bytes yet to be written */
|
| + DWORD nWrite; /* Bytes written by each WriteFile() call */
|
| + DWORD lastErrno = NO_ERROR; /* Value returned by GetLastError() */
|
| +
|
| +#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED)
|
| + memset(&overlapped, 0, sizeof(OVERLAPPED));
|
| + overlapped.Offset = (LONG)(offset & 0xffffffff);
|
| + overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff);
|
| +#endif
|
| +
|
| + while( nRem>0 ){
|
| +#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED)
|
| + if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){
|
| +#else
|
| + if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, &overlapped) ){
|
| +#endif
|
| + if( winRetryIoerr(&nRetry, &lastErrno) ) continue;
|
| + break;
|
| + }
|
| + assert( nWrite==0 || nWrite<=(DWORD)nRem );
|
| + if( nWrite==0 || nWrite>(DWORD)nRem ){
|
| + lastErrno = osGetLastError();
|
| + break;
|
| + }
|
| +#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED)
|
| + offset += nWrite;
|
| + overlapped.Offset = (LONG)(offset & 0xffffffff);
|
| + overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff);
|
| +#endif
|
| + aRem += nWrite;
|
| + nRem -= nWrite;
|
| + }
|
| + if( nRem>0 ){
|
| + pFile->lastErrno = lastErrno;
|
| + rc = 1;
|
| + }
|
| + }
|
| +
|
| + if( rc ){
|
| + if( ( pFile->lastErrno==ERROR_HANDLE_DISK_FULL )
|
| + || ( pFile->lastErrno==ERROR_DISK_FULL )){
|
| + OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_FULL\n",
|
| + osGetCurrentProcessId(), pFile, pFile->h));
|
| + return winLogError(SQLITE_FULL, pFile->lastErrno,
|
| + "winWrite1", pFile->zPath);
|
| + }
|
| + OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_WRITE\n",
|
| + osGetCurrentProcessId(), pFile, pFile->h));
|
| + return winLogError(SQLITE_IOERR_WRITE, pFile->lastErrno,
|
| + "winWrite2", pFile->zPath);
|
| + }else{
|
| + winLogIoerr(nRetry, __LINE__);
|
| + }
|
| + OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n",
|
| + osGetCurrentProcessId(), pFile, pFile->h));
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Truncate an open file to a specified size
|
| +*/
|
| +static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
|
| + winFile *pFile = (winFile*)id; /* File handle object */
|
| + int rc = SQLITE_OK; /* Return code for this function */
|
| + DWORD lastErrno;
|
| +
|
| + assert( pFile );
|
| + SimulateIOError(return SQLITE_IOERR_TRUNCATE);
|
| + OSTRACE(("TRUNCATE pid=%lu, pFile=%p, file=%p, size=%lld, lock=%d\n",
|
| + osGetCurrentProcessId(), pFile, pFile->h, nByte, pFile->locktype));
|
| +
|
| + /* If the user has configured a chunk-size for this file, truncate the
|
| + ** file so that it consists of an integer number of chunks (i.e. the
|
| + ** actual file size after the operation may be larger than the requested
|
| + ** size).
|
| + */
|
| + if( pFile->szChunk>0 ){
|
| + nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
|
| + }
|
| +
|
| + /* SetEndOfFile() returns non-zero when successful, or zero when it fails. */
|
| + if( winSeekFile(pFile, nByte) ){
|
| + rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno,
|
| + "winTruncate1", pFile->zPath);
|
| + }else if( 0==osSetEndOfFile(pFile->h) &&
|
| + ((lastErrno = osGetLastError())!=ERROR_USER_MAPPED_FILE) ){
|
| + pFile->lastErrno = lastErrno;
|
| + rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno,
|
| + "winTruncate2", pFile->zPath);
|
| + }
|
| +
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| + /* If the file was truncated to a size smaller than the currently
|
| + ** mapped region, reduce the effective mapping size as well. SQLite will
|
| + ** use read() and write() to access data beyond this point from now on.
|
| + */
|
| + if( pFile->pMapRegion && nByte<pFile->mmapSize ){
|
| + pFile->mmapSize = nByte;
|
| + }
|
| +#endif
|
| +
|
| + OSTRACE(("TRUNCATE pid=%lu, pFile=%p, file=%p, rc=%s\n",
|
| + osGetCurrentProcessId(), pFile, pFile->h, sqlite3ErrName(rc)));
|
| + return rc;
|
| +}
|
| +
|
| +#ifdef SQLITE_TEST
|
| +/*
|
| +** Count the number of fullsyncs and normal syncs. This is used to test
|
| +** that syncs and fullsyncs are occuring at the right times.
|
| +*/
|
| +SQLITE_API int sqlite3_sync_count = 0;
|
| +SQLITE_API int sqlite3_fullsync_count = 0;
|
| +#endif
|
| +
|
| +/*
|
| +** Make sure all writes to a particular file are committed to disk.
|
| +*/
|
| +static int winSync(sqlite3_file *id, int flags){
|
| +#ifndef SQLITE_NO_SYNC
|
| + /*
|
| + ** Used only when SQLITE_NO_SYNC is not defined.
|
| + */
|
| + BOOL rc;
|
| +#endif
|
| +#if !defined(NDEBUG) || !defined(SQLITE_NO_SYNC) || \
|
| + defined(SQLITE_HAVE_OS_TRACE)
|
| + /*
|
| + ** Used when SQLITE_NO_SYNC is not defined and by the assert() and/or
|
| + ** OSTRACE() macros.
|
| + */
|
| + winFile *pFile = (winFile*)id;
|
| +#else
|
| + UNUSED_PARAMETER(id);
|
| +#endif
|
| +
|
| + assert( pFile );
|
| + /* Check that one of SQLITE_SYNC_NORMAL or FULL was passed */
|
| + assert((flags&0x0F)==SQLITE_SYNC_NORMAL
|
| + || (flags&0x0F)==SQLITE_SYNC_FULL
|
| + );
|
| +
|
| + /* Unix cannot, but some systems may return SQLITE_FULL from here. This
|
| + ** line is to test that doing so does not cause any problems.
|
| + */
|
| + SimulateDiskfullError( return SQLITE_FULL );
|
| +
|
| + OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, flags=%x, lock=%d\n",
|
| + osGetCurrentProcessId(), pFile, pFile->h, flags,
|
| + pFile->locktype));
|
| +
|
| +#ifndef SQLITE_TEST
|
| + UNUSED_PARAMETER(flags);
|
| +#else
|
| + if( (flags&0x0F)==SQLITE_SYNC_FULL ){
|
| + sqlite3_fullsync_count++;
|
| + }
|
| + sqlite3_sync_count++;
|
| +#endif
|
| +
|
| + /* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a
|
| + ** no-op
|
| + */
|
| +#ifdef SQLITE_NO_SYNC
|
| + OSTRACE(("SYNC-NOP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n",
|
| + osGetCurrentProcessId(), pFile, pFile->h));
|
| + return SQLITE_OK;
|
| +#else
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| + if( pFile->pMapRegion ){
|
| + if( osFlushViewOfFile(pFile->pMapRegion, 0) ){
|
| + OSTRACE(("SYNC-MMAP pid=%lu, pFile=%p, pMapRegion=%p, "
|
| + "rc=SQLITE_OK\n", osGetCurrentProcessId(),
|
| + pFile, pFile->pMapRegion));
|
| + }else{
|
| + pFile->lastErrno = osGetLastError();
|
| + OSTRACE(("SYNC-MMAP pid=%lu, pFile=%p, pMapRegion=%p, "
|
| + "rc=SQLITE_IOERR_MMAP\n", osGetCurrentProcessId(),
|
| + pFile, pFile->pMapRegion));
|
| + return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno,
|
| + "winSync1", pFile->zPath);
|
| + }
|
| + }
|
| +#endif
|
| + rc = osFlushFileBuffers(pFile->h);
|
| + SimulateIOError( rc=FALSE );
|
| + if( rc ){
|
| + OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n",
|
| + osGetCurrentProcessId(), pFile, pFile->h));
|
| + return SQLITE_OK;
|
| + }else{
|
| + pFile->lastErrno = osGetLastError();
|
| + OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_FSYNC\n",
|
| + osGetCurrentProcessId(), pFile, pFile->h));
|
| + return winLogError(SQLITE_IOERR_FSYNC, pFile->lastErrno,
|
| + "winSync2", pFile->zPath);
|
| + }
|
| +#endif
|
| +}
|
| +
|
| +/*
|
| +** Determine the current size of a file in bytes
|
| +*/
|
| +static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){
|
| + winFile *pFile = (winFile*)id;
|
| + int rc = SQLITE_OK;
|
| +
|
| + assert( id!=0 );
|
| + assert( pSize!=0 );
|
| + SimulateIOError(return SQLITE_IOERR_FSTAT);
|
| + OSTRACE(("SIZE file=%p, pSize=%p\n", pFile->h, pSize));
|
| +
|
| +#if SQLITE_OS_WINRT
|
| + {
|
| + FILE_STANDARD_INFO info;
|
| + if( osGetFileInformationByHandleEx(pFile->h, FileStandardInfo,
|
| + &info, sizeof(info)) ){
|
| + *pSize = info.EndOfFile.QuadPart;
|
| + }else{
|
| + pFile->lastErrno = osGetLastError();
|
| + rc = winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno,
|
| + "winFileSize", pFile->zPath);
|
| + }
|
| + }
|
| +#else
|
| + {
|
| + DWORD upperBits;
|
| + DWORD lowerBits;
|
| + DWORD lastErrno;
|
| +
|
| + lowerBits = osGetFileSize(pFile->h, &upperBits);
|
| + *pSize = (((sqlite3_int64)upperBits)<<32) + lowerBits;
|
| + if( (lowerBits == INVALID_FILE_SIZE)
|
| + && ((lastErrno = osGetLastError())!=NO_ERROR) ){
|
| + pFile->lastErrno = lastErrno;
|
| + rc = winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno,
|
| + "winFileSize", pFile->zPath);
|
| + }
|
| + }
|
| +#endif
|
| + OSTRACE(("SIZE file=%p, pSize=%p, *pSize=%lld, rc=%s\n",
|
| + pFile->h, pSize, *pSize, sqlite3ErrName(rc)));
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems.
|
| +*/
|
| +#ifndef LOCKFILE_FAIL_IMMEDIATELY
|
| +# define LOCKFILE_FAIL_IMMEDIATELY 1
|
| +#endif
|
| +
|
| +#ifndef LOCKFILE_EXCLUSIVE_LOCK
|
| +# define LOCKFILE_EXCLUSIVE_LOCK 2
|
| +#endif
|
| +
|
| +/*
|
| +** Historically, SQLite has used both the LockFile and LockFileEx functions.
|
| +** When the LockFile function was used, it was always expected to fail
|
| +** immediately if the lock could not be obtained. Also, it always expected to
|
| +** obtain an exclusive lock. These flags are used with the LockFileEx function
|
| +** and reflect those expectations; therefore, they should not be changed.
|
| +*/
|
| +#ifndef SQLITE_LOCKFILE_FLAGS
|
| +# define SQLITE_LOCKFILE_FLAGS (LOCKFILE_FAIL_IMMEDIATELY | \
|
| + LOCKFILE_EXCLUSIVE_LOCK)
|
| +#endif
|
| +
|
| +/*
|
| +** Currently, SQLite never calls the LockFileEx function without wanting the
|
| +** call to fail immediately if the lock cannot be obtained.
|
| +*/
|
| +#ifndef SQLITE_LOCKFILEEX_FLAGS
|
| +# define SQLITE_LOCKFILEEX_FLAGS (LOCKFILE_FAIL_IMMEDIATELY)
|
| +#endif
|
| +
|
| +/*
|
| +** Acquire a reader lock.
|
| +** Different API routines are called depending on whether or not this
|
| +** is Win9x or WinNT.
|
| +*/
|
| +static int winGetReadLock(winFile *pFile){
|
| + int res;
|
| + OSTRACE(("READ-LOCK file=%p, lock=%d\n", pFile->h, pFile->locktype));
|
| + if( osIsNT() ){
|
| +#if SQLITE_OS_WINCE
|
| + /*
|
| + ** NOTE: Windows CE is handled differently here due its lack of the Win32
|
| + ** API LockFileEx.
|
| + */
|
| + res = winceLockFile(&pFile->h, SHARED_FIRST, 0, 1, 0);
|
| +#else
|
| + res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS, SHARED_FIRST, 0,
|
| + SHARED_SIZE, 0);
|
| +#endif
|
| + }
|
| +#ifdef SQLITE_WIN32_HAS_ANSI
|
| + else{
|
| + int lk;
|
| + sqlite3_randomness(sizeof(lk), &lk);
|
| + pFile->sharedLockByte = (short)((lk & 0x7fffffff)%(SHARED_SIZE - 1));
|
| + res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS,
|
| + SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0);
|
| + }
|
| +#endif
|
| + if( res == 0 ){
|
| + pFile->lastErrno = osGetLastError();
|
| + /* No need to log a failure to lock */
|
| + }
|
| + OSTRACE(("READ-LOCK file=%p, result=%d\n", pFile->h, res));
|
| + return res;
|
| +}
|
| +
|
| +/*
|
| +** Undo a readlock
|
| +*/
|
| +static int winUnlockReadLock(winFile *pFile){
|
| + int res;
|
| + DWORD lastErrno;
|
| + OSTRACE(("READ-UNLOCK file=%p, lock=%d\n", pFile->h, pFile->locktype));
|
| + if( osIsNT() ){
|
| + res = winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
|
| + }
|
| +#ifdef SQLITE_WIN32_HAS_ANSI
|
| + else{
|
| + res = winUnlockFile(&pFile->h, SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0);
|
| + }
|
| +#endif
|
| + if( res==0 && ((lastErrno = osGetLastError())!=ERROR_NOT_LOCKED) ){
|
| + pFile->lastErrno = lastErrno;
|
| + winLogError(SQLITE_IOERR_UNLOCK, pFile->lastErrno,
|
| + "winUnlockReadLock", pFile->zPath);
|
| + }
|
| + OSTRACE(("READ-UNLOCK file=%p, result=%d\n", pFile->h, res));
|
| + return res;
|
| +}
|
| +
|
| +/*
|
| +** Lock the file with the lock specified by parameter locktype - 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
|
| +**
|
| +** This routine will only increase a lock. The winUnlock() routine
|
| +** erases all locks at once and returns us immediately to locking level 0.
|
| +** It is not possible to lower the locking level one step at a time. You
|
| +** must go straight to locking level 0.
|
| +*/
|
| +static int winLock(sqlite3_file *id, int locktype){
|
| + int rc = SQLITE_OK; /* Return code from subroutines */
|
| + int res = 1; /* Result of a Windows lock call */
|
| + int newLocktype; /* Set pFile->locktype to this value before exiting */
|
| + int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */
|
| + winFile *pFile = (winFile*)id;
|
| + DWORD lastErrno = NO_ERROR;
|
| +
|
| + assert( id!=0 );
|
| + OSTRACE(("LOCK file=%p, oldLock=%d(%d), newLock=%d\n",
|
| + pFile->h, pFile->locktype, pFile->sharedLockByte, locktype));
|
| +
|
| + /* If there is already a lock of this type or more restrictive on the
|
| + ** OsFile, do nothing. Don't use the end_lock: exit path, as
|
| + ** sqlite3OsEnterMutex() hasn't been called yet.
|
| + */
|
| + if( pFile->locktype>=locktype ){
|
| + OSTRACE(("LOCK-HELD file=%p, rc=SQLITE_OK\n", pFile->h));
|
| + return SQLITE_OK;
|
| + }
|
| +
|
| + /* Do not allow any kind of write-lock on a read-only database
|
| + */
|
| + if( (pFile->ctrlFlags & WINFILE_RDONLY)!=0 && locktype>=RESERVED_LOCK ){
|
| + return SQLITE_IOERR_LOCK;
|
| + }
|
| +
|
| + /* Make sure the locking sequence is correct
|
| + */
|
| + assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
|
| + assert( locktype!=PENDING_LOCK );
|
| + assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK );
|
| +
|
| + /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or
|
| + ** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of
|
| + ** the PENDING_LOCK byte is temporary.
|
| + */
|
| + newLocktype = pFile->locktype;
|
| + if( pFile->locktype==NO_LOCK
|
| + || (locktype==EXCLUSIVE_LOCK && pFile->locktype<=RESERVED_LOCK)
|
| + ){
|
| + int cnt = 3;
|
| + while( cnt-->0 && (res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS,
|
| + PENDING_BYTE, 0, 1, 0))==0 ){
|
| + /* Try 3 times to get the pending lock. This is needed to work
|
| + ** around problems caused by indexing and/or anti-virus software on
|
| + ** Windows systems.
|
| + ** If you are using this code as a model for alternative VFSes, do not
|
| + ** copy this retry logic. It is a hack intended for Windows only.
|
| + */
|
| + lastErrno = osGetLastError();
|
| + OSTRACE(("LOCK-PENDING-FAIL file=%p, count=%d, result=%d\n",
|
| + pFile->h, cnt, res));
|
| + if( lastErrno==ERROR_INVALID_HANDLE ){
|
| + pFile->lastErrno = lastErrno;
|
| + rc = SQLITE_IOERR_LOCK;
|
| + OSTRACE(("LOCK-FAIL file=%p, count=%d, rc=%s\n",
|
| + pFile->h, cnt, sqlite3ErrName(rc)));
|
| + return rc;
|
| + }
|
| + if( cnt ) sqlite3_win32_sleep(1);
|
| + }
|
| + gotPendingLock = res;
|
| + if( !res ){
|
| + lastErrno = osGetLastError();
|
| + }
|
| + }
|
| +
|
| + /* Acquire a shared lock
|
| + */
|
| + if( locktype==SHARED_LOCK && res ){
|
| + assert( pFile->locktype==NO_LOCK );
|
| + res = winGetReadLock(pFile);
|
| + if( res ){
|
| + newLocktype = SHARED_LOCK;
|
| + }else{
|
| + lastErrno = osGetLastError();
|
| + }
|
| + }
|
| +
|
| + /* Acquire a RESERVED lock
|
| + */
|
| + if( locktype==RESERVED_LOCK && res ){
|
| + assert( pFile->locktype==SHARED_LOCK );
|
| + res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, RESERVED_BYTE, 0, 1, 0);
|
| + if( res ){
|
| + newLocktype = RESERVED_LOCK;
|
| + }else{
|
| + lastErrno = osGetLastError();
|
| + }
|
| + }
|
| +
|
| + /* Acquire a PENDING lock
|
| + */
|
| + if( locktype==EXCLUSIVE_LOCK && res ){
|
| + newLocktype = PENDING_LOCK;
|
| + gotPendingLock = 0;
|
| + }
|
| +
|
| + /* Acquire an EXCLUSIVE lock
|
| + */
|
| + if( locktype==EXCLUSIVE_LOCK && res ){
|
| + assert( pFile->locktype>=SHARED_LOCK );
|
| + res = winUnlockReadLock(pFile);
|
| + res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, SHARED_FIRST, 0,
|
| + SHARED_SIZE, 0);
|
| + if( res ){
|
| + newLocktype = EXCLUSIVE_LOCK;
|
| + }else{
|
| + lastErrno = osGetLastError();
|
| + winGetReadLock(pFile);
|
| + }
|
| + }
|
| +
|
| + /* If we are holding a PENDING lock that ought to be released, then
|
| + ** release it now.
|
| + */
|
| + if( gotPendingLock && locktype==SHARED_LOCK ){
|
| + winUnlockFile(&pFile->h, PENDING_BYTE, 0, 1, 0);
|
| + }
|
| +
|
| + /* Update the state of the lock has held in the file descriptor then
|
| + ** return the appropriate result code.
|
| + */
|
| + if( res ){
|
| + rc = SQLITE_OK;
|
| + }else{
|
| + pFile->lastErrno = lastErrno;
|
| + rc = SQLITE_BUSY;
|
| + OSTRACE(("LOCK-FAIL file=%p, wanted=%d, got=%d\n",
|
| + pFile->h, locktype, newLocktype));
|
| + }
|
| + pFile->locktype = (u8)newLocktype;
|
| + OSTRACE(("LOCK file=%p, lock=%d, rc=%s\n",
|
| + pFile->h, pFile->locktype, sqlite3ErrName(rc)));
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** 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, return
|
| +** non-zero, otherwise zero.
|
| +*/
|
| +static int winCheckReservedLock(sqlite3_file *id, int *pResOut){
|
| + int res;
|
| + winFile *pFile = (winFile*)id;
|
| +
|
| + SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
|
| + OSTRACE(("TEST-WR-LOCK file=%p, pResOut=%p\n", pFile->h, pResOut));
|
| +
|
| + assert( id!=0 );
|
| + if( pFile->locktype>=RESERVED_LOCK ){
|
| + res = 1;
|
| + OSTRACE(("TEST-WR-LOCK file=%p, result=%d (local)\n", pFile->h, res));
|
| + }else{
|
| + res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS,RESERVED_BYTE,0,1,0);
|
| + if( res ){
|
| + winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0);
|
| + }
|
| + res = !res;
|
| + OSTRACE(("TEST-WR-LOCK file=%p, result=%d (remote)\n", pFile->h, res));
|
| + }
|
| + *pResOut = res;
|
| + OSTRACE(("TEST-WR-LOCK file=%p, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n",
|
| + pFile->h, pResOut, *pResOut));
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Lower the locking level on file descriptor id to locktype. locktype
|
| +** 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.
|
| +**
|
| +** It is not possible for this routine to fail if the second argument
|
| +** is NO_LOCK. If the second argument is SHARED_LOCK then this routine
|
| +** might return SQLITE_IOERR;
|
| +*/
|
| +static int winUnlock(sqlite3_file *id, int locktype){
|
| + int type;
|
| + winFile *pFile = (winFile*)id;
|
| + int rc = SQLITE_OK;
|
| + assert( pFile!=0 );
|
| + assert( locktype<=SHARED_LOCK );
|
| + OSTRACE(("UNLOCK file=%p, oldLock=%d(%d), newLock=%d\n",
|
| + pFile->h, pFile->locktype, pFile->sharedLockByte, locktype));
|
| + type = pFile->locktype;
|
| + if( type>=EXCLUSIVE_LOCK ){
|
| + winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
|
| + if( locktype==SHARED_LOCK && !winGetReadLock(pFile) ){
|
| + /* This should never happen. We should always be able to
|
| + ** reacquire the read lock */
|
| + rc = winLogError(SQLITE_IOERR_UNLOCK, osGetLastError(),
|
| + "winUnlock", pFile->zPath);
|
| + }
|
| + }
|
| + if( type>=RESERVED_LOCK ){
|
| + winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0);
|
| + }
|
| + if( locktype==NO_LOCK && type>=SHARED_LOCK ){
|
| + winUnlockReadLock(pFile);
|
| + }
|
| + if( type>=PENDING_LOCK ){
|
| + winUnlockFile(&pFile->h, PENDING_BYTE, 0, 1, 0);
|
| + }
|
| + pFile->locktype = (u8)locktype;
|
| + OSTRACE(("UNLOCK file=%p, lock=%d, rc=%s\n",
|
| + pFile->h, pFile->locktype, sqlite3ErrName(rc)));
|
| + return rc;
|
| +}
|
| +
|
| +/******************************************************************************
|
| +****************************** No-op Locking **********************************
|
| +**
|
| +** Of the various locking implementations available, this is by far the
|
| +** simplest: locking is ignored. No attempt is made to lock the database
|
| +** file for reading or writing.
|
| +**
|
| +** This locking mode is appropriate for use on read-only databases
|
| +** (ex: databases that are burned into CD-ROM, for example.) It can
|
| +** also be used if the application employs some external mechanism to
|
| +** prevent simultaneous access of the same database by two or more
|
| +** database connections. But there is a serious risk of database
|
| +** corruption if this locking mode is used in situations where multiple
|
| +** database connections are accessing the same database file at the same
|
| +** time and one or more of those connections are writing.
|
| +*/
|
| +
|
| +static int winNolockLock(sqlite3_file *id, int locktype){
|
| + UNUSED_PARAMETER(id);
|
| + UNUSED_PARAMETER(locktype);
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +static int winNolockCheckReservedLock(sqlite3_file *id, int *pResOut){
|
| + UNUSED_PARAMETER(id);
|
| + UNUSED_PARAMETER(pResOut);
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +static int winNolockUnlock(sqlite3_file *id, int locktype){
|
| + UNUSED_PARAMETER(id);
|
| + UNUSED_PARAMETER(locktype);
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/******************* End of the no-op lock implementation *********************
|
| +******************************************************************************/
|
| +
|
| +/*
|
| +** If *pArg is initially negative then this is a query. Set *pArg to
|
| +** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set.
|
| +**
|
| +** If *pArg is 0 or 1, then clear or set the mask bit of pFile->ctrlFlags.
|
| +*/
|
| +static void winModeBit(winFile *pFile, unsigned char mask, int *pArg){
|
| + if( *pArg<0 ){
|
| + *pArg = (pFile->ctrlFlags & mask)!=0;
|
| + }else if( (*pArg)==0 ){
|
| + pFile->ctrlFlags &= ~mask;
|
| + }else{
|
| + pFile->ctrlFlags |= mask;
|
| + }
|
| +}
|
| +
|
| +/* Forward references to VFS helper methods used for temporary files */
|
| +static int winGetTempname(sqlite3_vfs *, char **);
|
| +static int winIsDir(const void *);
|
| +static BOOL winIsDriveLetterAndColon(const char *);
|
| +
|
| +/*
|
| +** Control and query of the open file handle.
|
| +*/
|
| +static int winFileControl(sqlite3_file *id, int op, void *pArg){
|
| + winFile *pFile = (winFile*)id;
|
| + OSTRACE(("FCNTL file=%p, op=%d, pArg=%p\n", pFile->h, op, pArg));
|
| + switch( op ){
|
| + case SQLITE_FCNTL_LOCKSTATE: {
|
| + *(int*)pArg = pFile->locktype;
|
| + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
|
| + return SQLITE_OK;
|
| + }
|
| + case SQLITE_FCNTL_LAST_ERRNO: {
|
| + *(int*)pArg = (int)pFile->lastErrno;
|
| + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
|
| + return SQLITE_OK;
|
| + }
|
| + case SQLITE_FCNTL_CHUNK_SIZE: {
|
| + pFile->szChunk = *(int *)pArg;
|
| + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
|
| + return SQLITE_OK;
|
| + }
|
| + case SQLITE_FCNTL_SIZE_HINT: {
|
| + if( pFile->szChunk>0 ){
|
| + sqlite3_int64 oldSz;
|
| + int rc = winFileSize(id, &oldSz);
|
| + if( rc==SQLITE_OK ){
|
| + sqlite3_int64 newSz = *(sqlite3_int64*)pArg;
|
| + if( newSz>oldSz ){
|
| + SimulateIOErrorBenign(1);
|
| + rc = winTruncate(id, newSz);
|
| + SimulateIOErrorBenign(0);
|
| + }
|
| + }
|
| + OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc)));
|
| + return rc;
|
| + }
|
| + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
|
| + return SQLITE_OK;
|
| + }
|
| + case SQLITE_FCNTL_PERSIST_WAL: {
|
| + winModeBit(pFile, WINFILE_PERSIST_WAL, (int*)pArg);
|
| + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
|
| + return SQLITE_OK;
|
| + }
|
| + case SQLITE_FCNTL_POWERSAFE_OVERWRITE: {
|
| + winModeBit(pFile, WINFILE_PSOW, (int*)pArg);
|
| + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
|
| + return SQLITE_OK;
|
| + }
|
| + case SQLITE_FCNTL_VFSNAME: {
|
| + *(char**)pArg = sqlite3_mprintf("%s", pFile->pVfs->zName);
|
| + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
|
| + return SQLITE_OK;
|
| + }
|
| + case SQLITE_FCNTL_WIN32_AV_RETRY: {
|
| + int *a = (int*)pArg;
|
| + if( a[0]>0 ){
|
| + winIoerrRetry = a[0];
|
| + }else{
|
| + a[0] = winIoerrRetry;
|
| + }
|
| + if( a[1]>0 ){
|
| + winIoerrRetryDelay = a[1];
|
| + }else{
|
| + a[1] = winIoerrRetryDelay;
|
| + }
|
| + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
|
| + return SQLITE_OK;
|
| + }
|
| + case SQLITE_FCNTL_WIN32_GET_HANDLE: {
|
| + LPHANDLE phFile = (LPHANDLE)pArg;
|
| + *phFile = pFile->h;
|
| + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
|
| + return SQLITE_OK;
|
| + }
|
| +#ifdef SQLITE_TEST
|
| + case SQLITE_FCNTL_WIN32_SET_HANDLE: {
|
| + LPHANDLE phFile = (LPHANDLE)pArg;
|
| + HANDLE hOldFile = pFile->h;
|
| + pFile->h = *phFile;
|
| + *phFile = hOldFile;
|
| + OSTRACE(("FCNTL oldFile=%p, newFile=%p, rc=SQLITE_OK\n",
|
| + hOldFile, pFile->h));
|
| + return SQLITE_OK;
|
| + }
|
| +#endif
|
| + case SQLITE_FCNTL_TEMPFILENAME: {
|
| + char *zTFile = 0;
|
| + int rc = winGetTempname(pFile->pVfs, &zTFile);
|
| + if( rc==SQLITE_OK ){
|
| + *(char**)pArg = zTFile;
|
| + }
|
| + OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc)));
|
| + return rc;
|
| + }
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| + case SQLITE_FCNTL_MMAP_SIZE: {
|
| + i64 newLimit = *(i64*)pArg;
|
| + int rc = SQLITE_OK;
|
| + if( newLimit>sqlite3GlobalConfig.mxMmap ){
|
| + newLimit = sqlite3GlobalConfig.mxMmap;
|
| + }
|
| + *(i64*)pArg = pFile->mmapSizeMax;
|
| + if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){
|
| + pFile->mmapSizeMax = newLimit;
|
| + if( pFile->mmapSize>0 ){
|
| + winUnmapfile(pFile);
|
| + rc = winMapfile(pFile, -1);
|
| + }
|
| + }
|
| + OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc)));
|
| + return rc;
|
| + }
|
| +#endif
|
| + }
|
| + OSTRACE(("FCNTL file=%p, rc=SQLITE_NOTFOUND\n", pFile->h));
|
| + return SQLITE_NOTFOUND;
|
| +}
|
| +
|
| +/*
|
| +** Return the sector size in bytes of the underlying block device for
|
| +** the specified file. This is almost always 512 bytes, but may be
|
| +** larger for some devices.
|
| +**
|
| +** SQLite code assumes this function cannot fail. It also assumes that
|
| +** if two files are created in the same file-system directory (i.e.
|
| +** a database and its journal file) that the sector size will be the
|
| +** same for both.
|
| +*/
|
| +static int winSectorSize(sqlite3_file *id){
|
| + (void)id;
|
| + return SQLITE_DEFAULT_SECTOR_SIZE;
|
| +}
|
| +
|
| +/*
|
| +** Return a vector of device characteristics.
|
| +*/
|
| +static int winDeviceCharacteristics(sqlite3_file *id){
|
| + winFile *p = (winFile*)id;
|
| + return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN |
|
| + ((p->ctrlFlags & WINFILE_PSOW)?SQLITE_IOCAP_POWERSAFE_OVERWRITE:0);
|
| +}
|
| +
|
| +/*
|
| +** Windows will only let you create file view mappings
|
| +** on allocation size granularity boundaries.
|
| +** During sqlite3_os_init() we do a GetSystemInfo()
|
| +** to get the granularity size.
|
| +*/
|
| +static SYSTEM_INFO winSysInfo;
|
| +
|
| +#ifndef SQLITE_OMIT_WAL
|
| +
|
| +/*
|
| +** Helper functions to obtain and relinquish the global mutex. The
|
| +** global mutex is used to protect the winLockInfo objects used by
|
| +** this file, all of which may be shared by multiple threads.
|
| +**
|
| +** Function winShmMutexHeld() is used to assert() that the global mutex
|
| +** is held when required. This function is only used as part of assert()
|
| +** statements. e.g.
|
| +**
|
| +** winShmEnterMutex()
|
| +** assert( winShmMutexHeld() );
|
| +** winShmLeaveMutex()
|
| +*/
|
| +static void winShmEnterMutex(void){
|
| + sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
|
| +}
|
| +static void winShmLeaveMutex(void){
|
| + sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
|
| +}
|
| +#ifndef NDEBUG
|
| +static int winShmMutexHeld(void) {
|
| + return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
|
| +}
|
| +#endif
|
| +
|
| +/*
|
| +** Object used to represent a single file opened and mmapped to provide
|
| +** shared memory. When multiple threads all reference the same
|
| +** log-summary, each thread has its own winFile object, but they all
|
| +** point to a single instance of this object. In other words, each
|
| +** log-summary is opened only once per process.
|
| +**
|
| +** winShmMutexHeld() must be true when creating or destroying
|
| +** this object or while reading or writing the following fields:
|
| +**
|
| +** nRef
|
| +** pNext
|
| +**
|
| +** The following fields are read-only after the object is created:
|
| +**
|
| +** fid
|
| +** zFilename
|
| +**
|
| +** Either winShmNode.mutex must be held or winShmNode.nRef==0 and
|
| +** winShmMutexHeld() is true when reading or writing any other field
|
| +** in this structure.
|
| +**
|
| +*/
|
| +struct winShmNode {
|
| + sqlite3_mutex *mutex; /* Mutex to access this object */
|
| + char *zFilename; /* Name of the file */
|
| + winFile hFile; /* File handle from winOpen */
|
| +
|
| + int szRegion; /* Size of shared-memory regions */
|
| + int nRegion; /* Size of array apRegion */
|
| + struct ShmRegion {
|
| + HANDLE hMap; /* File handle from CreateFileMapping */
|
| + void *pMap;
|
| + } *aRegion;
|
| + DWORD lastErrno; /* The Windows errno from the last I/O error */
|
| +
|
| + int nRef; /* Number of winShm objects pointing to this */
|
| + winShm *pFirst; /* All winShm objects pointing to this */
|
| + winShmNode *pNext; /* Next in list of all winShmNode objects */
|
| +#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
|
| + u8 nextShmId; /* Next available winShm.id value */
|
| +#endif
|
| +};
|
| +
|
| +/*
|
| +** A global array of all winShmNode objects.
|
| +**
|
| +** The winShmMutexHeld() must be true while reading or writing this list.
|
| +*/
|
| +static winShmNode *winShmNodeList = 0;
|
| +
|
| +/*
|
| +** Structure used internally by this VFS to record the state of an
|
| +** open shared memory connection.
|
| +**
|
| +** The following fields are initialized when this object is created and
|
| +** are read-only thereafter:
|
| +**
|
| +** winShm.pShmNode
|
| +** winShm.id
|
| +**
|
| +** All other fields are read/write. The winShm.pShmNode->mutex must be held
|
| +** while accessing any read/write fields.
|
| +*/
|
| +struct winShm {
|
| + winShmNode *pShmNode; /* The underlying winShmNode object */
|
| + winShm *pNext; /* Next winShm with the same winShmNode */
|
| + u8 hasMutex; /* True if holding the winShmNode mutex */
|
| + u16 sharedMask; /* Mask of shared locks held */
|
| + u16 exclMask; /* Mask of exclusive locks held */
|
| +#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
|
| + u8 id; /* Id of this connection with its winShmNode */
|
| +#endif
|
| +};
|
| +
|
| +/*
|
| +** Constants used for locking
|
| +*/
|
| +#define WIN_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */
|
| +#define WIN_SHM_DMS (WIN_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */
|
| +
|
| +/*
|
| +** Apply advisory locks for all n bytes beginning at ofst.
|
| +*/
|
| +#define WINSHM_UNLCK 1
|
| +#define WINSHM_RDLCK 2
|
| +#define WINSHM_WRLCK 3
|
| +static int winShmSystemLock(
|
| + winShmNode *pFile, /* Apply locks to this open shared-memory segment */
|
| + int lockType, /* WINSHM_UNLCK, WINSHM_RDLCK, or WINSHM_WRLCK */
|
| + int ofst, /* Offset to first byte to be locked/unlocked */
|
| + int nByte /* Number of bytes to lock or unlock */
|
| +){
|
| + int rc = 0; /* Result code form Lock/UnlockFileEx() */
|
| +
|
| + /* Access to the winShmNode object is serialized by the caller */
|
| + assert( sqlite3_mutex_held(pFile->mutex) || pFile->nRef==0 );
|
| +
|
| + OSTRACE(("SHM-LOCK file=%p, lock=%d, offset=%d, size=%d\n",
|
| + pFile->hFile.h, lockType, ofst, nByte));
|
| +
|
| + /* Release/Acquire the system-level lock */
|
| + if( lockType==WINSHM_UNLCK ){
|
| + rc = winUnlockFile(&pFile->hFile.h, ofst, 0, nByte, 0);
|
| + }else{
|
| + /* Initialize the locking parameters */
|
| + DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY;
|
| + if( lockType == WINSHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
|
| + rc = winLockFile(&pFile->hFile.h, dwFlags, ofst, 0, nByte, 0);
|
| + }
|
| +
|
| + if( rc!= 0 ){
|
| + rc = SQLITE_OK;
|
| + }else{
|
| + pFile->lastErrno = osGetLastError();
|
| + rc = SQLITE_BUSY;
|
| + }
|
| +
|
| + OSTRACE(("SHM-LOCK file=%p, func=%s, errno=%lu, rc=%s\n",
|
| + pFile->hFile.h, (lockType == WINSHM_UNLCK) ? "winUnlockFile" :
|
| + "winLockFile", pFile->lastErrno, sqlite3ErrName(rc)));
|
| +
|
| + return rc;
|
| +}
|
| +
|
| +/* Forward references to VFS methods */
|
| +static int winOpen(sqlite3_vfs*,const char*,sqlite3_file*,int,int*);
|
| +static int winDelete(sqlite3_vfs *,const char*,int);
|
| +
|
| +/*
|
| +** Purge the winShmNodeList list of all entries with winShmNode.nRef==0.
|
| +**
|
| +** This is not a VFS shared-memory method; it is a utility function called
|
| +** by VFS shared-memory methods.
|
| +*/
|
| +static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
|
| + winShmNode **pp;
|
| + winShmNode *p;
|
| + assert( winShmMutexHeld() );
|
| + OSTRACE(("SHM-PURGE pid=%lu, deleteFlag=%d\n",
|
| + osGetCurrentProcessId(), deleteFlag));
|
| + pp = &winShmNodeList;
|
| + while( (p = *pp)!=0 ){
|
| + if( p->nRef==0 ){
|
| + int i;
|
| + if( p->mutex ){ sqlite3_mutex_free(p->mutex); }
|
| + for(i=0; i<p->nRegion; i++){
|
| + BOOL bRc = osUnmapViewOfFile(p->aRegion[i].pMap);
|
| + OSTRACE(("SHM-PURGE-UNMAP pid=%lu, region=%d, rc=%s\n",
|
| + osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
|
| + UNUSED_VARIABLE_VALUE(bRc);
|
| + bRc = osCloseHandle(p->aRegion[i].hMap);
|
| + OSTRACE(("SHM-PURGE-CLOSE pid=%lu, region=%d, rc=%s\n",
|
| + osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
|
| + UNUSED_VARIABLE_VALUE(bRc);
|
| + }
|
| + if( p->hFile.h!=NULL && p->hFile.h!=INVALID_HANDLE_VALUE ){
|
| + SimulateIOErrorBenign(1);
|
| + winClose((sqlite3_file *)&p->hFile);
|
| + SimulateIOErrorBenign(0);
|
| + }
|
| + if( deleteFlag ){
|
| + SimulateIOErrorBenign(1);
|
| + sqlite3BeginBenignMalloc();
|
| + winDelete(pVfs, p->zFilename, 0);
|
| + sqlite3EndBenignMalloc();
|
| + SimulateIOErrorBenign(0);
|
| + }
|
| + *pp = p->pNext;
|
| + sqlite3_free(p->aRegion);
|
| + sqlite3_free(p);
|
| + }else{
|
| + pp = &p->pNext;
|
| + }
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Open the shared-memory area associated with database file pDbFd.
|
| +**
|
| +** When opening a new shared-memory file, if no other instances of that
|
| +** file are currently open, in this process or in other processes, then
|
| +** the file must be truncated to zero length or have its header cleared.
|
| +*/
|
| +static int winOpenSharedMemory(winFile *pDbFd){
|
| + struct winShm *p; /* The connection to be opened */
|
| + struct winShmNode *pShmNode = 0; /* The underlying mmapped file */
|
| + int rc; /* Result code */
|
| + struct winShmNode *pNew; /* Newly allocated winShmNode */
|
| + int nName; /* Size of zName in bytes */
|
| +
|
| + assert( pDbFd->pShm==0 ); /* Not previously opened */
|
| +
|
| + /* Allocate space for the new sqlite3_shm object. Also speculatively
|
| + ** allocate space for a new winShmNode and filename.
|
| + */
|
| + p = sqlite3MallocZero( sizeof(*p) );
|
| + if( p==0 ) return SQLITE_IOERR_NOMEM_BKPT;
|
| + nName = sqlite3Strlen30(pDbFd->zPath);
|
| + pNew = sqlite3MallocZero( sizeof(*pShmNode) + nName + 17 );
|
| + if( pNew==0 ){
|
| + sqlite3_free(p);
|
| + return SQLITE_IOERR_NOMEM_BKPT;
|
| + }
|
| + pNew->zFilename = (char*)&pNew[1];
|
| + sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath);
|
| + sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename);
|
| +
|
| + /* Look to see if there is an existing winShmNode that can be used.
|
| + ** If no matching winShmNode currently exists, create a new one.
|
| + */
|
| + winShmEnterMutex();
|
| + for(pShmNode = winShmNodeList; pShmNode; pShmNode=pShmNode->pNext){
|
| + /* TBD need to come up with better match here. Perhaps
|
| + ** use FILE_ID_BOTH_DIR_INFO Structure.
|
| + */
|
| + if( sqlite3StrICmp(pShmNode->zFilename, pNew->zFilename)==0 ) break;
|
| + }
|
| + if( pShmNode ){
|
| + sqlite3_free(pNew);
|
| + }else{
|
| + pShmNode = pNew;
|
| + pNew = 0;
|
| + ((winFile*)(&pShmNode->hFile))->h = INVALID_HANDLE_VALUE;
|
| + pShmNode->pNext = winShmNodeList;
|
| + winShmNodeList = pShmNode;
|
| +
|
| + if( sqlite3GlobalConfig.bCoreMutex ){
|
| + pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
|
| + if( pShmNode->mutex==0 ){
|
| + rc = SQLITE_IOERR_NOMEM_BKPT;
|
| + goto shm_open_err;
|
| + }
|
| + }
|
| +
|
| + rc = winOpen(pDbFd->pVfs,
|
| + pShmNode->zFilename, /* Name of the file (UTF-8) */
|
| + (sqlite3_file*)&pShmNode->hFile, /* File handle here */
|
| + SQLITE_OPEN_WAL | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
|
| + 0);
|
| + if( SQLITE_OK!=rc ){
|
| + goto shm_open_err;
|
| + }
|
| +
|
| + /* Check to see if another process is holding the dead-man switch.
|
| + ** If not, truncate the file to zero length.
|
| + */
|
| + if( winShmSystemLock(pShmNode, WINSHM_WRLCK, WIN_SHM_DMS, 1)==SQLITE_OK ){
|
| + rc = winTruncate((sqlite3_file *)&pShmNode->hFile, 0);
|
| + if( rc!=SQLITE_OK ){
|
| + rc = winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(),
|
| + "winOpenShm", pDbFd->zPath);
|
| + }
|
| + }
|
| + if( rc==SQLITE_OK ){
|
| + winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
|
| + rc = winShmSystemLock(pShmNode, WINSHM_RDLCK, WIN_SHM_DMS, 1);
|
| + }
|
| + if( rc ) goto shm_open_err;
|
| + }
|
| +
|
| + /* Make the new connection a child of the winShmNode */
|
| + p->pShmNode = pShmNode;
|
| +#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
|
| + p->id = pShmNode->nextShmId++;
|
| +#endif
|
| + pShmNode->nRef++;
|
| + pDbFd->pShm = p;
|
| + winShmLeaveMutex();
|
| +
|
| + /* The reference count on pShmNode has already been incremented under
|
| + ** the cover of the winShmEnterMutex() mutex and the pointer from the
|
| + ** new (struct winShm) object to the pShmNode has been set. All that is
|
| + ** left to do is to link the new object into the linked list starting
|
| + ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex
|
| + ** mutex.
|
| + */
|
| + sqlite3_mutex_enter(pShmNode->mutex);
|
| + p->pNext = pShmNode->pFirst;
|
| + pShmNode->pFirst = p;
|
| + sqlite3_mutex_leave(pShmNode->mutex);
|
| + return SQLITE_OK;
|
| +
|
| + /* Jump here on any error */
|
| +shm_open_err:
|
| + winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
|
| + winShmPurge(pDbFd->pVfs, 0); /* This call frees pShmNode if required */
|
| + sqlite3_free(p);
|
| + sqlite3_free(pNew);
|
| + winShmLeaveMutex();
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Close a connection to shared-memory. Delete the underlying
|
| +** storage if deleteFlag is true.
|
| +*/
|
| +static int winShmUnmap(
|
| + sqlite3_file *fd, /* Database holding shared memory */
|
| + int deleteFlag /* Delete after closing if true */
|
| +){
|
| + winFile *pDbFd; /* Database holding shared-memory */
|
| + winShm *p; /* The connection to be closed */
|
| + winShmNode *pShmNode; /* The underlying shared-memory file */
|
| + winShm **pp; /* For looping over sibling connections */
|
| +
|
| + pDbFd = (winFile*)fd;
|
| + p = pDbFd->pShm;
|
| + if( p==0 ) return SQLITE_OK;
|
| + pShmNode = p->pShmNode;
|
| +
|
| + /* Remove connection p from the set of connections associated
|
| + ** with pShmNode */
|
| + sqlite3_mutex_enter(pShmNode->mutex);
|
| + for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){}
|
| + *pp = p->pNext;
|
| +
|
| + /* Free the connection p */
|
| + sqlite3_free(p);
|
| + pDbFd->pShm = 0;
|
| + sqlite3_mutex_leave(pShmNode->mutex);
|
| +
|
| + /* If pShmNode->nRef has reached 0, then close the underlying
|
| + ** shared-memory file, too */
|
| + winShmEnterMutex();
|
| + assert( pShmNode->nRef>0 );
|
| + pShmNode->nRef--;
|
| + if( pShmNode->nRef==0 ){
|
| + winShmPurge(pDbFd->pVfs, deleteFlag);
|
| + }
|
| + winShmLeaveMutex();
|
| +
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Change the lock state for a shared-memory segment.
|
| +*/
|
| +static int winShmLock(
|
| + sqlite3_file *fd, /* Database file holding the shared memory */
|
| + int ofst, /* First lock to acquire or release */
|
| + int n, /* Number of locks to acquire or release */
|
| + int flags /* What to do with the lock */
|
| +){
|
| + winFile *pDbFd = (winFile*)fd; /* Connection holding shared memory */
|
| + winShm *p = pDbFd->pShm; /* The shared memory being locked */
|
| + winShm *pX; /* For looping over all siblings */
|
| + winShmNode *pShmNode = p->pShmNode;
|
| + int rc = SQLITE_OK; /* Result code */
|
| + u16 mask; /* Mask of locks to take or release */
|
| +
|
| + assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK );
|
| + assert( n>=1 );
|
| + assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED)
|
| + || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE)
|
| + || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
|
| + || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
|
| + assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );
|
| +
|
| + mask = (u16)((1U<<(ofst+n)) - (1U<<ofst));
|
| + assert( n>1 || mask==(1<<ofst) );
|
| + sqlite3_mutex_enter(pShmNode->mutex);
|
| + if( flags & SQLITE_SHM_UNLOCK ){
|
| + u16 allMask = 0; /* Mask of locks held by siblings */
|
| +
|
| + /* See if any siblings hold this same lock */
|
| + for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
|
| + if( pX==p ) continue;
|
| + assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 );
|
| + allMask |= pX->sharedMask;
|
| + }
|
| +
|
| + /* Unlock the system-level locks */
|
| + if( (mask & allMask)==0 ){
|
| + rc = winShmSystemLock(pShmNode, WINSHM_UNLCK, ofst+WIN_SHM_BASE, n);
|
| + }else{
|
| + rc = SQLITE_OK;
|
| + }
|
| +
|
| + /* Undo the local locks */
|
| + if( rc==SQLITE_OK ){
|
| + p->exclMask &= ~mask;
|
| + p->sharedMask &= ~mask;
|
| + }
|
| + }else if( flags & SQLITE_SHM_SHARED ){
|
| + u16 allShared = 0; /* Union of locks held by connections other than "p" */
|
| +
|
| + /* Find out which shared locks are already held by sibling connections.
|
| + ** If any sibling already holds an exclusive lock, go ahead and return
|
| + ** SQLITE_BUSY.
|
| + */
|
| + for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
|
| + if( (pX->exclMask & mask)!=0 ){
|
| + rc = SQLITE_BUSY;
|
| + break;
|
| + }
|
| + allShared |= pX->sharedMask;
|
| + }
|
| +
|
| + /* Get shared locks at the system level, if necessary */
|
| + if( rc==SQLITE_OK ){
|
| + if( (allShared & mask)==0 ){
|
| + rc = winShmSystemLock(pShmNode, WINSHM_RDLCK, ofst+WIN_SHM_BASE, n);
|
| + }else{
|
| + rc = SQLITE_OK;
|
| + }
|
| + }
|
| +
|
| + /* Get the local shared locks */
|
| + if( rc==SQLITE_OK ){
|
| + p->sharedMask |= mask;
|
| + }
|
| + }else{
|
| + /* Make sure no sibling connections hold locks that will block this
|
| + ** lock. If any do, return SQLITE_BUSY right away.
|
| + */
|
| + for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
|
| + if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){
|
| + rc = SQLITE_BUSY;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + /* Get the exclusive locks at the system level. Then if successful
|
| + ** also mark the local connection as being locked.
|
| + */
|
| + if( rc==SQLITE_OK ){
|
| + rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, ofst+WIN_SHM_BASE, n);
|
| + if( rc==SQLITE_OK ){
|
| + assert( (p->sharedMask & mask)==0 );
|
| + p->exclMask |= mask;
|
| + }
|
| + }
|
| + }
|
| + sqlite3_mutex_leave(pShmNode->mutex);
|
| + OSTRACE(("SHM-LOCK pid=%lu, id=%d, sharedMask=%03x, exclMask=%03x, rc=%s\n",
|
| + osGetCurrentProcessId(), p->id, p->sharedMask, p->exclMask,
|
| + sqlite3ErrName(rc)));
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Implement a memory barrier or memory fence on shared memory.
|
| +**
|
| +** All loads and stores begun before the barrier must complete before
|
| +** any load or store begun after the barrier.
|
| +*/
|
| +static void winShmBarrier(
|
| + sqlite3_file *fd /* Database holding the shared memory */
|
| +){
|
| + UNUSED_PARAMETER(fd);
|
| + sqlite3MemoryBarrier(); /* compiler-defined memory barrier */
|
| + winShmEnterMutex(); /* Also mutex, for redundancy */
|
| + winShmLeaveMutex();
|
| +}
|
| +
|
| +/*
|
| +** This function is called to obtain a pointer to region iRegion of the
|
| +** shared-memory associated with the database file fd. Shared-memory regions
|
| +** are numbered starting from zero. Each shared-memory region is szRegion
|
| +** bytes in size.
|
| +**
|
| +** If an error occurs, an error code is returned and *pp is set to NULL.
|
| +**
|
| +** Otherwise, if the isWrite parameter is 0 and the requested shared-memory
|
| +** region has not been allocated (by any client, including one running in a
|
| +** separate process), then *pp is set to NULL and SQLITE_OK returned. If
|
| +** isWrite is non-zero and the requested shared-memory region has not yet
|
| +** been allocated, it is allocated by this function.
|
| +**
|
| +** If the shared-memory region has already been allocated or is allocated by
|
| +** this call as described above, then it is mapped into this processes
|
| +** address space (if it is not already), *pp is set to point to the mapped
|
| +** memory and SQLITE_OK returned.
|
| +*/
|
| +static int winShmMap(
|
| + sqlite3_file *fd, /* Handle open on database file */
|
| + int iRegion, /* Region to retrieve */
|
| + int szRegion, /* Size of regions */
|
| + int isWrite, /* True to extend file if necessary */
|
| + void volatile **pp /* OUT: Mapped memory */
|
| +){
|
| + winFile *pDbFd = (winFile*)fd;
|
| + winShm *pShm = pDbFd->pShm;
|
| + winShmNode *pShmNode;
|
| + int rc = SQLITE_OK;
|
| +
|
| + if( !pShm ){
|
| + rc = winOpenSharedMemory(pDbFd);
|
| + if( rc!=SQLITE_OK ) return rc;
|
| + pShm = pDbFd->pShm;
|
| + }
|
| + pShmNode = pShm->pShmNode;
|
| +
|
| + sqlite3_mutex_enter(pShmNode->mutex);
|
| + assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 );
|
| +
|
| + if( pShmNode->nRegion<=iRegion ){
|
| + struct ShmRegion *apNew; /* New aRegion[] array */
|
| + int nByte = (iRegion+1)*szRegion; /* Minimum required file size */
|
| + sqlite3_int64 sz; /* Current size of wal-index file */
|
| +
|
| + pShmNode->szRegion = szRegion;
|
| +
|
| + /* The requested region is not mapped into this processes address space.
|
| + ** Check to see if it has been allocated (i.e. if the wal-index file is
|
| + ** large enough to contain the requested region).
|
| + */
|
| + rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz);
|
| + if( rc!=SQLITE_OK ){
|
| + rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(),
|
| + "winShmMap1", pDbFd->zPath);
|
| + goto shmpage_out;
|
| + }
|
| +
|
| + if( sz<nByte ){
|
| + /* The requested memory region does not exist. If isWrite is set to
|
| + ** zero, exit early. *pp will be set to NULL and SQLITE_OK returned.
|
| + **
|
| + ** Alternatively, if isWrite is non-zero, use ftruncate() to allocate
|
| + ** the requested memory region.
|
| + */
|
| + if( !isWrite ) goto shmpage_out;
|
| + rc = winTruncate((sqlite3_file *)&pShmNode->hFile, nByte);
|
| + if( rc!=SQLITE_OK ){
|
| + rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(),
|
| + "winShmMap2", pDbFd->zPath);
|
| + goto shmpage_out;
|
| + }
|
| + }
|
| +
|
| + /* Map the requested memory region into this processes address space. */
|
| + apNew = (struct ShmRegion *)sqlite3_realloc64(
|
| + pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0])
|
| + );
|
| + if( !apNew ){
|
| + rc = SQLITE_IOERR_NOMEM_BKPT;
|
| + goto shmpage_out;
|
| + }
|
| + pShmNode->aRegion = apNew;
|
| +
|
| + while( pShmNode->nRegion<=iRegion ){
|
| + HANDLE hMap = NULL; /* file-mapping handle */
|
| + void *pMap = 0; /* Mapped memory region */
|
| +
|
| +#if SQLITE_OS_WINRT
|
| + hMap = osCreateFileMappingFromApp(pShmNode->hFile.h,
|
| + NULL, PAGE_READWRITE, nByte, NULL
|
| + );
|
| +#elif defined(SQLITE_WIN32_HAS_WIDE)
|
| + hMap = osCreateFileMappingW(pShmNode->hFile.h,
|
| + NULL, PAGE_READWRITE, 0, nByte, NULL
|
| + );
|
| +#elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA
|
| + hMap = osCreateFileMappingA(pShmNode->hFile.h,
|
| + NULL, PAGE_READWRITE, 0, nByte, NULL
|
| + );
|
| +#endif
|
| + OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n",
|
| + osGetCurrentProcessId(), pShmNode->nRegion, nByte,
|
| + hMap ? "ok" : "failed"));
|
| + if( hMap ){
|
| + int iOffset = pShmNode->nRegion*szRegion;
|
| + int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity;
|
| +#if SQLITE_OS_WINRT
|
| + pMap = osMapViewOfFileFromApp(hMap, FILE_MAP_WRITE | FILE_MAP_READ,
|
| + iOffset - iOffsetShift, szRegion + iOffsetShift
|
| + );
|
| +#else
|
| + pMap = osMapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ,
|
| + 0, iOffset - iOffsetShift, szRegion + iOffsetShift
|
| + );
|
| +#endif
|
| + OSTRACE(("SHM-MAP-MAP pid=%lu, region=%d, offset=%d, size=%d, rc=%s\n",
|
| + osGetCurrentProcessId(), pShmNode->nRegion, iOffset,
|
| + szRegion, pMap ? "ok" : "failed"));
|
| + }
|
| + if( !pMap ){
|
| + pShmNode->lastErrno = osGetLastError();
|
| + rc = winLogError(SQLITE_IOERR_SHMMAP, pShmNode->lastErrno,
|
| + "winShmMap3", pDbFd->zPath);
|
| + if( hMap ) osCloseHandle(hMap);
|
| + goto shmpage_out;
|
| + }
|
| +
|
| + pShmNode->aRegion[pShmNode->nRegion].pMap = pMap;
|
| + pShmNode->aRegion[pShmNode->nRegion].hMap = hMap;
|
| + pShmNode->nRegion++;
|
| + }
|
| + }
|
| +
|
| +shmpage_out:
|
| + if( pShmNode->nRegion>iRegion ){
|
| + int iOffset = iRegion*szRegion;
|
| + int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity;
|
| + char *p = (char *)pShmNode->aRegion[iRegion].pMap;
|
| + *pp = (void *)&p[iOffsetShift];
|
| + }else{
|
| + *pp = 0;
|
| + }
|
| + sqlite3_mutex_leave(pShmNode->mutex);
|
| + return rc;
|
| +}
|
| +
|
| +#else
|
| +# define winShmMap 0
|
| +# define winShmLock 0
|
| +# define winShmBarrier 0
|
| +# define winShmUnmap 0
|
| +#endif /* #ifndef SQLITE_OMIT_WAL */
|
| +
|
| +/*
|
| +** Cleans up the mapped region of the specified file, if any.
|
| +*/
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| +static int winUnmapfile(winFile *pFile){
|
| + assert( pFile!=0 );
|
| + OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, pMapRegion=%p, "
|
| + "mmapSize=%lld, mmapSizeActual=%lld, mmapSizeMax=%lld\n",
|
| + osGetCurrentProcessId(), pFile, pFile->hMap, pFile->pMapRegion,
|
| + pFile->mmapSize, pFile->mmapSizeActual, pFile->mmapSizeMax));
|
| + if( pFile->pMapRegion ){
|
| + if( !osUnmapViewOfFile(pFile->pMapRegion) ){
|
| + pFile->lastErrno = osGetLastError();
|
| + OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, pMapRegion=%p, "
|
| + "rc=SQLITE_IOERR_MMAP\n", osGetCurrentProcessId(), pFile,
|
| + pFile->pMapRegion));
|
| + return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno,
|
| + "winUnmapfile1", pFile->zPath);
|
| + }
|
| + pFile->pMapRegion = 0;
|
| + pFile->mmapSize = 0;
|
| + pFile->mmapSizeActual = 0;
|
| + }
|
| + if( pFile->hMap!=NULL ){
|
| + if( !osCloseHandle(pFile->hMap) ){
|
| + pFile->lastErrno = osGetLastError();
|
| + OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, rc=SQLITE_IOERR_MMAP\n",
|
| + osGetCurrentProcessId(), pFile, pFile->hMap));
|
| + return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno,
|
| + "winUnmapfile2", pFile->zPath);
|
| + }
|
| + pFile->hMap = NULL;
|
| + }
|
| + OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, rc=SQLITE_OK\n",
|
| + osGetCurrentProcessId(), pFile));
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Memory map or remap the file opened by file-descriptor pFd (if the file
|
| +** is already mapped, the existing mapping is replaced by the new). Or, if
|
| +** there already exists a mapping for this file, and there are still
|
| +** outstanding xFetch() references to it, this function is a no-op.
|
| +**
|
| +** If parameter nByte is non-negative, then it is the requested size of
|
| +** the mapping to create. Otherwise, if nByte is less than zero, then the
|
| +** requested size is the size of the file on disk. The actual size of the
|
| +** created mapping is either the requested size or the value configured
|
| +** using SQLITE_FCNTL_MMAP_SIZE, whichever is smaller.
|
| +**
|
| +** SQLITE_OK is returned if no error occurs (even if the mapping is not
|
| +** recreated as a result of outstanding references) or an SQLite error
|
| +** code otherwise.
|
| +*/
|
| +static int winMapfile(winFile *pFd, sqlite3_int64 nByte){
|
| + sqlite3_int64 nMap = nByte;
|
| + int rc;
|
| +
|
| + assert( nMap>=0 || pFd->nFetchOut==0 );
|
| + OSTRACE(("MAP-FILE pid=%lu, pFile=%p, size=%lld\n",
|
| + osGetCurrentProcessId(), pFd, nByte));
|
| +
|
| + if( pFd->nFetchOut>0 ) return SQLITE_OK;
|
| +
|
| + if( nMap<0 ){
|
| + rc = winFileSize((sqlite3_file*)pFd, &nMap);
|
| + if( rc ){
|
| + OSTRACE(("MAP-FILE pid=%lu, pFile=%p, rc=SQLITE_IOERR_FSTAT\n",
|
| + osGetCurrentProcessId(), pFd));
|
| + return SQLITE_IOERR_FSTAT;
|
| + }
|
| + }
|
| + if( nMap>pFd->mmapSizeMax ){
|
| + nMap = pFd->mmapSizeMax;
|
| + }
|
| + nMap &= ~(sqlite3_int64)(winSysInfo.dwPageSize - 1);
|
| +
|
| + if( nMap==0 && pFd->mmapSize>0 ){
|
| + winUnmapfile(pFd);
|
| + }
|
| + if( nMap!=pFd->mmapSize ){
|
| + void *pNew = 0;
|
| + DWORD protect = PAGE_READONLY;
|
| + DWORD flags = FILE_MAP_READ;
|
| +
|
| + winUnmapfile(pFd);
|
| +#ifdef SQLITE_MMAP_READWRITE
|
| + if( (pFd->ctrlFlags & WINFILE_RDONLY)==0 ){
|
| + protect = PAGE_READWRITE;
|
| + flags |= FILE_MAP_WRITE;
|
| + }
|
| +#endif
|
| +#if SQLITE_OS_WINRT
|
| + pFd->hMap = osCreateFileMappingFromApp(pFd->h, NULL, protect, nMap, NULL);
|
| +#elif defined(SQLITE_WIN32_HAS_WIDE)
|
| + pFd->hMap = osCreateFileMappingW(pFd->h, NULL, protect,
|
| + (DWORD)((nMap>>32) & 0xffffffff),
|
| + (DWORD)(nMap & 0xffffffff), NULL);
|
| +#elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA
|
| + pFd->hMap = osCreateFileMappingA(pFd->h, NULL, protect,
|
| + (DWORD)((nMap>>32) & 0xffffffff),
|
| + (DWORD)(nMap & 0xffffffff), NULL);
|
| +#endif
|
| + if( pFd->hMap==NULL ){
|
| + pFd->lastErrno = osGetLastError();
|
| + rc = winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno,
|
| + "winMapfile1", pFd->zPath);
|
| + /* Log the error, but continue normal operation using xRead/xWrite */
|
| + OSTRACE(("MAP-FILE-CREATE pid=%lu, pFile=%p, rc=%s\n",
|
| + osGetCurrentProcessId(), pFd, sqlite3ErrName(rc)));
|
| + return SQLITE_OK;
|
| + }
|
| + assert( (nMap % winSysInfo.dwPageSize)==0 );
|
| + assert( sizeof(SIZE_T)==sizeof(sqlite3_int64) || nMap<=0xffffffff );
|
| +#if SQLITE_OS_WINRT
|
| + pNew = osMapViewOfFileFromApp(pFd->hMap, flags, 0, (SIZE_T)nMap);
|
| +#else
|
| + pNew = osMapViewOfFile(pFd->hMap, flags, 0, 0, (SIZE_T)nMap);
|
| +#endif
|
| + if( pNew==NULL ){
|
| + osCloseHandle(pFd->hMap);
|
| + pFd->hMap = NULL;
|
| + pFd->lastErrno = osGetLastError();
|
| + rc = winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno,
|
| + "winMapfile2", pFd->zPath);
|
| + /* Log the error, but continue normal operation using xRead/xWrite */
|
| + OSTRACE(("MAP-FILE-MAP pid=%lu, pFile=%p, rc=%s\n",
|
| + osGetCurrentProcessId(), pFd, sqlite3ErrName(rc)));
|
| + return SQLITE_OK;
|
| + }
|
| + pFd->pMapRegion = pNew;
|
| + pFd->mmapSize = nMap;
|
| + pFd->mmapSizeActual = nMap;
|
| + }
|
| +
|
| + OSTRACE(("MAP-FILE pid=%lu, pFile=%p, rc=SQLITE_OK\n",
|
| + osGetCurrentProcessId(), pFd));
|
| + return SQLITE_OK;
|
| +}
|
| +#endif /* SQLITE_MAX_MMAP_SIZE>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 winUnfetch().
|
| +*/
|
| +static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| + winFile *pFd = (winFile*)fd; /* The underlying database file */
|
| +#endif
|
| + *pp = 0;
|
| +
|
| + OSTRACE(("FETCH pid=%lu, pFile=%p, offset=%lld, amount=%d, pp=%p\n",
|
| + osGetCurrentProcessId(), fd, iOff, nAmt, pp));
|
| +
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| + if( pFd->mmapSizeMax>0 ){
|
| + if( pFd->pMapRegion==0 ){
|
| + int rc = winMapfile(pFd, -1);
|
| + if( rc!=SQLITE_OK ){
|
| + OSTRACE(("FETCH pid=%lu, pFile=%p, rc=%s\n",
|
| + osGetCurrentProcessId(), pFd, sqlite3ErrName(rc)));
|
| + return rc;
|
| + }
|
| + }
|
| + if( pFd->mmapSize >= iOff+nAmt ){
|
| + *pp = &((u8 *)pFd->pMapRegion)[iOff];
|
| + pFd->nFetchOut++;
|
| + }
|
| + }
|
| +#endif
|
| +
|
| + OSTRACE(("FETCH pid=%lu, pFile=%p, pp=%p, *pp=%p, rc=SQLITE_OK\n",
|
| + osGetCurrentProcessId(), fd, pp, *pp));
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** If the third argument is non-NULL, then this function releases a
|
| +** reference obtained by an earlier call to winFetch(). The second
|
| +** argument passed to this function must be the same as the corresponding
|
| +** argument that was passed to the winFetch() 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 winUnfetch(sqlite3_file *fd, i64 iOff, void *p){
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| + winFile *pFd = (winFile*)fd; /* The underlying database file */
|
| +
|
| + /* If p==0 (unmap the entire file) then there must be no outstanding
|
| + ** xFetch references. Or, if p!=0 (meaning it is an xFetch reference),
|
| + ** then there must be at least one outstanding. */
|
| + assert( (p==0)==(pFd->nFetchOut==0) );
|
| +
|
| + /* If p!=0, it must match the iOff value. */
|
| + assert( p==0 || p==&((u8 *)pFd->pMapRegion)[iOff] );
|
| +
|
| + OSTRACE(("UNFETCH pid=%lu, pFile=%p, offset=%lld, p=%p\n",
|
| + osGetCurrentProcessId(), pFd, iOff, p));
|
| +
|
| + if( p ){
|
| + pFd->nFetchOut--;
|
| + }else{
|
| + /* FIXME: If Windows truly always prevents truncating or deleting a
|
| + ** file while a mapping is held, then the following winUnmapfile() call
|
| + ** is unnecessary can be omitted - potentially improving
|
| + ** performance. */
|
| + winUnmapfile(pFd);
|
| + }
|
| +
|
| + assert( pFd->nFetchOut>=0 );
|
| +#endif
|
| +
|
| + OSTRACE(("UNFETCH pid=%lu, pFile=%p, rc=SQLITE_OK\n",
|
| + osGetCurrentProcessId(), fd));
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Here ends the implementation of all sqlite3_file methods.
|
| +**
|
| +********************** End sqlite3_file Methods *******************************
|
| +******************************************************************************/
|
| +
|
| +/*
|
| +** This vector defines all the methods that can operate on an
|
| +** sqlite3_file for win32.
|
| +*/
|
| +static const sqlite3_io_methods winIoMethod = {
|
| + 3, /* iVersion */
|
| + winClose, /* xClose */
|
| + winRead, /* xRead */
|
| + winWrite, /* xWrite */
|
| + winTruncate, /* xTruncate */
|
| + winSync, /* xSync */
|
| + winFileSize, /* xFileSize */
|
| + winLock, /* xLock */
|
| + winUnlock, /* xUnlock */
|
| + winCheckReservedLock, /* xCheckReservedLock */
|
| + winFileControl, /* xFileControl */
|
| + winSectorSize, /* xSectorSize */
|
| + winDeviceCharacteristics, /* xDeviceCharacteristics */
|
| + winShmMap, /* xShmMap */
|
| + winShmLock, /* xShmLock */
|
| + winShmBarrier, /* xShmBarrier */
|
| + winShmUnmap, /* xShmUnmap */
|
| + winFetch, /* xFetch */
|
| + winUnfetch /* xUnfetch */
|
| +};
|
| +
|
| +/*
|
| +** This vector defines all the methods that can operate on an
|
| +** sqlite3_file for win32 without performing any locking.
|
| +*/
|
| +static const sqlite3_io_methods winIoNolockMethod = {
|
| + 3, /* iVersion */
|
| + winClose, /* xClose */
|
| + winRead, /* xRead */
|
| + winWrite, /* xWrite */
|
| + winTruncate, /* xTruncate */
|
| + winSync, /* xSync */
|
| + winFileSize, /* xFileSize */
|
| + winNolockLock, /* xLock */
|
| + winNolockUnlock, /* xUnlock */
|
| + winNolockCheckReservedLock, /* xCheckReservedLock */
|
| + winFileControl, /* xFileControl */
|
| + winSectorSize, /* xSectorSize */
|
| + winDeviceCharacteristics, /* xDeviceCharacteristics */
|
| + winShmMap, /* xShmMap */
|
| + winShmLock, /* xShmLock */
|
| + winShmBarrier, /* xShmBarrier */
|
| + winShmUnmap, /* xShmUnmap */
|
| + winFetch, /* xFetch */
|
| + winUnfetch /* xUnfetch */
|
| +};
|
| +
|
| +static winVfsAppData winAppData = {
|
| + &winIoMethod, /* pMethod */
|
| + 0, /* pAppData */
|
| + 0 /* bNoLock */
|
| +};
|
| +
|
| +static winVfsAppData winNolockAppData = {
|
| + &winIoNolockMethod, /* pMethod */
|
| + 0, /* pAppData */
|
| + 1 /* bNoLock */
|
| +};
|
| +
|
| +/****************************************************************************
|
| +**************************** sqlite3_vfs methods ****************************
|
| +**
|
| +** This division contains the implementation of methods on the
|
| +** sqlite3_vfs object.
|
| +*/
|
| +
|
| +#if defined(__CYGWIN__)
|
| +/*
|
| +** Convert a filename from whatever the underlying operating system
|
| +** supports for filenames into UTF-8. Space to hold the result is
|
| +** obtained from malloc and must be freed by the calling function.
|
| +*/
|
| +static char *winConvertToUtf8Filename(const void *zFilename){
|
| + char *zConverted = 0;
|
| + if( osIsNT() ){
|
| + zConverted = winUnicodeToUtf8(zFilename);
|
| + }
|
| +#ifdef SQLITE_WIN32_HAS_ANSI
|
| + else{
|
| + zConverted = winMbcsToUtf8(zFilename, osAreFileApisANSI());
|
| + }
|
| +#endif
|
| + /* caller will handle out of memory */
|
| + return zConverted;
|
| +}
|
| +#endif
|
| +
|
| +/*
|
| +** Convert a UTF-8 filename into whatever form the underlying
|
| +** operating system wants filenames in. Space to hold the result
|
| +** is obtained from malloc and must be freed by the calling
|
| +** function.
|
| +*/
|
| +static void *winConvertFromUtf8Filename(const char *zFilename){
|
| + void *zConverted = 0;
|
| + if( osIsNT() ){
|
| + zConverted = winUtf8ToUnicode(zFilename);
|
| + }
|
| +#ifdef SQLITE_WIN32_HAS_ANSI
|
| + else{
|
| + zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI());
|
| + }
|
| +#endif
|
| + /* caller will handle out of memory */
|
| + return zConverted;
|
| +}
|
| +
|
| +/*
|
| +** This function returns non-zero if the specified UTF-8 string buffer
|
| +** ends with a directory separator character or one was successfully
|
| +** added to it.
|
| +*/
|
| +static int winMakeEndInDirSep(int nBuf, char *zBuf){
|
| + if( zBuf ){
|
| + int nLen = sqlite3Strlen30(zBuf);
|
| + if( nLen>0 ){
|
| + if( winIsDirSep(zBuf[nLen-1]) ){
|
| + return 1;
|
| + }else if( nLen+1<nBuf ){
|
| + zBuf[nLen] = winGetDirSep();
|
| + zBuf[nLen+1] = '\0';
|
| + return 1;
|
| + }
|
| + }
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| +/*
|
| +** Create a temporary file name and store the resulting pointer into pzBuf.
|
| +** The pointer returned in pzBuf must be freed via sqlite3_free().
|
| +*/
|
| +static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
|
| + static char zChars[] =
|
| + "abcdefghijklmnopqrstuvwxyz"
|
| + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
| + "0123456789";
|
| + size_t i, j;
|
| + int nPre = sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX);
|
| + int nMax, nBuf, nDir, nLen;
|
| + char *zBuf;
|
| +
|
| + /* It's odd to simulate an io-error here, but really this is just
|
| + ** using the io-error infrastructure to test that SQLite handles this
|
| + ** function failing.
|
| + */
|
| + SimulateIOError( return SQLITE_IOERR );
|
| +
|
| + /* Allocate a temporary buffer to store the fully qualified file
|
| + ** name for the temporary file. If this fails, we cannot continue.
|
| + */
|
| + nMax = pVfs->mxPathname; nBuf = nMax + 2;
|
| + zBuf = sqlite3MallocZero( nBuf );
|
| + if( !zBuf ){
|
| + OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
|
| + return SQLITE_IOERR_NOMEM_BKPT;
|
| + }
|
| +
|
| + /* Figure out the effective temporary directory. First, check if one
|
| + ** has been explicitly set by the application; otherwise, use the one
|
| + ** configured by the operating system.
|
| + */
|
| + nDir = nMax - (nPre + 15);
|
| + assert( nDir>0 );
|
| + if( sqlite3_temp_directory ){
|
| + int nDirLen = sqlite3Strlen30(sqlite3_temp_directory);
|
| + if( nDirLen>0 ){
|
| + if( !winIsDirSep(sqlite3_temp_directory[nDirLen-1]) ){
|
| + nDirLen++;
|
| + }
|
| + if( nDirLen>nDir ){
|
| + sqlite3_free(zBuf);
|
| + OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n"));
|
| + return winLogError(SQLITE_ERROR, 0, "winGetTempname1", 0);
|
| + }
|
| + sqlite3_snprintf(nMax, zBuf, "%s", sqlite3_temp_directory);
|
| + }
|
| + }
|
| +#if defined(__CYGWIN__)
|
| + else{
|
| + static const char *azDirs[] = {
|
| + 0, /* getenv("SQLITE_TMPDIR") */
|
| + 0, /* getenv("TMPDIR") */
|
| + 0, /* getenv("TMP") */
|
| + 0, /* getenv("TEMP") */
|
| + 0, /* getenv("USERPROFILE") */
|
| + "/var/tmp",
|
| + "/usr/tmp",
|
| + "/tmp",
|
| + ".",
|
| + 0 /* List terminator */
|
| + };
|
| + unsigned int i;
|
| + const char *zDir = 0;
|
| +
|
| + if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR");
|
| + if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR");
|
| + if( !azDirs[2] ) azDirs[2] = getenv("TMP");
|
| + if( !azDirs[3] ) azDirs[3] = getenv("TEMP");
|
| + if( !azDirs[4] ) azDirs[4] = getenv("USERPROFILE");
|
| + for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
|
| + void *zConverted;
|
| + if( zDir==0 ) continue;
|
| + /* If the path starts with a drive letter followed by the colon
|
| + ** character, assume it is already a native Win32 path; otherwise,
|
| + ** it must be converted to a native Win32 path via the Cygwin API
|
| + ** prior to using it.
|
| + */
|
| + if( winIsDriveLetterAndColon(zDir) ){
|
| + zConverted = winConvertFromUtf8Filename(zDir);
|
| + if( !zConverted ){
|
| + sqlite3_free(zBuf);
|
| + OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
|
| + return SQLITE_IOERR_NOMEM_BKPT;
|
| + }
|
| + if( winIsDir(zConverted) ){
|
| + sqlite3_snprintf(nMax, zBuf, "%s", zDir);
|
| + sqlite3_free(zConverted);
|
| + break;
|
| + }
|
| + sqlite3_free(zConverted);
|
| + }else{
|
| + zConverted = sqlite3MallocZero( nMax+1 );
|
| + if( !zConverted ){
|
| + sqlite3_free(zBuf);
|
| + OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
|
| + return SQLITE_IOERR_NOMEM_BKPT;
|
| + }
|
| + if( cygwin_conv_path(
|
| + osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A, zDir,
|
| + zConverted, nMax+1)<0 ){
|
| + sqlite3_free(zConverted);
|
| + sqlite3_free(zBuf);
|
| + OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_CONVPATH\n"));
|
| + return winLogError(SQLITE_IOERR_CONVPATH, (DWORD)errno,
|
| + "winGetTempname2", zDir);
|
| + }
|
| + if( winIsDir(zConverted) ){
|
| + /* At this point, we know the candidate directory exists and should
|
| + ** be used. However, we may need to convert the string containing
|
| + ** its name into UTF-8 (i.e. if it is UTF-16 right now).
|
| + */
|
| + char *zUtf8 = winConvertToUtf8Filename(zConverted);
|
| + if( !zUtf8 ){
|
| + sqlite3_free(zConverted);
|
| + sqlite3_free(zBuf);
|
| + OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
|
| + return SQLITE_IOERR_NOMEM_BKPT;
|
| + }
|
| + sqlite3_snprintf(nMax, zBuf, "%s", zUtf8);
|
| + sqlite3_free(zUtf8);
|
| + sqlite3_free(zConverted);
|
| + break;
|
| + }
|
| + sqlite3_free(zConverted);
|
| + }
|
| + }
|
| + }
|
| +#elif !SQLITE_OS_WINRT && !defined(__CYGWIN__)
|
| + else if( osIsNT() ){
|
| + char *zMulti;
|
| + LPWSTR zWidePath = sqlite3MallocZero( nMax*sizeof(WCHAR) );
|
| + if( !zWidePath ){
|
| + sqlite3_free(zBuf);
|
| + OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
|
| + return SQLITE_IOERR_NOMEM_BKPT;
|
| + }
|
| + if( osGetTempPathW(nMax, zWidePath)==0 ){
|
| + sqlite3_free(zWidePath);
|
| + sqlite3_free(zBuf);
|
| + OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n"));
|
| + return winLogError(SQLITE_IOERR_GETTEMPPATH, osGetLastError(),
|
| + "winGetTempname2", 0);
|
| + }
|
| + zMulti = winUnicodeToUtf8(zWidePath);
|
| + if( zMulti ){
|
| + sqlite3_snprintf(nMax, zBuf, "%s", zMulti);
|
| + sqlite3_free(zMulti);
|
| + sqlite3_free(zWidePath);
|
| + }else{
|
| + sqlite3_free(zWidePath);
|
| + sqlite3_free(zBuf);
|
| + OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
|
| + return SQLITE_IOERR_NOMEM_BKPT;
|
| + }
|
| + }
|
| +#ifdef SQLITE_WIN32_HAS_ANSI
|
| + else{
|
| + char *zUtf8;
|
| + char *zMbcsPath = sqlite3MallocZero( nMax );
|
| + if( !zMbcsPath ){
|
| + sqlite3_free(zBuf);
|
| + OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
|
| + return SQLITE_IOERR_NOMEM_BKPT;
|
| + }
|
| + if( osGetTempPathA(nMax, zMbcsPath)==0 ){
|
| + sqlite3_free(zBuf);
|
| + OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n"));
|
| + return winLogError(SQLITE_IOERR_GETTEMPPATH, osGetLastError(),
|
| + "winGetTempname3", 0);
|
| + }
|
| + zUtf8 = winMbcsToUtf8(zMbcsPath, osAreFileApisANSI());
|
| + if( zUtf8 ){
|
| + sqlite3_snprintf(nMax, zBuf, "%s", zUtf8);
|
| + sqlite3_free(zUtf8);
|
| + }else{
|
| + sqlite3_free(zBuf);
|
| + OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
|
| + return SQLITE_IOERR_NOMEM_BKPT;
|
| + }
|
| + }
|
| +#endif /* SQLITE_WIN32_HAS_ANSI */
|
| +#endif /* !SQLITE_OS_WINRT */
|
| +
|
| + /*
|
| + ** Check to make sure the temporary directory ends with an appropriate
|
| + ** separator. If it does not and there is not enough space left to add
|
| + ** one, fail.
|
| + */
|
| + if( !winMakeEndInDirSep(nDir+1, zBuf) ){
|
| + sqlite3_free(zBuf);
|
| + OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n"));
|
| + return winLogError(SQLITE_ERROR, 0, "winGetTempname4", 0);
|
| + }
|
| +
|
| + /*
|
| + ** Check that the output buffer is large enough for the temporary file
|
| + ** name in the following format:
|
| + **
|
| + ** "<temporary_directory>/etilqs_XXXXXXXXXXXXXXX\0\0"
|
| + **
|
| + ** If not, return SQLITE_ERROR. The number 17 is used here in order to
|
| + ** account for the space used by the 15 character random suffix and the
|
| + ** two trailing NUL characters. The final directory separator character
|
| + ** has already added if it was not already present.
|
| + */
|
| + nLen = sqlite3Strlen30(zBuf);
|
| + if( (nLen + nPre + 17) > nBuf ){
|
| + sqlite3_free(zBuf);
|
| + OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n"));
|
| + return winLogError(SQLITE_ERROR, 0, "winGetTempname5", 0);
|
| + }
|
| +
|
| + sqlite3_snprintf(nBuf-16-nLen, zBuf+nLen, SQLITE_TEMP_FILE_PREFIX);
|
| +
|
| + j = sqlite3Strlen30(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;
|
| + *pzBuf = zBuf;
|
| +
|
| + OSTRACE(("TEMP-FILENAME name=%s, rc=SQLITE_OK\n", zBuf));
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Return TRUE if the named file is really a directory. Return false if
|
| +** it is something other than a directory, or if there is any kind of memory
|
| +** allocation failure.
|
| +*/
|
| +static int winIsDir(const void *zConverted){
|
| + DWORD attr;
|
| + int rc = 0;
|
| + DWORD lastErrno;
|
| +
|
| + if( osIsNT() ){
|
| + int cnt = 0;
|
| + WIN32_FILE_ATTRIBUTE_DATA sAttrData;
|
| + memset(&sAttrData, 0, sizeof(sAttrData));
|
| + while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted,
|
| + GetFileExInfoStandard,
|
| + &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){}
|
| + if( !rc ){
|
| + return 0; /* Invalid name? */
|
| + }
|
| + attr = sAttrData.dwFileAttributes;
|
| +#if SQLITE_OS_WINCE==0
|
| + }else{
|
| + attr = osGetFileAttributesA((char*)zConverted);
|
| +#endif
|
| + }
|
| + return (attr!=INVALID_FILE_ATTRIBUTES) && (attr&FILE_ATTRIBUTE_DIRECTORY);
|
| +}
|
| +
|
| +/*
|
| +** Open a file.
|
| +*/
|
| +static int winOpen(
|
| + sqlite3_vfs *pVfs, /* Used to get maximum path length and AppData */
|
| + const char *zName, /* Name of the file (UTF-8) */
|
| + sqlite3_file *id, /* Write the SQLite file handle here */
|
| + int flags, /* Open mode flags */
|
| + int *pOutFlags /* Status return flags */
|
| +){
|
| + HANDLE h;
|
| + DWORD lastErrno = 0;
|
| + DWORD dwDesiredAccess;
|
| + DWORD dwShareMode;
|
| + DWORD dwCreationDisposition;
|
| + DWORD dwFlagsAndAttributes = 0;
|
| +#if SQLITE_OS_WINCE
|
| + int isTemp = 0;
|
| +#endif
|
| + winVfsAppData *pAppData;
|
| + winFile *pFile = (winFile*)id;
|
| + void *zConverted; /* Filename in OS encoding */
|
| + const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
|
| + int cnt = 0;
|
| +
|
| + /* 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 = 0; /* For temporary filename, if necessary. */
|
| +
|
| + int rc = SQLITE_OK; /* Function Return Code */
|
| +#if !defined(NDEBUG) || SQLITE_OS_WINCE
|
| + int eType = flags&0xFFFFFF00; /* Type of file to open */
|
| +#endif
|
| +
|
| + 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);
|
| +
|
| +#ifndef NDEBUG
|
| + int isOpenJournal = (isCreate && (
|
| + eType==SQLITE_OPEN_MASTER_JOURNAL
|
| + || eType==SQLITE_OPEN_MAIN_JOURNAL
|
| + || eType==SQLITE_OPEN_WAL
|
| + ));
|
| +#endif
|
| +
|
| + OSTRACE(("OPEN name=%s, pFile=%p, flags=%x, pOutFlags=%p\n",
|
| + zUtf8Name, id, flags, pOutFlags));
|
| +
|
| + /* 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 && zName) || eType!=SQLITE_OPEN_MAIN_DB );
|
| + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL );
|
| + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL );
|
| + assert( (!isDelete && zName) || 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
|
| + );
|
| +
|
| + assert( pFile!=0 );
|
| + memset(pFile, 0, sizeof(winFile));
|
| + pFile->h = INVALID_HANDLE_VALUE;
|
| +
|
| +#if SQLITE_OS_WINRT
|
| + if( !zUtf8Name && !sqlite3_temp_directory ){
|
| + sqlite3_log(SQLITE_ERROR,
|
| + "sqlite3_temp_directory variable should be set for WinRT");
|
| + }
|
| +#endif
|
| +
|
| + /* If the second argument to this function is NULL, generate a
|
| + ** temporary file name to use
|
| + */
|
| + if( !zUtf8Name ){
|
| + assert( isDelete && !isOpenJournal );
|
| + rc = winGetTempname(pVfs, &zTmpname);
|
| + if( rc!=SQLITE_OK ){
|
| + OSTRACE(("OPEN name=%s, rc=%s", zUtf8Name, sqlite3ErrName(rc)));
|
| + return rc;
|
| + }
|
| + zUtf8Name = zTmpname;
|
| + }
|
| +
|
| + /* 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) ||
|
| + zUtf8Name[sqlite3Strlen30(zUtf8Name)+1]==0 );
|
| +
|
| + /* Convert the filename to the system encoding. */
|
| + zConverted = winConvertFromUtf8Filename(zUtf8Name);
|
| + if( zConverted==0 ){
|
| + sqlite3_free(zTmpname);
|
| + OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8Name));
|
| + return SQLITE_IOERR_NOMEM_BKPT;
|
| + }
|
| +
|
| + if( winIsDir(zConverted) ){
|
| + sqlite3_free(zConverted);
|
| + sqlite3_free(zTmpname);
|
| + OSTRACE(("OPEN name=%s, rc=SQLITE_CANTOPEN_ISDIR", zUtf8Name));
|
| + return SQLITE_CANTOPEN_ISDIR;
|
| + }
|
| +
|
| + if( isReadWrite ){
|
| + dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
|
| + }else{
|
| + dwDesiredAccess = GENERIC_READ;
|
| + }
|
| +
|
| + /* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is
|
| + ** created. SQLite doesn't use it to indicate "exclusive access"
|
| + ** as it is usually understood.
|
| + */
|
| + if( isExclusive ){
|
| + /* Creates a new file, only if it does not already exist. */
|
| + /* If the file exists, it fails. */
|
| + dwCreationDisposition = CREATE_NEW;
|
| + }else if( isCreate ){
|
| + /* Open existing file, or create if it doesn't exist */
|
| + dwCreationDisposition = OPEN_ALWAYS;
|
| + }else{
|
| + /* Opens a file, only if it exists. */
|
| + dwCreationDisposition = OPEN_EXISTING;
|
| + }
|
| +
|
| + dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
| +
|
| + if( isDelete ){
|
| +#if SQLITE_OS_WINCE
|
| + dwFlagsAndAttributes = FILE_ATTRIBUTE_HIDDEN;
|
| + isTemp = 1;
|
| +#else
|
| + dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY
|
| + | FILE_ATTRIBUTE_HIDDEN
|
| + | FILE_FLAG_DELETE_ON_CLOSE;
|
| +#endif
|
| + }else{
|
| + dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
|
| + }
|
| + /* Reports from the internet are that performance is always
|
| + ** better if FILE_FLAG_RANDOM_ACCESS is used. Ticket #2699. */
|
| +#if SQLITE_OS_WINCE
|
| + dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
|
| +#endif
|
| +
|
| + if( osIsNT() ){
|
| +#if SQLITE_OS_WINRT
|
| + CREATEFILE2_EXTENDED_PARAMETERS extendedParameters;
|
| + extendedParameters.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
|
| + extendedParameters.dwFileAttributes =
|
| + dwFlagsAndAttributes & FILE_ATTRIBUTE_MASK;
|
| + extendedParameters.dwFileFlags = dwFlagsAndAttributes & FILE_FLAG_MASK;
|
| + extendedParameters.dwSecurityQosFlags = SECURITY_ANONYMOUS;
|
| + extendedParameters.lpSecurityAttributes = NULL;
|
| + extendedParameters.hTemplateFile = NULL;
|
| + while( (h = osCreateFile2((LPCWSTR)zConverted,
|
| + dwDesiredAccess,
|
| + dwShareMode,
|
| + dwCreationDisposition,
|
| + &extendedParameters))==INVALID_HANDLE_VALUE &&
|
| + winRetryIoerr(&cnt, &lastErrno) ){
|
| + /* Noop */
|
| + }
|
| +#else
|
| + while( (h = osCreateFileW((LPCWSTR)zConverted,
|
| + dwDesiredAccess,
|
| + dwShareMode, NULL,
|
| + dwCreationDisposition,
|
| + dwFlagsAndAttributes,
|
| + NULL))==INVALID_HANDLE_VALUE &&
|
| + winRetryIoerr(&cnt, &lastErrno) ){
|
| + /* Noop */
|
| + }
|
| +#endif
|
| + }
|
| +#ifdef SQLITE_WIN32_HAS_ANSI
|
| + else{
|
| + while( (h = osCreateFileA((LPCSTR)zConverted,
|
| + dwDesiredAccess,
|
| + dwShareMode, NULL,
|
| + dwCreationDisposition,
|
| + dwFlagsAndAttributes,
|
| + NULL))==INVALID_HANDLE_VALUE &&
|
| + winRetryIoerr(&cnt, &lastErrno) ){
|
| + /* Noop */
|
| + }
|
| + }
|
| +#endif
|
| + winLogIoerr(cnt, __LINE__);
|
| +
|
| + OSTRACE(("OPEN file=%p, name=%s, access=%lx, rc=%s\n", h, zUtf8Name,
|
| + dwDesiredAccess, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok"));
|
| +
|
| + if( h==INVALID_HANDLE_VALUE ){
|
| + pFile->lastErrno = lastErrno;
|
| + winLogError(SQLITE_CANTOPEN, pFile->lastErrno, "winOpen", zUtf8Name);
|
| + sqlite3_free(zConverted);
|
| + sqlite3_free(zTmpname);
|
| + if( isReadWrite && !isExclusive ){
|
| + return winOpen(pVfs, zName, id,
|
| + ((flags|SQLITE_OPEN_READONLY) &
|
| + ~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)),
|
| + pOutFlags);
|
| + }else{
|
| + return SQLITE_CANTOPEN_BKPT;
|
| + }
|
| + }
|
| +
|
| + if( pOutFlags ){
|
| + if( isReadWrite ){
|
| + *pOutFlags = SQLITE_OPEN_READWRITE;
|
| + }else{
|
| + *pOutFlags = SQLITE_OPEN_READONLY;
|
| + }
|
| + }
|
| +
|
| + OSTRACE(("OPEN file=%p, name=%s, access=%lx, pOutFlags=%p, *pOutFlags=%d, "
|
| + "rc=%s\n", h, zUtf8Name, dwDesiredAccess, pOutFlags, pOutFlags ?
|
| + *pOutFlags : 0, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok"));
|
| +
|
| + pAppData = (winVfsAppData*)pVfs->pAppData;
|
| +
|
| +#if SQLITE_OS_WINCE
|
| + {
|
| + if( isReadWrite && eType==SQLITE_OPEN_MAIN_DB
|
| + && ((pAppData==NULL) || !pAppData->bNoLock)
|
| + && (rc = winceCreateLock(zName, pFile))!=SQLITE_OK
|
| + ){
|
| + osCloseHandle(h);
|
| + sqlite3_free(zConverted);
|
| + sqlite3_free(zTmpname);
|
| + OSTRACE(("OPEN-CE-LOCK name=%s, rc=%s\n", zName, sqlite3ErrName(rc)));
|
| + return rc;
|
| + }
|
| + }
|
| + if( isTemp ){
|
| + pFile->zDeleteOnClose = zConverted;
|
| + }else
|
| +#endif
|
| + {
|
| + sqlite3_free(zConverted);
|
| + }
|
| +
|
| + sqlite3_free(zTmpname);
|
| + pFile->pMethod = pAppData ? pAppData->pMethod : &winIoMethod;
|
| + pFile->pVfs = pVfs;
|
| + pFile->h = h;
|
| + if( isReadonly ){
|
| + pFile->ctrlFlags |= WINFILE_RDONLY;
|
| + }
|
| + if( sqlite3_uri_boolean(zName, "psow", SQLITE_POWERSAFE_OVERWRITE) ){
|
| + pFile->ctrlFlags |= WINFILE_PSOW;
|
| + }
|
| + pFile->lastErrno = NO_ERROR;
|
| + pFile->zPath = zName;
|
| +#if SQLITE_MAX_MMAP_SIZE>0
|
| + pFile->hMap = NULL;
|
| + pFile->pMapRegion = 0;
|
| + pFile->mmapSize = 0;
|
| + pFile->mmapSizeActual = 0;
|
| + pFile->mmapSizeMax = sqlite3GlobalConfig.szMmap;
|
| +#endif
|
| +
|
| + OpenCounter(+1);
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Delete the named file.
|
| +**
|
| +** Note that Windows does not allow a file to be deleted if some other
|
| +** process has it open. Sometimes a virus scanner or indexing program
|
| +** will open a journal file shortly after it is created in order to do
|
| +** whatever it does. While this other process is holding the
|
| +** file open, we will be unable to delete it. To work around this
|
| +** problem, we delay 100 milliseconds and try to delete again. Up
|
| +** to MX_DELETION_ATTEMPTs deletion attempts are run before giving
|
| +** up and returning an error.
|
| +*/
|
| +static int winDelete(
|
| + sqlite3_vfs *pVfs, /* Not used on win32 */
|
| + const char *zFilename, /* Name of file to delete */
|
| + int syncDir /* Not used on win32 */
|
| +){
|
| + int cnt = 0;
|
| + int rc;
|
| + DWORD attr;
|
| + DWORD lastErrno = 0;
|
| + void *zConverted;
|
| + UNUSED_PARAMETER(pVfs);
|
| + UNUSED_PARAMETER(syncDir);
|
| +
|
| + SimulateIOError(return SQLITE_IOERR_DELETE);
|
| + OSTRACE(("DELETE name=%s, syncDir=%d\n", zFilename, syncDir));
|
| +
|
| + zConverted = winConvertFromUtf8Filename(zFilename);
|
| + if( zConverted==0 ){
|
| + OSTRACE(("DELETE name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename));
|
| + return SQLITE_IOERR_NOMEM_BKPT;
|
| + }
|
| + if( osIsNT() ){
|
| + do {
|
| +#if SQLITE_OS_WINRT
|
| + WIN32_FILE_ATTRIBUTE_DATA sAttrData;
|
| + memset(&sAttrData, 0, sizeof(sAttrData));
|
| + if ( osGetFileAttributesExW(zConverted, GetFileExInfoStandard,
|
| + &sAttrData) ){
|
| + attr = sAttrData.dwFileAttributes;
|
| + }else{
|
| + lastErrno = osGetLastError();
|
| + if( lastErrno==ERROR_FILE_NOT_FOUND
|
| + || lastErrno==ERROR_PATH_NOT_FOUND ){
|
| + rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */
|
| + }else{
|
| + rc = SQLITE_ERROR;
|
| + }
|
| + break;
|
| + }
|
| +#else
|
| + attr = osGetFileAttributesW(zConverted);
|
| +#endif
|
| + if ( attr==INVALID_FILE_ATTRIBUTES ){
|
| + lastErrno = osGetLastError();
|
| + if( lastErrno==ERROR_FILE_NOT_FOUND
|
| + || lastErrno==ERROR_PATH_NOT_FOUND ){
|
| + rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */
|
| + }else{
|
| + rc = SQLITE_ERROR;
|
| + }
|
| + break;
|
| + }
|
| + if ( attr&FILE_ATTRIBUTE_DIRECTORY ){
|
| + rc = SQLITE_ERROR; /* Files only. */
|
| + break;
|
| + }
|
| + if ( osDeleteFileW(zConverted) ){
|
| + rc = SQLITE_OK; /* Deleted OK. */
|
| + break;
|
| + }
|
| + if ( !winRetryIoerr(&cnt, &lastErrno) ){
|
| + rc = SQLITE_ERROR; /* No more retries. */
|
| + break;
|
| + }
|
| + } while(1);
|
| + }
|
| +#ifdef SQLITE_WIN32_HAS_ANSI
|
| + else{
|
| + do {
|
| + attr = osGetFileAttributesA(zConverted);
|
| + if ( attr==INVALID_FILE_ATTRIBUTES ){
|
| + lastErrno = osGetLastError();
|
| + if( lastErrno==ERROR_FILE_NOT_FOUND
|
| + || lastErrno==ERROR_PATH_NOT_FOUND ){
|
| + rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */
|
| + }else{
|
| + rc = SQLITE_ERROR;
|
| + }
|
| + break;
|
| + }
|
| + if ( attr&FILE_ATTRIBUTE_DIRECTORY ){
|
| + rc = SQLITE_ERROR; /* Files only. */
|
| + break;
|
| + }
|
| + if ( osDeleteFileA(zConverted) ){
|
| + rc = SQLITE_OK; /* Deleted OK. */
|
| + break;
|
| + }
|
| + if ( !winRetryIoerr(&cnt, &lastErrno) ){
|
| + rc = SQLITE_ERROR; /* No more retries. */
|
| + break;
|
| + }
|
| + } while(1);
|
| + }
|
| +#endif
|
| + if( rc && rc!=SQLITE_IOERR_DELETE_NOENT ){
|
| + rc = winLogError(SQLITE_IOERR_DELETE, lastErrno, "winDelete", zFilename);
|
| + }else{
|
| + winLogIoerr(cnt, __LINE__);
|
| + }
|
| + sqlite3_free(zConverted);
|
| + OSTRACE(("DELETE name=%s, rc=%s\n", zFilename, sqlite3ErrName(rc)));
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Check the existence and status of a file.
|
| +*/
|
| +static int winAccess(
|
| + sqlite3_vfs *pVfs, /* Not used on win32 */
|
| + const char *zFilename, /* Name of file to check */
|
| + int flags, /* Type of test to make on this file */
|
| + int *pResOut /* OUT: Result */
|
| +){
|
| + DWORD attr;
|
| + int rc = 0;
|
| + DWORD lastErrno = 0;
|
| + void *zConverted;
|
| + UNUSED_PARAMETER(pVfs);
|
| +
|
| + SimulateIOError( return SQLITE_IOERR_ACCESS; );
|
| + OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n",
|
| + zFilename, flags, pResOut));
|
| +
|
| + zConverted = winConvertFromUtf8Filename(zFilename);
|
| + if( zConverted==0 ){
|
| + OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename));
|
| + return SQLITE_IOERR_NOMEM_BKPT;
|
| + }
|
| + if( osIsNT() ){
|
| + int cnt = 0;
|
| + WIN32_FILE_ATTRIBUTE_DATA sAttrData;
|
| + memset(&sAttrData, 0, sizeof(sAttrData));
|
| + while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted,
|
| + GetFileExInfoStandard,
|
| + &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){}
|
| + if( rc ){
|
| + /* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file
|
| + ** as if it does not exist.
|
| + */
|
| + if( flags==SQLITE_ACCESS_EXISTS
|
| + && sAttrData.nFileSizeHigh==0
|
| + && sAttrData.nFileSizeLow==0 ){
|
| + attr = INVALID_FILE_ATTRIBUTES;
|
| + }else{
|
| + attr = sAttrData.dwFileAttributes;
|
| + }
|
| + }else{
|
| + winLogIoerr(cnt, __LINE__);
|
| + if( lastErrno!=ERROR_FILE_NOT_FOUND && lastErrno!=ERROR_PATH_NOT_FOUND ){
|
| + sqlite3_free(zConverted);
|
| + return winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess",
|
| + zFilename);
|
| + }else{
|
| + attr = INVALID_FILE_ATTRIBUTES;
|
| + }
|
| + }
|
| + }
|
| +#ifdef SQLITE_WIN32_HAS_ANSI
|
| + else{
|
| + attr = osGetFileAttributesA((char*)zConverted);
|
| + }
|
| +#endif
|
| + sqlite3_free(zConverted);
|
| + switch( flags ){
|
| + case SQLITE_ACCESS_READ:
|
| + case SQLITE_ACCESS_EXISTS:
|
| + rc = attr!=INVALID_FILE_ATTRIBUTES;
|
| + break;
|
| + case SQLITE_ACCESS_READWRITE:
|
| + rc = attr!=INVALID_FILE_ATTRIBUTES &&
|
| + (attr & FILE_ATTRIBUTE_READONLY)==0;
|
| + break;
|
| + default:
|
| + assert(!"Invalid flags argument");
|
| + }
|
| + *pResOut = rc;
|
| + OSTRACE(("ACCESS name=%s, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n",
|
| + zFilename, pResOut, *pResOut));
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Returns non-zero if the specified path name starts with a drive letter
|
| +** followed by a colon character.
|
| +*/
|
| +static BOOL winIsDriveLetterAndColon(
|
| + const char *zPathname
|
| +){
|
| + return ( sqlite3Isalpha(zPathname[0]) && zPathname[1]==':' );
|
| +}
|
| +
|
| +/*
|
| +** Returns non-zero if the specified path name should be used verbatim. If
|
| +** non-zero is returned from this function, the calling function must simply
|
| +** use the provided path name verbatim -OR- resolve it into a full path name
|
| +** using the GetFullPathName Win32 API function (if available).
|
| +*/
|
| +static BOOL winIsVerbatimPathname(
|
| + const char *zPathname
|
| +){
|
| + /*
|
| + ** If the path name starts with a forward slash or a backslash, it is either
|
| + ** a legal UNC name, a volume relative path, or an absolute path name in the
|
| + ** "Unix" format on Windows. There is no easy way to differentiate between
|
| + ** the final two cases; therefore, we return the safer return value of TRUE
|
| + ** so that callers of this function will simply use it verbatim.
|
| + */
|
| + if ( winIsDirSep(zPathname[0]) ){
|
| + return TRUE;
|
| + }
|
| +
|
| + /*
|
| + ** If the path name starts with a letter and a colon it is either a volume
|
| + ** relative path or an absolute path. Callers of this function must not
|
| + ** attempt to treat it as a relative path name (i.e. they should simply use
|
| + ** it verbatim).
|
| + */
|
| + if ( winIsDriveLetterAndColon(zPathname) ){
|
| + return TRUE;
|
| + }
|
| +
|
| + /*
|
| + ** If we get to this point, the path name should almost certainly be a purely
|
| + ** relative one (i.e. not a UNC name, not absolute, and not volume relative).
|
| + */
|
| + return FALSE;
|
| +}
|
| +
|
| +/*
|
| +** Turn a relative pathname into a full pathname. Write the full
|
| +** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname
|
| +** bytes in size.
|
| +*/
|
| +static int winFullPathname(
|
| + sqlite3_vfs *pVfs, /* Pointer to vfs object */
|
| + const char *zRelative, /* Possibly relative input path */
|
| + int nFull, /* Size of output buffer in bytes */
|
| + char *zFull /* Output buffer */
|
| +){
|
| +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__)
|
| + DWORD nByte;
|
| + void *zConverted;
|
| + char *zOut;
|
| +#endif
|
| +
|
| + /* If this path name begins with "/X:", where "X" is any alphabetic
|
| + ** character, discard the initial "/" from the pathname.
|
| + */
|
| + if( zRelative[0]=='/' && winIsDriveLetterAndColon(zRelative+1) ){
|
| + zRelative++;
|
| + }
|
| +
|
| +#if defined(__CYGWIN__)
|
| + SimulateIOError( return SQLITE_ERROR );
|
| + UNUSED_PARAMETER(nFull);
|
| + assert( nFull>=pVfs->mxPathname );
|
| + if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
|
| + /*
|
| + ** NOTE: We are dealing with a relative path name and the data
|
| + ** directory has been set. Therefore, use it as the basis
|
| + ** for converting the relative path name to an absolute
|
| + ** one by prepending the data directory and a slash.
|
| + */
|
| + char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 );
|
| + if( !zOut ){
|
| + return SQLITE_IOERR_NOMEM_BKPT;
|
| + }
|
| + if( cygwin_conv_path(
|
| + (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A) |
|
| + CCP_RELATIVE, zRelative, zOut, pVfs->mxPathname+1)<0 ){
|
| + sqlite3_free(zOut);
|
| + return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno,
|
| + "winFullPathname1", zRelative);
|
| + }else{
|
| + char *zUtf8 = winConvertToUtf8Filename(zOut);
|
| + if( !zUtf8 ){
|
| + sqlite3_free(zOut);
|
| + return SQLITE_IOERR_NOMEM_BKPT;
|
| + }
|
| + sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s",
|
| + sqlite3_data_directory, winGetDirSep(), zUtf8);
|
| + sqlite3_free(zUtf8);
|
| + sqlite3_free(zOut);
|
| + }
|
| + }else{
|
| + char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 );
|
| + if( !zOut ){
|
| + return SQLITE_IOERR_NOMEM_BKPT;
|
| + }
|
| + if( cygwin_conv_path(
|
| + (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A),
|
| + zRelative, zOut, pVfs->mxPathname+1)<0 ){
|
| + sqlite3_free(zOut);
|
| + return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno,
|
| + "winFullPathname2", zRelative);
|
| + }else{
|
| + char *zUtf8 = winConvertToUtf8Filename(zOut);
|
| + if( !zUtf8 ){
|
| + sqlite3_free(zOut);
|
| + return SQLITE_IOERR_NOMEM_BKPT;
|
| + }
|
| + sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zUtf8);
|
| + sqlite3_free(zUtf8);
|
| + sqlite3_free(zOut);
|
| + }
|
| + }
|
| + return SQLITE_OK;
|
| +#endif
|
| +
|
| +#if (SQLITE_OS_WINCE || SQLITE_OS_WINRT) && !defined(__CYGWIN__)
|
| + SimulateIOError( return SQLITE_ERROR );
|
| + /* WinCE has no concept of a relative pathname, or so I am told. */
|
| + /* WinRT has no way to convert a relative path to an absolute one. */
|
| + if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
|
| + /*
|
| + ** NOTE: We are dealing with a relative path name and the data
|
| + ** directory has been set. Therefore, use it as the basis
|
| + ** for converting the relative path name to an absolute
|
| + ** one by prepending the data directory and a backslash.
|
| + */
|
| + sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s",
|
| + sqlite3_data_directory, winGetDirSep(), zRelative);
|
| + }else{
|
| + sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zRelative);
|
| + }
|
| + return SQLITE_OK;
|
| +#endif
|
| +
|
| +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__)
|
| + /* It's odd to simulate an io-error here, but really this is just
|
| + ** using the io-error infrastructure to test that SQLite handles this
|
| + ** function failing. This function could fail if, for example, the
|
| + ** current working directory has been unlinked.
|
| + */
|
| + SimulateIOError( return SQLITE_ERROR );
|
| + if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
|
| + /*
|
| + ** NOTE: We are dealing with a relative path name and the data
|
| + ** directory has been set. Therefore, use it as the basis
|
| + ** for converting the relative path name to an absolute
|
| + ** one by prepending the data directory and a backslash.
|
| + */
|
| + sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s",
|
| + sqlite3_data_directory, winGetDirSep(), zRelative);
|
| + return SQLITE_OK;
|
| + }
|
| + zConverted = winConvertFromUtf8Filename(zRelative);
|
| + if( zConverted==0 ){
|
| + return SQLITE_IOERR_NOMEM_BKPT;
|
| + }
|
| + if( osIsNT() ){
|
| + LPWSTR zTemp;
|
| + nByte = osGetFullPathNameW((LPCWSTR)zConverted, 0, 0, 0);
|
| + if( nByte==0 ){
|
| + sqlite3_free(zConverted);
|
| + return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
|
| + "winFullPathname1", zRelative);
|
| + }
|
| + nByte += 3;
|
| + zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) );
|
| + if( zTemp==0 ){
|
| + sqlite3_free(zConverted);
|
| + return SQLITE_IOERR_NOMEM_BKPT;
|
| + }
|
| + nByte = osGetFullPathNameW((LPCWSTR)zConverted, nByte, zTemp, 0);
|
| + if( nByte==0 ){
|
| + sqlite3_free(zConverted);
|
| + sqlite3_free(zTemp);
|
| + return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
|
| + "winFullPathname2", zRelative);
|
| + }
|
| + sqlite3_free(zConverted);
|
| + zOut = winUnicodeToUtf8(zTemp);
|
| + sqlite3_free(zTemp);
|
| + }
|
| +#ifdef SQLITE_WIN32_HAS_ANSI
|
| + else{
|
| + char *zTemp;
|
| + nByte = osGetFullPathNameA((char*)zConverted, 0, 0, 0);
|
| + if( nByte==0 ){
|
| + sqlite3_free(zConverted);
|
| + return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
|
| + "winFullPathname3", zRelative);
|
| + }
|
| + nByte += 3;
|
| + zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) );
|
| + if( zTemp==0 ){
|
| + sqlite3_free(zConverted);
|
| + return SQLITE_IOERR_NOMEM_BKPT;
|
| + }
|
| + nByte = osGetFullPathNameA((char*)zConverted, nByte, zTemp, 0);
|
| + if( nByte==0 ){
|
| + sqlite3_free(zConverted);
|
| + sqlite3_free(zTemp);
|
| + return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
|
| + "winFullPathname4", zRelative);
|
| + }
|
| + sqlite3_free(zConverted);
|
| + zOut = winMbcsToUtf8(zTemp, osAreFileApisANSI());
|
| + sqlite3_free(zTemp);
|
| + }
|
| +#endif
|
| + if( zOut ){
|
| + sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut);
|
| + sqlite3_free(zOut);
|
| + return SQLITE_OK;
|
| + }else{
|
| + return SQLITE_IOERR_NOMEM_BKPT;
|
| + }
|
| +#endif
|
| +}
|
| +
|
| +#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
| +/*
|
| +** Interfaces for opening a shared library, finding entry points
|
| +** within the shared library, and closing the shared library.
|
| +*/
|
| +static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){
|
| + HANDLE h;
|
| +#if defined(__CYGWIN__)
|
| + int nFull = pVfs->mxPathname+1;
|
| + char *zFull = sqlite3MallocZero( nFull );
|
| + void *zConverted = 0;
|
| + if( zFull==0 ){
|
| + OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0));
|
| + return 0;
|
| + }
|
| + if( winFullPathname(pVfs, zFilename, nFull, zFull)!=SQLITE_OK ){
|
| + sqlite3_free(zFull);
|
| + OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0));
|
| + return 0;
|
| + }
|
| + zConverted = winConvertFromUtf8Filename(zFull);
|
| + sqlite3_free(zFull);
|
| +#else
|
| + void *zConverted = winConvertFromUtf8Filename(zFilename);
|
| + UNUSED_PARAMETER(pVfs);
|
| +#endif
|
| + if( zConverted==0 ){
|
| + OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0));
|
| + return 0;
|
| + }
|
| + if( osIsNT() ){
|
| +#if SQLITE_OS_WINRT
|
| + h = osLoadPackagedLibrary((LPCWSTR)zConverted, 0);
|
| +#else
|
| + h = osLoadLibraryW((LPCWSTR)zConverted);
|
| +#endif
|
| + }
|
| +#ifdef SQLITE_WIN32_HAS_ANSI
|
| + else{
|
| + h = osLoadLibraryA((char*)zConverted);
|
| + }
|
| +#endif
|
| + OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)h));
|
| + sqlite3_free(zConverted);
|
| + return (void*)h;
|
| +}
|
| +static void winDlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){
|
| + UNUSED_PARAMETER(pVfs);
|
| + winGetLastErrorMsg(osGetLastError(), nBuf, zBufOut);
|
| +}
|
| +static void (*winDlSym(sqlite3_vfs *pVfs,void *pH,const char *zSym))(void){
|
| + FARPROC proc;
|
| + UNUSED_PARAMETER(pVfs);
|
| + proc = osGetProcAddressA((HANDLE)pH, zSym);
|
| + OSTRACE(("DLSYM handle=%p, symbol=%s, address=%p\n",
|
| + (void*)pH, zSym, (void*)proc));
|
| + return (void(*)(void))proc;
|
| +}
|
| +static void winDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
| + UNUSED_PARAMETER(pVfs);
|
| + osFreeLibrary((HANDLE)pHandle);
|
| + OSTRACE(("DLCLOSE handle=%p\n", (void*)pHandle));
|
| +}
|
| +#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */
|
| + #define winDlOpen 0
|
| + #define winDlError 0
|
| + #define winDlSym 0
|
| + #define winDlClose 0
|
| +#endif
|
| +
|
| +/* State information for the randomness gatherer. */
|
| +typedef struct EntropyGatherer EntropyGatherer;
|
| +struct EntropyGatherer {
|
| + unsigned char *a; /* Gather entropy into this buffer */
|
| + int na; /* Size of a[] in bytes */
|
| + int i; /* XOR next input into a[i] */
|
| + int nXor; /* Number of XOR operations done */
|
| +};
|
| +
|
| +#if !defined(SQLITE_TEST) && !defined(SQLITE_OMIT_RANDOMNESS)
|
| +/* Mix sz bytes of entropy into p. */
|
| +static void xorMemory(EntropyGatherer *p, unsigned char *x, int sz){
|
| + int j, k;
|
| + for(j=0, k=p->i; j<sz; j++){
|
| + p->a[k++] ^= x[j];
|
| + if( k>=p->na ) k = 0;
|
| + }
|
| + p->i = k;
|
| + p->nXor += sz;
|
| +}
|
| +#endif /* !defined(SQLITE_TEST) && !defined(SQLITE_OMIT_RANDOMNESS) */
|
| +
|
| +/*
|
| +** Write up to nBuf bytes of randomness into zBuf.
|
| +*/
|
| +static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
|
| +#if defined(SQLITE_TEST) || defined(SQLITE_OMIT_RANDOMNESS)
|
| + UNUSED_PARAMETER(pVfs);
|
| + memset(zBuf, 0, nBuf);
|
| + return nBuf;
|
| +#else
|
| + EntropyGatherer e;
|
| + UNUSED_PARAMETER(pVfs);
|
| + memset(zBuf, 0, nBuf);
|
| +#if defined(_MSC_VER) && _MSC_VER>=1400 && !SQLITE_OS_WINCE
|
| + rand_s((unsigned int*)zBuf); /* rand_s() is not available with MinGW */
|
| +#endif /* defined(_MSC_VER) && _MSC_VER>=1400 */
|
| + e.a = (unsigned char*)zBuf;
|
| + e.na = nBuf;
|
| + e.nXor = 0;
|
| + e.i = 0;
|
| + {
|
| + SYSTEMTIME x;
|
| + osGetSystemTime(&x);
|
| + xorMemory(&e, (unsigned char*)&x, sizeof(SYSTEMTIME));
|
| + }
|
| + {
|
| + DWORD pid = osGetCurrentProcessId();
|
| + xorMemory(&e, (unsigned char*)&pid, sizeof(DWORD));
|
| + }
|
| +#if SQLITE_OS_WINRT
|
| + {
|
| + ULONGLONG cnt = osGetTickCount64();
|
| + xorMemory(&e, (unsigned char*)&cnt, sizeof(ULONGLONG));
|
| + }
|
| +#else
|
| + {
|
| + DWORD cnt = osGetTickCount();
|
| + xorMemory(&e, (unsigned char*)&cnt, sizeof(DWORD));
|
| + }
|
| +#endif /* SQLITE_OS_WINRT */
|
| + {
|
| + LARGE_INTEGER i;
|
| + osQueryPerformanceCounter(&i);
|
| + xorMemory(&e, (unsigned char*)&i, sizeof(LARGE_INTEGER));
|
| + }
|
| +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID
|
| + {
|
| + UUID id;
|
| + memset(&id, 0, sizeof(UUID));
|
| + osUuidCreate(&id);
|
| + xorMemory(&e, (unsigned char*)&id, sizeof(UUID));
|
| + memset(&id, 0, sizeof(UUID));
|
| + osUuidCreateSequential(&id);
|
| + xorMemory(&e, (unsigned char*)&id, sizeof(UUID));
|
| + }
|
| +#endif /* !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID */
|
| + return e.nXor>nBuf ? nBuf : e.nXor;
|
| +#endif /* defined(SQLITE_TEST) || defined(SQLITE_OMIT_RANDOMNESS) */
|
| +}
|
| +
|
| +
|
| +/*
|
| +** Sleep for a little while. Return the amount of time slept.
|
| +*/
|
| +static int winSleep(sqlite3_vfs *pVfs, int microsec){
|
| + sqlite3_win32_sleep((microsec+999)/1000);
|
| + UNUSED_PARAMETER(pVfs);
|
| + return ((microsec+999)/1000)*1000;
|
| +}
|
| +
|
| +/*
|
| +** The following variable, if set to a non-zero value, is interpreted as
|
| +** the number of seconds since 1970 and is used to set the result of
|
| +** sqlite3OsCurrentTime() during testing.
|
| +*/
|
| +#ifdef SQLITE_TEST
|
| +SQLITE_API int sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */
|
| +#endif
|
| +
|
| +/*
|
| +** Find the current time (in Universal Coordinated Time). Write into *piNow
|
| +** the current time and date as a Julian Day number times 86_400_000. In
|
| +** other words, write into *piNow the number of milliseconds since the Julian
|
| +** epoch of noon in Greenwich on November 24, 4714 B.C according to the
|
| +** proleptic Gregorian calendar.
|
| +**
|
| +** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date
|
| +** cannot be found.
|
| +*/
|
| +static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){
|
| + /* FILETIME structure is a 64-bit value representing the number of
|
| + 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5).
|
| + */
|
| + FILETIME ft;
|
| + static const sqlite3_int64 winFiletimeEpoch = 23058135*(sqlite3_int64)8640000;
|
| +#ifdef SQLITE_TEST
|
| + static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000;
|
| +#endif
|
| + /* 2^32 - to avoid use of LL and warnings in gcc */
|
| + static const sqlite3_int64 max32BitValue =
|
| + (sqlite3_int64)2000000000 + (sqlite3_int64)2000000000 +
|
| + (sqlite3_int64)294967296;
|
| +
|
| +#if SQLITE_OS_WINCE
|
| + SYSTEMTIME time;
|
| + osGetSystemTime(&time);
|
| + /* if SystemTimeToFileTime() fails, it returns zero. */
|
| + if (!osSystemTimeToFileTime(&time,&ft)){
|
| + return SQLITE_ERROR;
|
| + }
|
| +#else
|
| + osGetSystemTimeAsFileTime( &ft );
|
| +#endif
|
| +
|
| + *piNow = winFiletimeEpoch +
|
| + ((((sqlite3_int64)ft.dwHighDateTime)*max32BitValue) +
|
| + (sqlite3_int64)ft.dwLowDateTime)/(sqlite3_int64)10000;
|
| +
|
| +#ifdef SQLITE_TEST
|
| + if( sqlite3_current_time ){
|
| + *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch;
|
| + }
|
| +#endif
|
| + UNUSED_PARAMETER(pVfs);
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Find the current time (in Universal Coordinated Time). Write the
|
| +** current time and date as a Julian Day number into *prNow and
|
| +** return 0. Return 1 if the time and date cannot be found.
|
| +*/
|
| +static int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
|
| + int rc;
|
| + sqlite3_int64 i;
|
| + rc = winCurrentTimeInt64(pVfs, &i);
|
| + if( !rc ){
|
| + *prNow = i/86400000.0;
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** The idea is that this function works like a combination of
|
| +** GetLastError() and FormatMessage() on Windows (or errno and
|
| +** strerror_r() on Unix). After an error is returned by an OS
|
| +** function, SQLite calls this function with zBuf pointing to
|
| +** a buffer of nBuf bytes. The OS layer should populate the
|
| +** buffer with a nul-terminated UTF-8 encoded error message
|
| +** describing the last IO error to have occurred within the calling
|
| +** thread.
|
| +**
|
| +** If the error message is too large for the supplied buffer,
|
| +** it should be truncated. The return value of xGetLastError
|
| +** is zero if the error message fits in the buffer, or non-zero
|
| +** otherwise (if the message was truncated). If non-zero is returned,
|
| +** then it is not necessary to include the nul-terminator character
|
| +** in the output buffer.
|
| +**
|
| +** Not supplying an error message will have no adverse effect
|
| +** on SQLite. It is fine to have an implementation that never
|
| +** returns an error message:
|
| +**
|
| +** int xGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
|
| +** assert(zBuf[0]=='\0');
|
| +** return 0;
|
| +** }
|
| +**
|
| +** However if an error message is supplied, it will be incorporated
|
| +** by sqlite into the error message available to the user using
|
| +** sqlite3_errmsg(), possibly making IO errors easier to debug.
|
| +*/
|
| +static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
|
| + DWORD e = osGetLastError();
|
| + UNUSED_PARAMETER(pVfs);
|
| + if( nBuf>0 ) winGetLastErrorMsg(e, nBuf, zBuf);
|
| + return e;
|
| +}
|
| +
|
| +/*
|
| +** Initialize and deinitialize the operating system interface.
|
| +*/
|
| +SQLITE_API int sqlite3_os_init(void){
|
| + static sqlite3_vfs winVfs = {
|
| + 3, /* iVersion */
|
| + sizeof(winFile), /* szOsFile */
|
| + SQLITE_WIN32_MAX_PATH_BYTES, /* mxPathname */
|
| + 0, /* pNext */
|
| + "win32", /* zName */
|
| + &winAppData, /* pAppData */
|
| + winOpen, /* xOpen */
|
| + winDelete, /* xDelete */
|
| + winAccess, /* xAccess */
|
| + winFullPathname, /* xFullPathname */
|
| + winDlOpen, /* xDlOpen */
|
| + winDlError, /* xDlError */
|
| + winDlSym, /* xDlSym */
|
| + winDlClose, /* xDlClose */
|
| + winRandomness, /* xRandomness */
|
| + winSleep, /* xSleep */
|
| + winCurrentTime, /* xCurrentTime */
|
| + winGetLastError, /* xGetLastError */
|
| + winCurrentTimeInt64, /* xCurrentTimeInt64 */
|
| + winSetSystemCall, /* xSetSystemCall */
|
| + winGetSystemCall, /* xGetSystemCall */
|
| + winNextSystemCall, /* xNextSystemCall */
|
| + };
|
| +#if defined(SQLITE_WIN32_HAS_WIDE)
|
| + static sqlite3_vfs winLongPathVfs = {
|
| + 3, /* iVersion */
|
| + sizeof(winFile), /* szOsFile */
|
| + SQLITE_WINNT_MAX_PATH_BYTES, /* mxPathname */
|
| + 0, /* pNext */
|
| + "win32-longpath", /* zName */
|
| + &winAppData, /* pAppData */
|
| + winOpen, /* xOpen */
|
| + winDelete, /* xDelete */
|
| + winAccess, /* xAccess */
|
| + winFullPathname, /* xFullPathname */
|
| + winDlOpen, /* xDlOpen */
|
| + winDlError, /* xDlError */
|
| + winDlSym, /* xDlSym */
|
| + winDlClose, /* xDlClose */
|
| + winRandomness, /* xRandomness */
|
| + winSleep, /* xSleep */
|
| + winCurrentTime, /* xCurrentTime */
|
| + winGetLastError, /* xGetLastError */
|
| + winCurrentTimeInt64, /* xCurrentTimeInt64 */
|
| + winSetSystemCall, /* xSetSystemCall */
|
| + winGetSystemCall, /* xGetSystemCall */
|
| + winNextSystemCall, /* xNextSystemCall */
|
| + };
|
| +#endif
|
| + static sqlite3_vfs winNolockVfs = {
|
| + 3, /* iVersion */
|
| + sizeof(winFile), /* szOsFile */
|
| + SQLITE_WIN32_MAX_PATH_BYTES, /* mxPathname */
|
| + 0, /* pNext */
|
| + "win32-none", /* zName */
|
| + &winNolockAppData, /* pAppData */
|
| + winOpen, /* xOpen */
|
| + winDelete, /* xDelete */
|
| + winAccess, /* xAccess */
|
| + winFullPathname, /* xFullPathname */
|
| + winDlOpen, /* xDlOpen */
|
| + winDlError, /* xDlError */
|
| + winDlSym, /* xDlSym */
|
| + winDlClose, /* xDlClose */
|
| + winRandomness, /* xRandomness */
|
| + winSleep, /* xSleep */
|
| + winCurrentTime, /* xCurrentTime */
|
| + winGetLastError, /* xGetLastError */
|
| + winCurrentTimeInt64, /* xCurrentTimeInt64 */
|
| + winSetSystemCall, /* xSetSystemCall */
|
| + winGetSystemCall, /* xGetSystemCall */
|
| + winNextSystemCall, /* xNextSystemCall */
|
| + };
|
| +#if defined(SQLITE_WIN32_HAS_WIDE)
|
| + static sqlite3_vfs winLongPathNolockVfs = {
|
| + 3, /* iVersion */
|
| + sizeof(winFile), /* szOsFile */
|
| + SQLITE_WINNT_MAX_PATH_BYTES, /* mxPathname */
|
| + 0, /* pNext */
|
| + "win32-longpath-none", /* zName */
|
| + &winNolockAppData, /* pAppData */
|
| + winOpen, /* xOpen */
|
| + winDelete, /* xDelete */
|
| + winAccess, /* xAccess */
|
| + winFullPathname, /* xFullPathname */
|
| + winDlOpen, /* xDlOpen */
|
| + winDlError, /* xDlError */
|
| + winDlSym, /* xDlSym */
|
| + winDlClose, /* xDlClose */
|
| + winRandomness, /* xRandomness */
|
| + winSleep, /* xSleep */
|
| + winCurrentTime, /* xCurrentTime */
|
| + winGetLastError, /* xGetLastError */
|
| + winCurrentTimeInt64, /* xCurrentTimeInt64 */
|
| + winSetSystemCall, /* xSetSystemCall */
|
| + winGetSystemCall, /* xGetSystemCall */
|
| + winNextSystemCall, /* xNextSystemCall */
|
| + };
|
| +#endif
|
| +
|
| + /* Double-check that the aSyscall[] array has been constructed
|
| + ** correctly. See ticket [bb3a86e890c8e96ab] */
|
| + assert( ArraySize(aSyscall)==80 );
|
| +
|
| + /* get memory map allocation granularity */
|
| + memset(&winSysInfo, 0, sizeof(SYSTEM_INFO));
|
| +#if SQLITE_OS_WINRT
|
| + osGetNativeSystemInfo(&winSysInfo);
|
| +#else
|
| + osGetSystemInfo(&winSysInfo);
|
| +#endif
|
| + assert( winSysInfo.dwAllocationGranularity>0 );
|
| + assert( winSysInfo.dwPageSize>0 );
|
| +
|
| + sqlite3_vfs_register(&winVfs, 1);
|
| +
|
| +#if defined(SQLITE_WIN32_HAS_WIDE)
|
| + sqlite3_vfs_register(&winLongPathVfs, 0);
|
| +#endif
|
| +
|
| + sqlite3_vfs_register(&winNolockVfs, 0);
|
| +
|
| +#if defined(SQLITE_WIN32_HAS_WIDE)
|
| + sqlite3_vfs_register(&winLongPathNolockVfs, 0);
|
| +#endif
|
| +
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +SQLITE_API int sqlite3_os_end(void){
|
| +#if SQLITE_OS_WINRT
|
| + if( sleepObj!=NULL ){
|
| + osCloseHandle(sleepObj);
|
| + sleepObj = NULL;
|
| + }
|
| +#endif
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +CHROMIUM_SQLITE_API
|
| +void chromium_sqlite3_initialize_win_sqlite3_file(sqlite3_file* file, HANDLE handle) {
|
| + winFile* winSQLite3File = (winFile*)file;
|
| + memset(file, 0, sizeof(*file));
|
| + winSQLite3File->pMethod = &winIoMethod;
|
| + winSQLite3File->h = handle;
|
| +}
|
| +
|
| +#endif /* SQLITE_OS_WIN */
|
| +
|
| +/************** End of os_win.c **********************************************/
|
| +/************** Begin file bitvec.c ******************************************/
|
| +/*
|
| +** 2008 February 16
|
| +**
|
| +** 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 an object that represents a fixed-length
|
| +** bitmap. Bits are numbered starting with 1.
|
| +**
|
| +** A bitmap is used to record which pages of a database file have been
|
| +** journalled during a transaction, or which pages have the "dont-write"
|
| +** property. Usually only a few pages are meet either condition.
|
| +** So the bitmap is usually sparse and has low cardinality.
|
| +** But sometimes (for example when during a DROP of a large table) most
|
| +** or all of the pages in a database can get journalled. In those cases,
|
| +** the bitmap becomes dense with high cardinality. The algorithm needs
|
| +** to handle both cases well.
|
| +**
|
| +** The size of the bitmap is fixed when the object is created.
|
| +**
|
| +** All bits are clear when the bitmap is created. Individual bits
|
| +** may be set or cleared one at a time.
|
| +**
|
| +** Test operations are about 100 times more common that set operations.
|
| +** Clear operations are exceedingly rare. There are usually between
|
| +** 5 and 500 set operations per Bitvec object, though the number of sets can
|
| +** sometimes grow into tens of thousands or larger. The size of the
|
| +** Bitvec object is the number of pages in the database file at the
|
| +** start of a transaction, and is thus usually less than a few thousand,
|
| +** but can be as large as 2 billion for a really big database.
|
| +*/
|
| +/* #include "sqliteInt.h" */
|
| +
|
| +/* Size of the Bitvec structure in bytes. */
|
| +#define BITVEC_SZ 512
|
| +
|
| +/* Round the union size down to the nearest pointer boundary, since that's how
|
| +** it will be aligned within the Bitvec struct. */
|
| +#define BITVEC_USIZE \
|
| + (((BITVEC_SZ-(3*sizeof(u32)))/sizeof(Bitvec*))*sizeof(Bitvec*))
|
| +
|
| +/* Type of the array "element" for the bitmap representation.
|
| +** Should be a power of 2, and ideally, evenly divide into BITVEC_USIZE.
|
| +** Setting this to the "natural word" size of your CPU may improve
|
| +** performance. */
|
| +#define BITVEC_TELEM u8
|
| +/* Size, in bits, of the bitmap element. */
|
| +#define BITVEC_SZELEM 8
|
| +/* Number of elements in a bitmap array. */
|
| +#define BITVEC_NELEM (BITVEC_USIZE/sizeof(BITVEC_TELEM))
|
| +/* Number of bits in the bitmap array. */
|
| +#define BITVEC_NBIT (BITVEC_NELEM*BITVEC_SZELEM)
|
| +
|
| +/* Number of u32 values in hash table. */
|
| +#define BITVEC_NINT (BITVEC_USIZE/sizeof(u32))
|
| +/* Maximum number of entries in hash table before
|
| +** sub-dividing and re-hashing. */
|
| +#define BITVEC_MXHASH (BITVEC_NINT/2)
|
| +/* Hashing function for the aHash representation.
|
| +** Empirical testing showed that the *37 multiplier
|
| +** (an arbitrary prime)in the hash function provided
|
| +** no fewer collisions than the no-op *1. */
|
| +#define BITVEC_HASH(X) (((X)*1)%BITVEC_NINT)
|
| +
|
| +#define BITVEC_NPTR (BITVEC_USIZE/sizeof(Bitvec *))
|
| +
|
| +
|
| +/*
|
| +** A bitmap is an instance of the following structure.
|
| +**
|
| +** This bitmap records the existence of zero or more bits
|
| +** with values between 1 and iSize, inclusive.
|
| +**
|
| +** There are three possible representations of the bitmap.
|
| +** If iSize<=BITVEC_NBIT, then Bitvec.u.aBitmap[] is a straight
|
| +** bitmap. The least significant bit is bit 1.
|
| +**
|
| +** If iSize>BITVEC_NBIT and iDivisor==0 then Bitvec.u.aHash[] is
|
| +** a hash table that will hold up to BITVEC_MXHASH distinct values.
|
| +**
|
| +** Otherwise, the value i is redirected into one of BITVEC_NPTR
|
| +** sub-bitmaps pointed to by Bitvec.u.apSub[]. Each subbitmap
|
| +** handles up to iDivisor separate values of i. apSub[0] holds
|
| +** values between 1 and iDivisor. apSub[1] holds values between
|
| +** iDivisor+1 and 2*iDivisor. apSub[N] holds values between
|
| +** N*iDivisor+1 and (N+1)*iDivisor. Each subbitmap is normalized
|
| +** to hold deal with values between 1 and iDivisor.
|
| +*/
|
| +struct Bitvec {
|
| + u32 iSize; /* Maximum bit index. Max iSize is 4,294,967,296. */
|
| + u32 nSet; /* Number of bits that are set - only valid for aHash
|
| + ** element. Max is BITVEC_NINT. For BITVEC_SZ of 512,
|
| + ** this would be 125. */
|
| + u32 iDivisor; /* Number of bits handled by each apSub[] entry. */
|
| + /* Should >=0 for apSub element. */
|
| + /* Max iDivisor is max(u32) / BITVEC_NPTR + 1. */
|
| + /* For a BITVEC_SZ of 512, this would be 34,359,739. */
|
| + union {
|
| + BITVEC_TELEM aBitmap[BITVEC_NELEM]; /* Bitmap representation */
|
| + u32 aHash[BITVEC_NINT]; /* Hash table representation */
|
| + Bitvec *apSub[BITVEC_NPTR]; /* Recursive representation */
|
| + } u;
|
| +};
|
| +
|
| +/*
|
| +** Create a new bitmap object able to handle bits between 0 and iSize,
|
| +** inclusive. Return a pointer to the new object. Return NULL if
|
| +** malloc fails.
|
| +*/
|
| +SQLITE_PRIVATE Bitvec *sqlite3BitvecCreate(u32 iSize){
|
| + Bitvec *p;
|
| + assert( sizeof(*p)==BITVEC_SZ );
|
| + p = sqlite3MallocZero( sizeof(*p) );
|
| + if( p ){
|
| + p->iSize = iSize;
|
| + }
|
| + return p;
|
| +}
|
| +
|
| +/*
|
| +** Check to see if the i-th bit is set. Return true or false.
|
| +** If p is NULL (if the bitmap has not been created) or if
|
| +** i is out of range, then return false.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3BitvecTestNotNull(Bitvec *p, u32 i){
|
| + assert( p!=0 );
|
| + i--;
|
| + if( i>=p->iSize ) return 0;
|
| + while( p->iDivisor ){
|
| + u32 bin = i/p->iDivisor;
|
| + i = i%p->iDivisor;
|
| + p = p->u.apSub[bin];
|
| + if (!p) {
|
| + return 0;
|
| + }
|
| + }
|
| + if( p->iSize<=BITVEC_NBIT ){
|
| + return (p->u.aBitmap[i/BITVEC_SZELEM] & (1<<(i&(BITVEC_SZELEM-1))))!=0;
|
| + } else{
|
| + u32 h = BITVEC_HASH(i++);
|
| + while( p->u.aHash[h] ){
|
| + if( p->u.aHash[h]==i ) return 1;
|
| + h = (h+1) % BITVEC_NINT;
|
| + }
|
| + return 0;
|
| + }
|
| +}
|
| +SQLITE_PRIVATE int sqlite3BitvecTest(Bitvec *p, u32 i){
|
| + return p!=0 && sqlite3BitvecTestNotNull(p,i);
|
| +}
|
| +
|
| +/*
|
| +** Set the i-th bit. Return 0 on success and an error code if
|
| +** anything goes wrong.
|
| +**
|
| +** This routine might cause sub-bitmaps to be allocated. Failing
|
| +** to get the memory needed to hold the sub-bitmap is the only
|
| +** that can go wrong with an insert, assuming p and i are valid.
|
| +**
|
| +** The calling function must ensure that p is a valid Bitvec object
|
| +** and that the value for "i" is within range of the Bitvec object.
|
| +** Otherwise the behavior is undefined.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3BitvecSet(Bitvec *p, u32 i){
|
| + u32 h;
|
| + if( p==0 ) return SQLITE_OK;
|
| + assert( i>0 );
|
| + assert( i<=p->iSize );
|
| + i--;
|
| + while((p->iSize > BITVEC_NBIT) && p->iDivisor) {
|
| + u32 bin = i/p->iDivisor;
|
| + i = i%p->iDivisor;
|
| + if( p->u.apSub[bin]==0 ){
|
| + p->u.apSub[bin] = sqlite3BitvecCreate( p->iDivisor );
|
| + if( p->u.apSub[bin]==0 ) return SQLITE_NOMEM_BKPT;
|
| + }
|
| + p = p->u.apSub[bin];
|
| + }
|
| + if( p->iSize<=BITVEC_NBIT ){
|
| + p->u.aBitmap[i/BITVEC_SZELEM] |= 1 << (i&(BITVEC_SZELEM-1));
|
| + return SQLITE_OK;
|
| + }
|
| + h = BITVEC_HASH(i++);
|
| + /* if there wasn't a hash collision, and this doesn't */
|
| + /* completely fill the hash, then just add it without */
|
| + /* worring about sub-dividing and re-hashing. */
|
| + if( !p->u.aHash[h] ){
|
| + if (p->nSet<(BITVEC_NINT-1)) {
|
| + goto bitvec_set_end;
|
| + } else {
|
| + goto bitvec_set_rehash;
|
| + }
|
| + }
|
| + /* there was a collision, check to see if it's already */
|
| + /* in hash, if not, try to find a spot for it */
|
| + do {
|
| + if( p->u.aHash[h]==i ) return SQLITE_OK;
|
| + h++;
|
| + if( h>=BITVEC_NINT ) h = 0;
|
| + } while( p->u.aHash[h] );
|
| + /* we didn't find it in the hash. h points to the first */
|
| + /* available free spot. check to see if this is going to */
|
| + /* make our hash too "full". */
|
| +bitvec_set_rehash:
|
| + if( p->nSet>=BITVEC_MXHASH ){
|
| + unsigned int j;
|
| + int rc;
|
| + u32 *aiValues = sqlite3StackAllocRaw(0, sizeof(p->u.aHash));
|
| + if( aiValues==0 ){
|
| + return SQLITE_NOMEM_BKPT;
|
| + }else{
|
| + memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash));
|
| + memset(p->u.apSub, 0, sizeof(p->u.apSub));
|
| + p->iDivisor = (p->iSize + BITVEC_NPTR - 1)/BITVEC_NPTR;
|
| + rc = sqlite3BitvecSet(p, i);
|
| + for(j=0; j<BITVEC_NINT; j++){
|
| + if( aiValues[j] ) rc |= sqlite3BitvecSet(p, aiValues[j]);
|
| + }
|
| + sqlite3StackFree(0, aiValues);
|
| + return rc;
|
| + }
|
| + }
|
| +bitvec_set_end:
|
| + p->nSet++;
|
| + p->u.aHash[h] = i;
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Clear the i-th bit.
|
| +**
|
| +** pBuf must be a pointer to at least BITVEC_SZ bytes of temporary storage
|
| +** that BitvecClear can use to rebuilt its hash table.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3BitvecClear(Bitvec *p, u32 i, void *pBuf){
|
| + if( p==0 ) return;
|
| + assert( i>0 );
|
| + i--;
|
| + while( p->iDivisor ){
|
| + u32 bin = i/p->iDivisor;
|
| + i = i%p->iDivisor;
|
| + p = p->u.apSub[bin];
|
| + if (!p) {
|
| + return;
|
| + }
|
| + }
|
| + if( p->iSize<=BITVEC_NBIT ){
|
| + p->u.aBitmap[i/BITVEC_SZELEM] &= ~(1 << (i&(BITVEC_SZELEM-1)));
|
| + }else{
|
| + unsigned int j;
|
| + u32 *aiValues = pBuf;
|
| + memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash));
|
| + memset(p->u.aHash, 0, sizeof(p->u.aHash));
|
| + p->nSet = 0;
|
| + for(j=0; j<BITVEC_NINT; j++){
|
| + if( aiValues[j] && aiValues[j]!=(i+1) ){
|
| + u32 h = BITVEC_HASH(aiValues[j]-1);
|
| + p->nSet++;
|
| + while( p->u.aHash[h] ){
|
| + h++;
|
| + if( h>=BITVEC_NINT ) h = 0;
|
| + }
|
| + p->u.aHash[h] = aiValues[j];
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Destroy a bitmap object. Reclaim all memory used.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3BitvecDestroy(Bitvec *p){
|
| + if( p==0 ) return;
|
| + if( p->iDivisor ){
|
| + unsigned int i;
|
| + for(i=0; i<BITVEC_NPTR; i++){
|
| + sqlite3BitvecDestroy(p->u.apSub[i]);
|
| + }
|
| + }
|
| + sqlite3_free(p);
|
| +}
|
| +
|
| +/*
|
| +** Return the value of the iSize parameter specified when Bitvec *p
|
| +** was created.
|
| +*/
|
| +SQLITE_PRIVATE u32 sqlite3BitvecSize(Bitvec *p){
|
| + return p->iSize;
|
| +}
|
| +
|
| +#ifndef SQLITE_UNTESTABLE
|
| +/*
|
| +** Let V[] be an array of unsigned characters sufficient to hold
|
| +** up to N bits. Let I be an integer between 0 and N. 0<=I<N.
|
| +** Then the following macros can be used to set, clear, or test
|
| +** individual bits within V.
|
| +*/
|
| +#define SETBIT(V,I) V[I>>3] |= (1<<(I&7))
|
| +#define CLEARBIT(V,I) V[I>>3] &= ~(1<<(I&7))
|
| +#define TESTBIT(V,I) (V[I>>3]&(1<<(I&7)))!=0
|
| +
|
| +/*
|
| +** This routine runs an extensive test of the Bitvec code.
|
| +**
|
| +** The input is an array of integers that acts as a program
|
| +** to test the Bitvec. The integers are opcodes followed
|
| +** by 0, 1, or 3 operands, depending on the opcode. Another
|
| +** opcode follows immediately after the last operand.
|
| +**
|
| +** There are 6 opcodes numbered from 0 through 5. 0 is the
|
| +** "halt" opcode and causes the test to end.
|
| +**
|
| +** 0 Halt and return the number of errors
|
| +** 1 N S X Set N bits beginning with S and incrementing by X
|
| +** 2 N S X Clear N bits beginning with S and incrementing by X
|
| +** 3 N Set N randomly chosen bits
|
| +** 4 N Clear N randomly chosen bits
|
| +** 5 N S X Set N bits from S increment X in array only, not in bitvec
|
| +**
|
| +** The opcodes 1 through 4 perform set and clear operations are performed
|
| +** on both a Bitvec object and on a linear array of bits obtained from malloc.
|
| +** Opcode 5 works on the linear array only, not on the Bitvec.
|
| +** Opcode 5 is used to deliberately induce a fault in order to
|
| +** confirm that error detection works.
|
| +**
|
| +** At the conclusion of the test the linear array is compared
|
| +** against the Bitvec object. If there are any differences,
|
| +** an error is returned. If they are the same, zero is returned.
|
| +**
|
| +** If a memory allocation error occurs, return -1.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3BitvecBuiltinTest(int sz, int *aOp){
|
| + Bitvec *pBitvec = 0;
|
| + unsigned char *pV = 0;
|
| + int rc = -1;
|
| + int i, nx, pc, op;
|
| + void *pTmpSpace;
|
| +
|
| + /* Allocate the Bitvec to be tested and a linear array of
|
| + ** bits to act as the reference */
|
| + pBitvec = sqlite3BitvecCreate( sz );
|
| + pV = sqlite3MallocZero( (sz+7)/8 + 1 );
|
| + pTmpSpace = sqlite3_malloc64(BITVEC_SZ);
|
| + if( pBitvec==0 || pV==0 || pTmpSpace==0 ) goto bitvec_end;
|
| +
|
| + /* NULL pBitvec tests */
|
| + sqlite3BitvecSet(0, 1);
|
| + sqlite3BitvecClear(0, 1, pTmpSpace);
|
| +
|
| + /* Run the program */
|
| + pc = 0;
|
| + while( (op = aOp[pc])!=0 ){
|
| + switch( op ){
|
| + case 1:
|
| + case 2:
|
| + case 5: {
|
| + nx = 4;
|
| + i = aOp[pc+2] - 1;
|
| + aOp[pc+2] += aOp[pc+3];
|
| + break;
|
| + }
|
| + case 3:
|
| + case 4:
|
| + default: {
|
| + nx = 2;
|
| + sqlite3_randomness(sizeof(i), &i);
|
| + break;
|
| + }
|
| + }
|
| + if( (--aOp[pc+1]) > 0 ) nx = 0;
|
| + pc += nx;
|
| + i = (i & 0x7fffffff)%sz;
|
| + if( (op & 1)!=0 ){
|
| + SETBIT(pV, (i+1));
|
| + if( op!=5 ){
|
| + if( sqlite3BitvecSet(pBitvec, i+1) ) goto bitvec_end;
|
| + }
|
| + }else{
|
| + CLEARBIT(pV, (i+1));
|
| + sqlite3BitvecClear(pBitvec, i+1, pTmpSpace);
|
| + }
|
| + }
|
| +
|
| + /* Test to make sure the linear array exactly matches the
|
| + ** Bitvec object. Start with the assumption that they do
|
| + ** match (rc==0). Change rc to non-zero if a discrepancy
|
| + ** is found.
|
| + */
|
| + rc = sqlite3BitvecTest(0,0) + sqlite3BitvecTest(pBitvec, sz+1)
|
| + + sqlite3BitvecTest(pBitvec, 0)
|
| + + (sqlite3BitvecSize(pBitvec) - sz);
|
| + for(i=1; i<=sz; i++){
|
| + if( (TESTBIT(pV,i))!=sqlite3BitvecTest(pBitvec,i) ){
|
| + rc = i;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + /* Free allocated structure */
|
| +bitvec_end:
|
| + sqlite3_free(pTmpSpace);
|
| + sqlite3_free(pV);
|
| + sqlite3BitvecDestroy(pBitvec);
|
| + return rc;
|
| +}
|
| +#endif /* SQLITE_UNTESTABLE */
|
| +
|
| +/************** End of bitvec.c **********************************************/
|
| +/************** Begin file pcache.c ******************************************/
|
| +/*
|
| +** 2008 August 05
|
| +**
|
| +** 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 that page cache.
|
| +*/
|
| +/* #include "sqliteInt.h" */
|
| +
|
| +/*
|
| +** A complete page cache is an instance of this structure. Every
|
| +** entry in the cache holds a single page of the database file. The
|
| +** btree layer only operates on the cached copy of the database pages.
|
| +**
|
| +** A page cache entry is "clean" if it exactly matches what is currently
|
| +** on disk. A page is "dirty" if it has been modified and needs to be
|
| +** persisted to disk.
|
| +**
|
| +** pDirty, pDirtyTail, pSynced:
|
| +** All dirty pages are linked into the doubly linked list using
|
| +** PgHdr.pDirtyNext and pDirtyPrev. The list is maintained in LRU order
|
| +** such that p was added to the list more recently than p->pDirtyNext.
|
| +** PCache.pDirty points to the first (newest) element in the list and
|
| +** pDirtyTail to the last (oldest).
|
| +**
|
| +** The PCache.pSynced variable is used to optimize searching for a dirty
|
| +** page to eject from the cache mid-transaction. It is better to eject
|
| +** a page that does not require a journal sync than one that does.
|
| +** Therefore, pSynced is maintained to that it *almost* always points
|
| +** to either the oldest page in the pDirty/pDirtyTail list that has a
|
| +** clear PGHDR_NEED_SYNC flag or to a page that is older than this one
|
| +** (so that the right page to eject can be found by following pDirtyPrev
|
| +** pointers).
|
| +*/
|
| +struct PCache {
|
| + PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */
|
| + PgHdr *pSynced; /* Last synced page in dirty page list */
|
| + int nRefSum; /* Sum of ref counts over all pages */
|
| + int szCache; /* Configured cache size */
|
| + int szSpill; /* Size before spilling occurs */
|
| + int szPage; /* Size of every page in this cache */
|
| + int szExtra; /* Size of extra space for each page */
|
| + u8 bPurgeable; /* True if pages are on backing store */
|
| + u8 eCreate; /* eCreate value for for xFetch() */
|
| + int (*xStress)(void*,PgHdr*); /* Call to try make a page clean */
|
| + void *pStress; /* Argument to xStress */
|
| + sqlite3_pcache *pCache; /* Pluggable cache module */
|
| +};
|
| +
|
| +/********************************** Test and Debug Logic **********************/
|
| +/*
|
| +** Debug tracing macros. Enable by by changing the "0" to "1" and
|
| +** recompiling.
|
| +**
|
| +** When sqlite3PcacheTrace is 1, single line trace messages are issued.
|
| +** When sqlite3PcacheTrace is 2, a dump of the pcache showing all cache entries
|
| +** is displayed for many operations, resulting in a lot of output.
|
| +*/
|
| +#if defined(SQLITE_DEBUG) && 0
|
| + int sqlite3PcacheTrace = 2; /* 0: off 1: simple 2: cache dumps */
|
| + int sqlite3PcacheMxDump = 9999; /* Max cache entries for pcacheDump() */
|
| +# define pcacheTrace(X) if(sqlite3PcacheTrace){sqlite3DebugPrintf X;}
|
| + void pcacheDump(PCache *pCache){
|
| + int N;
|
| + int i, j;
|
| + sqlite3_pcache_page *pLower;
|
| + PgHdr *pPg;
|
| + unsigned char *a;
|
| +
|
| + if( sqlite3PcacheTrace<2 ) return;
|
| + if( pCache->pCache==0 ) return;
|
| + N = sqlite3PcachePagecount(pCache);
|
| + if( N>sqlite3PcacheMxDump ) N = sqlite3PcacheMxDump;
|
| + for(i=1; i<=N; i++){
|
| + pLower = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, i, 0);
|
| + if( pLower==0 ) continue;
|
| + pPg = (PgHdr*)pLower->pExtra;
|
| + printf("%3d: nRef %2d flgs %02x data ", i, pPg->nRef, pPg->flags);
|
| + a = (unsigned char *)pLower->pBuf;
|
| + for(j=0; j<12; j++) printf("%02x", a[j]);
|
| + printf("\n");
|
| + if( pPg->pPage==0 ){
|
| + sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, pLower, 0);
|
| + }
|
| + }
|
| + }
|
| + #else
|
| +# define pcacheTrace(X)
|
| +# define pcacheDump(X)
|
| +#endif
|
| +
|
| +/*
|
| +** Check invariants on a PgHdr entry. Return true if everything is OK.
|
| +** Return false if any invariant is violated.
|
| +**
|
| +** This routine is for use inside of assert() statements only. For
|
| +** example:
|
| +**
|
| +** assert( sqlite3PcachePageSanity(pPg) );
|
| +*/
|
| +#if SQLITE_DEBUG
|
| +SQLITE_PRIVATE int sqlite3PcachePageSanity(PgHdr *pPg){
|
| + PCache *pCache;
|
| + assert( pPg!=0 );
|
| + assert( pPg->pgno>0 || pPg->pPager==0 ); /* Page number is 1 or more */
|
| + pCache = pPg->pCache;
|
| + assert( pCache!=0 ); /* Every page has an associated PCache */
|
| + if( pPg->flags & PGHDR_CLEAN ){
|
| + assert( (pPg->flags & PGHDR_DIRTY)==0 );/* Cannot be both CLEAN and DIRTY */
|
| + assert( pCache->pDirty!=pPg ); /* CLEAN pages not on dirty list */
|
| + assert( pCache->pDirtyTail!=pPg );
|
| + }
|
| + /* WRITEABLE pages must also be DIRTY */
|
| + if( pPg->flags & PGHDR_WRITEABLE ){
|
| + assert( pPg->flags & PGHDR_DIRTY ); /* WRITEABLE implies DIRTY */
|
| + }
|
| + /* NEED_SYNC can be set independently of WRITEABLE. This can happen,
|
| + ** for example, when using the sqlite3PagerDontWrite() optimization:
|
| + ** (1) Page X is journalled, and gets WRITEABLE and NEED_SEEK.
|
| + ** (2) Page X moved to freelist, WRITEABLE is cleared
|
| + ** (3) Page X reused, WRITEABLE is set again
|
| + ** If NEED_SYNC had been cleared in step 2, then it would not be reset
|
| + ** in step 3, and page might be written into the database without first
|
| + ** syncing the rollback journal, which might cause corruption on a power
|
| + ** loss.
|
| + **
|
| + ** Another example is when the database page size is smaller than the
|
| + ** disk sector size. When any page of a sector is journalled, all pages
|
| + ** in that sector are marked NEED_SYNC even if they are still CLEAN, just
|
| + ** in case they are later modified, since all pages in the same sector
|
| + ** must be journalled and synced before any of those pages can be safely
|
| + ** written.
|
| + */
|
| + return 1;
|
| +}
|
| +#endif /* SQLITE_DEBUG */
|
| +
|
| +
|
| +/********************************** Linked List Management ********************/
|
| +
|
| +/* Allowed values for second argument to pcacheManageDirtyList() */
|
| +#define PCACHE_DIRTYLIST_REMOVE 1 /* Remove pPage from dirty list */
|
| +#define PCACHE_DIRTYLIST_ADD 2 /* Add pPage to the dirty list */
|
| +#define PCACHE_DIRTYLIST_FRONT 3 /* Move pPage to the front of the list */
|
| +
|
| +/*
|
| +** Manage pPage's participation on the dirty list. Bits of the addRemove
|
| +** argument determines what operation to do. The 0x01 bit means first
|
| +** remove pPage from the dirty list. The 0x02 means add pPage back to
|
| +** the dirty list. Doing both moves pPage to the front of the dirty list.
|
| +*/
|
| +static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){
|
| + PCache *p = pPage->pCache;
|
| +
|
| + pcacheTrace(("%p.DIRTYLIST.%s %d\n", p,
|
| + addRemove==1 ? "REMOVE" : addRemove==2 ? "ADD" : "FRONT",
|
| + pPage->pgno));
|
| + if( addRemove & PCACHE_DIRTYLIST_REMOVE ){
|
| + assert( pPage->pDirtyNext || pPage==p->pDirtyTail );
|
| + assert( pPage->pDirtyPrev || pPage==p->pDirty );
|
| +
|
| + /* Update the PCache1.pSynced variable if necessary. */
|
| + if( p->pSynced==pPage ){
|
| + p->pSynced = pPage->pDirtyPrev;
|
| + }
|
| +
|
| + if( pPage->pDirtyNext ){
|
| + pPage->pDirtyNext->pDirtyPrev = pPage->pDirtyPrev;
|
| + }else{
|
| + assert( pPage==p->pDirtyTail );
|
| + p->pDirtyTail = pPage->pDirtyPrev;
|
| + }
|
| + if( pPage->pDirtyPrev ){
|
| + pPage->pDirtyPrev->pDirtyNext = pPage->pDirtyNext;
|
| + }else{
|
| + /* If there are now no dirty pages in the cache, set eCreate to 2.
|
| + ** This is an optimization that allows sqlite3PcacheFetch() to skip
|
| + ** searching for a dirty page to eject from the cache when it might
|
| + ** otherwise have to. */
|
| + assert( pPage==p->pDirty );
|
| + p->pDirty = pPage->pDirtyNext;
|
| + assert( p->bPurgeable || p->eCreate==2 );
|
| + if( p->pDirty==0 ){ /*OPTIMIZATION-IF-TRUE*/
|
| + assert( p->bPurgeable==0 || p->eCreate==1 );
|
| + p->eCreate = 2;
|
| + }
|
| + }
|
| + pPage->pDirtyNext = 0;
|
| + pPage->pDirtyPrev = 0;
|
| + }
|
| + if( addRemove & PCACHE_DIRTYLIST_ADD ){
|
| + assert( pPage->pDirtyNext==0 && pPage->pDirtyPrev==0 && p->pDirty!=pPage );
|
| +
|
| + pPage->pDirtyNext = p->pDirty;
|
| + if( pPage->pDirtyNext ){
|
| + assert( pPage->pDirtyNext->pDirtyPrev==0 );
|
| + pPage->pDirtyNext->pDirtyPrev = pPage;
|
| + }else{
|
| + p->pDirtyTail = pPage;
|
| + if( p->bPurgeable ){
|
| + assert( p->eCreate==2 );
|
| + p->eCreate = 1;
|
| + }
|
| + }
|
| + p->pDirty = pPage;
|
| +
|
| + /* If pSynced is NULL and this page has a clear NEED_SYNC flag, set
|
| + ** pSynced to point to it. Checking the NEED_SYNC flag is an
|
| + ** optimization, as if pSynced points to a page with the NEED_SYNC
|
| + ** flag set sqlite3PcacheFetchStress() searches through all newer
|
| + ** entries of the dirty-list for a page with NEED_SYNC clear anyway. */
|
| + if( !p->pSynced
|
| + && 0==(pPage->flags&PGHDR_NEED_SYNC) /*OPTIMIZATION-IF-FALSE*/
|
| + ){
|
| + p->pSynced = pPage;
|
| + }
|
| + }
|
| + pcacheDump(p);
|
| +}
|
| +
|
| +/*
|
| +** Wrapper around the pluggable caches xUnpin method. If the cache is
|
| +** being used for an in-memory database, this function is a no-op.
|
| +*/
|
| +static void pcacheUnpin(PgHdr *p){
|
| + if( p->pCache->bPurgeable ){
|
| + pcacheTrace(("%p.UNPIN %d\n", p->pCache, p->pgno));
|
| + sqlite3GlobalConfig.pcache2.xUnpin(p->pCache->pCache, p->pPage, 0);
|
| + pcacheDump(p->pCache);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Compute the number of pages of cache requested. p->szCache is the
|
| +** cache size requested by the "PRAGMA cache_size" statement.
|
| +*/
|
| +static int numberOfCachePages(PCache *p){
|
| + if( p->szCache>=0 ){
|
| + /* IMPLEMENTATION-OF: R-42059-47211 If the argument N is positive then the
|
| + ** suggested cache size is set to N. */
|
| + return p->szCache;
|
| + }else{
|
| + /* IMPLEMENTATION-OF: R-61436-13639 If the argument N is negative, then
|
| + ** the number of cache pages is adjusted to use approximately abs(N*1024)
|
| + ** bytes of memory. */
|
| + return (int)((-1024*(i64)p->szCache)/(p->szPage+p->szExtra));
|
| + }
|
| +}
|
| +
|
| +/*************************************************** General Interfaces ******
|
| +**
|
| +** Initialize and shutdown the page cache subsystem. Neither of these
|
| +** functions are threadsafe.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3PcacheInitialize(void){
|
| + if( sqlite3GlobalConfig.pcache2.xInit==0 ){
|
| + /* IMPLEMENTATION-OF: R-26801-64137 If the xInit() method is NULL, then the
|
| + ** built-in default page cache is used instead of the application defined
|
| + ** page cache. */
|
| + sqlite3PCacheSetDefault();
|
| + }
|
| + return sqlite3GlobalConfig.pcache2.xInit(sqlite3GlobalConfig.pcache2.pArg);
|
| +}
|
| +SQLITE_PRIVATE void sqlite3PcacheShutdown(void){
|
| + if( sqlite3GlobalConfig.pcache2.xShutdown ){
|
| + /* IMPLEMENTATION-OF: R-26000-56589 The xShutdown() method may be NULL. */
|
| + sqlite3GlobalConfig.pcache2.xShutdown(sqlite3GlobalConfig.pcache2.pArg);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Return the size in bytes of a PCache object.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3PcacheSize(void){ return sizeof(PCache); }
|
| +
|
| +/*
|
| +** Create a new PCache object. Storage space to hold the object
|
| +** has already been allocated and is passed in as the p pointer.
|
| +** The caller discovers how much space needs to be allocated by
|
| +** calling sqlite3PcacheSize().
|
| +**
|
| +** szExtra is some extra space allocated for each page. The first
|
| +** 8 bytes of the extra space will be zeroed as the page is allocated,
|
| +** but remaining content will be uninitialized. Though it is opaque
|
| +** to this module, the extra space really ends up being the MemPage
|
| +** structure in the pager.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3PcacheOpen(
|
| + int szPage, /* Size of every page */
|
| + int szExtra, /* Extra space associated with each page */
|
| + int bPurgeable, /* True if pages are on backing store */
|
| + int (*xStress)(void*,PgHdr*),/* Call to try to make pages clean */
|
| + void *pStress, /* Argument to xStress */
|
| + PCache *p /* Preallocated space for the PCache */
|
| +){
|
| + memset(p, 0, sizeof(PCache));
|
| + p->szPage = 1;
|
| + p->szExtra = szExtra;
|
| + assert( szExtra>=8 ); /* First 8 bytes will be zeroed */
|
| + p->bPurgeable = bPurgeable;
|
| + p->eCreate = 2;
|
| + p->xStress = xStress;
|
| + p->pStress = pStress;
|
| + p->szCache = 100;
|
| + p->szSpill = 1;
|
| + pcacheTrace(("%p.OPEN szPage %d bPurgeable %d\n",p,szPage,bPurgeable));
|
| + return sqlite3PcacheSetPageSize(p, szPage);
|
| +}
|
| +
|
| +/*
|
| +** Change the page size for PCache object. The caller must ensure that there
|
| +** are no outstanding page references when this function is called.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3PcacheSetPageSize(PCache *pCache, int szPage){
|
| + assert( pCache->nRefSum==0 && pCache->pDirty==0 );
|
| + if( pCache->szPage ){
|
| + sqlite3_pcache *pNew;
|
| + pNew = sqlite3GlobalConfig.pcache2.xCreate(
|
| + szPage, pCache->szExtra + ROUND8(sizeof(PgHdr)),
|
| + pCache->bPurgeable
|
| + );
|
| + if( pNew==0 ) return SQLITE_NOMEM_BKPT;
|
| + sqlite3GlobalConfig.pcache2.xCachesize(pNew, numberOfCachePages(pCache));
|
| + if( pCache->pCache ){
|
| + sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache);
|
| + }
|
| + pCache->pCache = pNew;
|
| + pCache->szPage = szPage;
|
| + pcacheTrace(("%p.PAGESIZE %d\n",pCache,szPage));
|
| + }
|
| + return SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** Try to obtain a page from the cache.
|
| +**
|
| +** This routine returns a pointer to an sqlite3_pcache_page object if
|
| +** such an object is already in cache, or if a new one is created.
|
| +** This routine returns a NULL pointer if the object was not in cache
|
| +** and could not be created.
|
| +**
|
| +** The createFlags should be 0 to check for existing pages and should
|
| +** be 3 (not 1, but 3) to try to create a new page.
|
| +**
|
| +** If the createFlag is 0, then NULL is always returned if the page
|
| +** is not already in the cache. If createFlag is 1, then a new page
|
| +** is created only if that can be done without spilling dirty pages
|
| +** and without exceeding the cache size limit.
|
| +**
|
| +** The caller needs to invoke sqlite3PcacheFetchFinish() to properly
|
| +** initialize the sqlite3_pcache_page object and convert it into a
|
| +** PgHdr object. The sqlite3PcacheFetch() and sqlite3PcacheFetchFinish()
|
| +** routines are split this way for performance reasons. When separated
|
| +** they can both (usually) operate without having to push values to
|
| +** the stack on entry and pop them back off on exit, which saves a
|
| +** lot of pushing and popping.
|
| +*/
|
| +SQLITE_PRIVATE sqlite3_pcache_page *sqlite3PcacheFetch(
|
| + PCache *pCache, /* Obtain the page from this cache */
|
| + Pgno pgno, /* Page number to obtain */
|
| + int createFlag /* If true, create page if it does not exist already */
|
| +){
|
| + int eCreate;
|
| + sqlite3_pcache_page *pRes;
|
| +
|
| + assert( pCache!=0 );
|
| + assert( pCache->pCache!=0 );
|
| + assert( createFlag==3 || createFlag==0 );
|
| + assert( pCache->eCreate==((pCache->bPurgeable && pCache->pDirty) ? 1 : 2) );
|
| +
|
| + /* eCreate defines what to do if the page does not exist.
|
| + ** 0 Do not allocate a new page. (createFlag==0)
|
| + ** 1 Allocate a new page if doing so is inexpensive.
|
| + ** (createFlag==1 AND bPurgeable AND pDirty)
|
| + ** 2 Allocate a new page even it doing so is difficult.
|
| + ** (createFlag==1 AND !(bPurgeable AND pDirty)
|
| + */
|
| + eCreate = createFlag & pCache->eCreate;
|
| + assert( eCreate==0 || eCreate==1 || eCreate==2 );
|
| + assert( createFlag==0 || pCache->eCreate==eCreate );
|
| + assert( createFlag==0 || eCreate==1+(!pCache->bPurgeable||!pCache->pDirty) );
|
| + pRes = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate);
|
| + pcacheTrace(("%p.FETCH %d%s (result: %p)\n",pCache,pgno,
|
| + createFlag?" create":"",pRes));
|
| + return pRes;
|
| +}
|
| +
|
| +/*
|
| +** If the sqlite3PcacheFetch() routine is unable to allocate a new
|
| +** page because no clean pages are available for reuse and the cache
|
| +** size limit has been reached, then this routine can be invoked to
|
| +** try harder to allocate a page. This routine might invoke the stress
|
| +** callback to spill dirty pages to the journal. It will then try to
|
| +** allocate the new page and will only fail to allocate a new page on
|
| +** an OOM error.
|
| +**
|
| +** This routine should be invoked only after sqlite3PcacheFetch() fails.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3PcacheFetchStress(
|
| + PCache *pCache, /* Obtain the page from this cache */
|
| + Pgno pgno, /* Page number to obtain */
|
| + sqlite3_pcache_page **ppPage /* Write result here */
|
| +){
|
| + PgHdr *pPg;
|
| + if( pCache->eCreate==2 ) return 0;
|
| +
|
| + if( sqlite3PcachePagecount(pCache)>pCache->szSpill ){
|
| + /* Find a dirty page to write-out and recycle. First try to find a
|
| + ** page that does not require a journal-sync (one with PGHDR_NEED_SYNC
|
| + ** cleared), but if that is not possible settle for any other
|
| + ** unreferenced dirty page.
|
| + **
|
| + ** If the LRU page in the dirty list that has a clear PGHDR_NEED_SYNC
|
| + ** flag is currently referenced, then the following may leave pSynced
|
| + ** set incorrectly (pointing to other than the LRU page with NEED_SYNC
|
| + ** cleared). This is Ok, as pSynced is just an optimization. */
|
| + for(pPg=pCache->pSynced;
|
| + pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC));
|
| + pPg=pPg->pDirtyPrev
|
| + );
|
| + pCache->pSynced = pPg;
|
| + if( !pPg ){
|
| + for(pPg=pCache->pDirtyTail; pPg && pPg->nRef; pPg=pPg->pDirtyPrev);
|
| + }
|
| + if( pPg ){
|
| + int rc;
|
| +#ifdef SQLITE_LOG_CACHE_SPILL
|
| + sqlite3_log(SQLITE_FULL,
|
| + "spill page %d making room for %d - cache used: %d/%d",
|
| + pPg->pgno, pgno,
|
| + sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache),
|
| + numberOfCachePages(pCache));
|
| +#endif
|
| + pcacheTrace(("%p.SPILL %d\n",pCache,pPg->pgno));
|
| + rc = pCache->xStress(pCache->pStress, pPg);
|
| + pcacheDump(pCache);
|
| + if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){
|
| + return rc;
|
| + }
|
| + }
|
| + }
|
| + *ppPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, 2);
|
| + return *ppPage==0 ? SQLITE_NOMEM_BKPT : SQLITE_OK;
|
| +}
|
| +
|
| +/*
|
| +** This is a helper routine for sqlite3PcacheFetchFinish()
|
| +**
|
| +** In the uncommon case where the page being fetched has not been
|
| +** initialized, this routine is invoked to do the initialization.
|
| +** This routine is broken out into a separate function since it
|
| +** requires extra stack manipulation that can be avoided in the common
|
| +** case.
|
| +*/
|
| +static SQLITE_NOINLINE PgHdr *pcacheFetchFinishWithInit(
|
| + PCache *pCache, /* Obtain the page from this cache */
|
| + Pgno pgno, /* Page number obtained */
|
| + sqlite3_pcache_page *pPage /* Page obtained by prior PcacheFetch() call */
|
| +){
|
| + PgHdr *pPgHdr;
|
| + assert( pPage!=0 );
|
| + pPgHdr = (PgHdr*)pPage->pExtra;
|
| + assert( pPgHdr->pPage==0 );
|
| + memset(&pPgHdr->pDirty, 0, sizeof(PgHdr) - offsetof(PgHdr,pDirty));
|
| + pPgHdr->pPage = pPage;
|
| + pPgHdr->pData = pPage->pBuf;
|
| + pPgHdr->pExtra = (void *)&pPgHdr[1];
|
| + memset(pPgHdr->pExtra, 0, 8);
|
| + pPgHdr->pCache = pCache;
|
| + pPgHdr->pgno = pgno;
|
| + pPgHdr->flags = PGHDR_CLEAN;
|
| + return sqlite3PcacheFetchFinish(pCache,pgno,pPage);
|
| +}
|
| +
|
| +/*
|
| +** This routine converts the sqlite3_pcache_page object returned by
|
| +** sqlite3PcacheFetch() into an initialized PgHdr object. This routine
|
| +** must be called after sqlite3PcacheFetch() in order to get a usable
|
| +** result.
|
| +*/
|
| +SQLITE_PRIVATE PgHdr *sqlite3PcacheFetchFinish(
|
| + PCache *pCache, /* Obtain the page from this cache */
|
| + Pgno pgno, /* Page number obtained */
|
| + sqlite3_pcache_page *pPage /* Page obtained by prior PcacheFetch() call */
|
| +){
|
| + PgHdr *pPgHdr;
|
| +
|
| + assert( pPage!=0 );
|
| + pPgHdr = (PgHdr *)pPage->pExtra;
|
| +
|
| + if( !pPgHdr->pPage ){
|
| + return pcacheFetchFinishWithInit(pCache, pgno, pPage);
|
| + }
|
| + pCache->nRefSum++;
|
| + pPgHdr->nRef++;
|
| + assert( sqlite3PcachePageSanity(pPgHdr) );
|
| + return pPgHdr;
|
| +}
|
| +
|
| +/*
|
| +** Decrement the reference count on a page. If the page is clean and the
|
| +** reference count drops to 0, then it is made eligible for recycling.
|
| +*/
|
| +SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p){
|
| + assert( p->nRef>0 );
|
| + p->pCache->nRefSum--;
|
| + if( (--p->nRef)==0 ){
|
| + if( p->flags&PGHDR_CLEAN ){
|
| + pcacheUnpin(p);
|
| + }else if( p->pDirtyPrev!=0 ){ /*OPTIMIZATION-IF-FALSE*/
|
| + /* Move the page to the head of the dirty list. If p->pDirtyPrev==0,
|
| + ** then page p is already at the head of the dirty list and the
|
| + ** following call would be a no-op. Hence the OPTIMIZATION-IF-FALSE
|
| + ** tag above. */
|
| + pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT);
|
| + }
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Increase the reference count of a supplied page by 1.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3PcacheRef(PgHdr *p){
|
| + assert(p->nRef>0);
|
| + assert( sqlite3PcachePageSanity(p) );
|
| + p->nRef++;
|
| + p->pCache->nRefSum++;
|
| +}
|
| +
|
| +/*
|
| +** Drop a page from the cache. There must be exactly one reference to the
|
| +** page. This function deletes that reference, so after it returns the
|
| +** page pointed to by p is invalid.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3PcacheDrop(PgHdr *p){
|
| + assert( p->nRef==1 );
|
| + assert( sqlite3PcachePageSanity(p) );
|
| + if( p->flags&PGHDR_DIRTY ){
|
| + pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE);
|
| + }
|
| + p->pCache->nRefSum--;
|
| + sqlite3GlobalConfig.pcache2.xUnpin(p->pCache->pCache, p->pPage, 1);
|
| +}
|
| +
|
| +/*
|
| +** Make sure the page is marked as dirty. If it isn't dirty already,
|
| +** make it so.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3PcacheMakeDirty(PgHdr *p){
|
| + assert( p->nRef>0 );
|
| + assert( sqlite3PcachePageSanity(p) );
|
| + if( p->flags & (PGHDR_CLEAN|PGHDR_DONT_WRITE) ){ /*OPTIMIZATION-IF-FALSE*/
|
| + p->flags &= ~PGHDR_DONT_WRITE;
|
| + if( p->flags & PGHDR_CLEAN ){
|
| + p->flags ^= (PGHDR_DIRTY|PGHDR_CLEAN);
|
| + pcacheTrace(("%p.DIRTY %d\n",p->pCache,p->pgno));
|
| + assert( (p->flags & (PGHDR_DIRTY|PGHDR_CLEAN))==PGHDR_DIRTY );
|
| + pcacheManageDirtyList(p, PCACHE_DIRTYLIST_ADD);
|
| + }
|
| + assert( sqlite3PcachePageSanity(p) );
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Make sure the page is marked as clean. If it isn't clean already,
|
| +** make it so.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3PcacheMakeClean(PgHdr *p){
|
| + assert( sqlite3PcachePageSanity(p) );
|
| + if( ALWAYS((p->flags & PGHDR_DIRTY)!=0) ){
|
| + assert( (p->flags & PGHDR_CLEAN)==0 );
|
| + pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE);
|
| + p->flags &= ~(PGHDR_DIRTY|PGHDR_NEED_SYNC|PGHDR_WRITEABLE);
|
| + p->flags |= PGHDR_CLEAN;
|
| + pcacheTrace(("%p.CLEAN %d\n",p->pCache,p->pgno));
|
| + assert( sqlite3PcachePageSanity(p) );
|
| + if( p->nRef==0 ){
|
| + pcacheUnpin(p);
|
| + }
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Make every page in the cache clean.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3PcacheCleanAll(PCache *pCache){
|
| + PgHdr *p;
|
| + pcacheTrace(("%p.CLEAN-ALL\n",pCache));
|
| + while( (p = pCache->pDirty)!=0 ){
|
| + sqlite3PcacheMakeClean(p);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Clear the PGHDR_NEED_SYNC and PGHDR_WRITEABLE flag from all dirty pages.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3PcacheClearWritable(PCache *pCache){
|
| + PgHdr *p;
|
| + pcacheTrace(("%p.CLEAR-WRITEABLE\n",pCache));
|
| + for(p=pCache->pDirty; p; p=p->pDirtyNext){
|
| + p->flags &= ~(PGHDR_NEED_SYNC|PGHDR_WRITEABLE);
|
| + }
|
| + pCache->pSynced = pCache->pDirtyTail;
|
| +}
|
| +
|
| +/*
|
| +** Clear the PGHDR_NEED_SYNC flag from all dirty pages.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3PcacheClearSyncFlags(PCache *pCache){
|
| + PgHdr *p;
|
| + for(p=pCache->pDirty; p; p=p->pDirtyNext){
|
| + p->flags &= ~PGHDR_NEED_SYNC;
|
| + }
|
| + pCache->pSynced = pCache->pDirtyTail;
|
| +}
|
| +
|
| +/*
|
| +** Change the page number of page p to newPgno.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){
|
| + PCache *pCache = p->pCache;
|
| + assert( p->nRef>0 );
|
| + assert( newPgno>0 );
|
| + assert( sqlite3PcachePageSanity(p) );
|
| + pcacheTrace(("%p.MOVE %d -> %d\n",pCache,p->pgno,newPgno));
|
| + sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno);
|
| + p->pgno = newPgno;
|
| + if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){
|
| + pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Drop every cache entry whose page number is greater than "pgno". The
|
| +** caller must ensure that there are no outstanding references to any pages
|
| +** other than page 1 with a page number greater than pgno.
|
| +**
|
| +** If there is a reference to page 1 and the pgno parameter passed to this
|
| +** function is 0, then the data area associated with page 1 is zeroed, but
|
| +** the page object is not dropped.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
|
| + if( pCache->pCache ){
|
| + PgHdr *p;
|
| + PgHdr *pNext;
|
| + pcacheTrace(("%p.TRUNCATE %d\n",pCache,pgno));
|
| + for(p=pCache->pDirty; p; p=pNext){
|
| + pNext = p->pDirtyNext;
|
| + /* This routine never gets call with a positive pgno except right
|
| + ** after sqlite3PcacheCleanAll(). So if there are dirty pages,
|
| + ** it must be that pgno==0.
|
| + */
|
| + assert( p->pgno>0 );
|
| + if( p->pgno>pgno ){
|
| + assert( p->flags&PGHDR_DIRTY );
|
| + sqlite3PcacheMakeClean(p);
|
| + }
|
| + }
|
| + if( pgno==0 && pCache->nRefSum ){
|
| + sqlite3_pcache_page *pPage1;
|
| + pPage1 = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache,1,0);
|
| + if( ALWAYS(pPage1) ){ /* Page 1 is always available in cache, because
|
| + ** pCache->nRefSum>0 */
|
| + memset(pPage1->pBuf, 0, pCache->szPage);
|
| + pgno = 1;
|
| + }
|
| + }
|
| + sqlite3GlobalConfig.pcache2.xTruncate(pCache->pCache, pgno+1);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Close a cache.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3PcacheClose(PCache *pCache){
|
| + assert( pCache->pCache!=0 );
|
| + pcacheTrace(("%p.CLOSE\n",pCache));
|
| + sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache);
|
| +}
|
| +
|
| +/*
|
| +** Discard the contents of the cache.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3PcacheClear(PCache *pCache){
|
| + sqlite3PcacheTruncate(pCache, 0);
|
| +}
|
| +
|
| +/*
|
| +** Merge two lists of pages connected by pDirty and in pgno order.
|
| +** Do not bother fixing the pDirtyPrev pointers.
|
| +*/
|
| +static PgHdr *pcacheMergeDirtyList(PgHdr *pA, PgHdr *pB){
|
| + PgHdr result, *pTail;
|
| + pTail = &result;
|
| + assert( pA!=0 && pB!=0 );
|
| + for(;;){
|
| + if( pA->pgno<pB->pgno ){
|
| + pTail->pDirty = pA;
|
| + pTail = pA;
|
| + pA = pA->pDirty;
|
| + if( pA==0 ){
|
| + pTail->pDirty = pB;
|
| + break;
|
| + }
|
| + }else{
|
| + pTail->pDirty = pB;
|
| + pTail = pB;
|
| + pB = pB->pDirty;
|
| + if( pB==0 ){
|
| + pTail->pDirty = pA;
|
| + break;
|
| + }
|
| + }
|
| + }
|
| + return result.pDirty;
|
| +}
|
| +
|
| +/*
|
| +** Sort the list of pages in accending order by pgno. Pages are
|
| +** connected by pDirty pointers. The pDirtyPrev pointers are
|
| +** corrupted by this sort.
|
| +**
|
| +** Since there cannot be more than 2^31 distinct pages in a database,
|
| +** there cannot be more than 31 buckets required by the merge sorter.
|
| +** One extra bucket is added to catch overflow in case something
|
| +** ever changes to make the previous sentence incorrect.
|
| +*/
|
| +#define N_SORT_BUCKET 32
|
| +static PgHdr *pcacheSortDirtyList(PgHdr *pIn){
|
| + PgHdr *a[N_SORT_BUCKET], *p;
|
| + int i;
|
| + memset(a, 0, sizeof(a));
|
| + while( pIn ){
|
| + p = pIn;
|
| + pIn = p->pDirty;
|
| + p->pDirty = 0;
|
| + for(i=0; ALWAYS(i<N_SORT_BUCKET-1); i++){
|
| + if( a[i]==0 ){
|
| + a[i] = p;
|
| + break;
|
| + }else{
|
| + p = pcacheMergeDirtyList(a[i], p);
|
| + a[i] = 0;
|
| + }
|
| + }
|
| + if( NEVER(i==N_SORT_BUCKET-1) ){
|
| + /* To get here, there need to be 2^(N_SORT_BUCKET) elements in
|
| + ** the input list. But that is impossible.
|
| + */
|
| + a[i] = pcacheMergeDirtyList(a[i], p);
|
| + }
|
| + }
|
| + p = a[0];
|
| + for(i=1; i<N_SORT_BUCKET; i++){
|
| + if( a[i]==0 ) continue;
|
| + p = p ? pcacheMergeDirtyList(p, a[i]) : a[i];
|
| + }
|
| + return p;
|
| +}
|
| +
|
| +/*
|
| +** Return a list of all dirty pages in the cache, sorted by page number.
|
| +*/
|
| +SQLITE_PRIVATE PgHdr *sqlite3PcacheDirtyList(PCache *pCache){
|
| + PgHdr *p;
|
| + for(p=pCache->pDirty; p; p=p->pDirtyNext){
|
| + p->pDirty = p->pDirtyNext;
|
| + }
|
| + return pcacheSortDirtyList(pCache->pDirty);
|
| +}
|
| +
|
| +/*
|
| +** Return the total number of references to all pages held by the cache.
|
| +**
|
| +** This is not the total number of pages referenced, but the sum of the
|
| +** reference count for all pages.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3PcacheRefCount(PCache *pCache){
|
| + return pCache->nRefSum;
|
| +}
|
| +
|
| +/*
|
| +** Return the number of references to the page supplied as an argument.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3PcachePageRefcount(PgHdr *p){
|
| + return p->nRef;
|
| +}
|
| +
|
| +/*
|
| +** Return the total number of pages in the cache.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3PcachePagecount(PCache *pCache){
|
| + assert( pCache->pCache!=0 );
|
| + return sqlite3GlobalConfig.pcache2.xPagecount(pCache->pCache);
|
| +}
|
| +
|
| +#ifdef SQLITE_TEST
|
| +/*
|
| +** Get the suggested cache-size value.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3PcacheGetCachesize(PCache *pCache){
|
| + return numberOfCachePages(pCache);
|
| +}
|
| +#endif
|
| +
|
| +/*
|
| +** Set the suggested cache-size value.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3PcacheSetCachesize(PCache *pCache, int mxPage){
|
| + assert( pCache->pCache!=0 );
|
| + pCache->szCache = mxPage;
|
| + sqlite3GlobalConfig.pcache2.xCachesize(pCache->pCache,
|
| + numberOfCachePages(pCache));
|
| +}
|
| +
|
| +/*
|
| +** Set the suggested cache-spill value. Make no changes if if the
|
| +** argument is zero. Return the effective cache-spill size, which will
|
| +** be the larger of the szSpill and szCache.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3PcacheSetSpillsize(PCache *p, int mxPage){
|
| + int res;
|
| + assert( p->pCache!=0 );
|
| + if( mxPage ){
|
| + if( mxPage<0 ){
|
| + mxPage = (int)((-1024*(i64)mxPage)/(p->szPage+p->szExtra));
|
| + }
|
| + p->szSpill = mxPage;
|
| + }
|
| + res = numberOfCachePages(p);
|
| + if( res<p->szSpill ) res = p->szSpill;
|
| + return res;
|
| +}
|
| +
|
| +/*
|
| +** Free up as much memory as possible from the page cache.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3PcacheShrink(PCache *pCache){
|
| + assert( pCache->pCache!=0 );
|
| + sqlite3GlobalConfig.pcache2.xShrink(pCache->pCache);
|
| +}
|
| +
|
| +/*
|
| +** Return the size of the header added by this middleware layer
|
| +** in the page-cache hierarchy.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3HeaderSizePcache(void){ return ROUND8(sizeof(PgHdr)); }
|
| +
|
| +/*
|
| +** Return the number of dirty pages currently in the cache, as a percentage
|
| +** of the configured cache size.
|
| +*/
|
| +SQLITE_PRIVATE int sqlite3PCachePercentDirty(PCache *pCache){
|
| + PgHdr *pDirty;
|
| + int nDirty = 0;
|
| + int nCache = numberOfCachePages(pCache);
|
| + for(pDirty=pCache->pDirty; pDirty; pDirty=pDirty->pDirtyNext) nDirty++;
|
| + return nCache ? (int)(((i64)nDirty * 100) / nCache) : 0;
|
| +}
|
| +
|
| +#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG)
|
| +/*
|
| +** For all dirty pages currently in the cache, invoke the specified
|
| +** callback. This is only used if the SQLITE_CHECK_PAGES macro is
|
| +** defined.
|
| +*/
|
| +SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *)){
|
| + PgHdr *pDirty;
|
| + for(pDirty=pCache->pDirty; pDirty; pDirty=pDirty->pDirtyNext){
|
| + xIter(pDirty);
|
| + }
|
| +}
|
| +#endif
|
| +
|
| +/************** End of pcache.c **********************************************/
|
| +
|
| +/* Chain include. */
|
| +#include "sqlite3.02.c"
|
|
|