aboutsummaryrefslogtreecommitdiffstats
path: root/src/libs
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs')
m---------src/libs/3rdparty/googletest0
-rw-r--r--src/libs/3rdparty/sqlite/sqlite3.c484
-rw-r--r--src/libs/3rdparty/sqlite/sqlite3.h25
-rw-r--r--src/libs/languageutils/componentversion.cpp63
-rw-r--r--src/libs/languageutils/componentversion.h61
-rw-r--r--src/libs/nanotrace/CMakeLists.txt1
-rw-r--r--src/libs/nanotrace/nanotracehr.cpp56
-rw-r--r--src/libs/nanotrace/nanotracehr.h362
-rw-r--r--src/libs/nanotrace/staticstring.h116
-rw-r--r--src/libs/qmljs/CMakeLists.txt1
-rw-r--r--src/libs/qmljs/qmljs.qbs1
-rw-r--r--src/libs/qmljs/qmljsindenter.cpp603
-rw-r--r--src/libs/qmljs/qmljsindenter.h51
-rw-r--r--src/libs/qmljs/qmljsstaticanalysismessage.cpp5
-rw-r--r--src/libs/qmlpuppetcommunication/container/imagecontainer.cpp9
-rw-r--r--src/libs/sqlite/CMakeLists.txt4
-rw-r--r--src/libs/sqlite/sqlitebasestatement.cpp203
-rw-r--r--src/libs/sqlite/sqlitebasestatement.h82
-rw-r--r--src/libs/sqlite/sqliteexception.cpp30
-rw-r--r--src/libs/sqlite/sqliteexception.h9
-rw-r--r--src/libs/sqlite/sqliteids.h11
-rw-r--r--src/libs/sqlite/sqliteindex.h19
-rw-r--r--src/libs/sqlite/sqlitetracing.cpp38
-rw-r--r--src/libs/sqlite/sqlitetracing.h29
-rw-r--r--src/libs/sqlite/sqlitevalue.h62
-rw-r--r--src/libs/utils/smallstring.h113
26 files changed, 1302 insertions, 1136 deletions
diff --git a/src/libs/3rdparty/googletest b/src/libs/3rdparty/googletest
-Subproject b796f7d44681514f58a683a3a71ff17c94edb0c
+Subproject f8d7d77c06936315286eb55f8de22cd23c18857
diff --git a/src/libs/3rdparty/sqlite/sqlite3.c b/src/libs/3rdparty/sqlite/sqlite3.c
index 139ee46a6a..08c593e55c 100644
--- a/src/libs/3rdparty/sqlite/sqlite3.c
+++ b/src/libs/3rdparty/sqlite/sqlite3.c
@@ -1,6 +1,6 @@
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
-** version 3.45.1. By combining all the individual C code files into this
+** version 3.45.3. 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
-** e876e51a0ed5c5b3126f52e532044363a014.
+** 8653b758870e6ef0c98d46b3ace27849054a.
*/
#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.1"
-#define SQLITE_VERSION_NUMBER 3045001
-#define SQLITE_SOURCE_ID "2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a"
+#define SQLITE_VERSION "3.45.3"
+#define SQLITE_VERSION_NUMBER 3045003
+#define SQLITE_SOURCE_ID "2024-04-15 13:34:05 8653b758870e6ef0c98d46b3ace27849054af85da891eb121e9aaa537f1e8355"
/*
** 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(
@@ -2454,6 +2456,22 @@ struct sqlite3_mem_methods {
** configuration setting is never used, then the default maximum is determined
** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that
** compile-time option is not set, then the default maximum is 1073741824.
+**
+** [[SQLITE_CONFIG_ROWID_IN_VIEW]]
+** <dt>SQLITE_CONFIG_ROWID_IN_VIEW
+** <dd>The SQLITE_CONFIG_ROWID_IN_VIEW option enables or disables the ability
+** for VIEWs to have a ROWID. The capability can only be enabled if SQLite is
+** compiled with -DSQLITE_ALLOW_ROWID_IN_VIEW, in which case the capability
+** defaults to on. This configuration option queries the current setting or
+** changes the setting to off or on. The argument is a pointer to an integer.
+** If that integer initially holds a value of 1, then the ability for VIEWs to
+** have ROWIDs is activated. If the integer initially holds zero, then the
+** ability is deactivated. Any other initial value for the integer leaves the
+** setting unchanged. After changes, if any, the integer is written with
+** a 1 or 0, if the ability for VIEWs to have ROWIDs is on or off. If SQLite
+** is compiled without -DSQLITE_ALLOW_ROWID_IN_VIEW (which is the usual and
+** recommended case) then the integer is always filled with zero, regardless
+** if its initial value.
** </dl>
*/
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
@@ -2485,6 +2503,7 @@ struct sqlite3_mem_methods {
#define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */
#define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */
#define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */
+#define SQLITE_CONFIG_ROWID_IN_VIEW 30 /* int* */
/*
** CAPI3REF: Database Connection Configuration Options
@@ -15097,6 +15116,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
*/
/*
@@ -18427,6 +18447,15 @@ struct Table {
#define HasRowid(X) (((X)->tabFlags & TF_WithoutRowid)==0)
#define VisibleRowid(X) (((X)->tabFlags & TF_NoVisibleRowid)==0)
+/* Macro is true if the SQLITE_ALLOW_ROWID_IN_VIEW (mis-)feature is
+** available. By default, this macro is false
+*/
+#ifndef SQLITE_ALLOW_ROWID_IN_VIEW
+# define ViewCanHaveRowid 0
+#else
+# define ViewCanHaveRowid (sqlite3Config.mNoVisibleRowid==0)
+#endif
+
/*
** Each foreign key constraint is an instance of the following structure.
**
@@ -19346,6 +19375,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 +19399,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 */
@@ -20140,6 +20171,11 @@ struct Sqlite3Config {
#ifndef SQLITE_UNTESTABLE
int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */
#endif
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ u32 mNoVisibleRowid; /* TF_NoVisibleRowid if the ROWID_IN_VIEW
+ ** feature is disabled. 0 if rowids can
+ ** occur in views. */
+#endif
int bLocaltimeFault; /* True to fail localtime() calls */
int (*xAltLocaltime)(const void*,void*); /* Alternative localtime() routine */
int iOnceResetThreshold; /* When to reset OP_Once counters */
@@ -20595,10 +20631,13 @@ SQLITE_PRIVATE void sqlite3MutexWarnOnContention(sqlite3_mutex*);
# define EXP754 (((u64)0x7ff)<<52)
# define MAN754 ((((u64)1)<<52)-1)
# define IsNaN(X) (((X)&EXP754)==EXP754 && ((X)&MAN754)!=0)
+# define IsOvfl(X) (((X)&EXP754)==EXP754)
SQLITE_PRIVATE int sqlite3IsNaN(double);
+SQLITE_PRIVATE int sqlite3IsOverflow(double);
#else
-# define IsNaN(X) 0
-# define sqlite3IsNaN(X) 0
+# define IsNaN(X) 0
+# define sqlite3IsNaN(X) 0
+# define sqlite3IsOVerflow(X) 0
#endif
/*
@@ -21444,7 +21483,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*);
@@ -21834,6 +21873,9 @@ static const char * const sqlite3azCompileOpt[] = {
"ALLOW_COVERING_INDEX_SCAN=" CTIMEOPT_VAL(SQLITE_ALLOW_COVERING_INDEX_SCAN),
# endif
#endif
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ "ALLOW_ROWID_IN_VIEW",
+#endif
#ifdef SQLITE_ALLOW_URI_AUTHORITY
"ALLOW_URI_AUTHORITY",
#endif
@@ -22854,6 +22896,9 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = {
#ifndef SQLITE_UNTESTABLE
0, /* xTestCallback */
#endif
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ 0, /* mNoVisibleRowid. 0 == allow rowid-in-view */
+#endif
0, /* bLocaltimeFault */
0, /* xAltLocaltime */
0x7ffffffe, /* iOnceResetThreshold */
@@ -31309,6 +31354,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;
@@ -34640,6 +34686,19 @@ SQLITE_PRIVATE int sqlite3IsNaN(double x){
}
#endif /* SQLITE_OMIT_FLOATING_POINT */
+#ifndef SQLITE_OMIT_FLOATING_POINT
+/*
+** Return true if the floating point value is NaN or +Inf or -Inf.
+*/
+SQLITE_PRIVATE int sqlite3IsOverflow(double x){
+ int rc; /* The value return */
+ u64 y;
+ memcpy(&y,&x,sizeof(y));
+ rc = IsOvfl(y);
+ 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.
@@ -35199,6 +35258,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 +35703,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;
@@ -53262,6 +53324,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;
@@ -63785,7 +63855,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager *pPager){
** This will be either the rollback journal or the WAL file.
*/
SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager *pPager){
-#if SQLITE_OMIT_WAL
+#ifdef SQLITE_OMIT_WAL
return pPager->jfd;
#else
return pPager->pWal ? sqlite3WalFile(pPager->pWal) : pPager->jfd;
@@ -77088,7 +77158,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 );
@@ -79534,7 +79607,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);
@@ -79596,7 +79672,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
}else if( loc<0 && pPage->nCell>0 ){
assert( pPage->leaf );
idx = ++pCur->ix;
- pCur->curFlags &= ~BTCF_ValidNKey;
+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
}else{
assert( pPage->leaf );
}
@@ -79626,7 +79702,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
*/
if( pPage->nOverflow ){
assert( rc==SQLITE_OK );
- pCur->curFlags &= ~(BTCF_ValidNKey);
+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
rc = balance(pCur);
/* Must make sure nOverflow is reset to zero even if the balance()
@@ -88379,6 +88455,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 */
@@ -89058,7 +89151,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]);
@@ -89083,14 +89176,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);
}
}
@@ -89160,7 +89257,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 ){
@@ -94858,7 +94962,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 );
@@ -94867,7 +94973,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 );
@@ -106212,6 +106320,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 ){
@@ -106599,8 +106709,37 @@ static int lookupName(
}
}
if( 0==cnt && VisibleRowid(pTab) ){
+ /* pTab is a potential ROWID match. Keep track of it and match
+ ** the ROWID later if that seems appropriate. (Search for "cntTab"
+ ** to find related code.) Only allow a ROWID match if there is
+ ** a single ROWID match candidate.
+ */
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ /* In SQLITE_ALLOW_ROWID_IN_VIEW mode, allow a ROWID match
+ ** if there is a single VIEW candidate or if there is a single
+ ** non-VIEW candidate plus multiple VIEW candidates. In other
+ ** words non-VIEW candidate terms take precedence over VIEWs.
+ */
+ if( cntTab==0
+ || (cntTab==1
+ && ALWAYS(pMatch!=0)
+ && ALWAYS(pMatch->pTab!=0)
+ && (pMatch->pTab->tabFlags & TF_Ephemeral)!=0
+ && (pTab->tabFlags & TF_Ephemeral)==0)
+ ){
+ cntTab = 1;
+ pMatch = pItem;
+ }else{
+ cntTab++;
+ }
+#else
+ /* The (much more common) non-SQLITE_ALLOW_ROWID_IN_VIEW case is
+ ** simpler since we require exactly one candidate, which will
+ ** always be a non-VIEW
+ */
cntTab++;
pMatch = pItem;
+#endif
}
}
if( pMatch ){
@@ -106726,13 +106865,13 @@ static int lookupName(
** Perhaps the name is a reference to the ROWID
*/
if( cnt==0
- && cntTab==1
+ && cntTab>=1
&& pMatch
&& (pNC->ncFlags & (NC_IdxExpr|NC_GenCol))==0
&& sqlite3IsRowid(zCol)
&& ALWAYS(VisibleRowid(pMatch->pTab) || pMatch->fg.isNestedFrom)
){
- cnt = 1;
+ cnt = cntTab;
if( pMatch->fg.isNestedFrom==0 ) pExpr->iColumn = -1;
pExpr->affExpr = SQLITE_AFF_INTEGER;
}
@@ -107097,6 +107236,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: {
@@ -107107,19 +107259,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;
}
@@ -108019,7 +108188,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++){
@@ -108558,9 +108729,10 @@ SQLITE_PRIVATE Expr *sqlite3ExprSkipCollateAndLikely(Expr *pExpr){
assert( pExpr->x.pList->nExpr>0 );
assert( pExpr->op==TK_FUNCTION );
pExpr = pExpr->x.pList->a[0].pExpr;
- }else{
- assert( pExpr->op==TK_COLLATE );
+ }else if( pExpr->op==TK_COLLATE ){
pExpr = pExpr->pLeft;
+ }else{
+ break;
}
}
return pExpr;
@@ -111079,9 +111251,12 @@ SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){
return 0;
case TK_COLUMN:
assert( ExprUseYTab(p) );
- return ExprHasProperty(p, EP_CanBeNull) ||
- NEVER(p->y.pTab==0) || /* Reference to column of index on expr */
- (p->iColumn>=0
+ return ExprHasProperty(p, EP_CanBeNull)
+ || NEVER(p->y.pTab==0) /* Reference to column of index on expr */
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ || (p->iColumn==XN_ROWID && IsView(p->y.pTab))
+#endif
+ || (p->iColumn>=0
&& p->y.pTab->aCol!=0 /* Possible due to prior error */
&& ALWAYS(p->iColumn<p->y.pTab->nCol)
&& p->y.pTab->aCol[p->iColumn].notNull==0);
@@ -123572,9 +123747,12 @@ SQLITE_PRIVATE void sqlite3CreateView(
** on a view, even though views do not have rowids. The following flag
** setting fixes this problem. But the fix can be disabled by compiling
** with -DSQLITE_ALLOW_ROWID_IN_VIEW in case there are legacy apps that
- ** depend upon the old buggy behavior. */
-#ifndef SQLITE_ALLOW_ROWID_IN_VIEW
- p->tabFlags |= TF_NoVisibleRowid;
+ ** depend upon the old buggy behavior. The ability can also be toggled
+ ** using sqlite3_config(SQLITE_CONFIG_ROWID_IN_VIEW,...) */
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ p->tabFlags |= sqlite3Config.mNoVisibleRowid; /* Optional. Allow by default */
+#else
+ p->tabFlags |= TF_NoVisibleRowid; /* Never allow rowid in view */
#endif
sqlite3TwoPartName(pParse, pName1, pName2, &pName);
@@ -128947,13 +129125,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;
@@ -129255,7 +129433,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]);
@@ -129738,7 +129916,7 @@ static void sumFinalize(sqlite3_context *context){
if( p->approx ){
if( p->ovrfl ){
sqlite3_result_error(context,"integer overflow",-1);
- }else if( !sqlite3IsNaN(p->rErr) ){
+ }else if( !sqlite3IsOverflow(p->rErr) ){
sqlite3_result_double(context, p->rSum+p->rErr);
}else{
sqlite3_result_double(context, p->rSum);
@@ -129755,7 +129933,7 @@ static void avgFinalize(sqlite3_context *context){
double r;
if( p->approx ){
r = p->rSum;
- if( !sqlite3IsNaN(p->rErr) ) r += p->rErr;
+ if( !sqlite3IsOverflow(p->rErr) ) r += p->rErr;
}else{
r = (double)(p->iSum);
}
@@ -129769,7 +129947,7 @@ static void totalFinalize(sqlite3_context *context){
if( p ){
if( p->approx ){
r = p->rSum;
- if( !sqlite3IsNaN(p->rErr) ) r += p->rErr;
+ if( !sqlite3IsOverflow(p->rErr) ) r += p->rErr;
}else{
r = (double)(p->iSum);
}
@@ -133175,7 +133353,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;
}
}
@@ -135067,7 +135245,10 @@ static int xferOptimization(
}
}
#ifndef SQLITE_OMIT_CHECK
- if( pDest->pCheck && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1) ){
+ if( pDest->pCheck
+ && (db->mDbFlags & DBFLAG_Vacuum)==0
+ && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1)
+ ){
return 0; /* Tables have different CHECK constraints. Ticket #2252 */
}
#endif
@@ -139474,31 +139655,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;
@@ -139633,6 +139790,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);
}
@@ -139823,6 +139981,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);
@@ -140459,7 +140649,11 @@ static int pragmaVtabBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
j = seen[0]-1;
pIdxInfo->aConstraintUsage[j].argvIndex = 1;
pIdxInfo->aConstraintUsage[j].omit = 1;
- if( seen[1]==0 ) return SQLITE_OK;
+ if( seen[1]==0 ){
+ pIdxInfo->estimatedCost = (double)1000;
+ pIdxInfo->estimatedRows = 1000;
+ return SQLITE_OK;
+ }
pIdxInfo->estimatedCost = (double)20;
pIdxInfo->estimatedRows = 20;
j = seen[1]-1;
@@ -143686,11 +143880,7 @@ static const char *columnTypeImpl(
** data for the result-set column of the sub-select.
*/
if( iCol<pS->pEList->nExpr
-#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
- && iCol>=0
-#else
- && ALWAYS(iCol>=0)
-#endif
+ && (!ViewCanHaveRowid || iCol>=0)
){
/* If iCol is less than zero, then the expression requests the
** rowid of the sub-select or view. This expression is legal (see
@@ -146865,6 +147055,10 @@ static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){
**
** (11) The subquery is not a VALUES clause
**
+** (12) The WHERE clause is not "rowid ISNULL" or the equivalent. This
+** case only comes up if SQLite is compiled using
+** SQLITE_ALLOW_ROWID_IN_VIEW.
+**
** Return 0 if no changes are made and non-zero if one or more WHERE clause
** terms are duplicated into the subquery.
*/
@@ -146975,6 +147169,18 @@ static int pushDownWhereTerms(
}
#endif
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ if( ViewCanHaveRowid && (pWhere->op==TK_ISNULL || pWhere->op==TK_NOTNULL) ){
+ Expr *pLeft = pWhere->pLeft;
+ if( ALWAYS(pLeft)
+ && pLeft->op==TK_COLUMN
+ && pLeft->iColumn < 0
+ ){
+ return 0; /* Restriction (12) */
+ }
+ }
+#endif
+
if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc) ){
nChng++;
pSubq->selFlags |= SF_PushDown;
@@ -147602,12 +147808,14 @@ SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse *pParse, SrcItem *pFrom){
while( pSel->pPrior ){ pSel = pSel->pPrior; }
sqlite3ColumnsFromExprList(pParse, pSel->pEList,&pTab->nCol,&pTab->aCol);
pTab->iPKey = -1;
+ pTab->eTabType = TABTYP_VIEW;
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
#ifndef SQLITE_ALLOW_ROWID_IN_VIEW
/* The usual case - do not allow ROWID on a subquery */
pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid;
#else
- pTab->tabFlags |= TF_Ephemeral; /* Legacy compatibility mode */
+ /* Legacy compatibility mode */
+ pTab->tabFlags |= TF_Ephemeral | sqlite3Config.mNoVisibleRowid;
#endif
return pParse->nErr ? SQLITE_ERROR : SQLITE_OK;
}
@@ -147875,7 +148083,7 @@ static int selectExpander(Walker *pWalker, Select *p){
pNestedFrom = pFrom->pSelect->pEList;
assert( pNestedFrom!=0 );
assert( pNestedFrom->nExpr==pTab->nCol );
- assert( VisibleRowid(pTab)==0 );
+ assert( VisibleRowid(pTab)==0 || ViewCanHaveRowid );
}else{
if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){
continue;
@@ -147907,7 +148115,8 @@ static int selectExpander(Walker *pWalker, Select *p){
pUsing = 0;
}
- nAdd = pTab->nCol + (VisibleRowid(pTab) && (selFlags&SF_NestedFrom));
+ nAdd = pTab->nCol;
+ if( VisibleRowid(pTab) && (selFlags & SF_NestedFrom)!=0 ) nAdd++;
for(j=0; j<nAdd; j++){
const char *zName;
struct ExprList_item *pX; /* Newly added ExprList term */
@@ -147989,7 +148198,8 @@ static int selectExpander(Walker *pWalker, Select *p){
pX = &pNew->a[pNew->nExpr-1];
assert( pX->zEName==0 );
if( (selFlags & SF_NestedFrom)!=0 && !IN_RENAME_OBJECT ){
- if( pNestedFrom ){
+ if( pNestedFrom && (!ViewCanHaveRowid || j<pNestedFrom->nExpr) ){
+ assert( j<pNestedFrom->nExpr );
pX->zEName = sqlite3DbStrDup(db, pNestedFrom->a[j].zEName);
testcase( pX->zEName==0 );
}else{
@@ -152923,6 +153133,9 @@ SQLITE_PRIVATE void sqlite3Update(
}
}
if( chngRowid==0 && pPk==0 ){
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ if( isView ) sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid);
+#endif
sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid);
}
}
@@ -153460,7 +153673,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 */
@@ -153563,6 +153777,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 ){
@@ -153589,9 +153811,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;
}
@@ -166619,16 +166845,10 @@ static SQLITE_NOINLINE void whereAddIndexedExpr(
for(i=0; i<pIdx->nColumn; i++){
Expr *pExpr;
int j = pIdx->aiColumn[i];
- int bMaybeNullRow;
if( j==XN_EXPR ){
pExpr = pIdx->aColExpr->a[i].pExpr;
- testcase( pTabItem->fg.jointype & JT_LEFT );
- testcase( pTabItem->fg.jointype & JT_RIGHT );
- testcase( pTabItem->fg.jointype & JT_LTORJ );
- bMaybeNullRow = (pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0;
}else if( j>=0 && (pTab->aCol[j].colFlags & COLFLAG_VIRTUAL)!=0 ){
pExpr = sqlite3ColumnExpr(pTab, &pTab->aCol[j]);
- bMaybeNullRow = 0;
}else{
continue;
}
@@ -166660,7 +166880,7 @@ static SQLITE_NOINLINE void whereAddIndexedExpr(
p->iDataCur = pTabItem->iCursor;
p->iIdxCur = iIdxCur;
p->iIdxCol = i;
- p->bMaybeNullRow = bMaybeNullRow;
+ p->bMaybeNullRow = (pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0;
if( sqlite3IndexAffinityStr(pParse->db, pIdx) ){
p->aff = pIdx->zColAff[i];
}
@@ -178865,6 +179085,18 @@ SQLITE_API int sqlite3_config(int op, ...){
}
#endif /* SQLITE_OMIT_DESERIALIZE */
+ case SQLITE_CONFIG_ROWID_IN_VIEW: {
+ int *pVal = va_arg(ap,int*);
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ if( 0==*pVal ) sqlite3GlobalConfig.mNoVisibleRowid = TF_NoVisibleRowid;
+ if( 1==*pVal ) sqlite3GlobalConfig.mNoVisibleRowid = 0;
+ *pVal = (sqlite3GlobalConfig.mNoVisibleRowid==0);
+#else
+ *pVal = 0;
+#endif
+ break;
+ }
+
default: {
rc = SQLITE_ERROR;
break;
@@ -204785,6 +205017,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;
@@ -205183,6 +205416,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);
@@ -206508,8 +206745,9 @@ rebuild_from_cache:
}
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;
@@ -206675,10 +206913,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);
}
@@ -208086,6 +208324,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: {
@@ -208132,9 +208373,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;
}
@@ -209160,11 +209401,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);
}
/*
@@ -209208,7 +209447,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. */
@@ -209268,6 +209506,7 @@ static int nodeAcquire(
}
*ppNode = pNode;
}else{
+ nodeBlobReset(pRtree);
if( pNode ){
pRtree->nNodeRef--;
sqlite3_free(pNode);
@@ -209412,6 +209651,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);
}
@@ -209601,7 +209841,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;
}
@@ -210186,7 +210428,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;
}
@@ -210204,6 +210450,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 ){
@@ -211685,8 +211932,7 @@ constraint:
*/
static int rtreeBeginTransaction(sqlite3_vtab *pVtab){
Rtree *pRtree = (Rtree *)pVtab;
- assert( pRtree->inWrTrans==0 );
- pRtree->inWrTrans++;
+ pRtree->inWrTrans = 1;
return SQLITE_OK;
}
@@ -211700,6 +211946,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.
@@ -211818,7 +212067,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 */
@@ -245377,23 +245626,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);
+ }
}
/*
@@ -250547,7 +250799,7 @@ static void fts5SourceIdFunc(
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
- sqlite3_result_text(pCtx, "fts5: 2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a", -1, SQLITE_TRANSIENT);
+ sqlite3_result_text(pCtx, "fts5: 2024-04-15 13:34:05 8653b758870e6ef0c98d46b3ace27849054af85da891eb121e9aaa537f1e8355", -1, SQLITE_TRANSIENT);
}
/*
diff --git a/src/libs/3rdparty/sqlite/sqlite3.h b/src/libs/3rdparty/sqlite/sqlite3.h
index 4fdfde004e..2618b37a7b 100644
--- a/src/libs/3rdparty/sqlite/sqlite3.h
+++ b/src/libs/3rdparty/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.1"
-#define SQLITE_VERSION_NUMBER 3045001
-#define SQLITE_SOURCE_ID "2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a"
+#define SQLITE_VERSION "3.45.3"
+#define SQLITE_VERSION_NUMBER 3045003
+#define SQLITE_SOURCE_ID "2024-04-15 13:34:05 8653b758870e6ef0c98d46b3ace27849054af85da891eb121e9aaa537f1e8355"
/*
** 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(
@@ -2141,6 +2143,22 @@ struct sqlite3_mem_methods {
** configuration setting is never used, then the default maximum is determined
** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that
** compile-time option is not set, then the default maximum is 1073741824.
+**
+** [[SQLITE_CONFIG_ROWID_IN_VIEW]]
+** <dt>SQLITE_CONFIG_ROWID_IN_VIEW
+** <dd>The SQLITE_CONFIG_ROWID_IN_VIEW option enables or disables the ability
+** for VIEWs to have a ROWID. The capability can only be enabled if SQLite is
+** compiled with -DSQLITE_ALLOW_ROWID_IN_VIEW, in which case the capability
+** defaults to on. This configuration option queries the current setting or
+** changes the setting to off or on. The argument is a pointer to an integer.
+** If that integer initially holds a value of 1, then the ability for VIEWs to
+** have ROWIDs is activated. If the integer initially holds zero, then the
+** ability is deactivated. Any other initial value for the integer leaves the
+** setting unchanged. After changes, if any, the integer is written with
+** a 1 or 0, if the ability for VIEWs to have ROWIDs is on or off. If SQLite
+** is compiled without -DSQLITE_ALLOW_ROWID_IN_VIEW (which is the usual and
+** recommended case) then the integer is always filled with zero, regardless
+** if its initial value.
** </dl>
*/
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
@@ -2172,6 +2190,7 @@ struct sqlite3_mem_methods {
#define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */
#define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */
#define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */
+#define SQLITE_CONFIG_ROWID_IN_VIEW 30 /* int* */
/*
** CAPI3REF: Database Connection Configuration Options
diff --git a/src/libs/languageutils/componentversion.cpp b/src/libs/languageutils/componentversion.cpp
index 6c599e300b..f041740d3b 100644
--- a/src/libs/languageutils/componentversion.cpp
+++ b/src/libs/languageutils/componentversion.cpp
@@ -10,27 +10,17 @@
namespace LanguageUtils {
-const int ComponentVersion::NoVersion = -1;
-const int ComponentVersion::MaxVersion = std::numeric_limits<int>::max();
+// QTC_TEMP
-ComponentVersion::ComponentVersion()
- : _major(NoVersion), _minor(NoVersion)
+ComponentVersion::ComponentVersion(QStringView versionString)
+ : _major(NoVersion)
+ , _minor(NoVersion)
{
-}
-
-ComponentVersion::ComponentVersion(int major, int minor)
- : _major(major), _minor(minor)
-{
-}
-
-ComponentVersion::ComponentVersion(const QString &versionString)
- : _major(NoVersion), _minor(NoVersion)
-{
- int dotIdx = versionString.indexOf(QLatin1Char('.'));
+ auto dotIdx = versionString.indexOf(QLatin1Char('.'));
if (dotIdx == -1)
return;
bool ok = false;
- int maybeMajor = versionString.left(dotIdx).toInt(&ok);
+ auto maybeMajor = versionString.left(dotIdx).toInt(&ok);
if (!ok)
return;
int maybeMinor = versionString.mid(dotIdx + 1).toInt(&ok);
@@ -40,15 +30,6 @@ ComponentVersion::ComponentVersion(const QString &versionString)
_minor = maybeMinor;
}
-ComponentVersion::~ComponentVersion()
-{
-}
-
-bool ComponentVersion::isValid() const
-{
- return _major >= 0 && _minor >= 0;
-}
-
QString ComponentVersion::toString() const
{
QByteArray temp;
@@ -65,36 +46,4 @@ void ComponentVersion::addToHash(QCryptographicHash &hash) const
hash.addData(reinterpret_cast<const char *>(&_minor), sizeof(_minor));
}
-bool operator<(const ComponentVersion &lhs, const ComponentVersion &rhs)
-{
- return lhs.majorVersion() < rhs.majorVersion()
- || (lhs.majorVersion() == rhs.majorVersion() && lhs.minorVersion() < rhs.minorVersion());
-}
-
-bool operator<=(const ComponentVersion &lhs, const ComponentVersion &rhs)
-{
- return lhs.majorVersion() < rhs.majorVersion()
- || (lhs.majorVersion() == rhs.majorVersion() && lhs.minorVersion() <= rhs.minorVersion());
-}
-
-bool operator>(const ComponentVersion &lhs, const ComponentVersion &rhs)
-{
- return rhs < lhs;
-}
-
-bool operator>=(const ComponentVersion &lhs, const ComponentVersion &rhs)
-{
- return rhs <= lhs;
-}
-
-bool operator==(const ComponentVersion &lhs, const ComponentVersion &rhs)
-{
- return lhs.majorVersion() == rhs.majorVersion() && lhs.minorVersion() == rhs.minorVersion();
-}
-
-bool operator!=(const ComponentVersion &lhs, const ComponentVersion &rhs)
-{
- return !(lhs == rhs);
-}
-
} // namespace LanguageUtils
diff --git a/src/libs/languageutils/componentversion.h b/src/libs/languageutils/componentversion.h
index 4b39e8402a..2b8311a6cb 100644
--- a/src/libs/languageutils/componentversion.h
+++ b/src/libs/languageutils/componentversion.h
@@ -9,6 +9,10 @@ QT_BEGIN_NAMESPACE
class QCryptographicHash;
QT_END_NAMESPACE
+#include <QStringView>
+
+#include <limits>
+
namespace LanguageUtils {
class LANGUAGEUTILS_EXPORT ComponentVersion
@@ -17,25 +21,56 @@ class LANGUAGEUTILS_EXPORT ComponentVersion
int _minor;
public:
- static const int NoVersion;
- static const int MaxVersion;
+ static constexpr int NoVersion = -1;
+ static constexpr int MaxVersion = std::numeric_limits<int>::max();
+
+ ComponentVersion()
+ : _major(NoVersion)
+ , _minor(NoVersion)
+ {}
- ComponentVersion();
- ComponentVersion(int major, int minor);
- explicit ComponentVersion(const QString &versionString);
- ~ComponentVersion();
+ ComponentVersion(int major, int minor)
+ : _major(major)
+ , _minor(minor)
+ {}
+
+ explicit ComponentVersion(QStringView versionString);
+ ~ComponentVersion() = default;
int majorVersion() const { return _major; }
int minorVersion() const { return _minor; }
- friend bool LANGUAGEUTILS_EXPORT operator<(const ComponentVersion &lhs, const ComponentVersion &rhs);
- friend bool LANGUAGEUTILS_EXPORT operator<=(const ComponentVersion &lhs, const ComponentVersion &rhs);
- friend bool LANGUAGEUTILS_EXPORT operator>(const ComponentVersion &lhs, const ComponentVersion &rhs);
- friend bool LANGUAGEUTILS_EXPORT operator>=(const ComponentVersion &lhs, const ComponentVersion &rhs);
- friend bool LANGUAGEUTILS_EXPORT operator==(const ComponentVersion &lhs, const ComponentVersion &rhs);
- friend bool LANGUAGEUTILS_EXPORT operator!=(const ComponentVersion &lhs, const ComponentVersion &rhs);
+ friend bool operator<(const ComponentVersion &lhs, const ComponentVersion &rhs)
+ {
+ return std::tie(lhs._major, lhs._minor) < std::tie(rhs._major, rhs._minor);
+ }
+
+ friend bool operator<=(const ComponentVersion &lhs, const ComponentVersion &rhs)
+ {
+ return std::tie(lhs._major, lhs._minor) <= std::tie(rhs._major, rhs._minor);
+ }
+
+ friend bool operator>(const ComponentVersion &lhs, const ComponentVersion &rhs)
+ {
+ return rhs < lhs;
+ }
+
+ friend bool operator>=(const ComponentVersion &lhs, const ComponentVersion &rhs)
+ {
+ return rhs <= lhs;
+ }
+
+ friend bool operator==(const ComponentVersion &lhs, const ComponentVersion &rhs)
+ {
+ return lhs.majorVersion() == rhs.majorVersion() && lhs.minorVersion() == rhs.minorVersion();
+ }
+
+ friend bool operator!=(const ComponentVersion &lhs, const ComponentVersion &rhs)
+ {
+ return !(lhs == rhs);
+ }
- bool isValid() const;
+ bool isValid() const { return _major >= 0 && _minor >= 0; }
QString toString() const;
void addToHash(QCryptographicHash &hash) const;
};
diff --git a/src/libs/nanotrace/CMakeLists.txt b/src/libs/nanotrace/CMakeLists.txt
index 011c26f049..50693644ea 100644
--- a/src/libs/nanotrace/CMakeLists.txt
+++ b/src/libs/nanotrace/CMakeLists.txt
@@ -4,6 +4,7 @@ add_qtc_library(Nanotrace
nanotraceglobals.h
nanotrace.cpp nanotrace.h
nanotracehr.cpp nanotracehr.h
+ staticstring.h
PUBLIC_DEPENDS Qt::Core Qt::Gui
PROPERTIES
CXX_VISIBILITY_PRESET default
diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp
index 07d2ff95ac..0006227434 100644
--- a/src/libs/nanotrace/nanotracehr.cpp
+++ b/src/libs/nanotrace/nanotracehr.cpp
@@ -45,6 +45,18 @@ unsigned int getUnsignedIntegerHash(std::thread::id id)
return static_cast<unsigned int>(std::hash<std::thread::id>{}(id) & 0xFFFFFFFF);
}
+template<std::size_t capacity>
+constexpr bool isArgumentValid(const StaticString<capacity> &string)
+{
+ return string.isValid() && string.size();
+}
+
+template<typename String>
+constexpr bool isArgumentValid(const String &string)
+{
+ return string.size();
+}
+
template<typename TraceEvent>
void printEvent(std::ostream &out, const TraceEvent &event, qint64 processId, std::thread::id threadId)
{
@@ -67,23 +79,24 @@ void printEvent(std::ostream &out, const TraceEvent &event, qint64 processId, st
out << R"(,"flow_in":true)";
}
- if (event.arguments.size())
+ if (isArgumentValid(event.arguments)) {
out << R"(,"args":)" << event.arguments;
+ }
out << "}";
}
-void writeMetaEvent(TraceFile<Tracing::IsEnabled> *file, std::string_view key, std::string_view value)
+void writeMetaEvent(TraceFile<Tracing::IsEnabled> &file, std::string_view key, std::string_view value)
{
- std::lock_guard lock{file->fileMutex};
- auto &out = file->out;
+ std::lock_guard lock{file.fileMutex};
+ auto &out = file.out;
if (out.is_open()) {
- file->out << R"({"name":")" << key << R"(","ph":"M", "pid":)"
- << getUnsignedIntegerHash(QCoreApplication::applicationPid()) << R"(,"tid":)"
- << getUnsignedIntegerHash(std::this_thread::get_id()) << R"(,"args":{"name":")"
- << value << R"("}})"
- << ",\n";
+ file.out << R"({"name":")" << key << R"(","ph":"M", "pid":)"
+ << getUnsignedIntegerHash(QCoreApplication::applicationPid()) << R"(,"tid":)"
+ << getUnsignedIntegerHash(std::this_thread::get_id()) << R"(,"args":{"name":")"
+ << value << R"("}})"
+ << ",\n";
}
}
@@ -103,7 +116,6 @@ std::string getThreadName()
} // namespace
-namespace Internal {
template<typename String>
void convertToString(String &string, const QImage &image)
{
@@ -132,14 +144,11 @@ void convertToString(String &string, const QImage &image)
return "alpha premultiplied"sv;
}))));
- Internal::convertToString(string, dict);
+ convertToString(string, dict);
}
-template NANOTRACE_EXPORT void convertToString(std::string &string, const QImage &image);
template NANOTRACE_EXPORT void convertToString(ArgumentsString &string, const QImage &image);
-} // namespace Internal
-
template<typename TraceEvent>
void flushEvents(const Utils::span<TraceEvent> events,
std::thread::id threadId,
@@ -148,8 +157,8 @@ void flushEvents(const Utils::span<TraceEvent> events,
if (events.empty())
return;
- std::lock_guard lock{eventQueue.file->fileMutex};
- auto &out = eventQueue.file->out;
+ std::lock_guard lock{eventQueue.file.fileMutex};
+ auto &out = eventQueue.file.out;
if (out.is_open()) {
auto processId = QCoreApplication::applicationPid();
@@ -200,17 +209,17 @@ void finalizeFile(EnabledTraceFile &file)
template<typename TraceEvent>
void flushInThread(EnabledEventQueue<TraceEvent> &eventQueue)
{
- if (eventQueue.file->processing.valid())
- eventQueue.file->processing.wait();
+ if (eventQueue.file.processing.valid())
+ eventQueue.file.processing.wait();
auto flush = [&](const Utils::span<TraceEvent> &events, std::thread::id threadId) {
flushEvents(events, threadId, eventQueue);
};
- eventQueue.file->processing = std::async(std::launch::async,
- flush,
- eventQueue.currentEvents.subspan(0, eventQueue.eventsIndex),
- eventQueue.threadId);
+ eventQueue.file.processing = std::async(std::launch::async,
+ flush,
+ eventQueue.currentEvents.subspan(0, eventQueue.eventsIndex),
+ eventQueue.threadId);
eventQueue.currentEvents = eventQueue.currentEvents.data() == eventQueue.eventsOne.data()
? eventQueue.eventsTwo
: eventQueue.eventsOne;
@@ -223,10 +232,11 @@ template NANOTRACE_EXPORT void flushInThread(
EnabledEventQueue<StringViewWithStringArgumentsTraceEvent> &eventQueue);
template<typename TraceEvent>
-EventQueue<TraceEvent, Tracing::IsEnabled>::EventQueue(EnabledTraceFile *file)
+EventQueue<TraceEvent, Tracing::IsEnabled>::EventQueue(EnabledTraceFile &file)
: file{file}
, threadId{std::this_thread::get_id()}
{
+ setEventsSpans(*eventArrayOne.get(), *eventArrayTwo.get());
Internal::EventQueueTracker<TraceEvent>::get().addQueue(this);
if (auto thread = QThread::currentThread()) {
auto name = getThreadName();
diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h
index 7856ea3582..810e4e3c5d 100644
--- a/src/libs/nanotrace/nanotracehr.h
+++ b/src/libs/nanotrace/nanotracehr.h
@@ -5,16 +5,22 @@
#include "nanotraceglobals.h"
+#include "staticstring.h"
+
#include <utils/smallstring.h>
#include <utils/span.h>
#include <QByteArrayView>
+#include <QList>
+#include <QMap>
#include <QStringView>
+#include <QVarLengthArray>
#include <QVariant>
#include <array>
#include <atomic>
#include <chrono>
+#include <exception>
#include <fstream>
#include <future>
#include <mutex>
@@ -41,7 +47,7 @@ enum class Tracing { IsDisabled, IsEnabled };
# define NO_UNIQUE_ADDRESS
#endif
-using ArgumentsString = Utils::BasicSmallString<510>;
+using ArgumentsString = StaticString<3700>;
namespace Literals {
struct TracerLiteral
@@ -80,44 +86,41 @@ struct IsDictonary
inline constexpr IsDictonary isDictonary;
-namespace Internal {
-
template<typename String>
void convertToString(String &string, std::string_view text)
{
- string.append(R"(")");
+ string.append('\"');
string.append(text);
- string.append(R"(")");
+ string.append('\"');
}
template<typename String>
void convertToString(String &string, const QImage &image);
-extern template NANOTRACE_EXPORT void convertToString(std::string &string, const QImage &image);
extern template NANOTRACE_EXPORT void convertToString(ArgumentsString &string, const QImage &image);
template<typename String, std::size_t size>
void convertToString(String &string, const char (&text)[size])
{
- string.append(R"(")");
- string.append(text);
- string.append(R"(")");
+ string.append('\"');
+ string.append(std::string_view{text, size - 1});
+ string.append('\"');
}
template<typename String>
void convertToString(String &string, QStringView text)
{
- string.append(R"(")");
+ string.append('\"');
string.append(Utils::PathString{text});
- string.append(R"(")");
+ string.append('\"');
}
template<typename String>
void convertToString(String &string, const QByteArray &text)
{
- string.append(R"(")");
+ string.append('\"');
string.append(std::string_view(text.data(), Utils::usize(text)));
- string.append(R"(")");
+ string.append('\"');
}
template<typename String>
@@ -129,28 +132,29 @@ void convertToString(String &string, bool isTrue)
string.append("false");
}
-template<typename String, typename Callable, typename = std::enable_if_t<std::is_invocable_v<Callable>>>
+template<typename String, typename Callable, typename std::enable_if_t<std::is_invocable_v<Callable>, bool> = true>
void convertToString(String &string, Callable &&callable)
{
convertToString(string, callable());
}
-template<typename String>
-void convertToString(String &string, int number)
+template<typename String, typename Number, typename std::enable_if_t<std::is_arithmetic_v<Number>, bool> = true>
+void convertToString(String &string, Number number)
{
- string.append(Utils::SmallString::number(number));
+ string.append(number);
}
-template<typename String>
-void convertToString(String &string, long long number)
+template<typename Enumeration>
+constexpr std::underlying_type_t<Enumeration> to_underlying(Enumeration enumeration) noexcept
{
- string.append(Utils::SmallString::number(number));
+ static_assert(std::is_enum_v<Enumeration>, "to_underlying expect an enumeration");
+ return static_cast<std::underlying_type_t<Enumeration>>(enumeration);
}
-template<typename String>
-void convertToString(String &string, double number)
+template<typename String, typename Enumeration, typename std::enable_if_t<std::is_enum_v<Enumeration>, bool> = true>
+void convertToString(String &string, Enumeration enumeration)
{
- string.append(Utils::SmallString::number(number));
+ string.append(to_underlying(enumeration));
}
template<typename String>
@@ -165,14 +169,14 @@ void convertToString(String &string, const QVariant &value)
convertToString(string, value.toString());
}
-template<typename String, typename... Arguments>
-void convertToString(String &string, const std::tuple<const IsDictonary &, Arguments...> &dictonary);
-
-template<typename String, typename... Arguments>
-void convertToString(String &string, const std::tuple<const IsArray &, Arguments...> &list);
-
-template<typename String, template<typename...> typename Container, typename... Arguments>
-void convertToString(String &string, const Container<Arguments...> &container);
+template<typename String, typename Type>
+void convertToString(String &string, const std::optional<Type> &value)
+{
+ if (value)
+ convertToString(string, *value);
+ else
+ convertToString(string, "empty optional");
+}
template<typename String, typename Value>
void convertArrayEntryToString(String &string, const Value &value)
@@ -184,15 +188,15 @@ void convertArrayEntryToString(String &string, const Value &value)
template<typename String, typename... Entries>
void convertArrayToString(String &string, const IsArray &, Entries &...entries)
{
- string.append(R"([)");
+ string.append('[');
(convertArrayEntryToString(string, entries), ...);
if (sizeof...(entries))
string.pop_back();
- string.append("]");
+ string.append(']');
}
template<typename String, typename... Arguments>
-void convertToString(String &string, const std::tuple<const IsArray &, Arguments...> &list)
+void convertToString(String &string, const std::tuple<IsArray, Arguments...> &list)
{
std::apply([&](auto &&...entries) { convertArrayToString(string, entries...); }, list);
}
@@ -202,55 +206,107 @@ void convertDictonaryEntryToString(String &string, const std::tuple<Key, Value>
{
const auto &[key, value] = argument;
convertToString(string, key);
- string.append(":");
+ string.append(':');
convertToString(string, value);
- string.append(",");
+ string.append(',');
}
template<typename String, typename... Entries>
void convertDictonaryToString(String &string, const IsDictonary &, Entries &...entries)
{
- string.append(R"({)");
+ string.append('{');
(convertDictonaryEntryToString(string, entries), ...);
if (sizeof...(entries))
string.pop_back();
- string.append("}");
+ string.append('}');
}
template<typename String, typename... Arguments>
-void convertToString(String &string, const std::tuple<const IsDictonary &, Arguments...> &dictonary)
+void convertToString(String &string, const std::tuple<IsDictonary, Arguments...> &dictonary)
{
std::apply([&](auto &&...entries) { convertDictonaryToString(string, entries...); }, dictonary);
}
-template<typename String, template<typename...> typename Container, typename... Arguments>
-void convertToString(String &string, const Container<Arguments...> &container)
+template<typename T>
+struct is_container : std::false_type
+{};
+
+template<typename... Arguments>
+struct is_container<std::vector<Arguments...>> : std::true_type
+{};
+
+template<typename... Arguments>
+struct is_container<QList<Arguments...>> : std::true_type
+{};
+
+template<typename T, qsizetype Prealloc>
+struct is_container<QVarLengthArray<T, Prealloc>> : std::true_type
+{};
+
+template<typename String, typename Container, typename std::enable_if_t<is_container<Container>::value, bool> = true>
+void convertToString(String &string, const Container &values)
+{
+ string.append('[');
+
+ for (const auto &value : values) {
+ convertToString(string, value);
+ string.append(',');
+ }
+
+ if (values.size())
+ string.pop_back();
+
+ string.append(']');
+}
+
+template<typename T>
+struct is_map : std::false_type
+{};
+
+template<typename... Arguments>
+struct is_map<QtPrivate::QKeyValueRange<Arguments...>> : std::true_type
+{};
+
+template<typename... Arguments>
+struct is_map<std::map<Arguments...>> : std::true_type
+{};
+
+template<typename String, typename Map, typename std::enable_if_t<is_map<Map>::value, bool> = true>
+void convertToString(String &string, const Map &map)
{
- string.append("[");
- for (const auto &entry : container) {
- convertToString(string, entry);
- string.append(",");
+ string.append('{');
+
+ for (const auto &[key, value] : map) {
+ convertToString(string, key);
+ string.append(':');
+ convertToString(string, value);
+ string.append(',');
}
- if (container.size())
+ if (map.begin() != map.end())
string.pop_back();
- string.append("]");
+ string.append('}');
+}
+
+template<typename String, typename Key, typename Value>
+void convertToString(String &string, const QMap<Key, Value> &dictonary)
+{
+ convertToString(string, dictonary.asKeyValueRange());
}
+namespace Internal {
+
template<typename String, typename... Arguments>
-String toArguments(Arguments &&...arguments)
+void toArguments(String &text, Arguments &&...arguments)
{
- String text;
constexpr auto argumentCount = sizeof...(Arguments);
- text.append("{");
+ text.append('{');
(convertDictonaryEntryToString(text, arguments), ...);
if (argumentCount)
text.pop_back();
- text.append("}");
-
- return text;
+ text.append('}');
}
inline std::string_view toArguments(std::string_view arguments)
@@ -259,48 +315,58 @@ inline std::string_view toArguments(std::string_view arguments)
}
template<typename String>
-void appendArguments(String &eventArguments)
+void setArguments(String &eventArguments)
{
- eventArguments = {};
+ if constexpr (std::is_same_v<String, std::string_view>)
+ eventArguments = {};
+ else
+ eventArguments.clear();
}
template<typename String>
-void appendArguments(String &eventArguments, TracerLiteral arguments)
+void setArguments(String &eventArguments, TracerLiteral arguments)
{
eventArguments = arguments;
}
template<typename String, typename... Arguments>
-[[maybe_unused]] void appendArguments(String &eventArguments, Arguments &&...arguments)
+[[maybe_unused]] void setArguments(String &eventArguments, Arguments &&...arguments)
{
static_assert(
!std::is_same_v<String, std::string_view>,
R"(The arguments type of the tracing event queue is a string view. You can only provide trace token arguments as TracerLiteral (""_t).)");
- eventArguments = Internal::toArguments<String>(std::forward<Arguments>(arguments)...);
+ if constexpr (std::is_same_v<String, std::string_view>)
+ eventArguments = {};
+ else
+ eventArguments.clear();
+ Internal::toArguments(eventArguments, std::forward<Arguments>(arguments)...);
}
} // namespace Internal
template<typename Key, typename Value>
-auto keyValue(Key &&key, Value &&value)
+auto keyValue(const Key &key, Value &&value)
{
- return std::forward_as_tuple(std::forward<Key>(key), std::forward<Value>(value));
+ if constexpr (std::is_lvalue_reference_v<Value>)
+ return std::tuple<const Key &, const Value &>(key, value);
+ else
+ return std::tuple<const Key &, std::decay_t<Value>>(key, value);
}
template<typename... Entries>
auto dictonary(Entries &&...entries)
{
- return std::forward_as_tuple(isDictonary, std::forward<Entries>(entries)...);
+ return std::make_tuple(isDictonary, std::forward<Entries>(entries)...);
}
template<typename... Entries>
auto array(Entries &&...entries)
{
- return std::forward_as_tuple(isArray, std::forward<Entries>(entries)...);
+ return std::make_tuple(isArray, std::forward<Entries>(entries)...);
}
-enum class IsFlow : std::size_t { No = 0, Out = 1 << 0, In = 1 << 1, InOut = In | Out };
+enum class IsFlow : char { No = 0, Out = 1 << 0, In = 1 << 1, InOut = In | Out };
inline bool operator&(IsFlow first, IsFlow second)
{
@@ -309,7 +375,7 @@ inline bool operator&(IsFlow first, IsFlow second)
}
template<typename String, typename ArgumentsString>
-struct TraceEvent
+struct alignas(4096) TraceEvent
{
using StringType = String;
using ArgumentType = std::conditional_t<std::is_same_v<String, std::string_view>, TracerLiteral, String>;
@@ -324,13 +390,13 @@ struct TraceEvent
String name;
String category;
- ArgumentsString arguments;
TimePoint time;
Duration duration;
std::size_t id = 0;
- std::size_t bindId : 62;
- IsFlow flow : 2;
+ std::size_t bindId = 0;
+ IsFlow flow = IsFlow::No;
char type = ' ';
+ ArgumentsString arguments;
};
using StringViewTraceEvent = TraceEvent<std::string_view, std::string_view>;
@@ -413,6 +479,8 @@ class EventQueue
{
public:
using IsActive = std::false_type;
+
+ template<typename TraceFile> EventQueue(TraceFile &) {}
};
namespace Internal {
@@ -423,7 +491,13 @@ class EventQueueTracker
using Queue = EventQueue<TraceEvent, Tracing::IsEnabled>;
public:
- EventQueueTracker() = default;
+ EventQueueTracker()
+ {
+ terminateHandler = std::get_terminate();
+
+ std::set_terminate([]() { EventQueueTracker::get().terminate(); });
+ }
+
EventQueueTracker(const EventQueueTracker &) = delete;
EventQueueTracker(EventQueueTracker &&) = delete;
EventQueueTracker &operator=(const EventQueueTracker &) = delete;
@@ -457,8 +531,25 @@ public:
}
private:
+ void terminate()
+ {
+ flushAll();
+ if (terminateHandler)
+ terminateHandler();
+ }
+
+ void flushAll()
+ {
+ std::lock_guard lock{mutex};
+
+ for (auto queue : queues)
+ queue->flush();
+ }
+
+private:
std::mutex mutex;
std::vector<Queue *> queues;
+ std::terminate_handler terminateHandler = nullptr;
};
} // namespace Internal
@@ -466,11 +557,12 @@ template<typename TraceEvent>
class EventQueue<TraceEvent, Tracing::IsEnabled>
{
using TraceEventsSpan = Utils::span<TraceEvent>;
+ using TraceEvents = std::array<TraceEvent, 1000>;
public:
using IsActive = std::true_type;
- EventQueue(EnabledTraceFile *file);
+ EventQueue(EnabledTraceFile &file);
~EventQueue();
@@ -483,7 +575,9 @@ public:
EventQueue &operator=(const EventQueue &) = delete;
EventQueue &operator=(EventQueue &&) = delete;
- EnabledTraceFile *file = nullptr;
+ EnabledTraceFile &file;
+ std::unique_ptr<TraceEvents> eventArrayOne = std::make_unique<TraceEvents>();
+ std::unique_ptr<TraceEvents> eventArrayTwo = std::make_unique<TraceEvents>();
TraceEventsSpan eventsOne;
TraceEventsSpan eventsTwo;
TraceEventsSpan currentEvents;
@@ -505,35 +599,6 @@ extern template class NANOTRACE_EXPORT_EXTERN_TEMPLATE EventQueue<StringTraceEve
extern template class NANOTRACE_EXPORT_EXTERN_TEMPLATE
EventQueue<StringViewWithStringArgumentsTraceEvent, Tracing::IsEnabled>;
-template<typename TraceEvent, std::size_t eventCount, Tracing isEnabled>
-class EventQueueData : public EventQueue<TraceEvent, isEnabled>
-{
-public:
- using IsActive = std::true_type;
-
- EventQueueData(TraceFile<Tracing::IsDisabled> &) {}
-};
-
-template<typename TraceEvent, std::size_t eventCount>
-class EventQueueData<TraceEvent, eventCount, Tracing::IsEnabled>
- : public EventQueue<TraceEvent, Tracing::IsEnabled>
-{
- using TraceEvents = std::array<TraceEvent, eventCount>;
- using Base = EventQueue<TraceEvent, Tracing::IsEnabled>;
-
-public:
- using IsActive = std::true_type;
-
- EventQueueData(EnabledTraceFile &file)
- : Base{&file}
- {
- Base::setEventsSpans(*eventsOne.get(), *eventsTwo.get());
- }
-
- std::unique_ptr<TraceEvents> eventsOne = std::make_unique<TraceEvents>();
- std::unique_ptr<TraceEvents> eventsTwo = std::make_unique<TraceEvents>();
-};
-
template<typename TraceEvent>
TraceEvent &getTraceEvent(EnabledEventQueue<TraceEvent> &eventQueue)
{
@@ -1150,6 +1215,10 @@ public:
return std::pair<TracerType, FlowTokenType>();
}
+ template<typename... Arguments>
+ void threadEvent(ArgumentType, Arguments &&...)
+ {}
+
static constexpr bool isActive() { return false; }
};
@@ -1237,6 +1306,24 @@ public:
std::forward_as_tuple(PrivateTag{}, traceName, bindId, m_self)};
}
+ template<typename... Arguments>
+ void threadEvent(ArgumentType traceName, Arguments &&...arguments)
+ {
+ if (isEnabled == IsEnabled::No)
+ return;
+
+ auto &traceEvent = getTraceEvent(m_eventQueue);
+
+ traceEvent.time = Clock::now();
+ traceEvent.name = std::move(traceName);
+ traceEvent.category = traceName;
+ traceEvent.type = 'i';
+ traceEvent.id = 0;
+ traceEvent.bindId = 0;
+ traceEvent.flow = IsFlow::No;
+ Internal::setArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
+ }
+
EnabledEventQueue<TraceEvent> &eventQueue() const { return m_eventQueue; }
std::string_view name() const { return m_name; }
@@ -1270,7 +1357,7 @@ private:
traceEvent.id = id;
traceEvent.bindId = bindId;
traceEvent.flow = flow;
- Internal::appendArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
+ Internal::setArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
traceEvent.time = Clock::now();
}
@@ -1296,7 +1383,7 @@ private:
traceEvent.id = id;
traceEvent.bindId = bindId;
traceEvent.flow = flow;
- Internal::appendArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
+ Internal::setArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
}
template<typename... Arguments>
@@ -1316,9 +1403,11 @@ private:
traceEvent.id = id;
traceEvent.bindId = 0;
traceEvent.flow = IsFlow::No;
- Internal::appendArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
+ Internal::setArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
}
+ CategoryFunctionPointer self() { return m_self; }
+
private:
StringType m_name;
EnabledEventQueue<TraceEvent> &m_eventQueue;
@@ -1403,11 +1492,8 @@ class Tracer<Category, std::true_type>
, flow{flow}
, m_category{category}
{
- if (category().isEnabled == IsEnabled::Yes) {
- Internal::appendArguments<ArgumentsStringType>(m_arguments,
- std::forward<Arguments>(arguments)...);
- m_start = Clock::now();
- }
+ if (category().isEnabled == IsEnabled::Yes)
+ sendBeginTrace(std::forward<Arguments>(arguments)...);
}
public:
@@ -1426,13 +1512,15 @@ public:
: m_name{name}
, m_category{category}
{
- if (category().isEnabled == IsEnabled::Yes) {
- Internal::appendArguments<ArgumentsStringType>(m_arguments,
- std::forward<Arguments>(arguments)...);
- m_start = Clock::now();
- }
+ if (category().isEnabled == IsEnabled::Yes)
+ sendBeginTrace(std::forward<Arguments>(arguments)...);
}
+ template<typename... Arguments>
+ [[nodiscard]] Tracer(ArgumentType name, Category &category, Arguments &&...arguments)
+ : Tracer(std::move(name), category.self(), std::forward<Arguments>(arguments)...)
+ {}
+
Tracer(const Tracer &) = delete;
Tracer &operator=(const Tracer &) = delete;
Tracer(Tracer &&other) noexcept = delete;
@@ -1440,7 +1528,7 @@ public:
TokenType createToken() { return {0, m_category}; }
- ~Tracer() { sendTrace(); }
+ ~Tracer() { sendEndTrace(); }
template<typename... Arguments>
Tracer beginDuration(ArgumentType name, Arguments &&...arguments)
@@ -1457,44 +1545,52 @@ public:
template<typename... Arguments>
void end(Arguments &&...arguments)
{
- sendTrace(std::forward<Arguments>(arguments)...);
+ sendEndTrace(std::forward<Arguments>(arguments)...);
m_name = {};
}
private:
template<typename... Arguments>
- void sendTrace(Arguments &&...arguments)
+ void sendBeginTrace(Arguments &&...arguments)
+ {
+ auto &category = m_category();
+ if (category.isEnabled == IsEnabled::Yes) {
+ auto &traceEvent = getTraceEvent(category.eventQueue());
+ traceEvent.name = m_name;
+ traceEvent.category = category.name();
+ traceEvent.bindId = m_bindId;
+ traceEvent.flow = flow;
+ traceEvent.type = 'B';
+ Internal::setArguments<ArgumentsStringType>(traceEvent.arguments,
+ std::forward<Arguments>(arguments)...);
+ traceEvent.time = Clock::now();
+ }
+ }
+
+ template<typename... Arguments>
+ void sendEndTrace(Arguments &&...arguments)
{
if (m_name.size()) {
- auto category = m_category();
+ auto &category = m_category();
if (category.isEnabled == IsEnabled::Yes) {
- auto duration = Clock::now() - m_start;
+ auto end = Clock::now();
auto &traceEvent = getTraceEvent(category.eventQueue());
- traceEvent.name = m_name;
+ traceEvent.name = std::move(m_name);
traceEvent.category = category.name();
- traceEvent.time = m_start;
- traceEvent.duration = duration;
+ traceEvent.time = end;
traceEvent.bindId = m_bindId;
traceEvent.flow = flow;
- traceEvent.type = 'X';
- if (sizeof...(arguments)) {
- m_arguments.clear();
- Internal::appendArguments<ArgumentsStringType>(traceEvent.arguments,
- std::forward<Arguments>(
- arguments)...);
- } else {
- traceEvent.arguments = m_arguments;
- }
+ traceEvent.type = 'E';
+ Internal::setArguments<ArgumentsStringType>(traceEvent.arguments,
+ std::forward<Arguments>(arguments)...);
}
}
}
private:
- TimePoint m_start;
StringType m_name;
- ArgumentsStringType m_arguments;
- std::size_t m_bindId;
- IsFlow flow;
+ std::size_t m_bindId = 0;
+ IsFlow flow = IsFlow::No;
CategoryFunctionPointer m_category;
};
diff --git a/src/libs/nanotrace/staticstring.h b/src/libs/nanotrace/staticstring.h
new file mode 100644
index 0000000000..d787bd2fe3
--- /dev/null
+++ b/src/libs/nanotrace/staticstring.h
@@ -0,0 +1,116 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include <utils/smallstringview.h>
+
+#if !(defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L))
+# include <QLocale>
+#endif
+
+#include <array>
+#include <charconv>
+#include <limits>
+
+namespace NanotraceHR {
+
+template<std::size_t Capacity>
+class StaticString
+{
+public:
+ StaticString() = default;
+ StaticString(const StaticString &) = delete;
+ StaticString &operator=(const StaticString &) = delete;
+
+ char *data() { return m_data.data(); }
+
+ const char *data() const { return m_data.data(); }
+
+ void append(Utils::SmallStringView string) noexcept
+ {
+ auto newSize = m_size + string.size();
+
+ if (newSize <= Capacity) {
+ std::char_traits<char>::copy(std::next(data(), static_cast<std::ptrdiff_t>(m_size)),
+ string.data(),
+ string.size());
+ m_size = newSize;
+ } else {
+ m_size = Capacity + 1;
+ }
+ }
+
+ void append(char character) noexcept
+ {
+ auto newSize = m_size + 1;
+
+ if (newSize <= Capacity) {
+ auto current = std::next(data(), static_cast<std::ptrdiff_t>(m_size));
+ *current = character;
+
+ m_size = newSize;
+ } else {
+ m_size = Capacity + 1;
+ }
+ }
+
+ template<typename Type, typename std::enable_if_t<std::is_arithmetic_v<Type>, bool> = true>
+ void append(Type number)
+ {
+#if !(defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L))
+ if constexpr (std::is_floating_point_v<Type>) {
+ QLocale locale{QLocale::Language::C};
+ append(locale.toString(number).toStdString());
+ return;
+ }
+#endif
+ // 2 bytes for the sign and because digits10 returns the floor
+ char buffer[std::numeric_limits<Type>::digits10 + 2];
+ auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
+ auto endOfConversionString = result.ptr;
+
+ append({buffer, endOfConversionString});
+ }
+
+ void pop_back() { --m_size; }
+
+ StaticString &operator+=(Utils::SmallStringView string) noexcept
+ {
+ append(string);
+
+ return *this;
+ }
+
+ StaticString &operator+=(char character) noexcept
+ {
+ append(character);
+
+ return *this;
+ }
+
+ template<typename Type, typename = std::enable_if_t<std::is_arithmetic_v<Type>>>
+ StaticString &operator+=(Type number) noexcept
+ {
+ append(number);
+
+ return *this;
+ }
+
+ bool isValid() const { return m_size <= Capacity; }
+
+ std::size_t size() const { return m_size; }
+
+ friend std::ostream &operator<<(std::ostream &out, const StaticString &text)
+ {
+ return out << std::string_view{text.data(), text.size()};
+ }
+
+ void clear() { m_size = 0; }
+
+private:
+ std::array<char, Capacity> m_data;
+ std::size_t m_size = 0;
+};
+
+} // namespace NanotraceHR
diff --git a/src/libs/qmljs/CMakeLists.txt b/src/libs/qmljs/CMakeLists.txt
index 4ca15cf7cc..a4966a8b4a 100644
--- a/src/libs/qmljs/CMakeLists.txt
+++ b/src/libs/qmljs/CMakeLists.txt
@@ -33,7 +33,6 @@ add_qtc_library(QmlJS
qmljsfindexportedcpptypes.cpp qmljsfindexportedcpptypes.h
qmljsicons.cpp qmljsicons.h
qmljsimportdependencies.cpp qmljsimportdependencies.h
- qmljsindenter.cpp qmljsindenter.h
qmljsinterpreter.cpp qmljsinterpreter.h
qmljslineinfo.cpp qmljslineinfo.h
qmljslink.cpp qmljslink.h
diff --git a/src/libs/qmljs/qmljs.qbs b/src/libs/qmljs/qmljs.qbs
index 18434c83ba..d5cc21faf1 100644
--- a/src/libs/qmljs/qmljs.qbs
+++ b/src/libs/qmljs/qmljs.qbs
@@ -28,7 +28,6 @@ QtcLibrary {
"qmljsfindexportedcpptypes.cpp", "qmljsfindexportedcpptypes.h",
"qmljsicons.cpp", "qmljsicons.h",
"qmljsimportdependencies.cpp", "qmljsimportdependencies.h",
- "qmljsindenter.cpp", "qmljsindenter.h",
"qmljsinterpreter.cpp", "qmljsinterpreter.h",
"qmljsdialect.cpp", "qmljsdialect.h",
"qmljslineinfo.cpp", "qmljslineinfo.h",
diff --git a/src/libs/qmljs/qmljsindenter.cpp b/src/libs/qmljs/qmljsindenter.cpp
deleted file mode 100644
index 8055847d90..0000000000
--- a/src/libs/qmljs/qmljsindenter.cpp
+++ /dev/null
@@ -1,603 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-/*
- This file is a self-contained interactive indenter for Qt Script.
-
- The general problem of indenting a program is ill posed. On
- the one hand, an indenter has to analyze programs written in a
- free-form formal language that is best described in terms of
- tokens, not characters, not lines. On the other hand, indentation
- applies to lines and white space characters matter, and otherwise
- the programs to indent are formally invalid in general, as they
- are begin edited.
-
- The approach taken here works line by line. We receive a program
- consisting of N lines or more, and we want to compute the
- indentation appropriate for the Nth line. Lines beyond the Nth
- lines are of no concern to us, so for simplicity we pretend the
- program has exactly N lines and we call the Nth line the "bottom
- line". Typically, we have to indent the bottom line when it's
- still empty, so we concentrate our analysis on the N - 1 lines
- that precede.
-
- By inspecting the (N - 1)-th line, the (N - 2)-th line, ...
- backwards, we determine the kind of the bottom line and indent it
- accordingly.
-
- * The bottom line is a comment line. See
- bottomLineStartsInCComment() and
- indentWhenBottomLineStartsInCComment().
- * The bottom line is a continuation line. See isContinuationLine()
- and indentForContinuationLine().
- * The bottom line is a standalone line. See
- indentForStandaloneLine().
-
- Certain tokens that influence the indentation, notably braces,
- are looked for in the lines. This is done by simple string
- comparison, without a real tokenizer. Confusing constructs such
- as comments and string literals are removed beforehand.
-*/
-
-#include <qmljs/qmljsindenter.h>
-#include <qmljs/qmljsscanner.h>
-
-#include <QTextBlock>
-
-using namespace QmlJS;
-
-/*
- Saves and restores the state of the global linizer. This enables
- backtracking.
-
- Identical to the defines in qmljslineinfo.cpp
-*/
-#define YY_SAVE() LinizerState savedState = yyLinizerState
-#define YY_RESTORE() yyLinizerState = savedState
-
-
-QmlJSIndenter::QmlJSIndenter()
- : caseOrDefault(QRegularExpression(QLatin1String(
- "^\\s*(?:"
- "case\\b[^:]+|"
- "default)"
- "\\s*:.*$")))
-
-{
-
- /*
- The indenter supports a few parameters:
-
- * ppHardwareTabSize is the size of a '\t' in your favorite editor.
- * ppIndentSize is the size of an indentation, or software tab
- size.
- * ppContinuationIndentSize is the extra indent for a continuation
- line, when there is nothing to align against on the previous
- line.
- * ppCommentOffset is the indentation within a C-style comment,
- when it cannot be picked up.
- */
-
- ppHardwareTabSize = 8;
- ppIndentSize = 4;
- ppContinuationIndentSize = 8;
- ppCommentOffset = 2;
-}
-
-QmlJSIndenter::~QmlJSIndenter()
-{
-}
-
-void QmlJSIndenter::setTabSize(int size)
-{
- ppHardwareTabSize = size;
-}
-
-void QmlJSIndenter::setIndentSize(int size)
-{
- ppIndentSize = size;
- ppContinuationIndentSize = 2 * size;
-}
-
-/*
- Returns true if string t is made only of white space; otherwise
- returns false.
-*/
-bool QmlJSIndenter::isOnlyWhiteSpace(const QString &t) const
-{
- return firstNonWhiteSpace(t).isNull();
-}
-
-/*
- Assuming string t is a line, returns the column number of a given
- index. Column numbers and index are identical for strings that don't
- contain '\t's.
-*/
-int QmlJSIndenter::columnForIndex(const QString &t, int index) const
-{
- int col = 0;
- if (index > t.length())
- index = t.length();
-
- for (int i = 0; i < index; i++) {
- if (t.at(i) == QLatin1Char('\t'))
- col = ((col / ppHardwareTabSize) + 1) * ppHardwareTabSize;
- else
- col++;
- }
- return col;
-}
-
-/*
- Returns the indentation size of string t.
-*/
-int QmlJSIndenter::indentOfLine(const QString &t) const
-{
- return columnForIndex(t, t.indexOf(firstNonWhiteSpace(t)));
-}
-
-/*
- Replaces t[k] by ch, unless t[k] is '\t'. Tab characters are better
- left alone since they break the "index equals column" rule. No
- provisions are taken against '\n' or '\r', which shouldn't occur in
- t anyway.
-*/
-void QmlJSIndenter::eraseChar(QString &t, int k, QChar ch) const
-{
- if (t.at(k) != QLatin1Char('\t'))
- t[k] = ch;
-}
-
-/*
- Returns '(' if the last parenthesis is opening, ')' if it is
- closing, and QChar() if there are no parentheses in t.
-*/
-QChar QmlJSIndenter::lastParen() const
-{
- for (int index = yyLinizerState.tokens.size() - 1; index != -1; --index) {
- const Token &token = yyLinizerState.tokens.at(index);
-
- if (token.is(Token::LeftParenthesis))
- return QLatin1Char('(');
-
- else if (token.is(Token::RightParenthesis))
- return QLatin1Char(')');
- }
-
- return QChar();
-}
-
-/*
- Returns true if typedIn the same as okayCh or is null; otherwise
- returns false.
-*/
-bool QmlJSIndenter::okay(QChar typedIn, QChar okayCh) const
-{
- return typedIn == QChar() || typedIn == okayCh;
-}
-
-/*
- Returns the recommended indent for the bottom line of yyProgram
- assuming that it starts in a C-style comment, a condition that is
- tested elsewhere.
-
- Essentially, we're trying to align against some text on the
- previous line.
-*/
-int QmlJSIndenter::indentWhenBottomLineStartsInMultiLineComment()
-{
- QTextBlock block = yyProgram.lastBlock().previous();
- QString blockText;
-
- for (; block.isValid(); block = block.previous()) {
- blockText = block.text();
-
- if (! isOnlyWhiteSpace(blockText))
- break;
- }
-
- return indentOfLine(blockText);
-}
-
-/*
- Returns the recommended indent for the bottom line of yyProgram,
- assuming it's a continuation line.
-
- We're trying to align the continuation line against some parenthesis
- or other bracked left opened on a previous line, or some interesting
- operator such as '='.
-*/
-int QmlJSIndenter::indentForContinuationLine()
-{
- int braceDepth = 0;
- int delimDepth = 0;
-
- bool leftBraceFollowed = *yyLeftBraceFollows;
-
- for (int i = 0; i < SmallRoof; i++) {
- int hook = -1;
-
- int j = yyLine->length();
- while (j > 0 && hook < 0) {
- j--;
- QChar ch = yyLine->at(j);
-
- switch (ch.unicode()) {
- case ')':
- delimDepth++;
- break;
- case ']':
- braceDepth++;
- break;
- case '}':
- braceDepth++;
- break;
- case '(':
- delimDepth--;
- /*
- An unclosed delimiter is a good place to align at,
- at least for some styles (including Qt's).
- */
- if (delimDepth == -1)
- hook = j;
- break;
-
- case '[':
- braceDepth--;
- /*
- An unclosed delimiter is a good place to align at,
- at least for some styles (including Qt's).
- */
- if (braceDepth == -1)
- hook = j;
- break;
- case '{':
- braceDepth--;
- /*
- A left brace followed by other stuff on the same
- line is typically for an enum or an initializer.
- Such a brace must be treated just like the other
- delimiters.
- */
- if (braceDepth == -1) {
- if (j < yyLine->length() - 1)
- hook = j;
- else
- return 0; // shouldn't happen
- }
- break;
- case '=':
- /*
- An equal sign is a very natural alignment hook
- because it's usually the operator with the lowest
- precedence in statements it appears in. Case in
- point:
-
- int x = 1 +
- 2;
-
- However, we have to beware of constructs such as
- default arguments and explicit enum constant
- values:
-
- void foo(int x = 0,
- int y = 0);
-
- And not
-
- void foo(int x = 0,
- int y = 0);
-
- These constructs are caracterized by a ',' at the
- end of the unfinished lines or by unbalanced
- parentheses.
- */
- Q_ASSERT(j - 1 >= 0);
-
- if (QString::fromLatin1("!=<>").indexOf(yyLine->at(j - 1)) == -1 &&
- j + 1 < yyLine->length() && yyLine->at(j + 1) != QLatin1Char('=')) {
- if (braceDepth == 0 && delimDepth == 0 &&
- j < yyLine->length() - 1 &&
- !yyLine->endsWith(QLatin1Char(',')) &&
- (yyLine->contains(QLatin1Char('(')) == yyLine->contains(QLatin1Char(')'))))
- hook = j;
- }
- }
- }
-
- if (hook >= 0) {
- /*
- Yes, we have a delimiter or an operator to align
- against! We don't really align against it, but rather
- against the following token, if any. In this example,
- the following token is "11":
-
- int x = (11 +
- 2);
-
- If there is no such token, we use a continuation indent:
-
- static QRegExp foo(QString(
- "foo foo foo foo foo foo foo foo foo"));
- */
- hook++;
- while (hook < yyLine->length()) {
- if (!yyLine->at(hook).isSpace())
- return columnForIndex(*yyLine, hook);
- hook++;
- }
- return indentOfLine(*yyLine) + ppContinuationIndentSize;
- }
-
- if (braceDepth != 0)
- break;
-
- /*
- The line's delimiters are balanced. It looks like a
- continuation line or something.
- */
- if (delimDepth == 0) {
- if (leftBraceFollowed) {
- /*
- We have
-
- int main()
- {
-
- or
-
- Bar::Bar()
- : Foo(x)
- {
-
- The "{" should be flush left.
- */
- if (!isContinuationLine())
- return indentOfLine(*yyLine);
- } else if (isContinuationLine() || yyLine->endsWith(QLatin1Char(','))) {
- /*
- We have
-
- x = a +
- b +
- c;
-
- or
-
- int t[] = {
- 1, 2, 3,
- 4, 5, 6
-
- The "c;" should fall right under the "b +", and the
- "4, 5, 6" right under the "1, 2, 3,".
- */
- return indentOfLine(*yyLine);
- } else {
- /*
- We have
-
- stream << 1 +
- 2;
-
- We could, but we don't, try to analyze which
- operator has precedence over which and so on, to
- obtain the excellent result
-
- stream << 1 +
- 2;
-
- We do have a special trick above for the assignment
- operator above, though.
- */
- return indentOfLine(*yyLine) + ppContinuationIndentSize;
- }
- }
-
- if (!readLine())
- break;
- }
- return 0;
-}
-
-/*
- Returns the recommended indent for the bottom line of yyProgram if
- that line is standalone (or should be indented likewise).
-
- Indenting a standalone line is tricky, mostly because of braceless
- control statements. Grossly, we are looking backwards for a special
- line, a "hook line", that we can use as a starting point to indent,
- and then modify the indentation level according to the braces met
- along the way to that hook.
-
- Let's consider a few examples. In all cases, we want to indent the
- bottom line.
-
- Example 1:
-
- x = 1;
- y = 2;
-
- The hook line is "x = 1;". We met 0 opening braces and 0 closing
- braces. Therefore, "y = 2;" inherits the indent of "x = 1;".
-
- Example 2:
-
- if (x) {
- y;
-
- The hook line is "if (x) {". No matter what precedes it, "y;" has
- to be indented one level deeper than the hook line, since we met one
- opening brace along the way.
-
- Example 3:
-
- if (a)
- while (b) {
- c;
- }
- d;
-
- To indent "d;" correctly, we have to go as far as the "if (a)".
- Compare with
-
- if (a) {
- while (b) {
- c;
- }
- d;
-
- Still, we're striving to go back as little as possible to
- accommodate people with irregular indentation schemes. A hook line
- near at hand is much more reliable than a remote one.
-*/
-int QmlJSIndenter::indentForStandaloneLine()
-{
- for (int i = 0; i < SmallRoof; i++) {
- if (!*yyLeftBraceFollows) {
- YY_SAVE();
-
- if (matchBracelessControlStatement()) {
- /*
- The situation is this, and we want to indent "z;":
-
- if (x &&
- y)
- z;
-
- yyLine is "if (x &&".
- */
- return indentOfLine(*yyLine) + ppIndentSize;
- }
- YY_RESTORE();
- }
-
- if (yyLine->endsWith(QLatin1Char(';')) || yyLine->contains(QLatin1Char('{'))) {
- /*
- The situation is possibly this, and we want to indent
- "z;":
-
- while (x)
- y;
- z;
-
- We return the indent of "while (x)". In place of "y;",
- any arbitrarily complex compound statement can appear.
- */
-
- if (*yyBraceDepth > 0) {
- do {
- if (!readLine())
- break;
- } while (*yyBraceDepth > 0);
- }
-
- LinizerState hookState;
-
- while (isContinuationLine())
- readLine();
- hookState = yyLinizerState;
-
- readLine();
- if (*yyBraceDepth <= 0) {
- do {
- if (!matchBracelessControlStatement())
- break;
- hookState = yyLinizerState;
- } while (readLine());
- }
-
- yyLinizerState = hookState;
-
- while (isContinuationLine())
- readLine();
-
- int indentChange = - *yyBraceDepth;
- if (caseOrDefault.match(*yyLine).hasMatch())
- ++indentChange;
-
- /*
- Never trust lines containing only '{' or '}', as some
- people (Richard M. Stallman) format them weirdly.
- */
- if (yyLine->trimmed().length() > 1)
- return indentOfLine(*yyLine) + indentChange * ppIndentSize;
- }
-
- if (!readLine())
- return -*yyBraceDepth * ppIndentSize;
- }
- return 0;
-}
-
-/*
- Returns the recommended indent for the bottom line of program.
- Unless null, typedIn stores the character of yyProgram that
- triggered reindentation.
-
- This function works better if typedIn is set properly; it is
- slightly more conservative if typedIn is completely wild, and
- slighly more liberal if typedIn is always null. The user might be
- annoyed by the liberal behavior.
-*/
-int QmlJSIndenter::indentForBottomLine(QTextBlock begin, QTextBlock end, QChar typedIn)
-{
- if (begin == end)
- return 0;
-
- const QTextBlock last = end.previous();
-
- initialize(begin, last);
-
- QString bottomLine = last.text();
- QChar firstCh = firstNonWhiteSpace(bottomLine);
- int indent = 0;
-
- if (bottomLineStartsInMultilineComment()) {
- /*
- The bottom line starts in a C-style comment. Indent it
- smartly, unless the user has already played around with it,
- in which case it's better to leave her stuff alone.
- */
- if (isOnlyWhiteSpace(bottomLine))
- indent = indentWhenBottomLineStartsInMultiLineComment();
- else
- indent = indentOfLine(bottomLine);
- } else {
- if (isUnfinishedLine())
- indent = indentForContinuationLine();
- else
- indent = indentForStandaloneLine();
-
- if ((okay(typedIn, QLatin1Char('}')) && firstCh == QLatin1Char('}'))
- || (okay(typedIn, QLatin1Char(']')) && firstCh == QLatin1Char(']'))) {
- /*
- A closing brace is one level more to the left than the
- code it follows.
- */
- indent -= ppIndentSize;
- } else if (okay(typedIn, QLatin1Char(':'))) {
- if (caseOrDefault.match(bottomLine).hasMatch()) {
- /*
- Move a case label (or the ':' in front of a
- constructor initialization list) one level to the
- left, but only if the user did not play around with
- it yet. Some users have exotic tastes in the
- matter, and most users probably are not patient
- enough to wait for the final ':' to format their
- code properly.
-
- We don't attempt the same for goto labels, as the
- user is probably the middle of "foo::bar". (Who
- uses goto, anyway?)
- */
- if (indentOfLine(bottomLine) <= indent)
- indent -= ppIndentSize;
- else
- indent = indentOfLine(bottomLine);
- }
- }
- }
-
- return qMax(0, indent);
-}
-
diff --git a/src/libs/qmljs/qmljsindenter.h b/src/libs/qmljs/qmljsindenter.h
deleted file mode 100644
index 55141c1b3b..0000000000
--- a/src/libs/qmljs/qmljsindenter.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <qmljs/qmljs_global.h>
-#include <qmljs/qmljslineinfo.h>
-
-#include <QRegularExpression>
-
-QT_FORWARD_DECLARE_CLASS(QTextBlock)
-
-namespace QmlJS {
-
-class QMLJS_EXPORT QmlJSIndenter : public LineInfo
-{
- Q_DISABLE_COPY(QmlJSIndenter)
-
-public:
- QmlJSIndenter();
- ~QmlJSIndenter();
-
- void setTabSize(int size);
- void setIndentSize(int size);
-
- int indentForBottomLine(QTextBlock firstBlock, QTextBlock lastBlock, QChar typedIn);
-
-private:
- bool isOnlyWhiteSpace(const QString &t) const;
- int columnForIndex(const QString &t, int index) const;
- int indentOfLine(const QString &t) const;
-
- void eraseChar(QString &t, int k, QChar ch) const;
- QChar lastParen() const;
- bool okay(QChar typedIn, QChar okayCh) const;
-
- int indentWhenBottomLineStartsInMultiLineComment();
- int indentForContinuationLine();
- int indentForStandaloneLine();
-
-private:
- int ppHardwareTabSize;
- int ppIndentSize;
- int ppContinuationIndentSize;
- int ppCommentOffset;
-
-private:
- QRegularExpression caseOrDefault;
-};
-
-} // namespace QmlJS
diff --git a/src/libs/qmljs/qmljsstaticanalysismessage.cpp b/src/libs/qmljs/qmljsstaticanalysismessage.cpp
index 5b15e69428..432b97aba7 100644
--- a/src/libs/qmljs/qmljsstaticanalysismessage.cpp
+++ b/src/libs/qmljs/qmljsstaticanalysismessage.cpp
@@ -189,8 +189,9 @@ StaticAnalysisMessages::StaticAnalysisMessages()
Tr::tr("Maximum string value length is %1."), 1);
newMsg(ErrInvalidArrayValueLength, Error,
Tr::tr("%1 elements expected in array value."), 1);
- newMsg(WarnImperativeCodeNotEditableInVisualDesigner, Warning,
- Tr::tr("Imperative code is not supported in Qt Design Studio."));
+ newMsg(WarnImperativeCodeNotEditableInVisualDesigner,
+ Warning,
+ Tr::tr("JavaScript can break the visual tooling in Qt Design Studio."));
newMsg(WarnUnsupportedTypeInVisualDesigner, Warning,
Tr::tr("This type (%1) is not supported in Qt Design Studio."), 1);
newMsg(WarnReferenceToParentItemNotSupportedByVisualDesigner, Warning,
diff --git a/src/libs/qmlpuppetcommunication/container/imagecontainer.cpp b/src/libs/qmlpuppetcommunication/container/imagecontainer.cpp
index 545d4d927d..7747a9d118 100644
--- a/src/libs/qmlpuppetcommunication/container/imagecontainer.cpp
+++ b/src/libs/qmlpuppetcommunication/container/imagecontainer.cpp
@@ -15,7 +15,7 @@
#define QTC_ASSERT_STRING(cond) qDebug("SOFT ASSERT: \"" cond"\" in file " __FILE__ ", line " QTC_ASSERT_STRINGIFY(__LINE__))
#define QTC_ASSERT(cond, action) if (cond) {} else { QTC_ASSERT_STRING(#cond); action; } do {} while (0)
-static Q_LOGGING_CATEGORY(imageContainerDebug, "qtc.imagecontainer.debug", QtDebugMsg)
+static Q_LOGGING_CATEGORY(imageContainerDebug, "qtc.imagecontainer")
namespace QmlDesigner {
@@ -194,9 +194,10 @@ static void readSharedMemory(qint32 key, ImageContainer &container)
QImage image = QImage(imageWidth, imageHeight, QImage::Format(imageFormat));
image.setDevicePixelRatio(pixelRatio);
- if (image.isNull())
- qCInfo(imageContainerDebug()) << Q_FUNC_INFO << "Not able to create image:" << imageWidth << imageHeight << imageFormat;
- else
+ if (image.isNull()) {
+ if (imageWidth || imageHeight || imageFormat)
+ qCWarning(imageContainerDebug) << Q_FUNC_INFO << "Not able to create image:" << imageWidth << imageHeight << imageFormat;
+ } else
std::memcpy(image.bits(), reinterpret_cast<const qint32*>(sharedMemory.constData()) + 6, byteCount);
container.setImage(image);
diff --git a/src/libs/sqlite/CMakeLists.txt b/src/libs/sqlite/CMakeLists.txt
index 2c7e1ebbf3..f86f31871a 100644
--- a/src/libs/sqlite/CMakeLists.txt
+++ b/src/libs/sqlite/CMakeLists.txt
@@ -31,7 +31,7 @@ endif()
add_qtc_library(Sqlite
PROPERTIES AUTOMOC OFF AUTOUIC OFF
- DEPENDS Qt::Core Threads::Threads ${CMAKE_DL_LIBS} SqliteInternal
+ DEPENDS Qt::Core Threads::Threads ${CMAKE_DL_LIBS} SqliteInternal Nanotrace
INCLUDES
../3rdparty/sqlite
PUBLIC_INCLUDES
@@ -58,7 +58,7 @@ add_qtc_library(Sqlite
sqlitesessionchangeset.cpp sqlitesessionchangeset.h
sqlitesessions.cpp sqlitesessions.h
sqlitetable.h
- sqlitetransaction.h
+ sqlitetracing.cpp sqlitetracing.h
sqlitetransaction.h
sqlitevalue.h
sqlitewritestatement.h
diff --git a/src/libs/sqlite/sqlitebasestatement.cpp b/src/libs/sqlite/sqlitebasestatement.cpp
index 23466c0cea..8557bf6ad2 100644
--- a/src/libs/sqlite/sqlitebasestatement.cpp
+++ b/src/libs/sqlite/sqlitebasestatement.cpp
@@ -26,32 +26,7 @@ extern "C" int sqlite3_carray_bind(
namespace Sqlite {
-namespace {
-using TraceFile = NanotraceHR::TraceFile<sqliteTracingStatus()>;
-
-TraceFile traceFile{"sqlite.json"};
-
-thread_local NanotraceHR::EventQueueData<NanotraceHR::StringViewTraceEvent, 10000, sqliteTracingStatus()>
- eventQueueData(traceFile);
-
-NanotraceHR::StringViewCategory<sqliteTracingStatus()> &sqliteLowLevelCategory();
-
-thread_local NanotraceHR::StringViewCategory<sqliteTracingStatus()> sqliteLowLevelCategory_{
- "sqlite low level"_t, eventQueueData, sqliteLowLevelCategory};
-
-NanotraceHR::StringViewCategory<sqliteTracingStatus()> &sqliteLowLevelCategory()
-{
- return sqliteLowLevelCategory_;
-}
-
-thread_local NanotraceHR::StringViewCategory<sqliteTracingStatus()> sqliteHighLevelCategory_{
- "sqlite high level"_t, eventQueueData, sqliteHighLevelCategory};
-} // namespace
-
-NanotraceHR::StringViewCategory<sqliteTracingStatus()> &sqliteHighLevelCategory()
-{
- return sqliteHighLevelCategory_;
-}
+using NanotraceHR::keyValue;
BaseStatement::BaseStatement(Utils::SmallStringView sqlStatement, Database &database)
: m_database(database)
@@ -107,14 +82,18 @@ void BaseStatement::waitForUnlockNotify() const
void BaseStatement::reset() const noexcept
{
- NanotraceHR::Tracer tracer{"reset"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"reset"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle())};
sqlite3_reset(m_compiledStatement.get());
}
bool BaseStatement::next() const
{
- NanotraceHR::Tracer tracer{"next"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"next"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle())};
int resultCode;
do {
@@ -141,7 +120,10 @@ void BaseStatement::step() const
void BaseStatement::bindNull(int index)
{
- NanotraceHR::Tracer tracer{"bind null"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind null"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index)};
int resultCode = sqlite3_bind_null(m_compiledStatement.get(), index);
if (resultCode != SQLITE_OK)
@@ -155,7 +137,11 @@ void BaseStatement::bind(int index, NullValue)
void BaseStatement::bind(int index, int value)
{
- NanotraceHR::Tracer tracer{"bind int"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind int"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("value", value)};
int resultCode = sqlite3_bind_int(m_compiledStatement.get(), index, value);
if (resultCode != SQLITE_OK)
@@ -164,7 +150,11 @@ void BaseStatement::bind(int index, int value)
void BaseStatement::bind(int index, long long value)
{
- NanotraceHR::Tracer tracer{"bind long long"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind long long"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("value", value)};
int resultCode = sqlite3_bind_int64(m_compiledStatement.get(), index, value);
if (resultCode != SQLITE_OK)
@@ -173,7 +163,11 @@ void BaseStatement::bind(int index, long long value)
void BaseStatement::bind(int index, double value)
{
- NanotraceHR::Tracer tracer{"bind double"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind double"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("value", value)};
int resultCode = sqlite3_bind_double(m_compiledStatement.get(), index, value);
if (resultCode != SQLITE_OK)
@@ -182,7 +176,11 @@ void BaseStatement::bind(int index, double value)
void BaseStatement::bind(int index, void *pointer)
{
- NanotraceHR::Tracer tracer{"bind pointer"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind pointer"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(pointer))};
int resultCode = sqlite3_bind_pointer(m_compiledStatement.get(), index, pointer, "carray", nullptr);
if (resultCode != SQLITE_OK)
@@ -191,7 +189,12 @@ void BaseStatement::bind(int index, void *pointer)
void BaseStatement::bind(int index, Utils::span<const int> values)
{
- NanotraceHR::Tracer tracer{"bind int span"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind int span"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(values.data())),
+ keyValue("size", values.size())};
int resultCode = sqlite3_carray_bind(m_compiledStatement.get(),
index,
@@ -205,7 +208,12 @@ void BaseStatement::bind(int index, Utils::span<const int> values)
void BaseStatement::bind(int index, Utils::span<const long long> values)
{
- NanotraceHR::Tracer tracer{"bind long long span"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind long long span"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(values.data())),
+ keyValue("size", values.size())};
int resultCode = sqlite3_carray_bind(m_compiledStatement.get(),
index,
@@ -219,7 +227,12 @@ void BaseStatement::bind(int index, Utils::span<const long long> values)
void BaseStatement::bind(int index, Utils::span<const double> values)
{
- NanotraceHR::Tracer tracer{"bind double span"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind double span"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(values.data())),
+ keyValue("size", values.size())};
int resultCode = sqlite3_carray_bind(m_compiledStatement.get(),
index,
@@ -233,7 +246,12 @@ void BaseStatement::bind(int index, Utils::span<const double> values)
void BaseStatement::bind(int index, Utils::span<const char *> values)
{
- NanotraceHR::Tracer tracer{"bind const char* span"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind const char* span"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(values.data())),
+ keyValue("size", values.size())};
int resultCode = sqlite3_carray_bind(m_compiledStatement.get(),
index,
@@ -247,7 +265,11 @@ void BaseStatement::bind(int index, Utils::span<const char *> values)
void BaseStatement::bind(int index, Utils::SmallStringView text)
{
- NanotraceHR::Tracer tracer{"bind string"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind string"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("text", text)};
int resultCode = sqlite3_bind_text(m_compiledStatement.get(),
index,
@@ -260,7 +282,12 @@ void BaseStatement::bind(int index, Utils::SmallStringView text)
void BaseStatement::bind(int index, BlobView blobView)
{
- NanotraceHR::Tracer tracer{"bind blob"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind blob"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(blobView.data())),
+ keyValue("size", blobView.size())};
int resultCode = SQLITE_OK;
@@ -280,7 +307,11 @@ void BaseStatement::bind(int index, BlobView blobView)
void BaseStatement::bind(int index, const Value &value)
{
- NanotraceHR::Tracer tracer{"bind value"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{
+ "bind value"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ };
switch (value.type()) {
case ValueType::Integer:
@@ -303,7 +334,11 @@ void BaseStatement::bind(int index, const Value &value)
void BaseStatement::bind(int index, ValueView value)
{
- NanotraceHR::Tracer tracer{"bind value"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{
+ "bind value"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ };
switch (value.type()) {
case ValueType::Integer:
@@ -326,7 +361,9 @@ void BaseStatement::bind(int index, ValueView value)
void BaseStatement::prepare(Utils::SmallStringView sqlStatement)
{
- NanotraceHR::Tracer tracer{"prepare"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"prepare"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sql statement", sqlStatement)};
if (!m_database.isLocked())
throw DatabaseIsNotLocked{};
@@ -342,14 +379,18 @@ void BaseStatement::prepare(Utils::SmallStringView sqlStatement)
nullptr);
m_compiledStatement.reset(sqliteStatement);
- if (resultCode == SQLITE_LOCKED)
+ if (resultCode == SQLITE_LOCKED) {
+ tracer.tick("wait for unlock"_t);
waitForUnlockNotify();
+ }
} while (resultCode == SQLITE_LOCKED);
if (resultCode != SQLITE_OK)
Sqlite::throwError(resultCode, sqliteDatabaseHandle());
+
+ tracer.end(keyValue("sqlite statement", handle()));
}
sqlite3 *BaseStatement::sqliteDatabaseHandle() const
@@ -427,7 +468,10 @@ StringType convertToTextForColumn(sqlite3_stmt *sqlStatment, int column)
Type BaseStatement::fetchType(int column) const
{
- NanotraceHR::Tracer tracer{"fetch type"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"fetch type"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
auto dataType = sqlite3_column_type(m_compiledStatement.get(), column);
@@ -449,9 +493,16 @@ Type BaseStatement::fetchType(int column) const
int BaseStatement::fetchIntValue(int column) const
{
- NanotraceHR::Tracer tracer{"fetch int"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"fetch int"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
+ auto value = sqlite3_column_int(m_compiledStatement.get(), column);
- return sqlite3_column_int(m_compiledStatement.get(), column);
+ tracer.end(keyValue("value", value));
+
+ return value;
}
template<>
@@ -473,9 +524,16 @@ long BaseStatement::fetchValue<long>(int column) const
long long BaseStatement::fetchLongLongValue(int column) const
{
- NanotraceHR::Tracer tracer{"fetch long long"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"fetch long long"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
+ auto value = sqlite3_column_int64(m_compiledStatement.get(), column);
- return sqlite3_column_int64(m_compiledStatement.get(), column);
+ tracer.end(keyValue("value", value));
+
+ return value;
}
template<>
@@ -486,14 +544,24 @@ long long BaseStatement::fetchValue<long long>(int column) const
double BaseStatement::fetchDoubleValue(int column) const
{
- NanotraceHR::Tracer tracer{"fetch double"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"fetch double"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
+ auto value = sqlite3_column_double(m_compiledStatement.get(), column);
+
+ tracer.end(keyValue("value", value));
- return sqlite3_column_double(m_compiledStatement.get(), column);
+ return value;
}
BlobView BaseStatement::fetchBlobValue(int column) const
{
- NanotraceHR::Tracer tracer{"fetch blob"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"fetch blob"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
return convertToBlobForColumn(m_compiledStatement.get(), column);
}
@@ -507,7 +575,16 @@ double BaseStatement::fetchValue<double>(int column) const
template<typename StringType>
StringType BaseStatement::fetchValue(int column) const
{
- return convertToTextForColumn<StringType>(m_compiledStatement.get(), column);
+ NanotraceHR::Tracer tracer{"fetch string value"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
+ auto text = convertToTextForColumn<StringType>(m_compiledStatement.get(), column);
+
+ tracer.end(keyValue("text", text));
+
+ return text;
}
template SQLITE_EXPORT Utils::SmallStringView BaseStatement::fetchValue<Utils::SmallStringView>(
@@ -519,11 +596,25 @@ template SQLITE_EXPORT Utils::PathString BaseStatement::fetchValue<Utils::PathSt
Utils::SmallStringView BaseStatement::fetchSmallStringViewValue(int column) const
{
- return fetchValue<Utils::SmallStringView>(column);
+ NanotraceHR::Tracer tracer{"fetch string view"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
+ auto text = fetchValue<Utils::SmallStringView>(column);
+
+ tracer.end(keyValue("text", text));
+
+ return text;
}
ValueView BaseStatement::fetchValueView(int column) const
{
+ NanotraceHR::Tracer tracer{"fetch value view"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
int dataType = sqlite3_column_type(m_compiledStatement.get(), column);
switch (dataType) {
case SQLITE_NULL:
@@ -543,7 +634,11 @@ ValueView BaseStatement::fetchValueView(int column) const
void BaseStatement::Deleter::operator()(sqlite3_stmt *statement)
{
- NanotraceHR::Tracer tracer{"finalize"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{
+ "finalize"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", reinterpret_cast<std::uintptr_t>(statement)),
+ };
sqlite3_finalize(statement);
}
diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h
index d2306c67a5..3710021ff5 100644
--- a/src/libs/sqlite/sqlitebasestatement.h
+++ b/src/libs/sqlite/sqlitebasestatement.h
@@ -9,6 +9,7 @@
#include "sqliteblob.h"
#include "sqliteexception.h"
#include "sqliteids.h"
+#include "sqlitetracing.h"
#include "sqlitetransaction.h"
#include "sqlitevalue.h"
@@ -30,8 +31,6 @@ using std::int64_t;
namespace Sqlite {
-using namespace NanotraceHR::Literals;
-
class Database;
class DatabaseBackend;
@@ -44,17 +43,6 @@ constexpr static std::underlying_type_t<Enumeration> to_underlying(Enumeration e
return static_cast<std::underlying_type_t<Enumeration>>(enumeration);
}
-constexpr NanotraceHR::Tracing sqliteTracingStatus()
-{
-#ifdef ENABLE_SQLITE_TRACING
- return NanotraceHR::tracingStatus();
-#else
- return NanotraceHR::Tracing::IsDisabled;
-#endif
-}
-
-SQLITE_EXPORT NanotraceHR::StringViewCategory<sqliteTracingStatus()> &sqliteHighLevelCategory();
-
class SQLITE_EXPORT BaseStatement
{
public:
@@ -136,6 +124,11 @@ public:
protected:
~BaseStatement() = default;
+ std::uintptr_t handle() const
+ {
+ return reinterpret_cast<std::uintptr_t>(m_compiledStatement.get());
+ }
+
private:
struct Deleter
{
@@ -166,7 +159,12 @@ public:
void execute()
{
- NanotraceHR::Tracer tracer{"execute"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{
+ "execute"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle()),
+ };
Resetter resetter{this};
BaseStatement::next();
@@ -175,7 +173,10 @@ public:
template<typename... ValueType>
void bindValues(const ValueType &...values)
{
- NanotraceHR::Tracer tracer{"bind"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"bind"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
static_assert(BindParameterCount == sizeof...(values), "Wrong binding parameter count!");
@@ -186,7 +187,10 @@ public:
template<typename... ValueType>
void write(const ValueType&... values)
{
- NanotraceHR::Tracer tracer{"write"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"write"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
bindValues(values...);
@@ -206,24 +210,37 @@ public:
struct is_container<QVarLengthArray<T, Prealloc>> : std::true_type
{};
+ template<typename T>
+ struct is_small_container : std::false_type
+ {};
+
+ template<typename T, qsizetype Prealloc>
+ struct is_small_container<QVarLengthArray<T, Prealloc>> : std::true_type
+ {};
+
template<typename Container,
std::size_t capacity = 32,
typename = std::enable_if_t<is_container<Container>::value>,
typename... QueryTypes>
auto values(const QueryTypes &...queryValues)
{
- NanotraceHR::Tracer tracer{"values"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"values"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
Container resultValues;
- resultValues.reserve(std::max(capacity, m_maximumResultCount));
+ using size_tupe = typename Container::size_type;
+ if constexpr (!is_small_container<Container>::value)
+ resultValues.reserve(static_cast<size_tupe>(std::max(capacity, m_maximumResultCount)));
bindValues(queryValues...);
while (BaseStatement::next())
emplaceBackValues(resultValues);
- setMaximumResultCount(resultValues.size());
+ setMaximumResultCount(static_cast<std::size_t>(resultValues.size()));
return resultValues;
}
@@ -241,7 +258,10 @@ public:
template<typename ResultType, typename... QueryTypes>
auto value(const QueryTypes &...queryValues)
{
- NanotraceHR::Tracer tracer{"value"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"value"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
ResultType resultValue{};
@@ -257,7 +277,10 @@ public:
template<typename ResultType, typename... QueryTypes>
auto optionalValue(const QueryTypes &...queryValues)
{
- NanotraceHR::Tracer tracer{"optionalValue"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"optionalValue"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
std::optional<ResultType> resultValue;
@@ -273,6 +296,7 @@ public:
template<typename Type>
static auto toValue(Utils::SmallStringView sqlStatement, Database &database)
{
+ using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"toValue"_t, sqliteHighLevelCategory()};
StatementImplementation statement(sqlStatement, database);
@@ -287,7 +311,10 @@ public:
template<typename Callable, typename... QueryTypes>
void readCallback(Callable &&callable, const QueryTypes &...queryValues)
{
- NanotraceHR::Tracer tracer{"readCallback"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"readCallback"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
@@ -304,7 +331,10 @@ public:
template<typename Container, typename... QueryTypes>
void readTo(Container &container, const QueryTypes &...queryValues)
{
- NanotraceHR::Tracer tracer{"readTo"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"readTo"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
@@ -402,9 +432,11 @@ public:
private:
using TracerCategory = std::decay_t<decltype(sqliteHighLevelCategory())>;
- NanotraceHR::Tracer<TracerCategory, typename TracerCategory::IsActive> tracer{
- "range"_t, sqliteHighLevelCategory()};
StatementImplementation &m_statement;
+ NanotraceHR::Tracer<TracerCategory, typename TracerCategory::IsActive> tracer{
+ "range"_t,
+ sqliteHighLevelCategory(),
+ NanotraceHR::keyValue("sqlite statement", m_statement.handle())};
};
template<typename ResultType>
diff --git a/src/libs/sqlite/sqliteexception.cpp b/src/libs/sqlite/sqliteexception.cpp
index b5f581ad68..bb4a474adb 100644
--- a/src/libs/sqlite/sqliteexception.cpp
+++ b/src/libs/sqlite/sqliteexception.cpp
@@ -3,14 +3,20 @@
#include "sqliteexception.h"
+#include "sqlitetracing.h"
+
#include <utils/smallstringio.h>
+#include <nanotrace/nanotracehr.h>
+
#include <sqlite.h>
#include <QDebug>
namespace Sqlite {
+using NanotraceHR::keyValue;
+
const char *Exception::what() const noexcept
{
return "Sqlite::Exception";
@@ -18,7 +24,10 @@ const char *Exception::what() const noexcept
const char *ExceptionWithMessage::what() const noexcept
{
- return "Sqlite::ExceptionWithMessage";
+ static Utils::SmallString text = Utils::SmallString::join(
+ {"Sqlite::ExceptionWithMessage", m_sqliteErrorMessage});
+
+ return text.data();
}
void ExceptionWithMessage::printWarning() const
@@ -26,6 +35,13 @@ void ExceptionWithMessage::printWarning() const
qWarning() << what() << m_sqliteErrorMessage;
}
+StatementIsBusy::StatementIsBusy(Utils::SmallString &&sqliteErrorMessage)
+ : ExceptionWithMessage{std::move(sqliteErrorMessage)}
+{
+ sqliteHighLevelCategory().threadEvent("StatementIsBusy"_t,
+ keyValue("error message", std::string_view{what()}));
+}
+
const char *StatementIsBusy::what() const noexcept
{
return "Sqlite::StatementIsBusy";
@@ -36,9 +52,19 @@ const char *DatabaseIsBusy::what() const noexcept
return "Sqlite::DatabaseIsBusy";
}
+StatementHasError::StatementHasError(Utils::SmallString &&sqliteErrorMessage)
+ : ExceptionWithMessage{std::move(sqliteErrorMessage)}
+{
+ sqliteHighLevelCategory().threadEvent("StatementHasError"_t,
+ keyValue("error message", std::string_view{what()}));
+}
+
const char *StatementHasError::what() const noexcept
{
- return "Sqlite::StatementHasError";
+ static Utils::SmallString text = Utils::SmallString::join(
+ {"Sqlite::StatementHasError: ", message()});
+
+ return text.data();
}
const char *StatementIsMisused::what() const noexcept
diff --git a/src/libs/sqlite/sqliteexception.h b/src/libs/sqlite/sqliteexception.h
index f0cadfc748..17a0639e19 100644
--- a/src/libs/sqlite/sqliteexception.h
+++ b/src/libs/sqlite/sqliteexception.h
@@ -23,13 +23,15 @@ public:
class SQLITE_EXPORT ExceptionWithMessage : public Exception
{
public:
- ExceptionWithMessage(Utils::SmallString &&sqliteErrorMessage = Utils::SmallString{})
+ ExceptionWithMessage(Utils::SmallString &&sqliteErrorMessage = {})
: m_sqliteErrorMessage(std::move(sqliteErrorMessage))
{}
const char *what() const noexcept override;
void printWarning() const;
+ std::string_view message() const noexcept { return m_sqliteErrorMessage; }
+
private:
Utils::SmallString m_sqliteErrorMessage;
};
@@ -37,7 +39,7 @@ private:
class SQLITE_EXPORT StatementIsBusy : public ExceptionWithMessage
{
public:
- using ExceptionWithMessage::ExceptionWithMessage;
+ StatementIsBusy(Utils::SmallString &&sqliteErrorMessage);
const char *what() const noexcept override;
};
@@ -90,7 +92,8 @@ public:
class SQLITE_EXPORT StatementHasError : public ExceptionWithMessage
{
public:
- using ExceptionWithMessage::ExceptionWithMessage;
+ StatementHasError(Utils::SmallString &&sqliteErrorMessage);
+
const char *what() const noexcept override;
};
diff --git a/src/libs/sqlite/sqliteids.h b/src/libs/sqlite/sqliteids.h
index d64e4d9645..1ffd546d9f 100644
--- a/src/libs/sqlite/sqliteids.h
+++ b/src/libs/sqlite/sqliteids.h
@@ -5,6 +5,7 @@
#include <utils/span.h>
+#include <nanotrace/nanotracehr.h>
#include <type_traits>
#include <vector>
@@ -64,6 +65,15 @@ public:
[[noreturn, deprecated]] InternalIntegerType operator&() const { throw std::exception{}; }
+ template<typename String>
+ friend void convertToString(String &string, BasicId id)
+ {
+ if (id.isValid())
+ NanotraceHR::convertToString(string, id.internalId());
+ else
+ NanotraceHR::convertToString(string, "invalid");
+ }
+
private:
InternalIntegerType id = 0;
};
@@ -88,4 +98,5 @@ struct hash<Sqlite::BasicId<Type, InternalIntegerType>>
return std::hash<InternalIntegerType>{}(id.internalId());
}
};
+
} // namespace std
diff --git a/src/libs/sqlite/sqliteindex.h b/src/libs/sqlite/sqliteindex.h
index f320fcd599..7c7a8dbb2b 100644
--- a/src/libs/sqlite/sqliteindex.h
+++ b/src/libs/sqlite/sqliteindex.h
@@ -40,6 +40,8 @@ public:
return Utils::SmallString::join({"CREATE ",
m_indexType == IndexType::Unique ? "UNIQUE " : "",
"INDEX IF NOT EXISTS index_",
+ kindName(),
+ "_",
m_tableName,
"_",
m_columnNames.join("_"),
@@ -65,6 +67,23 @@ public:
}
private:
+ std::string_view kindName() const
+ {
+ using namespace std::string_view_literals;
+
+ if (m_indexType == IndexType::Unique && m_condition.hasContent())
+ return "unique_partial"sv;
+
+ if (m_indexType == IndexType::Unique)
+ return "unique"sv;
+
+ if (m_condition.hasContent())
+ return "partial"sv;
+
+ return "normal"sv;
+ }
+
+private:
Utils::SmallString m_tableName;
Utils::SmallStringVector m_columnNames;
IndexType m_indexType;
diff --git a/src/libs/sqlite/sqlitetracing.cpp b/src/libs/sqlite/sqlitetracing.cpp
new file mode 100644
index 0000000000..700546f146
--- /dev/null
+++ b/src/libs/sqlite/sqlitetracing.cpp
@@ -0,0 +1,38 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "sqlitetracing.h"
+
+namespace Sqlite {
+
+TraceFile &traceFile()
+{
+ static TraceFile traceFile{"tracing.json"};
+
+ return traceFile;
+}
+
+namespace {
+
+thread_local NanotraceHR::EventQueue<NanotraceHR::StringViewWithStringArgumentsTraceEvent,
+ sqliteTracingStatus()>
+ eventQueue(traceFile());
+
+} // namespace
+
+NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()> &sqliteLowLevelCategory()
+{
+ thread_local NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()>
+ sqliteLowLevelCategory_{"sqlite low level"_t, eventQueue, sqliteLowLevelCategory};
+ return sqliteLowLevelCategory_;
+}
+
+NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()> &sqliteHighLevelCategory()
+{
+ thread_local NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()>
+ sqliteHighLevelCategory_{"sqlite high level"_t, eventQueue, sqliteHighLevelCategory};
+
+ return sqliteHighLevelCategory_;
+}
+
+} // namespace Sqlite
diff --git a/src/libs/sqlite/sqlitetracing.h b/src/libs/sqlite/sqlitetracing.h
new file mode 100644
index 0000000000..8dadc6de0d
--- /dev/null
+++ b/src/libs/sqlite/sqlitetracing.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "sqliteglobal.h"
+
+#include <nanotrace/nanotracehr.h>
+
+namespace Sqlite {
+using namespace NanotraceHR::Literals;
+
+constexpr NanotraceHR::Tracing sqliteTracingStatus()
+{
+#ifdef ENABLE_SQLITE_TRACING
+ return NanotraceHR::Tracing::IsEnabled;
+#else
+ return NanotraceHR::Tracing::IsDisabled;
+#endif
+}
+
+using TraceFile = NanotraceHR::TraceFile<sqliteTracingStatus()>;
+
+SQLITE_EXPORT TraceFile &traceFile();
+
+NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()> &sqliteLowLevelCategory();
+
+SQLITE_EXPORT NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()> &
+sqliteHighLevelCategory();
+
+} // namespace Sqlite
diff --git a/src/libs/sqlite/sqlitevalue.h b/src/libs/sqlite/sqlitevalue.h
index fe576f3fec..725682494b 100644
--- a/src/libs/sqlite/sqlitevalue.h
+++ b/src/libs/sqlite/sqlitevalue.h
@@ -6,6 +6,7 @@
#include "sqliteblob.h"
#include "sqliteexception.h"
+#include <nanotrace/nanotracehr.h>
#include <utils/smallstring.h>
#include <QVariant>
@@ -33,7 +34,7 @@ public:
explicit ValueBase(NullValue) {}
explicit ValueBase(VariantType &&value)
- : value(value)
+ : value(std::move(value))
{}
explicit ValueBase(const char *value)
@@ -43,6 +44,7 @@ public:
explicit ValueBase(long long value)
: value(value)
{}
+
explicit ValueBase(int value)
: value(static_cast<long long>(value))
{}
@@ -60,11 +62,6 @@ public:
{}
- explicit ValueBase(StringType &&value)
- : value(std::move(value))
-
- {}
-
explicit ValueBase(BlobView value)
: value(value)
@@ -229,14 +226,42 @@ public:
class ValueView : public ValueBase<Utils::SmallStringView, BlobView>
{
public:
+ ValueView() = default;
+
+ explicit ValueView(NullValue) {}
+
explicit ValueView(ValueBase &&base)
: ValueBase(std::move(base))
{}
+ explicit ValueView(Utils::SmallStringView value)
+ : ValueBase(value)
+ {}
+
+ explicit ValueView(BlobView value)
+ : ValueBase(value)
+ {}
+
+ explicit ValueView(long long value)
+ : ValueBase(value)
+ {}
+
+ explicit ValueView(int value)
+ : ValueBase(static_cast<long long>(value))
+ {}
+
+ explicit ValueView(uint value)
+ : ValueBase(static_cast<long long>(value))
+ {}
+
+ explicit ValueView(double value)
+ : ValueBase(value)
+ {}
+
template<typename Type>
static ValueView create(Type &&value_)
{
- return ValueView{ValueBase{value_}};
+ return ValueView(std::forward<Type>(value_));
}
};
@@ -386,4 +411,27 @@ private:
};
using Values = std::vector<Value>;
+
+template<typename String>
+void convertToString(String &string, const Value &value)
+{
+ switch (value.type()) {
+ case ValueType::Null:
+ convertToString(string, "null");
+ break;
+ case ValueType::Integer:
+ convertToString(string, value.toInteger());
+ break;
+ case ValueType::Float:
+ convertToString(string, value.toFloat());
+ break;
+ case ValueType::String:
+ convertToString(string, value.toStringView());
+ break;
+ case ValueType::Blob:
+ convertToString(string, "blob");
+ break;
+ }
+}
+
} // namespace Sqlite
diff --git a/src/libs/utils/smallstring.h b/src/libs/utils/smallstring.h
index c522a6cae9..e8c3d74b92 100644
--- a/src/libs/utils/smallstring.h
+++ b/src/libs/utils/smallstring.h
@@ -93,7 +93,7 @@ public:
static_cast<std::size_t>(std::distance(begin, end))}
{}
- template<typename Type, typename = std::enable_if_t<std::is_pointer<Type>::value>>
+ template<typename Type, typename std::enable_if_t<std::is_pointer<Type>::value, bool> = true>
BasicSmallString(Type characterPointer) noexcept
: BasicSmallString(characterPointer, std::char_traits<char>::length(characterPointer))
{
@@ -118,7 +118,7 @@ public:
template<typename BeginIterator,
typename EndIterator,
- typename = std::enable_if_t<std::is_same<BeginIterator, EndIterator>::value>>
+ typename std::enable_if_t<std::is_same<BeginIterator, EndIterator>::value, bool> = true>
BasicSmallString(BeginIterator begin, EndIterator end) noexcept
: BasicSmallString(&(*begin), size_type(end - begin))
{}
@@ -354,6 +354,14 @@ public:
return false;
}
+ bool startsWith(QStringView subStringToSearch) const noexcept
+ {
+ if (size() >= Utils::usize(subStringToSearch))
+ return subStringToSearch == QLatin1StringView{data(), subStringToSearch.size()};
+
+ return false;
+ }
+
bool startsWith(char characterToSearch) const noexcept
{
return data()[0] == characterToSearch;
@@ -423,13 +431,55 @@ public:
size_type oldSize = size();
size_type newSize = oldSize + string.size();
- reserve(optimalCapacity(newSize));
+ if (fitsNotInCapacity(newSize))
+ reserve(optimalCapacity(newSize));
+
std::char_traits<char>::copy(std::next(data(), static_cast<std::ptrdiff_t>(oldSize)),
string.data(),
string.size());
setSize(newSize);
}
+ void append(char character) noexcept
+ {
+ size_type oldSize = size();
+ size_type newSize = oldSize + 1;
+
+ if (fitsNotInCapacity(newSize))
+ reserve(optimalCapacity(newSize));
+
+ auto current = std::next(data(), static_cast<std::ptrdiff_t>(oldSize));
+ *current = character;
+ setSize(newSize);
+ }
+
+ template<typename Type, typename std::enable_if_t<std::is_arithmetic_v<Type>, bool> = true>
+ void append(Type number)
+ {
+#if defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L)
+ // 2 bytes for the sign and because digits10 returns the floor
+ char buffer[std::numeric_limits<Type>::digits10 + 2];
+ auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
+ auto endOfConversionString = result.ptr;
+
+ append({buffer, endOfConversionString});
+#else
+ if constexpr (std::is_floating_point_v<Type>) {
+ QLocale locale{QLocale::Language::C};
+ append(locale.toString(number));
+ return;
+ } else {
+ // 2 bytes for the sign and because digits10 returns the floor
+ char buffer[std::numeric_limits<Type>::digits10 + 2];
+ auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
+ auto endOfConversionString = result.ptr;
+
+ append({buffer, endOfConversionString});
+ }
+
+#endif
+ }
+
void append(QStringView string) noexcept
{
QStringEncoder encoder{QStringEncoder::Utf8};
@@ -469,6 +519,13 @@ public:
return *this;
}
+ BasicSmallString &operator+=(char character) noexcept
+ {
+ append(character);
+
+ return *this;
+ }
+
BasicSmallString &operator+=(QStringView string) noexcept
{
append(string);
@@ -476,6 +533,14 @@ public:
return *this;
}
+ template<typename Type, typename std::enable_if_t<std::is_arithmetic_v<Type>, bool> = true>
+ BasicSmallString &operator+=(Type number) noexcept
+ {
+ append(number);
+
+ return *this;
+ }
+
BasicSmallString &operator+=(std::initializer_list<SmallStringView> list) noexcept
{
appendInitializerList(list, size());
@@ -580,37 +645,12 @@ public:
return joinedString;
}
- static
- BasicSmallString number(int number)
+ template<typename Type, typename std::enable_if_t<std::is_arithmetic_v<Type>, bool> = true>
+ static BasicSmallString number(Type number)
{
- // 2 bytes for the sign and because digits10 returns the floor
- char buffer[std::numeric_limits<int>::digits10 + 2];
- auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
- auto endOfConversionString = result.ptr;
- return BasicSmallString(buffer, endOfConversionString);
- }
-
- static BasicSmallString number(long long int number) noexcept
- {
- // 2 bytes for the sign and because digits10 returns the floor
- char buffer[std::numeric_limits<long long int>::digits10 + 2];
- auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
- auto endOfConversionString = result.ptr;
- return BasicSmallString(buffer, endOfConversionString);
- }
-
- static BasicSmallString number(double number) noexcept
- {
-#if defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L)
- // 2 bytes for the sign and because digits10 returns the floor
- char buffer[std::numeric_limits<double>::digits10 + 2];
- auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
- auto endOfConversionString = result.ptr;
- return BasicSmallString(buffer, endOfConversionString);
-#else
- QLocale locale{QLocale::Language::C};
- return BasicSmallString{locale.toString(number)};
-#endif
+ BasicSmallString string;
+ string.append(number);
+ return string;
}
char &operator[](std::size_t index) noexcept { return *(data() + index); }
@@ -655,7 +695,6 @@ public:
friend BasicSmallString operator+(const BasicSmallString &first,
const char (&second)[ArraySize]) noexcept
{
-
return operator+(first, SmallStringView(second));
}
@@ -687,8 +726,10 @@ unittest_public:
bool fitsNotInCapacity(size_type capacity) const noexcept
{
- return (isShortString() && capacity > shortStringCapacity())
- || (!isShortString() && capacity > m_data.reference.capacity);
+ if (isShortString())
+ return capacity > shortStringCapacity();
+
+ return capacity > m_data.reference.capacity;
}
static size_type optimalHeapCapacity(const size_type size) noexcept