Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Merge remote-tracking branch 'upstream/master'
Shawn Webb committed 2 years ago
commit 825ab9d6f87714144c189a7eeee5174d6fdf6443
parent 5856096
15 files changed +644 -218
modified NEWS
@@ -1,3 +1,8 @@
+
Changes from 1.20.99.11 to 1.20.99.12
+
- ECC signature: plenty of fixes
+
- plist now have a "@var key value" to deal with %%KEY%% variables
+
- upgrade sqlite to latest version
+

Changes from 1.20.99.10 to 1.20.99.11
- solver now excludes removal of packages flagged as vital or locked from
  removal
modified external/libder/README.md
@@ -8,6 +8,11 @@ re-encode the resulting tree would apply any normalization expected by a DER
decoder.  The author's use is primarily to decode/encode ECC keys for
interoperability with OpenSSL.

+
The authoritative source for this software is located at
+
https://git.kevans.dev/kevans/libder, but it's additionally mirrored to
+
[GitHub](https://github.com/kevans91/libder) for user-facing interactions.
+
Pull requests and issues are open on GitHub.
+

## What is libder not?

libder is not intended to be a general-purpose library for working with DER/BER
modified external/libder/libder/libder_obj.c
@@ -74,7 +74,11 @@ libder_obj_alloc(struct libder_ctx *ctx, struct libder_tag *type,

	obj = libder_obj_alloc_internal(ctx, type, payload, length, 0);
	if (obj == NULL) {
-
		free(payload);
+
		if (length != 0) {
+
			libder_bzero(payload, length);
+
			free(payload);
+
		}
+

		libder_set_error(ctx, LDE_NOMEM);
	}

@@ -102,7 +106,11 @@ libder_obj_alloc_simple(struct libder_ctx *ctx, uint8_t stype,

	obj = libder_obj_alloc_internal(ctx, type, payload, length, LDO_OWNTAG);
	if (obj == NULL) {
-
		free(payload);
+
		if (length != 0) {
+
			libder_bzero(payload, length);
+
			free(payload);
+
		}
+

		libder_type_free(type);
		libder_set_error(ctx, LDE_NOMEM);
	}
@@ -241,7 +249,11 @@ libder_obj_free(struct libder_object *obj)
	DER_FOREACH_CHILD_SAFE(child, obj, tmp)
		libder_obj_free(child);

-
	free(obj->payload);
+
	if (obj->payload != NULL) {
+
		libder_bzero(obj->payload, obj->length);
+
		free(obj->payload);
+
	}
+

	libder_type_free(obj->type);
	free(obj);
}
@@ -817,12 +829,20 @@ violated:
	obj->children = NULL;

	if (strict_violation) {
-
		free(coalesced_data);
+
		if (coalesced_data != NULL) {
+
			libder_bzero(coalesced_data, offset);
+
			free(coalesced_data);
+
		}
+

		return (false);
	}

	/* Finally, swap out the payload. */
-
	free(obj->payload);
+
	if (obj->payload != NULL) {
+
		libder_bzero(obj->payload, obj->length);
+
		free(obj->payload);
+
	}
+

	obj->length = offset;
	obj->payload = coalesced_data;
	obj->type->tag_constructed = false;
modified external/libder/libder/libder_private.h
@@ -11,7 +11,12 @@
#include <assert.h>
#include <signal.h>
#include <stdbool.h>
-

+
#ifdef __APPLE__
+
#define	__STDC_WANT_LIB_EXT1__	1
+
#include <string.h>	/* memset_s */
+
#else
+
#include <strings.h>	/* explicit_bzero */
+
#endif
#include "libder.h"

/* FreeBSD's sys/cdefs.h */
@@ -139,6 +144,17 @@ libder_type_simple(const struct libder_tag *type)
	return (encoded);
}

+
static inline void
+
libder_bzero(uint8_t *buf, size_t bufsz)
+
{
+

+
#ifdef __APPLE__
+
	memset_s(buf, bufsz, 0, bufsz);
+
#else
+
	explicit_bzero(buf, bufsz);
+
#endif
+
}
+

size_t	 libder_get_buffer_size(struct libder_ctx *);
void	 libder_set_error(struct libder_ctx *, int, const char *, int);

modified external/libder/libder/libder_read.c
@@ -83,7 +83,10 @@ payload_free(struct libder_payload *payload)
	if (!payload->payload_heap)
		return;

-
	free(payload->payload_data);
+
	if (payload->payload_data != NULL) {
+
		libder_bzero(payload->payload_data, payload->payload_size);
+
		free(payload->payload_data);
+
	}

	payload->payload_heap = false;
	payload->payload_data = NULL;
@@ -126,7 +129,10 @@ libder_stream_init(struct libder_ctx *ctx, struct libder_stream *stream)
static void
libder_stream_free(struct libder_stream *stream)
{
-
	free(stream->stream_buf);
+
	if (stream->stream_buf != NULL) {
+
		libder_bzero(stream->stream_buf, stream->stream_bufsz);
+
		free(stream->stream_buf);
+
	}
}

static void
@@ -325,6 +331,36 @@ libder_stream_refill(struct libder_stream *stream, size_t req)
	return (&stream->stream_buf[offset]);
}

+
/*
+
 * We can't just use realloc() because it won't provide any guarantees about
+
 * the previous region if it can't just resize in-place, so we'll always just
+
 * allocate a new one and copy ourselves.
+
 */
+
static uint8_t *
+
libder_read_realloc(uint8_t *ptr, size_t oldsz, size_t newsz)
+
{
+
	uint8_t *newbuf;
+

+
	if (oldsz == 0)
+
		assert(ptr == NULL);
+
	else
+
		assert(ptr != NULL);
+
	assert(newsz > oldsz);
+

+
	newbuf = malloc(newsz);
+
	if (newbuf == NULL)
+
		return (NULL);
+

+
	if (oldsz != 0) {
+
		memcpy(newbuf, ptr, oldsz);
+

+
		libder_bzero(ptr, oldsz);
+
		free(ptr);
+
	}
+

+
	return (newbuf);
+
}
+

#define	BER_TYPE_LONG_BATCH	0x04

static bool
@@ -487,6 +523,13 @@ der_read_structure(struct libder_ctx *ctx, struct libder_stream *stream,
		} else {
			uint8_t *payload_data;

+
			/*
+
			 * We play it conservative here: we could allocate the
+
			 * buffer up-front, but we have no idea how much data we
+
			 * actually have to receive!  The length is a potentially
+
			 * attacker-controlled aspect, so we're cautiously optimistic
+
			 * that it's accurate.
+
			 */
			payload_data = NULL;

			offset = 0;
@@ -497,14 +540,17 @@ der_read_structure(struct libder_ctx *ctx, struct libder_stream *stream,

				req = MIN(stream->stream_bufsz, resid);
				if ((buf = libder_stream_refill(stream, req)) == NULL) {
+
					libder_bzero(payload_data, offset);
					free(payload_data);

					libder_set_error(ctx, LDE_SHORTDATA);
					goto failed;
				}

-
				next_data = realloc(payload_data, offset + req);
+
				next_data = libder_read_realloc(payload_data,
+
				    offset, offset + req);
				if (next_data == NULL) {
+
					libder_bzero(payload_data, offset);
					free(payload_data);

					libder_set_error(ctx, LDE_NOMEM);
modified external/libder/libder/libder_write.c
@@ -201,7 +201,7 @@ libder_write(struct libder_ctx *ctx, struct libder_object *root, uint8_t *buf,
	/* Allocate if we weren't passed a buffer. */
	if (*bufsz == 0) {
		*bufsz = needed;
-
		buf = malloc(needed);
+
		buf = malloc(needed + 1);
		if (buf == NULL)
			return (NULL);
	} else if (needed > *bufsz) {
@@ -213,6 +213,7 @@ libder_write(struct libder_ctx *ctx, struct libder_object *root, uint8_t *buf,
	mwrite.buf = buf;
	mwrite.bufsz = *bufsz;
	if (!libder_write_object(ctx, root, &memory_write, &mwrite)) {
+
		libder_bzero(mwrite.buf, mwrite.offset);
		free(buf);
		return (NULL);	/* XXX Error */
	}
modified external/sqlite/shell.c
@@ -580,6 +580,9 @@ zSkipValidUtf8(const char *z, int nAccept, long ccm);
#ifndef HAVE_CONSOLE_IO_H
# include "console_io.h"
#endif
+
#if defined(_MSC_VER)
+
# pragma warning(disable : 4204)
+
#endif

#ifndef SQLITE_CIO_NO_TRANSLATE
# if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT
@@ -678,6 +681,10 @@ static short streamOfConsole(FILE *pf, /* out */ PerStreamTags *ppst){
# endif
}

+
# ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+
#  define ENABLE_VIRTUAL_TERMINAL_PROCESSING  (0x4)
+
# endif
+

# if CIO_WIN_WC_XLATE
/* Define console modes for use with the Windows Console API. */
#  define SHELL_CONI_MODE \
@@ -1228,6 +1235,10 @@ SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn){
}
#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */

+
#if defined(_MSC_VER)
+
# pragma warning(default : 4204)
+
#endif
+

#undef SHELL_INVALID_FILE_PTR

/************************* End ../ext/consio/console_io.c ********************/
@@ -20619,6 +20630,7 @@ static void exec_prepared_stmt_columnar(
  rc = sqlite3_step(pStmt);
  if( rc!=SQLITE_ROW ) return;
  nColumn = sqlite3_column_count(pStmt);
+
  if( nColumn==0 ) goto columnar_end;
  nAlloc = nColumn*4;
  if( nAlloc<=0 ) nAlloc = 1;
  azData = sqlite3_malloc64( nAlloc*sizeof(char*) );
@@ -20704,7 +20716,6 @@ static void exec_prepared_stmt_columnar(
    if( n>p->actualWidth[j] ) p->actualWidth[j] = n;
  }
  if( seenInterrupt ) goto columnar_end;
-
  if( nColumn==0 ) goto columnar_end;
  switch( p->cMode ){
    case MODE_Column: {
      colSep = "  ";
@@ -25553,16 +25564,15 @@ static int do_meta_command(char *zLine, ShellState *p){
#ifndef SQLITE_SHELL_FIDDLE
  if( c=='i' && cli_strncmp(azArg[0], "import", n)==0 ){
    char *zTable = 0;           /* Insert data into this table */
-
    char *zSchema = 0;          /* within this schema (may default to "main") */
+
    char *zSchema = 0;          /* Schema of zTable */
    char *zFile = 0;            /* Name of file to extra content from */
    sqlite3_stmt *pStmt = NULL; /* A statement */
    int nCol;                   /* Number of columns in the table */
-
    int nByte;                  /* Number of bytes in an SQL string */
+
    i64 nByte;                  /* Number of bytes in an SQL string */
    int i, j;                   /* Loop counters */
    int needCommit;             /* True to COMMIT or ROLLBACK at end */
    int nSep;                   /* Number of bytes in p->colSeparator[] */
-
    char *zSql;                 /* An SQL statement */
-
    char *zFullTabName;         /* Table name with schema if applicable */
+
    char *zSql = 0;             /* An SQL statement */
    ImportCtx sCtx;             /* Reader context */
    char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */
    int eVerbose = 0;           /* Larger for more console output */
@@ -25696,24 +25706,14 @@ static int do_meta_command(char *zLine, ShellState *p){
    while( (nSkip--)>0 ){
      while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){}
    }
-
    if( zSchema!=0 ){
-
      zFullTabName = sqlite3_mprintf("\"%w\".\"%w\"", zSchema, zTable);
-
    }else{
-
      zFullTabName = sqlite3_mprintf("\"%w\"", zTable);
-
    }
-
    zSql = sqlite3_mprintf("SELECT * FROM %s", zFullTabName);
-
    if( zSql==0 || zFullTabName==0 ){
-
      import_cleanup(&sCtx);
-
      shell_out_of_memory();
-
    }
-
    nByte = strlen30(zSql);
-
    rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
    import_append_char(&sCtx, 0);    /* To ensure sCtx.z is allocated */
-
    if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(p->db))==0 ){
+
    if( sqlite3_table_column_metadata(p->db, zSchema, zTable,0,0,0,0,0,0) ){
+
      /* Table does not exist.  Create it. */
      sqlite3 *dbCols = 0;
      char *zRenames = 0;
      char *zColDefs;
-
      zCreate = sqlite3_mprintf("CREATE TABLE %s", zFullTabName);
+
      zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"", 
+
                    zSchema ? zSchema : "main", zTable);
      while( xRead(&sCtx) ){
        zAutoColumn(sCtx.z, &dbCols, 0);
        if( sCtx.cTerm!=sCtx.cColSep ) break;
@@ -25728,34 +25728,50 @@ static int do_meta_command(char *zLine, ShellState *p){
      assert(dbCols==0);
      if( zColDefs==0 ){
        eputf("%s: empty file\n", sCtx.zFile);
-
      import_fail:
-
        sqlite3_free(zCreate);
-
        sqlite3_free(zSql);
-
        sqlite3_free(zFullTabName);
        import_cleanup(&sCtx);
        rc = 1;
        goto meta_command_exit;
      }
      zCreate = sqlite3_mprintf("%z%z\n", zCreate, zColDefs);
+
      if( zCreate==0 ){
+
        import_cleanup(&sCtx);
+
        shell_out_of_memory();
+
      }
      if( eVerbose>=1 ){
        oputf("%s\n", zCreate);
      }
      rc = sqlite3_exec(p->db, zCreate, 0, 0, 0);
+
      sqlite3_free(zCreate);
+
      zCreate = 0;
      if( rc ){
        eputf("%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db));
-
        goto import_fail;
+
        import_cleanup(&sCtx);
+
        rc = 1;
+
        goto meta_command_exit;
      }
-
      sqlite3_free(zCreate);
-
      zCreate = 0;
-
      rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
    }
+
    zSql = sqlite3_mprintf("SELECT count(*) FROM pragma_table_info(%Q,%Q);",
+
                           zTable, zSchema);
+
    if( zSql==0 ){
+
      import_cleanup(&sCtx);
+
      shell_out_of_memory();
+
    }
+
    nByte = strlen(zSql);    
+
    rc =  sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+
    sqlite3_free(zSql);
+
    zSql = 0;
    if( rc ){
      if (pStmt) sqlite3_finalize(pStmt);
      eputf("Error: %s\n", sqlite3_errmsg(p->db));
-
      goto import_fail;
+
      import_cleanup(&sCtx);
+
      rc = 1;
+
      goto meta_command_exit;
+
    }
+
    if( sqlite3_step(pStmt)==SQLITE_ROW ){
+
      nCol = sqlite3_column_int(pStmt, 0);
+
    }else{
+
      nCol = 0;
    }
-
    sqlite3_free(zSql);
-
    nCol = sqlite3_column_count(pStmt);
    sqlite3_finalize(pStmt);
    pStmt = 0;
    if( nCol==0 ) return 0; /* no columns, no error */
@@ -25764,7 +25780,12 @@ static int do_meta_command(char *zLine, ShellState *p){
      import_cleanup(&sCtx);
      shell_out_of_memory();
    }
-
    sqlite3_snprintf(nByte+20, zSql, "INSERT INTO %s VALUES(?", zFullTabName);
+
    if( zSchema ){
+
      sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\".\"%w\" VALUES(?", 
+
                       zSchema, zTable);
+
    }else{
+
      sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable);
+
    }
    j = strlen30(zSql);
    for(i=1; i<nCol; i++){
      zSql[j++] = ',';
@@ -25776,13 +25797,15 @@ static int do_meta_command(char *zLine, ShellState *p){
      oputf("Insert using: %s\n", zSql);
    }
    rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+
    sqlite3_free(zSql);
+
    zSql = 0;
    if( rc ){
      eputf("Error: %s\n", sqlite3_errmsg(p->db));
      if (pStmt) sqlite3_finalize(pStmt);
-
      goto import_fail;
+
      import_cleanup(&sCtx);
+
      rc = 1;
+
      goto meta_command_exit;
    }
-
    sqlite3_free(zSql);
-
    sqlite3_free(zFullTabName);
    needCommit = sqlite3_get_autocommit(p->db);
    if( needCommit ) sqlite3_exec(p->db, "BEGIN", 0, 0, 0);
    do{
modified external/sqlite/sqlite3.c
@@ -1,6 +1,6 @@
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
-
** version 3.45.0.  By combining all the individual C code files into this
+
** version 3.45.2.  By combining all the individual C code files into this
** single large file, the entire code can be compiled as a single translation
** unit.  This allows many compilers to do optimizations that would not be
** possible if the files were compiled separately.  Performance improvements
@@ -18,7 +18,7 @@
** separate file. This file contains only code for the core SQLite library.
**
** The content in this amalgamation comes from Fossil check-in
-
** 1066602b2b1976fe58b5150777cced894af1.
+
** d8cd6d49b46a395b13955387d05e9e1a2a47.
*/
#define SQLITE_CORE 1
#define SQLITE_AMALGAMATION 1
@@ -459,9 +459,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-
#define SQLITE_VERSION        "3.45.0"
-
#define SQLITE_VERSION_NUMBER 3045000
-
#define SQLITE_SOURCE_ID      "2024-01-15 17:01:13 1066602b2b1976fe58b5150777cced894af17c803e068f5918390d6915b46e1d"
+
#define SQLITE_VERSION        "3.45.2"
+
#define SQLITE_VERSION_NUMBER 3045002
+
#define SQLITE_SOURCE_ID      "2024-03-12 11:06:23 d8cd6d49b46a395b13955387d05e9e1a2a47e54fb99f3c9b59835bbefad6af77"

/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -733,6 +733,8 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
**      the 1st parameter to sqlite3_exec() while sqlite3_exec() is running.
** <li> The application must not modify the SQL statement text passed into
**      the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running.
+
** <li> The application must not dereference the arrays or string pointers
+
**       passed as the 3rd and 4th callback parameters after it returns.
** </ul>
*/
SQLITE_API int sqlite3_exec(
@@ -15097,6 +15099,7 @@ SQLITE_PRIVATE u32 sqlite3TreeTrace;
**   0x00010000     Beginning of DELETE/INSERT/UPDATE processing
**   0x00020000     Transform DISTINCT into GROUP BY
**   0x00040000     SELECT tree dump after all code has been generated
+
**   0x00080000     NOT NULL strength reduction
*/

/*
@@ -19346,6 +19349,7 @@ struct NameContext {
#define NC_InAggFunc 0x020000 /* True if analyzing arguments to an agg func */
#define NC_FromDDL   0x040000 /* SQL text comes from sqlite_schema */
#define NC_NoSelect  0x080000 /* Do not descend into sub-selects */
+
#define NC_Where     0x100000 /* Processing WHERE clause of a SELECT */
#define NC_OrderAgg 0x8000000 /* Has an aggregate other than count/min/max */

/*
@@ -19369,6 +19373,7 @@ struct Upsert {
  Expr *pUpsertWhere;       /* WHERE clause for the ON CONFLICT UPDATE */
  Upsert *pNextUpsert;      /* Next ON CONFLICT clause in the list */
  u8 isDoUpdate;            /* True for DO UPDATE.  False for DO NOTHING */
+
  u8 isDup;                 /* True if 2nd or later with same pUpsertIdx */
  /* Above this point is the parse tree for the ON CONFLICT clauses.
  ** The next group of fields stores intermediate data. */
  void *pToFree;            /* Free memory when deleting the Upsert object */
@@ -21444,7 +21449,7 @@ SQLITE_PRIVATE With *sqlite3WithPush(Parse*, With*, u8);
SQLITE_PRIVATE   Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*,Upsert*);
SQLITE_PRIVATE   void sqlite3UpsertDelete(sqlite3*,Upsert*);
SQLITE_PRIVATE   Upsert *sqlite3UpsertDup(sqlite3*,Upsert*);
-
SQLITE_PRIVATE   int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*);
+
SQLITE_PRIVATE   int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*,Upsert*);
SQLITE_PRIVATE   void sqlite3UpsertDoUpdate(Parse*,Upsert*,Table*,Index*,int);
SQLITE_PRIVATE   Upsert *sqlite3UpsertOfIndex(Upsert*,Index*);
SQLITE_PRIVATE   int sqlite3UpsertNextIsIPK(Upsert*);
@@ -31309,6 +31314,7 @@ SQLITE_API void sqlite3_str_vappendf(
        if( xtype==etFLOAT ){
          iRound = -precision;
        }else if( xtype==etGENERIC ){
+
          if( precision==0 ) precision = 1;
          iRound = precision;
        }else{
          iRound = precision+1;
@@ -35199,6 +35205,9 @@ do_atof_calc:
    u64 s2;
    rr[0] = (double)s;
    s2 = (u64)rr[0];
+
#if defined(_MSC_VER) && _MSC_VER<1700
+
    if( s2==0x8000000000000000LL ){ s2 = 2*(u64)(0.5*rr[0]); }
+
#endif
    rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s);
    if( e>0 ){
      while( e>=100  ){
@@ -35641,7 +35650,7 @@ SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRou
  assert( p->n>0 );
  assert( p->n<sizeof(p->zBuf) );
  p->iDP = p->n + exp;
-
  if( iRound<0 ){
+
  if( iRound<=0 ){
    iRound = p->iDP - iRound;
    if( iRound==0 && p->zBuf[i+1]>='5' ){
      iRound = 1;
@@ -43408,11 +43417,16 @@ static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){

#if SQLITE_MAX_MMAP_SIZE>0
  if( pFd->mmapSizeMax>0 ){
+
    /* Ensure that there is always at least a 256 byte buffer of addressable
+
    ** memory following the returned page. If the database is corrupt,
+
    ** SQLite may overread the page slightly (in practice only a few bytes,
+
    ** but 256 is safe, round, number).  */
+
    const int nEofBuffer = 256;
    if( pFd->pMapRegion==0 ){
      int rc = unixMapfile(pFd, -1);
      if( rc!=SQLITE_OK ) return rc;
    }
-
    if( pFd->mmapSize >= iOff+nAmt ){
+
    if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){
      *pp = &((u8 *)pFd->pMapRegion)[iOff];
      pFd->nFetchOut++;
    }
@@ -50765,6 +50779,11 @@ static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){

#if SQLITE_MAX_MMAP_SIZE>0
  if( pFd->mmapSizeMax>0 ){
+
    /* Ensure that there is always at least a 256 byte buffer of addressable
+
    ** memory following the returned page. If the database is corrupt,
+
    ** SQLite may overread the page slightly (in practice only a few bytes,
+
    ** but 256 is safe, round, number).  */
+
    const int nEofBuffer = 256;
    if( pFd->pMapRegion==0 ){
      int rc = winMapfile(pFd, -1);
      if( rc!=SQLITE_OK ){
@@ -50773,7 +50792,7 @@ static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
        return rc;
      }
    }
-
    if( pFd->mmapSize >= iOff+nAmt ){
+
    if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){
      assert( pFd->pMapRegion!=0 );
      *pp = &((u8 *)pFd->pMapRegion)[iOff];
      pFd->nFetchOut++;
@@ -53252,6 +53271,14 @@ SQLITE_API unsigned char *sqlite3_serialize(
    pOut = 0;
  }else{
    sz = sqlite3_column_int64(pStmt, 0)*szPage;
+
    if( sz==0 ){
+
      sqlite3_reset(pStmt);
+
      sqlite3_exec(db, "BEGIN IMMEDIATE; COMMIT;", 0, 0, 0);
+
      rc = sqlite3_step(pStmt);
+
      if( rc==SQLITE_ROW ){
+
        sz = sqlite3_column_int64(pStmt, 0)*szPage;
+
      }
+
    }
    if( piSize ) *piSize = sz;
    if( mFlags & SQLITE_SERIALIZE_NOCOPY ){
      pOut = 0;
@@ -76402,7 +76429,10 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur){
  }

  pPage = pCur->pPage;
-
  assert( pPage->isInit );
+
  if( sqlite3FaultSim(412) ) pPage->isInit = 0;
+
  if( !pPage->isInit ){
+
    return SQLITE_CORRUPT_BKPT;
+
  }
  if( !pPage->leaf ){
    int idx = pCur->ix;
    rc = moveToChild(pCur, get4byte(findCell(pPage, idx)));
@@ -77075,7 +77105,10 @@ static int fillInCell(
    n = nHeader + nPayload;
    testcase( n==3 );
    testcase( n==4 );
-
    if( n<4 ) n = 4;
+
    if( n<4 ){
+
      n = 4;
+
      pPayload[nPayload] = 0;
+
    }
    *pnSize = n;
    assert( nSrc<=nPayload );
    testcase( nSrc<nPayload );
@@ -79521,7 +79554,10 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
  if( flags & BTREE_PREFORMAT ){
    rc = SQLITE_OK;
    szNew = p->pBt->nPreformatSize;
-
    if( szNew<4 ) szNew = 4;
+
    if( szNew<4 ){
+
      szNew = 4;
+
      newCell[3] = 0;
+
    }
    if( ISAUTOVACUUM(p->pBt) && szNew>pPage->maxLocal ){
      CellInfo info;
      pPage->xParseCell(pPage, newCell, &info);
@@ -88366,6 +88402,23 @@ static void serialGet(
    pMem->flags = IsNaN(x) ? MEM_Null : MEM_Real;
  }
}
+
static int serialGet7(
+
  const unsigned char *buf,     /* Buffer to deserialize from */
+
  Mem *pMem                     /* Memory cell to write value into */
+
){
+
  u64 x = FOUR_BYTE_UINT(buf);
+
  u32 y = FOUR_BYTE_UINT(buf+4);
+
  x = (x<<32) + y;
+
  assert( sizeof(x)==8 && sizeof(pMem->u.r)==8 );
+
  swapMixedEndianFloat(x);
+
  memcpy(&pMem->u.r, &x, sizeof(x));
+
  if( IsNaN(x) ){
+
    pMem->flags = MEM_Null;
+
    return 1;
+
  }
+
  pMem->flags = MEM_Real;
+
  return 0;
+
}
SQLITE_PRIVATE void sqlite3VdbeSerialGet(
  const unsigned char *buf,     /* Buffer to deserialize from */
  u32 serial_type,              /* Serial type to deserialize */
@@ -89045,7 +89098,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
      }else if( serial_type==0 ){
        rc = -1;
      }else if( serial_type==7 ){
-
        sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1);
+
        serialGet7(&aKey1[d1], &mem1);
        rc = -sqlite3IntFloatCompare(pRhs->u.i, mem1.u.r);
      }else{
        i64 lhs = vdbeRecordDecodeInt(serial_type, &aKey1[d1]);
@@ -89070,14 +89123,18 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
      }else if( serial_type==0 ){
        rc = -1;
      }else{
-
        sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1);
        if( serial_type==7 ){
-
          if( mem1.u.r<pRhs->u.r ){
+
          if( serialGet7(&aKey1[d1], &mem1) ){
+
            rc = -1;  /* mem1 is a NaN */
+
          }else if( mem1.u.r<pRhs->u.r ){
            rc = -1;
          }else if( mem1.u.r>pRhs->u.r ){
            rc = +1;
+
          }else{
+
            assert( rc==0 );
          }
        }else{
+
          sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1);
          rc = sqlite3IntFloatCompare(mem1.u.i, pRhs->u.r);
        }
      }
@@ -89147,7 +89204,14 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
    /* RHS is null */
    else{
      serial_type = aKey1[idx1];
-
      rc = (serial_type!=0 && serial_type!=10);
+
      if( serial_type==0
+
       || serial_type==10
+
       || (serial_type==7 && serialGet7(&aKey1[d1], &mem1)!=0)
+
      ){
+
        assert( rc==0 );
+
      }else{
+
        rc = 1;
+
      }
    }

    if( rc!=0 ){
@@ -94845,7 +94909,9 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
        }
      }
    }else if( affinity==SQLITE_AFF_TEXT && ((flags1 | flags3) & MEM_Str)!=0 ){
-
      if( (flags1 & MEM_Str)==0 && (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
+
      if( (flags1 & MEM_Str)!=0 ){
+
        pIn1->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal);
+
      }else if( (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
        testcase( pIn1->flags & MEM_Int );
        testcase( pIn1->flags & MEM_Real );
        testcase( pIn1->flags & MEM_IntReal );
@@ -94854,7 +94920,9 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
        flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask);
        if( NEVER(pIn1==pIn3) ) flags3 = flags1 | MEM_Str;
      }
-
      if( (flags3 & MEM_Str)==0 && (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
+
      if( (flags3 & MEM_Str)!=0 ){
+
        pIn3->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal);
+
      }else if( (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
        testcase( pIn3->flags & MEM_Int );
        testcase( pIn3->flags & MEM_Real );
        testcase( pIn3->flags & MEM_IntReal );
@@ -106199,6 +106267,8 @@ static void resolveAlias(
  assert( iCol>=0 && iCol<pEList->nExpr );
  pOrig = pEList->a[iCol].pExpr;
  assert( pOrig!=0 );
+
  assert( !ExprHasProperty(pExpr, EP_Reduced|EP_TokenOnly) );
+
  if( pExpr->pAggInfo ) return;
  db = pParse->db;
  pDup = sqlite3ExprDup(db, pOrig, 0);
  if( db->mallocFailed ){
@@ -107084,6 +107154,19 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
    ** resolved.  This prevents "column" from being counted as having been
    ** referenced, which might prevent a SELECT from being erroneously
    ** marked as correlated.
+
    **
+
    ** 2024-03-28: Beware of aggregates.  A bare column of aggregated table
+
    ** can still evaluate to NULL even though it is marked as NOT NULL.
+
    ** Example:
+
    **
+
    **       CREATE TABLE t1(a INT NOT NULL);
+
    **       SELECT a, a IS NULL, a IS NOT NULL, count(*) FROM t1;
+
    **
+
    ** The "a IS NULL" and "a IS NOT NULL" expressions cannot be optimized
+
    ** here because at the time this case is hit, we do not yet know whether
+
    ** or not t1 is being aggregated.  We have to assume the worst and omit
+
    ** the optimization.  The only time it is safe to apply this optimization
+
    ** is within the WHERE clause.
    */
    case TK_NOTNULL:
    case TK_ISNULL: {
@@ -107094,19 +107177,36 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
        anRef[i] = p->nRef;
      }
      sqlite3WalkExpr(pWalker, pExpr->pLeft);
-
      if( 0==sqlite3ExprCanBeNull(pExpr->pLeft) && !IN_RENAME_OBJECT ){
-
        testcase( ExprHasProperty(pExpr, EP_OuterON) );
-
        assert( !ExprHasProperty(pExpr, EP_IntValue) );
-
        pExpr->u.iValue = (pExpr->op==TK_NOTNULL);
-
        pExpr->flags |= EP_IntValue;
-
        pExpr->op = TK_INTEGER;
+
      if( IN_RENAME_OBJECT ) return WRC_Prune;
+
      if( sqlite3ExprCanBeNull(pExpr->pLeft) ){
+
        /* The expression can be NULL.  So the optimization does not apply */
+
        return WRC_Prune;
+
      }

-
        for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){
-
          p->nRef = anRef[i];
+
      for(i=0, p=pNC; p; p=p->pNext, i++){
+
        if( (p->ncFlags & NC_Where)==0 ){
+
          return WRC_Prune;  /* Not in a WHERE clause.  Unsafe to optimize. */
        }
-
        sqlite3ExprDelete(pParse->db, pExpr->pLeft);
-
        pExpr->pLeft = 0;
      }
+
      testcase( ExprHasProperty(pExpr, EP_OuterON) );
+
      assert( !ExprHasProperty(pExpr, EP_IntValue) );
+
#if TREETRACE_ENABLED
+
      if( sqlite3TreeTrace & 0x80000 ){
+
        sqlite3DebugPrintf(
+
           "NOT NULL strength reduction converts the following to %d:\n",
+
           pExpr->op==TK_NOTNULL
+
        );
+
        sqlite3ShowExpr(pExpr);
+
      }
+
#endif /* TREETRACE_ENABLED */
+
      pExpr->u.iValue = (pExpr->op==TK_NOTNULL);
+
      pExpr->flags |= EP_IntValue;
+
      pExpr->op = TK_INTEGER;
+
      for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){
+
        p->nRef = anRef[i];
+
      }
+
      sqlite3ExprDelete(pParse->db, pExpr->pLeft);
+
      pExpr->pLeft = 0;
      return WRC_Prune;
    }

@@ -108006,7 +108106,9 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
      }
      if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort;
    }
+
    sNC.ncFlags |= NC_Where;
    if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort;
+
    sNC.ncFlags &= ~NC_Where;

    /* Resolve names in table-valued-function arguments */
    for(i=0; i<p->pSrc->nSrc; i++){
@@ -128934,13 +129036,13 @@ SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue){
      double r1, r2;
      const char *zVal;
      r1 = sqlite3_value_double(pValue);
-
      sqlite3_str_appendf(pStr, "%!.15g", r1);
+
      sqlite3_str_appendf(pStr, "%!0.15g", r1);
      zVal = sqlite3_str_value(pStr);
      if( zVal ){
        sqlite3AtoF(zVal, &r2, pStr->nChar, SQLITE_UTF8);
        if( r1!=r2 ){
          sqlite3_str_reset(pStr);
-
          sqlite3_str_appendf(pStr, "%!.20e", r1);
+
          sqlite3_str_appendf(pStr, "%!0.20e", r1);
        }
      }
      break;
@@ -129242,7 +129344,7 @@ static void replaceFunc(
  }
  if( zPattern[0]==0 ){
    assert( sqlite3_value_type(argv[1])!=SQLITE_NULL );
-
    sqlite3_result_value(context, argv[0]);
+
    sqlite3_result_text(context, (const char*)zStr, nStr, SQLITE_TRANSIENT);
    return;
  }
  nPattern = sqlite3_value_bytes(argv[1]);
@@ -133162,7 +133264,7 @@ SQLITE_PRIVATE void sqlite3Insert(
      pNx->iDataCur = iDataCur;
      pNx->iIdxCur = iIdxCur;
      if( pNx->pUpsertTarget ){
-
        if( sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx) ){
+
        if( sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx, pUpsert) ){
          goto insert_cleanup;
        }
      }
@@ -139461,31 +139563,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
        int mxCol;              /* Maximum non-virtual column number */

        if( pObjTab && pObjTab!=pTab ) continue;
-
        if( !IsOrdinaryTable(pTab) ){
-
#ifndef SQLITE_OMIT_VIRTUALTABLE
-
          sqlite3_vtab *pVTab;
-
          int a1;
-
          if( !IsVirtual(pTab) ) continue;
-
          if( pTab->nCol<=0 ){
-
            const char *zMod = pTab->u.vtab.azArg[0];
-
            if( sqlite3HashFind(&db->aModule, zMod)==0 ) continue;
-
          }
-
          sqlite3ViewGetColumnNames(pParse, pTab);
-
          if( pTab->u.vtab.p==0 ) continue;
-
          pVTab = pTab->u.vtab.p->pVtab;
-
          if( NEVER(pVTab==0) ) continue;
-
          if( NEVER(pVTab->pModule==0) ) continue;
-
          if( pVTab->pModule->iVersion<4 ) continue;
-
          if( pVTab->pModule->xIntegrity==0 ) continue;
-
          sqlite3VdbeAddOp3(v, OP_VCheck, i, 3, isQuick);
-
          pTab->nTabRef++;
-
          sqlite3VdbeAppendP4(v, pTab, P4_TABLEREF);
-
          a1 = sqlite3VdbeAddOp1(v, OP_IsNull, 3); VdbeCoverage(v);
-
          integrityCheckResultRow(v);
-
          sqlite3VdbeJumpHere(v, a1);
-
#endif
-
          continue;
-
        }
+
        if( !IsOrdinaryTable(pTab) ) continue;
        if( isQuick || HasRowid(pTab) ){
          pPk = 0;
          r2 = 0;
@@ -139620,6 +139698,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
              ** is REAL, we have to load the actual data using OP_Column
              ** to reliably determine if the value is a NULL. */
              sqlite3VdbeAddOp3(v, OP_Column, p1, p3, 3);
+
              sqlite3ColumnDefault(v, pTab, j, 3);
              jmp3 = sqlite3VdbeAddOp2(v, OP_NotNull, 3, labelOk);
              VdbeCoverage(v);
            }
@@ -139810,6 +139889,38 @@ SQLITE_PRIVATE void sqlite3Pragma(
          }
        }
      }
+

+
#ifndef SQLITE_OMIT_VIRTUALTABLE
+
      /* Second pass to invoke the xIntegrity method on all virtual
+
      ** tables.
+
      */
+
      for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
+
        Table *pTab = sqliteHashData(x);
+
        sqlite3_vtab *pVTab;
+
        int a1;
+
        if( pObjTab && pObjTab!=pTab ) continue;
+
        if( IsOrdinaryTable(pTab) ) continue;
+
        if( !IsVirtual(pTab) ) continue;
+
        if( pTab->nCol<=0 ){
+
          const char *zMod = pTab->u.vtab.azArg[0];
+
          if( sqlite3HashFind(&db->aModule, zMod)==0 ) continue;
+
        }
+
        sqlite3ViewGetColumnNames(pParse, pTab);
+
        if( pTab->u.vtab.p==0 ) continue;
+
        pVTab = pTab->u.vtab.p->pVtab;
+
        if( NEVER(pVTab==0) ) continue;
+
        if( NEVER(pVTab->pModule==0) ) continue;
+
        if( pVTab->pModule->iVersion<4 ) continue;
+
        if( pVTab->pModule->xIntegrity==0 ) continue;
+
        sqlite3VdbeAddOp3(v, OP_VCheck, i, 3, isQuick);
+
        pTab->nTabRef++;
+
        sqlite3VdbeAppendP4(v, pTab, P4_TABLEREF);
+
        a1 = sqlite3VdbeAddOp1(v, OP_IsNull, 3); VdbeCoverage(v);
+
        integrityCheckResultRow(v);
+
        sqlite3VdbeJumpHere(v, a1);
+
        continue;
+
      }
+
#endif
    }
    {
      static const int iLn = VDBE_OFFSET_LINENO(2);
@@ -153447,7 +153558,8 @@ SQLITE_PRIVATE Upsert *sqlite3UpsertNew(
SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(
  Parse *pParse,     /* The parsing context */
  SrcList *pTabList, /* Table into which we are inserting */
-
  Upsert *pUpsert    /* The ON CONFLICT clauses */
+
  Upsert *pUpsert,   /* The ON CONFLICT clauses */
+
  Upsert *pAll       /* Complete list of all ON CONFLICT clauses */
){
  Table *pTab;            /* That table into which we are inserting */
  int rc;                 /* Result code */
@@ -153550,6 +153662,14 @@ SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(
        continue;
      }
      pUpsert->pUpsertIdx = pIdx;
+
      if( sqlite3UpsertOfIndex(pAll,pIdx)!=pUpsert ){
+
        /* Really this should be an error.  The isDup ON CONFLICT clause will
+
        ** never fire.  But this problem was not discovered until three years
+
        ** after multi-CONFLICT upsert was added, and so we silently ignore
+
        ** the problem to prevent breaking applications that might actually
+
        ** have redundant ON CONFLICT clauses. */
+
        pUpsert->isDup = 1;
+
      }
      break;
    }
    if( pUpsert->pUpsertIdx==0 ){
@@ -153576,9 +153696,13 @@ SQLITE_PRIVATE int sqlite3UpsertNextIsIPK(Upsert *pUpsert){
  Upsert *pNext;
  if( NEVER(pUpsert==0) ) return 0;
  pNext = pUpsert->pNextUpsert;
-
  if( pNext==0 ) return 1;
-
  if( pNext->pUpsertTarget==0 ) return 1;
-
  if( pNext->pUpsertIdx==0 ) return 1;
+
  while( 1 /*exit-by-return*/ ){
+
    if( pNext==0 ) return 1;
+
    if( pNext->pUpsertTarget==0 ) return 1;
+
    if( pNext->pUpsertIdx==0 ) return 1;
+
    if( !pNext->isDup ) return 0;
+
    pNext = pNext->pNextUpsert;
+
  }
  return 0;
}

@@ -166812,7 +166936,10 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(

  /* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */
  testcase( pOrderBy && pOrderBy->nExpr==BMS-1 );
-
  if( pOrderBy && pOrderBy->nExpr>=BMS ) pOrderBy = 0;
+
  if( pOrderBy && pOrderBy->nExpr>=BMS ){
+
    pOrderBy = 0;
+
    wctrlFlags &= ~WHERE_WANT_DISTINCT;
+
  }

  /* The number of tables in the FROM clause is limited by the number of
  ** bits in a Bitmask
@@ -184749,6 +184876,8 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int);

SQLITE_PRIVATE int sqlite3Fts3ExprIterate(Fts3Expr*, int (*x)(Fts3Expr*,int,void*), void*);

+
SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk);
+

#endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */
#endif /* _FTSINT_H */

@@ -188471,7 +188600,7 @@ static int fts3ShadowName(const char *zName){
** Implementation of the xIntegrity() method on the FTS3/FTS4 virtual
** table.
*/
-
static int fts3Integrity(
+
static int fts3IntegrityMethod(
  sqlite3_vtab *pVtab,      /* The virtual table to be checked */
  const char *zSchema,      /* Name of schema in which pVtab lives */
  const char *zTabname,     /* Name of the pVTab table */
@@ -188479,30 +188608,21 @@ static int fts3Integrity(
  char **pzErr              /* Write error message here */
){
  Fts3Table *p = (Fts3Table*)pVtab;
-
  char *zSql;
  int rc;
-
  char *zErr = 0;
+
  int bOk = 0;

-
  assert( pzErr!=0 );
-
  assert( *pzErr==0 );
  UNUSED_PARAMETER(isQuick);
-
  zSql = sqlite3_mprintf(
-
            "INSERT INTO \"%w\".\"%w\"(\"%w\") VALUES('integrity-check');",
-
            zSchema, zTabname, zTabname);
-
  if( zSql==0 ){
-
    return SQLITE_NOMEM;
-
  }
-
  rc = sqlite3_exec(p->db, zSql, 0, 0, &zErr);
-
  sqlite3_free(zSql);
-
  if( (rc&0xff)==SQLITE_CORRUPT ){
-
    *pzErr = sqlite3_mprintf("malformed inverted index for FTS%d table %s.%s",
-
                p->bFts4 ? 4 : 3, zSchema, zTabname);
-
  }else if( rc!=SQLITE_OK ){
+
  rc = sqlite3Fts3IntegrityCheck(p, &bOk);
+
  assert( rc!=SQLITE_CORRUPT_VTAB || bOk==0 );
+
  if( rc!=SQLITE_OK && rc!=SQLITE_CORRUPT_VTAB ){
    *pzErr = sqlite3_mprintf("unable to validate the inverted index for"
                             " FTS%d table %s.%s: %s",
-
                p->bFts4 ? 4 : 3, zSchema, zTabname, zErr);
+
                p->bFts4 ? 4 : 3, zSchema, zTabname, sqlite3_errstr(rc));
+
  }else if( bOk==0 ){
+
    *pzErr = sqlite3_mprintf("malformed inverted index for FTS%d table %s.%s",
+
                p->bFts4 ? 4 : 3, zSchema, zTabname);
  }
-
  sqlite3_free(zErr);
+
  sqlite3Fts3SegmentsClose(p);
  return SQLITE_OK;
}

@@ -188533,7 +188653,7 @@ static const sqlite3_module fts3Module = {
  /* xRelease      */ fts3ReleaseMethod,
  /* xRollbackTo   */ fts3RollbackToMethod,
  /* xShadowName   */ fts3ShadowName,
-
  /* xIntegrity    */ fts3Integrity,
+
  /* xIntegrity    */ fts3IntegrityMethod,
};

/*
@@ -200087,7 +200207,7 @@ static u64 fts3ChecksumIndex(
** If an error occurs (e.g. an OOM or IO error), return an SQLite error
** code. The final value of *pbOk is undefined in this case.
*/
-
static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){
+
SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk){
  int rc = SQLITE_OK;             /* Return code */
  u64 cksum1 = 0;                 /* Checksum based on FTS index contents */
  u64 cksum2 = 0;                 /* Checksum based on %_content contents */
@@ -200165,7 +200285,7 @@ static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){
    sqlite3_finalize(pStmt);
  }

-
  *pbOk = (cksum1==cksum2);
+
  *pbOk = (rc==SQLITE_OK && cksum1==cksum2);
  return rc;
}

@@ -200205,7 +200325,7 @@ static int fts3DoIntegrityCheck(
){
  int rc;
  int bOk = 0;
-
  rc = fts3IntegrityCheck(p, &bOk);
+
  rc = sqlite3Fts3IntegrityCheck(p, &bOk);
  if( rc==SQLITE_OK && bOk==0 ) rc = FTS_CORRUPT_VTAB;
  return rc;
}
@@ -203758,6 +203878,16 @@ static void jsonAppendChar(JsonString *p, char c){
  }
}

+
/* Remove a single character from the end of the string
+
*/
+
static void jsonStringTrimOneChar(JsonString *p){
+
  if( p->eErr==0 ){
+
    assert( p->nUsed>0 );
+
    p->nUsed--;
+
  }
+
}
+

+

/* Make sure there is a zero terminator on p->zBuf[]
**
** Return true on success.  Return false if an OOM prevents this
@@ -203765,7 +203895,7 @@ static void jsonAppendChar(JsonString *p, char c){
*/
static int jsonStringTerminate(JsonString *p){
  jsonAppendChar(p, 0);
-
  p->nUsed--;
+
  jsonStringTrimOneChar(p);
  return p->eErr==0;
}

@@ -204766,6 +204896,7 @@ json_parse_restart:
  case '[': {
    /* Parse array */
    iThis = pParse->nBlob;
+
    assert( i<=(u32)pParse->nJson );
    jsonBlobAppendNode(pParse, JSONB_ARRAY, pParse->nJson - i, 0);
    iStart = pParse->nBlob;
    if( pParse->oom ) return -1;
@@ -205164,6 +205295,10 @@ static void jsonReturnStringAsBlob(JsonString *pStr){
  JsonParse px;
  memset(&px, 0, sizeof(px));
  jsonStringTerminate(pStr);
+
  if( pStr->eErr ){
+
    sqlite3_result_error_nomem(pStr->pCtx);
+
    return;
+
  }
  px.zJson = pStr->zBuf;
  px.nJson = pStr->nUsed;
  px.db = sqlite3_context_db_handle(pStr->pCtx);
@@ -205231,8 +205366,8 @@ static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){
         (pParse->aBlob[i+7]<<8) + pParse->aBlob[i+8];
    n = 9;
  }
-
  if( i+sz+n > pParse->nBlob
-
   && i+sz+n > pParse->nBlob-pParse->delta
+
  if( (i64)i+sz+n > pParse->nBlob
+
   && (i64)i+sz+n > pParse->nBlob-pParse->delta
  ){
    sz = 0;
    n = 0;
@@ -205282,6 +205417,7 @@ static u32 jsonTranslateBlobToText(
    }
    case JSONB_INT:
    case JSONB_FLOAT: {
+
      if( sz==0 ) goto malformed_jsonb;
      jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz);
      break;
    }
@@ -205290,6 +205426,7 @@ static u32 jsonTranslateBlobToText(
      sqlite3_uint64 u = 0;
      const char *zIn = (const char*)&pParse->aBlob[i+n];
      int bOverflow = 0;
+
      if( sz==0 ) goto malformed_jsonb;
      if( zIn[0]=='-' ){
        jsonAppendChar(pOut, '-');
        k++;
@@ -205312,6 +205449,7 @@ static u32 jsonTranslateBlobToText(
    case JSONB_FLOAT5: { /* Float literal missing digits beside "." */
      u32 k = 0;
      const char *zIn = (const char*)&pParse->aBlob[i+n];
+
      if( sz==0 ) goto malformed_jsonb;
      if( zIn[0]=='-' ){
        jsonAppendChar(pOut, '-');
        k++;
@@ -205425,11 +205563,12 @@ static u32 jsonTranslateBlobToText(
      jsonAppendChar(pOut, '[');
      j = i+n;
      iEnd = j+sz;
-
      while( j<iEnd ){
+
      while( j<iEnd && pOut->eErr==0 ){
        j = jsonTranslateBlobToText(pParse, j, pOut);
        jsonAppendChar(pOut, ',');
      }
-
      if( sz>0 ) pOut->nUsed--;
+
      if( j>iEnd ) pOut->eErr |= JSTRING_MALFORMED;
+
      if( sz>0 ) jsonStringTrimOneChar(pOut);
      jsonAppendChar(pOut, ']');
      break;
    }
@@ -205438,17 +205577,18 @@ static u32 jsonTranslateBlobToText(
      jsonAppendChar(pOut, '{');
      j = i+n;
      iEnd = j+sz;
-
      while( j<iEnd ){
+
      while( j<iEnd && pOut->eErr==0 ){
        j = jsonTranslateBlobToText(pParse, j, pOut);
        jsonAppendChar(pOut, (x++ & 1) ? ',' : ':');
      }
-
      if( x & 1 ) pOut->eErr |= JSTRING_MALFORMED;
-
      if( sz>0 ) pOut->nUsed--;
+
      if( (x & 1)!=0 || j>iEnd ) pOut->eErr |= JSTRING_MALFORMED;
+
      if( sz>0 ) jsonStringTrimOneChar(pOut);
      jsonAppendChar(pOut, '}');
      break;
    }

    default: {
+
      malformed_jsonb:
      pOut->eErr |= JSTRING_MALFORMED;
      break;
    }
@@ -206376,6 +206516,38 @@ jsonInsertIntoBlob_patherror:
}

/*
+
** If pArg is a blob that seems like a JSONB blob, then initialize
+
** p to point to that JSONB and return TRUE.  If pArg does not seem like
+
** a JSONB blob, then return FALSE;
+
**
+
** This routine is only called if it is already known that pArg is a
+
** blob.  The only open question is whether or not the blob appears
+
** to be a JSONB blob.
+
*/
+
static int jsonArgIsJsonb(sqlite3_value *pArg, JsonParse *p){
+
  u32 n, sz = 0;
+
  p->aBlob = (u8*)sqlite3_value_blob(pArg);
+
  p->nBlob = (u32)sqlite3_value_bytes(pArg);
+
  if( p->nBlob==0 ){
+
    p->aBlob = 0;
+
    return 0;
+
  }
+
  if( NEVER(p->aBlob==0) ){
+
    return 0;
+
  }
+
  if( (p->aBlob[0] & 0x0f)<=JSONB_OBJECT
+
   && (n = jsonbPayloadSize(p, 0, &sz))>0
+
   && sz+n==p->nBlob
+
   && ((p->aBlob[0] & 0x0f)>JSONB_FALSE || sz==0)
+
  ){
+
    return 1;
+
  }
+
  p->aBlob = 0;
+
  p->nBlob = 0;
+
  return 0;
+
}
+

+
/*
** Generate a JsonParse object, containing valid JSONB in aBlob and nBlob,
** from the SQL function argument pArg.  Return a pointer to the new
** JsonParse object.
@@ -206431,34 +206603,30 @@ rebuild_from_cache:
    return p;
  }
  if( eType==SQLITE_BLOB ){
-
    u32 n, sz = 0;
-
    p->aBlob = (u8*)sqlite3_value_blob(pArg);
-
    p->nBlob = (u32)sqlite3_value_bytes(pArg);
-
    if( p->nBlob==0 ){
-
      goto json_pfa_malformed;
-
    }
-
    if( NEVER(p->aBlob==0) ){
-
      goto json_pfa_oom;
-
    }
-
    if( (p->aBlob[0] & 0x0f)>JSONB_OBJECT ){
-
      goto json_pfa_malformed;
-
    }
-
    n = jsonbPayloadSize(p, 0, &sz);
-
    if( n==0
-
     || sz+n!=p->nBlob
-
     || ((p->aBlob[0] & 0x0f)<=JSONB_FALSE && sz>0)
-
    ){
-
      goto json_pfa_malformed;
-
    }
-
    if( (flgs & JSON_EDITABLE)!=0 && jsonBlobMakeEditable(p, 0)==0 ){
-
      goto json_pfa_oom;
+
    if( jsonArgIsJsonb(pArg,p) ){
+
      if( (flgs & JSON_EDITABLE)!=0 && jsonBlobMakeEditable(p, 0)==0 ){
+
        goto json_pfa_oom;
+
      }
+
      return p;
    }
-
    return p;
+
    /* If the blob is not valid JSONB, fall through into trying to cast
+
    ** the blob into text which is then interpreted as JSON.  (tag-20240123-a)
+
    **
+
    ** This goes against all historical documentation about how the SQLite
+
    ** JSON functions were suppose to work.  From the beginning, blob was
+
    ** reserved for expansion and a blob value should have raised an error.
+
    ** But it did not, due to a bug.  And many applications came to depend
+
    ** upon this buggy behavior, espeically when using the CLI and reading
+
    ** JSON text using readfile(), which returns a blob.  For this reason
+
    ** we will continue to support the bug moving forward.
+
    ** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d
+
    */
  }
  p->zJson = (char*)sqlite3_value_text(pArg);
  p->nJson = sqlite3_value_bytes(pArg);
+
  if( db->mallocFailed ) goto json_pfa_oom;
  if( p->nJson==0 ) goto json_pfa_malformed;
-
  if( NEVER(p->zJson==0) ) goto json_pfa_oom;
+
  assert( p->zJson!=0 );
  if( jsonConvertTextToBlob(p, (flgs & JSON_KEEPERROR) ? 0 : ctx) ){
    if( flgs & JSON_KEEPERROR ){
      p->nErr = 1;
@@ -206624,10 +206792,10 @@ static void jsonDebugPrintBlob(
      if( sz==0 && x<=JSONB_FALSE ){
        sqlite3_str_append(pOut, "\n", 1);
      }else{
-
        u32 i;
+
        u32 j;
        sqlite3_str_appendall(pOut, ": \"");
-
        for(i=iStart+n; i<iStart+n+sz; i++){
-
          u8 c = pParse->aBlob[i];
+
        for(j=iStart+n; j<iStart+n+sz; j++){
+
          u8 c = pParse->aBlob[j];
          if( c<0x20 || c>=0x7f ) c = '.';
          sqlite3_str_append(pOut, (char*)&c, 1);
        }
@@ -207429,12 +207597,12 @@ static void jsonValidFunc(
      return;
    }
    case SQLITE_BLOB: {
-
      if( (flags & 0x0c)!=0 && jsonFuncArgMightBeBinary(argv[0]) ){
+
      if( jsonFuncArgMightBeBinary(argv[0]) ){
        if( flags & 0x04 ){
          /* Superficial checking only - accomplished by the
          ** jsonFuncArgMightBeBinary() call above. */
          res = 1;
-
        }else{
+
        }else if( flags & 0x08 ){
          /* Strict checking.  Check by translating BLOB->TEXT->BLOB.  If
          ** no errors occur, call that a "strict check". */
          JsonParse px;
@@ -207445,8 +207613,11 @@ static void jsonValidFunc(
          iErr = jsonbValidityCheck(&px, 0, px.nBlob, 1);
          res = iErr==0;
        }
+
        break;
      }
-
      break;
+
      /* Fall through into interpreting the input as text.  See note
+
      ** above at tag-20240123-a. */
+
      /* no break */ deliberate_fall_through
    }
    default: {
      JsonParse px;
@@ -207571,7 +207742,7 @@ static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){
      if( isFinal ){
        if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf);
      }else{
-
        pStr->nUsed--;
+
        jsonStringTrimOneChar(pStr);
      }
      return;
    }else if( isFinal ){
@@ -207581,7 +207752,7 @@ static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){
      pStr->bStatic = 1;
    }else{
      sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
-
      pStr->nUsed--;
+
      jsonStringTrimOneChar(pStr);
    }
  }else{
    sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC);
@@ -207691,7 +207862,7 @@ static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){
      if( isFinal ){
        if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf);
      }else{
-
        pStr->nUsed--;
+
        jsonStringTrimOneChar(pStr);
      }
      return;
    }else if( isFinal ){
@@ -207701,7 +207872,7 @@ static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){
      pStr->bStatic = 1;
    }else{
      sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
-
      pStr->nUsed--;
+
      jsonStringTrimOneChar(pStr);
    }
  }else{
    sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC);
@@ -208032,6 +208203,9 @@ static int jsonEachColumn(
    case JEACH_VALUE: {
      u32 i = jsonSkipLabel(p);
      jsonReturnFromBlob(&p->sParse, i, ctx, 1);
+
      if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY ){
+
        sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+
      }
      break;
    }
    case JEACH_TYPE: {
@@ -208078,9 +208252,9 @@ static int jsonEachColumn(
    case JEACH_JSON: {
      if( p->sParse.zJson==0 ){
        sqlite3_result_blob(ctx, p->sParse.aBlob, p->sParse.nBlob,
-
                            SQLITE_STATIC);
+
                            SQLITE_TRANSIENT);
      }else{
-
        sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);
+
        sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_TRANSIENT);
      }
      break;
    }
@@ -208182,13 +208356,9 @@ static int jsonEachFilter(
  memset(&p->sParse, 0, sizeof(p->sParse));
  p->sParse.nJPRef = 1;
  p->sParse.db = p->db;
-
  if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){
-
    if( jsonFuncArgMightBeBinary(argv[0]) ){
-
      p->sParse.nBlob = sqlite3_value_bytes(argv[0]);
-
      p->sParse.aBlob = (u8*)sqlite3_value_blob(argv[0]);
-
    }else{
-
      goto json_each_malformed_input;
-
    }
+
  if( jsonFuncArgMightBeBinary(argv[0]) ){
+
    p->sParse.nBlob = sqlite3_value_bytes(argv[0]);
+
    p->sParse.aBlob = (u8*)sqlite3_value_blob(argv[0]);
  }else{
    p->sParse.zJson = (char*)sqlite3_value_text(argv[0]);
    p->sParse.nJson = sqlite3_value_bytes(argv[0]);
@@ -209110,11 +209280,9 @@ static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){
** Clear the Rtree.pNodeBlob object
*/
static void nodeBlobReset(Rtree *pRtree){
-
  if( pRtree->pNodeBlob && pRtree->inWrTrans==0 && pRtree->nCursor==0 ){
-
    sqlite3_blob *pBlob = pRtree->pNodeBlob;
-
    pRtree->pNodeBlob = 0;
-
    sqlite3_blob_close(pBlob);
-
  }
+
  sqlite3_blob *pBlob = pRtree->pNodeBlob;
+
  pRtree->pNodeBlob = 0;
+
  sqlite3_blob_close(pBlob);
}

/*
@@ -209158,7 +209326,6 @@ static int nodeAcquire(
                           &pRtree->pNodeBlob);
  }
  if( rc ){
-
    nodeBlobReset(pRtree);
    *ppNode = 0;
    /* If unable to open an sqlite3_blob on the desired row, that can only
    ** be because the shadow tables hold erroneous data. */
@@ -209218,6 +209385,7 @@ static int nodeAcquire(
    }
    *ppNode = pNode;
  }else{
+
    nodeBlobReset(pRtree);
    if( pNode ){
      pRtree->nNodeRef--;
      sqlite3_free(pNode);
@@ -209362,6 +209530,7 @@ static void nodeGetCoord(
  int iCoord,                  /* Which coordinate to extract */
  RtreeCoord *pCoord           /* OUT: Space to write result to */
){
+
  assert( iCell<NCELL(pNode) );
  readCoord(&pNode->zData[12 + pRtree->nBytesPerCell*iCell + 4*iCoord], pCoord);
}

@@ -209551,7 +209720,9 @@ static int rtreeClose(sqlite3_vtab_cursor *cur){
  sqlite3_finalize(pCsr->pReadAux);
  sqlite3_free(pCsr);
  pRtree->nCursor--;
-
  nodeBlobReset(pRtree);
+
  if( pRtree->nCursor==0 && pRtree->inWrTrans==0 ){
+
    nodeBlobReset(pRtree);
+
  }
  return SQLITE_OK;
}

@@ -210136,7 +210307,11 @@ static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){
  int rc = SQLITE_OK;
  RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc);
  if( rc==SQLITE_OK && ALWAYS(p) ){
-
    *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell);
+
    if( p->iCell>=NCELL(pNode) ){
+
      rc = SQLITE_ABORT;
+
    }else{
+
      *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell);
+
    }
  }
  return rc;
}
@@ -210154,6 +210329,7 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){

  if( rc ) return rc;
  if( NEVER(p==0) ) return SQLITE_OK;
+
  if( p->iCell>=NCELL(pNode) ) return SQLITE_ABORT;
  if( i==0 ){
    sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell));
  }else if( i<=pRtree->nDim2 ){
@@ -211635,8 +211811,7 @@ constraint:
*/
static int rtreeBeginTransaction(sqlite3_vtab *pVtab){
  Rtree *pRtree = (Rtree *)pVtab;
-
  assert( pRtree->inWrTrans==0 );
-
  pRtree->inWrTrans++;
+
  pRtree->inWrTrans = 1;
  return SQLITE_OK;
}

@@ -211650,6 +211825,9 @@ static int rtreeEndTransaction(sqlite3_vtab *pVtab){
  nodeBlobReset(pRtree);
  return SQLITE_OK;
}
+
static int rtreeRollback(sqlite3_vtab *pVtab){
+
  return rtreeEndTransaction(pVtab);
+
}

/*
** The xRename method for rtree module virtual tables.
@@ -211768,7 +211946,7 @@ static sqlite3_module rtreeModule = {
  rtreeBeginTransaction,      /* xBegin - begin transaction */
  rtreeEndTransaction,        /* xSync - sync transaction */
  rtreeEndTransaction,        /* xCommit - commit transaction */
-
  rtreeEndTransaction,        /* xRollback - rollback transaction */
+
  rtreeRollback,              /* xRollback - rollback transaction */
  0,                          /* xFindFunction - function overloading */
  rtreeRename,                /* xRename - rename the table */
  rtreeSavepoint,             /* xSavepoint */
@@ -245327,23 +245505,26 @@ static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){
static void fts5TokendataIterNext(Fts5Iter *pIter, int bFrom, i64 iFrom){
  int ii;
  Fts5TokenDataIter *pT = pIter->pTokenDataIter;
+
  Fts5Index *pIndex = pIter->pIndex;

  for(ii=0; ii<pT->nIter; ii++){
    Fts5Iter *p = pT->apIter[ii];
    if( p->base.bEof==0
     && (p->base.iRowid==pIter->base.iRowid || (bFrom && p->base.iRowid<iFrom))
    ){
-
      fts5MultiIterNext(p->pIndex, p, bFrom, iFrom);
+
      fts5MultiIterNext(pIndex, p, bFrom, iFrom);
      while( bFrom && p->base.bEof==0
          && p->base.iRowid<iFrom
-
          && p->pIndex->rc==SQLITE_OK
+
          && pIndex->rc==SQLITE_OK
      ){
-
        fts5MultiIterNext(p->pIndex, p, 0, 0);
+
        fts5MultiIterNext(pIndex, p, 0, 0);
      }
    }
  }

-
  fts5IterSetOutputsTokendata(pIter);
+
  if( pIndex->rc==SQLITE_OK ){
+
    fts5IterSetOutputsTokendata(pIter);
+
  }
}

/*
@@ -250497,7 +250678,7 @@ static void fts5SourceIdFunc(
){
  assert( nArg==0 );
  UNUSED_PARAM2(nArg, apUnused);
-
  sqlite3_result_text(pCtx, "fts5: 2024-01-15 17:01:13 1066602b2b1976fe58b5150777cced894af17c803e068f5918390d6915b46e1d", -1, SQLITE_TRANSIENT);
+
  sqlite3_result_text(pCtx, "fts5: 2024-03-12 11:06:23 d8cd6d49b46a395b13955387d05e9e1a2a47e54fb99f3c9b59835bbefad6af77", -1, SQLITE_TRANSIENT);
}

/*
@@ -250528,27 +250709,21 @@ static int fts5IntegrityMethod(
  char **pzErr            /* Write error message here */
){
  Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
-
  Fts5Config *pConfig = pTab->p.pConfig;
-
  char *zSql;
-
  char *zErr = 0;
  int rc;
+

  assert( pzErr!=0 && *pzErr==0 );
  UNUSED_PARAM(isQuick);
-
  zSql = sqlite3_mprintf(
-
            "INSERT INTO \"%w\".\"%w\"(\"%w\") VALUES('integrity-check');",
-
            zSchema, zTabname, pConfig->zName);
-
  if( zSql==0 ) return SQLITE_NOMEM;
-
  rc = sqlite3_exec(pConfig->db, zSql, 0, 0, &zErr);
-
  sqlite3_free(zSql);
+
  rc = sqlite3Fts5StorageIntegrity(pTab->pStorage, 0);
  if( (rc&0xff)==SQLITE_CORRUPT ){
    *pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s",
                zSchema, zTabname);
  }else if( rc!=SQLITE_OK ){
    *pzErr = sqlite3_mprintf("unable to validate the inverted index for"
                             " FTS5 table %s.%s: %s",
-
                zSchema, zTabname, zErr);
+
                zSchema, zTabname, sqlite3_errstr(rc));
  }
-
  sqlite3_free(zErr);
+
  sqlite3Fts5IndexCloseReader(pTab->p.pIndex);
+

  return SQLITE_OK;
}

modified external/sqlite/sqlite3.h
@@ -146,9 +146,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-
#define SQLITE_VERSION        "3.45.0"
-
#define SQLITE_VERSION_NUMBER 3045000
-
#define SQLITE_SOURCE_ID      "2024-01-15 17:01:13 1066602b2b1976fe58b5150777cced894af17c803e068f5918390d6915b46e1d"
+
#define SQLITE_VERSION        "3.45.2"
+
#define SQLITE_VERSION_NUMBER 3045002
+
#define SQLITE_SOURCE_ID      "2024-03-12 11:06:23 d8cd6d49b46a395b13955387d05e9e1a2a47e54fb99f3c9b59835bbefad6af77"

/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -420,6 +420,8 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
**      the 1st parameter to sqlite3_exec() while sqlite3_exec() is running.
** <li> The application must not modify the SQL statement text passed into
**      the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running.
+
** <li> The application must not dereference the arrays or string pointers
+
**       passed as the 3rd and 4th callback parameters after it returns.
** </ul>
*/
SQLITE_API int sqlite3_exec(
modified libpkg/pkg_ports.c
@@ -68,6 +68,7 @@ static int config(struct plist *, char *, struct file_attr *);
/* compat with old packages */
static int name_key(struct plist *, char *, struct file_attr *);
static int include_plist(struct plist *, char *, struct file_attr *);
+
static int add_variable(struct plist *, char *, struct file_attr *);

static struct action_cmd {
	const char *name;
@@ -477,6 +478,7 @@ static struct keyact {
	{ "owner", setowner },
	{ "group", setgroup },
	{ "override_prefix", override_prefix },
+
	{ "var", add_variable },
	/* old pkg compat */
	{ "name", name_key },
	{ NULL, NULL },
@@ -1077,6 +1079,64 @@ plist_free(struct plist *p)
	free(p);
}

+
static char *
+
expand_plist_variables(const char *in, kvlist_t *vars)
+
{
+
	xstring *buf;
+
	const char *cp;
+
	size_t len;
+

+
	if (tll_length(*vars) == 0)
+
		return (xstrdup(in));
+

+
	buf = xstring_new();
+
	cp = NULL;
+
	while (in[0] != '\0') {
+
		if (in[0] != '%') {
+
			fputc(in[0], buf->fp);
+
			in++;
+
			continue;
+
		}
+
		in++;
+
		if (in[0] == '\0')
+
			break;
+
		if (in[0] != '%') {
+
			fputc(in[0], buf->fp);
+
			in++;
+
			continue;
+
		}
+
		in++;
+
		cp = in;
+
		while (in[0] != '\0' && !isspace(in[0])) {
+
			if (in[0] == '%' && in[1] == '%') {
+
				in++;
+
				break;
+
			}
+
			in++;
+
		}
+
		if (in[0] != '%') {
+
			fprintf(buf->fp, "%%%%%.*s", (int)(in - cp), cp);
+
			in++;
+
			continue;
+
		}
+
		len = in - cp -1;
+
		/* we have a variable */
+
		bool found = false;
+
		tll_foreach(*vars, i) {
+
			if (strncmp(cp, i->item->key, len) != 0)
+
				continue;
+
			fputs(i->item->value, buf->fp);
+
			found = true;
+
			break;
+
		}
+
		if (found)
+
			continue;
+
		fprintf(buf->fp, "%%%%%.*s%%", (int)(in - cp), cp);
+
		in++;
+
	}
+
	return (xstring_get(buf));
+
}
+

static int
plist_parse(struct plist *pplist, FILE *f)
{
@@ -1084,11 +1144,14 @@ plist_parse(struct plist *pplist, FILE *f)
	size_t linecap = 0;
	ssize_t linelen;
	char *line = NULL;
+
	char *l;

	while ((linelen = getline(&line, &linecap, f)) > 0) {
		if (line[linelen - 1] == '\n')
			line[linelen - 1] = '\0';
-
		ret = plist_parse_line(pplist, line);
+
		l = expand_plist_variables(line, &pplist->variables);
+
		ret = plist_parse_line(pplist, l);
+
		free(l);
		if (ret != EPKG_OK && rc == EPKG_OK)
			rc = ret;
	}
@@ -1117,6 +1180,40 @@ open_directory_of(const char *file)
}

int
+
add_variable(struct plist *p, char *line, struct file_attr *a __unused)
+
{
+
	const char *key;
+
	char *val;
+

+
	key = val = line;
+
	while (*val != '\0' && !isspace(*val))
+
		val++;
+
	if (*val != '\0') {
+
		*val = '\0';
+
		val++;
+
	}
+

+
	if (*key == '\0') {
+
		pkg_emit_error("Inside in @include it is not allowed to reuse @include");
+
		return (EPKG_FATAL);
+
	}
+

+
	while (*val != '\0' && isspace(*val))
+
		val++;
+

+
	tll_foreach(p->variables, v) {
+
		if (strcmp(v->item->key, key) == 0) {
+
			free(v->item->value);
+
			v->item->value = xstrdup(val);
+
			return (EPKG_OK);
+
		}
+
	}
+
	struct pkg_kv *kv = pkg_kv_new(key, val);
+
	tll_push_back(p->variables, kv);
+
	return (EPKG_OK);
+
}
+

+
int
include_plist(struct plist *p, char *name, struct file_attr *a __unused)
{
	FILE *f;
modified libpkg/pkg_solve.c
@@ -776,7 +776,7 @@ pkg_solve_jobs_to_sat(struct pkg_jobs *j)
		un = (struct pkg_job_universe_item *)it.value;
		/* Add corresponding variables */
		if (pkg_solve_add_variable(un, problem, &i) == EPKG_FATAL)
-
			goto err;
+
			return (NULL);
	}

	/* Add rules for all conflict chains */
@@ -789,19 +789,16 @@ pkg_solve_jobs_to_sat(struct pkg_jobs *j)
		if (var == NULL) {
			pkg_emit_error("internal solver error: variable %s is not found",
			    un->pkg->uid);
-
			goto err;
+
			return (NULL);
		}
		if (pkg_solve_process_universe_variable(problem, var) != EPKG_OK)
-
			goto err;
+
		        return (NULL);
	}

	if (tll_length(problem->rules) == 0)
		pkg_debug(1, "problem has no requests");

	return (problem);
-

-
err:
-
	return (NULL);
}

static int
@@ -974,6 +971,8 @@ pkg_solve_set_initial_assumption(struct pkg_solve_problem *problem,
		/* XXX: deal with require rules somehow */
		break;
	case PKG_RULE_VITAL:
+
		assert (rule->items != NULL);
+
		item = rule->items;
		var = item->var;
		picosat_set_default_phase_lit(problem->sat, var->order, 1);
		break;
modified libpkg/private/pkg.h
@@ -599,6 +599,7 @@ struct plist {
	hardlinks_t hardlinks;
	mode_t perm;
	pkghash *keywords;
+
	kvlist_t variables;
};

struct file_attr {
modified libpkg/utils.c
@@ -57,6 +57,7 @@
#include "private/utils.h"
#include "private/pkg.h"
#include "xmalloc.h"
+
#include "tllist.h"

extern struct pkg_ctx ctx;

modified tests/frontend/create.sh
@@ -4,6 +4,7 @@

tests_init \
	create_from_plist \
+
	create_from_plist_with_variables \
	create_from_plist_set_owner \
	create_from_plist_set_group_space \
	create_from_plist_gather_mode \
@@ -818,3 +819,37 @@ create_from_plist_keyword_override_prefix_body()
	atf_check -o match:".*/plop/file$" -e ignore tar tf test-1.pkg
	atf_check -o inline:"/plop/file\n" pkg info -F test-1.pkg -ql
}
+

+

+
create_from_plist_with_variables_body() {
+
	touch file1 file2 plop file3
+
	genmanifest
+
	genplist "
+
@var key1
+
@var key2 
+
@var key3 plop
+
%%key1%%file1
+
%%key2%%file2
+
%%key3%%
+
@var key3 @comment 
+
%%key3%% file3"
+

+
	atf_check \
+
		-o empty \
+
		-e empty \
+
		-s exit:0 \
+
		pkg create -o ${TMPDIR} -m . -p test.plist -r .
+

+
	basic_validation
+
	atf_check \
+
		-o inline:"/file1\n/file2\n/plop\n" \
+
		 pkg info -F test-1.pkg -ql
+
	rm test-1.pkg
+
	echo "
+
%%plop%%
+
" >> test.plist
+
	atf_check \
+
		-e match:"%%plop%%" \
+
		-s exit:1 \
+
		pkg create -o ${TMPDIR} -m . -p test.plist -r .
+
}
modified tests/frontend/vital.sh
@@ -40,8 +40,8 @@ EOF

	atf_check \
		-o empty \
-
		-e inline:"${PROGNAME}: Cannot delete vital package: test!\n${PROGNAME}: If you are sure you want to remove test, \n${PROGNAME}: unset the 'vital' flag with: pkg set -v 0 test\n" \
-
		-s exit:3 \
+
		-e empty  \
+
		-s exit:1 \
		pkg -r ${TMPDIR}/target delete -qy test
	atf_check \
		-o empty \